initial project
This commit is contained in:
@@ -0,0 +1,689 @@
|
||||
<?
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\CartItems;
|
||||
use App\Models\MenuItemVariations;
|
||||
use App\Models\DeliverySettingModel;
|
||||
use App\Models\CartItemOptions;
|
||||
use App\Models\Carts;
|
||||
use App\Models\Customer;
|
||||
use App\Models\CustomerVoucherList;
|
||||
use App\Models\MenuItems;
|
||||
use App\Models\MenuImages;
|
||||
use App\Models\OutletMenus;
|
||||
use App\Models\OutletTax;
|
||||
use App\Models\StoreDiscount;
|
||||
use App\Models\SettingTax;
|
||||
use App\Models\Outlet;
|
||||
use App\Services\PromoService;
|
||||
use App\Models\PromoCode;
|
||||
use App\Libraries\Lalamove;
|
||||
use App\Models\PwpModel;
|
||||
|
||||
use Exception;
|
||||
use CodeIgniter\Database\Config;
|
||||
|
||||
|
||||
helper('general');
|
||||
class CalculateService
|
||||
{
|
||||
private $cart_items;
|
||||
private $variations;
|
||||
private $options;
|
||||
private $carts;
|
||||
private $menu_items;
|
||||
private $menu_images;
|
||||
private $outlet_menus;
|
||||
private $outlet_tax;
|
||||
private $setting_tax;
|
||||
private $cart_item_options;
|
||||
private $customer_voucher_list;
|
||||
private $promo_service;
|
||||
private $promo_codes;
|
||||
private $pwp;
|
||||
private $outlet;
|
||||
private $store_discount;
|
||||
private $customer;
|
||||
private $setting_delivery_fee;
|
||||
public function __construct(PromoService $promoService)
|
||||
{
|
||||
$this->cart_items = new CartItems();
|
||||
$this->variations = new MenuItemVariations();
|
||||
$this->options = new CartItemOptions();
|
||||
$this->carts = new Carts();
|
||||
$this->menu_items = new MenuItems();
|
||||
$this->menu_images = new MenuImages();
|
||||
$this->outlet_menus = new OutletMenus();
|
||||
$this->outlet_tax = new OutletTax();
|
||||
$this->setting_tax = new SettingTax();
|
||||
$this->cart_item_options = new CartItemOptions();
|
||||
$this->promo_service = $promoService;
|
||||
$this->promo_codes = new PromoCode();
|
||||
$this->customer_voucher_list = new CustomerVoucherList();
|
||||
$this->outlet = new Outlet();
|
||||
$this->store_discount = new StoreDiscount();
|
||||
$this->customer = new Customer();
|
||||
$this->pwp = new PwpModel();
|
||||
$this->setting_delivery_fee = new DeliverySettingModel();
|
||||
helper('order_helper');
|
||||
}
|
||||
|
||||
private function getOriginalItemPrice($menu_item_id)
|
||||
{
|
||||
$menu_item = $this->menu_items->find($menu_item_id);
|
||||
return $menu_item ? $menu_item['price'] : 0;
|
||||
}
|
||||
|
||||
private function checkItemPwpDiscount($cart_id)
|
||||
{
|
||||
$cart = $this->carts->find($cart_id);
|
||||
$cart_items = $this->cart_items->where('cart_id', $cart_id)->findAll();
|
||||
// print_r($cart_items);exit;
|
||||
$pwp_promos = $this->pwp->where('deleted_at', null)->orderBy('order_index', 'ASC')->findAll();
|
||||
$total_qualify = 0;
|
||||
foreach ($pwp_promos as $promo) {
|
||||
if($promo['mode'] == 'selected_item'){
|
||||
$requiredIds = explode(',', $promo['pwp_item_id']);
|
||||
foreach($cart_items as $item){
|
||||
if(in_array($item['menu_item_id'], $requiredIds)){
|
||||
// echo(123);exit;
|
||||
if($promo['amount_type'] == 'amount'){
|
||||
$total_qualify+=($item['quantity'] * $item['unit_price']);
|
||||
}else{
|
||||
$total_qualify+=$item['quantity'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if($promo['amount_type'] == 'amount'){
|
||||
$total_qualify+= $cart['grand_total'];
|
||||
}else{
|
||||
$total_qualify+= $cart['item_count'];
|
||||
}
|
||||
}
|
||||
// echo($total_qualify);exit;
|
||||
|
||||
if($total_qualify >= $promo['amount']){
|
||||
return [
|
||||
'has_discount' => true,
|
||||
'pwp_item' => explode(',', $promo['selected_item']),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return ['has_discount' => false];
|
||||
}
|
||||
|
||||
public function calculateCartTotals($cart_id, $outlet_id, $selected_date = null, $selected_time = null, $latitude = null, $longitude = null, $order_type = null, $address = null, $promo_or_voucher = null)
|
||||
{
|
||||
$cart = $this->carts->find($cart_id);
|
||||
|
||||
if ($selected_date == null && $selected_time == null) {
|
||||
$selected_date = $cart['selected_date'];
|
||||
$selected_time = $cart['selected_time'];
|
||||
}
|
||||
if ($order_type == null) {
|
||||
$order_type = $cart['order_type'];
|
||||
}
|
||||
if ($address == null) {
|
||||
$address = $cart['address'];
|
||||
}
|
||||
|
||||
if ($latitude == null && $longitude == null) {
|
||||
$latitude = $cart['latitude'];
|
||||
$longitude = $cart['longitude'];
|
||||
}
|
||||
|
||||
$cart_items = $this->cart_items->where('cart_id', $cart_id)->findAll();
|
||||
$customer_id = $cart['customer_id'];
|
||||
$customer = $this->customer->where('id', $customer_id)->first();
|
||||
$customer_tier = $customer['customer_tier_id'] ?? 0;
|
||||
|
||||
$subtotal = $subtotal_without_options = 0;
|
||||
$total_discount = $promo_discount_amount = 0;
|
||||
$total_tax = 0;
|
||||
$invalid_items = [];
|
||||
$promo_code_id = $cart['promo_code_id'];
|
||||
$free_item_list = [];
|
||||
$promo_code = '';
|
||||
$voucher_code = '';
|
||||
$packaging_charge = 0;
|
||||
$array_discount = [];
|
||||
$store_discount = $this->store_discount->where("FIND_IN_SET($outlet_id, outlet_list)")->where("FIND_IN_SET($customer_tier, tier_id_list)")->findAll();
|
||||
// print_r($cart_items);exit;
|
||||
foreach ($cart_items as $item) {
|
||||
$is_valid = true;
|
||||
if ($outlet_id) {
|
||||
$menu_available = $this->outlet_menus->where([
|
||||
'menu_item_id' => $item['menu_item_id'],
|
||||
'outlet_id' => $outlet_id
|
||||
])->first();
|
||||
|
||||
if (!$menu_available) {
|
||||
$is_valid = false;
|
||||
$invalid_items[] = $item['id'];
|
||||
$this->options->where('cart_item_id', $item['id'])->delete();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$unit_price = $item['unit_price'];
|
||||
$title = $item['title'];
|
||||
$variation_price = 0;
|
||||
$packaging_price = 0;
|
||||
if (!empty($item['variation_id']) && $item['variation_id'] > 0) {
|
||||
$menu_item = $this->menu_items->find($item['menu_item_id']);
|
||||
$variation = $this->variations->find($item['variation_id']);
|
||||
if ($variation) {
|
||||
$unit_price = $variation['price'];
|
||||
$packaging_price = $menu_item['packaging_price'] ?? 0;
|
||||
$packaging_charge = $packaging_price * $item['quantity'];
|
||||
}
|
||||
} else {
|
||||
$menu_item = $this->menu_items->find($item['menu_item_id']);
|
||||
if ($menu_item) {
|
||||
$unit_price = $item['is_pwp'] == true ? $menu_item['pwp_price'] : $menu_item['price'];
|
||||
$title = $menu_item['title'];
|
||||
$packaging_price = $menu_item['packaging_price'] ?? 0;
|
||||
$packaging_charge = $packaging_price * $item['quantity'];
|
||||
}
|
||||
}
|
||||
|
||||
if(is_array($store_discount)){
|
||||
foreach($store_discount as $discount){
|
||||
$menu_item_list = explode(',', $discount['menu_item_list']);
|
||||
if(in_array($item['menu_item_id'], $menu_item_list)){
|
||||
if($discount['discount_type'] == 'percentage'){
|
||||
$total_discount += ($unit_price * $item['quantity']) * ($discount['discount_value'] / 100);
|
||||
if(!array_key_exists($discount['id'], $array_discount)){
|
||||
$array_discount[$discount['id']] = [
|
||||
'discount_name' => $discount['discount_name'],
|
||||
'discount_type' => $discount['discount_type'],
|
||||
'discount_value' => $discount['discount_value'],
|
||||
'discount_amount' => number_format_no_round(($unit_price * $item['quantity']) * ($discount['discount_value'] / 100))
|
||||
];
|
||||
}else{
|
||||
$array_discount[$discount['id']]['discount_amount'] += number_format_no_round(($unit_price * $item['quantity']) * ($discount['discount_value'] / 100));
|
||||
}
|
||||
}else{
|
||||
if(!array_key_exists($discount['id'], $array_discount)){
|
||||
$total_discount = $discount['discount_value'];
|
||||
$array_discount[$discount['id']] = [
|
||||
'discount_name' => $discount['discount_name'],
|
||||
'discount_type' => $discount['discount_type'],
|
||||
'discount_value' => $discount['discount_value'],
|
||||
'discount_amount' => number_format_no_round(($unit_price * $item['quantity']) * ($discount['discount_value'] / 100))
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$item_subtotal = ($unit_price) * $item['quantity'];
|
||||
$item_subtotal_without_options = $item_subtotal;
|
||||
|
||||
$options = $this->options->where('cart_item_id', $item['id'])->findAll();
|
||||
$options_total = array_sum(array_column($options, 'price_adjustment'));
|
||||
$item_subtotal += $options_total;
|
||||
|
||||
if (($item['unit_price'] != $unit_price || $item['title'] != $title) && (($item['is_free_item'] == false || $item['is_free_item'] == '1') && $item['is_pwp'] == false)) {
|
||||
$this->cart_items->update($item['id'], [
|
||||
'unit_price' => $unit_price,
|
||||
'title' => $title,
|
||||
'line_subtotal' => $item_subtotal
|
||||
]);
|
||||
}
|
||||
|
||||
if ($item['is_free_item'] == true || $item['is_free_item'] == '1') {
|
||||
$item_subtotal = 0;
|
||||
}
|
||||
|
||||
if($item['is_pwp'] == true && ($item['unit_price'] != $unit_price || $item['title'] != $title)){
|
||||
// $item_subtotal = $item['pwp_price'] * $item['quantity'];
|
||||
$this->cart_items->update($item['id'], [
|
||||
'unit_price' => $unit_price,
|
||||
'title' => $item['title'],
|
||||
'line_subtotal' => $item_subtotal
|
||||
]);
|
||||
}
|
||||
|
||||
if ($is_valid && $item['is_free_item'] != true && $item['is_free_item'] != '1') {
|
||||
$subtotal += $item_subtotal;
|
||||
$subtotal_without_options += $item_subtotal_without_options;
|
||||
}
|
||||
}
|
||||
|
||||
if($subtotal < $total_discount){
|
||||
$total_discount = $subtotal;
|
||||
}
|
||||
|
||||
if (!empty($invalid_items)) {
|
||||
$this->cart_items->whereIn('id', $invalid_items)->delete();
|
||||
$cart_items = $this->cart_items->where('cart_id', $cart_id)->findAll();
|
||||
}
|
||||
|
||||
// Get cart items with options (after potential deletions)
|
||||
$final_cart_items = $this->cart_items
|
||||
->where('cart_id', $cart_id)
|
||||
->where('deleted_at', null)
|
||||
->findAll();
|
||||
|
||||
foreach ($final_cart_items as &$item) {
|
||||
$item['variation'] = $this->variations->find($item['variation_id']);
|
||||
$item['options'] = $this->options
|
||||
->where('cart_item_id', $item['id'])
|
||||
->where('deleted_at', null)
|
||||
->findAll();
|
||||
if ($item['variation_id'] > 0) {
|
||||
$item['image'] = $item['variation']['images'];
|
||||
} else {
|
||||
$image = $this->menu_images->where('menu_item_id', $item['menu_item_id'])->orderBy('order_index', 'ASC')->first();
|
||||
if ($image) {
|
||||
$item['image'] = 'https://icom.ipsgroup.com.my/backend/uploads/menu_images/'.$image['image_url'];
|
||||
} else {
|
||||
$item['image'] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//check store discount
|
||||
// $store_discount = $this->store_discount->where("FIND_IN_SET($outlet_id, outlet_list)")->where("FIND_IN_SET($order_type, order_type)")->findAll();
|
||||
|
||||
// print_r($final_cart_items);exit;
|
||||
|
||||
$quotation_id = null;
|
||||
$delivery_fee = 0;
|
||||
$delivery_service = null;
|
||||
|
||||
if ($order_type == 'delivery' && $latitude && $longitude && $address) {
|
||||
$outlet_data = $this->outlet->where('id', $outlet_id)->first();
|
||||
$delivery_options = explode(',', $outlet_data['delivery_options'] ?? '');
|
||||
|
||||
// Lalamove
|
||||
if (in_array('Lalamove', $delivery_options)) {
|
||||
try {
|
||||
$lalamove = new Lalamove();
|
||||
$delivery_response = $lalamove->requestQuotation(
|
||||
$selected_date,
|
||||
$selected_time,
|
||||
$subtotal,
|
||||
$outlet_id,
|
||||
$latitude,
|
||||
$longitude,
|
||||
$address
|
||||
);
|
||||
if (isset($delivery_response['data'])) {
|
||||
$quotation_id = $delivery_response['data']['quotationId'];
|
||||
$delivery_fee = $delivery_response['data']['priceBreakdown']['total'];
|
||||
$delivery_service = 'Lalamove';
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
log_message('error', 'Lalamove quotation failed: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Grab
|
||||
if (!$quotation_id && in_array('Grab Express', $delivery_options)) {
|
||||
try {
|
||||
$grab = new \App\Libraries\Grab();
|
||||
$delivery_response = $grab->requestQuotation(
|
||||
$selected_date,
|
||||
$selected_time,
|
||||
$subtotal,
|
||||
$outlet_id,
|
||||
$latitude,
|
||||
$longitude,
|
||||
$address
|
||||
);
|
||||
if (!empty($delivery_response['quotes'])) {
|
||||
$quotation_id = time();
|
||||
$delivery_fee = $delivery_response['quotes'][0]['amount'] ?? 0;
|
||||
$delivery_service = 'Grab';
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
log_message('error', 'Grab quotation failed: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Manual fallback
|
||||
if (!$quotation_id) {
|
||||
$distance = $this->calculateDistance($outlet_id, $latitude, $longitude);
|
||||
$delivery_fee = $this->calculateDeliveryFee($distance);
|
||||
}
|
||||
}
|
||||
|
||||
if ($cart['promo_code_id'] > 0 || $cart['customer_voucher_list_id'] > 0) {
|
||||
$promo_setting_id = 0;
|
||||
|
||||
//check promo
|
||||
if ($cart['promo_code_id'] > 0) {
|
||||
$promo_data = $this->promo_codes->find($cart['promo_code_id']);
|
||||
$promo_code = $promo_data['code'];
|
||||
$promo_setting_id = $promo_data['promo_setting_id'];
|
||||
} else if ($cart['customer_voucher_list_id'] > 0) {
|
||||
$voucher_data = $this->customer_voucher_list->where('id', $cart['customer_voucher_list_id'])->first();
|
||||
$voucher_code = $voucher_data['voucher_code'];
|
||||
$promo_setting_id = $voucher_data['promo_setting_id'];
|
||||
}
|
||||
// print_r($final_cart_items);exit;
|
||||
$promo = $this->promo_service->checkPromoLogic($promo_setting_id, $final_cart_items, number_format($subtotal_without_options, 2), $cart['customer_id'], $outlet_id, $order_type, $delivery_fee);
|
||||
|
||||
if (isset($promo['status']) && $promo['status'] == 400) {
|
||||
|
||||
//remove from cart
|
||||
$result = $this->resetVoucher($cart_id);
|
||||
// print_r($promo);
|
||||
// exit;
|
||||
return $promo;
|
||||
}
|
||||
|
||||
if (isset($promo['status']) && $promo['status'] == 200) {
|
||||
// print_r($promo);exit;
|
||||
if ($promo['delivery_fee'] > 0) {
|
||||
$promo_discount_amount += $promo['delivery_fee'];
|
||||
}
|
||||
|
||||
if ($promo['promo_discount_total'] > 0) {
|
||||
$promo_discount_amount += $promo['promo_discount_total'];
|
||||
}
|
||||
|
||||
$free_item_list = $promo['free_item_list'];
|
||||
}
|
||||
}
|
||||
|
||||
$tax_detail = [];
|
||||
|
||||
$grand_total = $subtotal + $packaging_charge - $total_discount - $promo_discount_amount;
|
||||
|
||||
if($grand_total < 0){
|
||||
$grand_total = 0;
|
||||
}
|
||||
|
||||
$tax_array = $this->setting_tax->where("FIND_IN_SET($outlet_id, outlet_id)")->where("FIND_IN_SET('" . $order_type . "', order_type)")->findAll();
|
||||
|
||||
foreach ($tax_array as $tax) {
|
||||
$add_tax = $grand_total * ($tax['tax_rate'] / 100);
|
||||
$total_tax += $add_tax;
|
||||
$tax_detail[] = [
|
||||
'tax_type' => $tax['tax_type'],
|
||||
'tax_rate' => $tax['tax_rate'],
|
||||
'tax_amount' => number_format_no_round($add_tax)
|
||||
];
|
||||
}
|
||||
|
||||
//add tax
|
||||
// echo $total_tax;exit;
|
||||
$grand_total += $total_tax;
|
||||
$grand_total = number_format_no_round($grand_total);
|
||||
// echo($grand_total);exit;
|
||||
$rounding_amount = calculateRoundingAmount($grand_total);
|
||||
// echo $rounding_amount;exit;
|
||||
$grand_total = $grand_total + $rounding_amount;
|
||||
// echo $grand_total;exit;
|
||||
//calculate rounding amount
|
||||
//call delivery service api if delivery
|
||||
// print_r($final_cart_items);exit;
|
||||
// Update cart totals in database
|
||||
$this->carts->update($cart_id, [
|
||||
'item_count' => count($cart_items),
|
||||
'subtotal_amount' => number_format($subtotal, 2),
|
||||
'discount_amount' => number_format($total_discount, 2),
|
||||
'tax_amount' => number_format($total_tax, 2),
|
||||
'grand_total' => number_format($grand_total, 2),
|
||||
'rounding_amount' => number_format($rounding_amount, 2),
|
||||
'promo_discount_amount' => $promo_or_voucher == 'promo' ? number_format($promo_discount_amount, 2) : 0.00,
|
||||
'voucher_discount_amount' => $promo_or_voucher == 'voucher' ? number_format($promo_discount_amount, 2) : 0.00,
|
||||
'delivery_fee' => number_format($delivery_fee, 2),
|
||||
'selected_date' => $selected_date,
|
||||
'selected_time' => $selected_time,
|
||||
'latitude' => $latitude,
|
||||
'longitude' => $longitude,
|
||||
'order_type' => $order_type,
|
||||
'address' => $address,
|
||||
'lalamove_quot_id' => $delivery_service === 'Lalamove' ? $quotation_id : null,
|
||||
'grab_quot_id' => $delivery_service === 'Grab' ? $quotation_id : null
|
||||
]);
|
||||
|
||||
// print_r($final_cart_items);exit;
|
||||
//check pwp
|
||||
$pwp_menu = [];
|
||||
$pwp_discount = $this->checkItemPwpDiscount($cart_id);
|
||||
// print_r($final_cart_items);exit;
|
||||
if($pwp_discount['has_discount']){
|
||||
$pwp_items = $pwp_discount['pwp_item'];
|
||||
foreach($pwp_items as $pwp_item){
|
||||
$pwp_menu_item = $this->menu_items->where('id', $pwp_item)->first();
|
||||
if(isset($pwp_menu_item['pwp_price'])){
|
||||
if($pwp_menu_item['pwp_price'] > 0){
|
||||
$pwp_menu[] = $pwp_menu_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// print_r($pwp_menu);exit;
|
||||
// Return the complete structure
|
||||
return [
|
||||
'status' => 200,
|
||||
'message' => 'Cart retrieved successfully.',
|
||||
'data' => [
|
||||
'id' => $cart['id'],
|
||||
'customer_id' => $cart['customer_id'],
|
||||
'outlet_id' => $cart['outlet_id'],
|
||||
'status' => $cart['status'],
|
||||
'order_summary' => [
|
||||
'item_count' => count($final_cart_items),
|
||||
'subtotal_amount' => number_format($subtotal, 2),
|
||||
'discount_amount' => number_format($total_discount, 2),
|
||||
'tax_amount' => number_format($total_tax, 2),
|
||||
'delivery_fee' => number_format($delivery_fee, 2),
|
||||
'packaging_charge' => number_format($packaging_charge, 2),
|
||||
// 'packaging_charge' => "123",
|
||||
'grand_total' => number_format($grand_total, 2),
|
||||
'store_discount' => $array_discount,
|
||||
'store_discount_amount' => number_format($total_discount, 2),
|
||||
'grand_total_without_rounding' => number_format($grand_total - $rounding_amount, 2),
|
||||
'rounding_amount' => number_format($rounding_amount, 2),
|
||||
'promo_code_id' => $promo_code_id ?? 0,
|
||||
'promo_discount_amount' => $promo_or_voucher == 'promo' ? number_format($promo_discount_amount, 2) : 0.00,
|
||||
'promo_code' => $promo_code,
|
||||
'customer_voucher_list_id' => $cart['customer_voucher_list_id'],
|
||||
'voucher_discount_amount' => $promo_or_voucher == 'voucher' ? number_format($promo_discount_amount, 2) : 0.00,
|
||||
'voucher_code' => $voucher_code,
|
||||
'selected_date' => $selected_date,
|
||||
'selected_time' => $selected_time,
|
||||
'latitude' => $latitude,
|
||||
'longitude' => $longitude,
|
||||
'order_type' => $order_type,
|
||||
'address' => $address
|
||||
],
|
||||
'tax_detail' => $tax_detail,
|
||||
'invalid_items' => $invalid_items,
|
||||
'items' => $final_cart_items,
|
||||
'free_item_list' => $free_item_list,
|
||||
'pwp_menu' => $pwp_menu
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function resetVoucher($cart_id)
|
||||
{
|
||||
// Reset promo and voucher fields
|
||||
$this->carts->update($cart_id, [
|
||||
'promo_code_id' => 0,
|
||||
'promo_discount_amount' => 0,
|
||||
'customer_voucher_list_id' => 0,
|
||||
'voucher_discount_amount' => 0
|
||||
]);
|
||||
|
||||
// Get free items from cart
|
||||
$freeItems = $this->cart_items
|
||||
->select('id')
|
||||
->where('cart_id', $cart_id)
|
||||
->where('is_free_item', 1)
|
||||
->findAll();
|
||||
|
||||
if (!empty($freeItems)) {
|
||||
// Collect all item IDs
|
||||
$itemIds = array_column($freeItems, 'id');
|
||||
|
||||
// Delete all related item options in one query
|
||||
$this->cart_item_options->whereIn('cart_item_id', $itemIds)->delete();
|
||||
|
||||
// Delete all free items in one query
|
||||
$this->cart_items->whereIn('id', $itemIds)->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate distance from outlet to customer using Google Distance Matrix API
|
||||
* @param int $outlet_id
|
||||
* @param float $latitude
|
||||
* @param float $longitude
|
||||
* @return float Distance in kilometers
|
||||
*/
|
||||
public function calculateDistance($outlet_id, $latitude, $longitude)
|
||||
{
|
||||
try {
|
||||
// Validate input parameters
|
||||
if (empty($outlet_id) || !is_numeric($outlet_id)) {
|
||||
log_message('error', 'CalculateService: Invalid outlet ID provided: ' . $outlet_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!is_numeric($latitude) || !is_numeric($longitude)) {
|
||||
log_message('error', 'CalculateService: Invalid coordinates provided - lat: ' . $latitude . ', lng: ' . $longitude);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Validate coordinate ranges
|
||||
if ($latitude < -90 || $latitude > 90 || $longitude < -180 || $longitude > 180) {
|
||||
log_message('error', 'CalculateService: Coordinates out of valid range - lat: ' . $latitude . ', lng: ' . $longitude);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get outlet coordinates
|
||||
$outlet = $this->outlet->find($outlet_id);
|
||||
if (!$outlet || !isset($outlet['latitude']) || !isset($outlet['longitude'])) {
|
||||
log_message('error', 'CalculateService: Outlet not found or missing coordinates for outlet ID: ' . $outlet_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
$outlet_lat = $outlet['latitude'];
|
||||
$outlet_lng = $outlet['longitude'];
|
||||
|
||||
// Check if we have valid outlet coordinates
|
||||
if (!is_numeric($outlet_lat) || !is_numeric($outlet_lng)) {
|
||||
log_message('error', 'CalculateService: Invalid outlet coordinates - lat: ' . $outlet_lat . ', lng: ' . $outlet_lng);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if Google API key is configured
|
||||
if (empty(GOOGLE_DISTANCE_MATRIX_API_KEY) || GOOGLE_DISTANCE_MATRIX_API_KEY === 'YOUR_GOOGLE_API_KEY_HERE') {
|
||||
log_message('error', 'CalculateService: Google Distance Matrix API key not configured');
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Google Distance Matrix API endpoint
|
||||
$api_url = 'https://maps.googleapis.com/maps/api/distancematrix/json';
|
||||
|
||||
// Build the request URL
|
||||
$params = [
|
||||
'origins' => $outlet_lat . ',' . $outlet_lng,
|
||||
'destinations' => $latitude . ',' . $longitude,
|
||||
'mode' => 'driving',
|
||||
'units' => 'metric',
|
||||
'key' => GOOGLE_DISTANCE_MATRIX_API_KEY
|
||||
];
|
||||
|
||||
$url = $api_url . '?' . http_build_query($params);
|
||||
|
||||
log_message('info', 'CalculateService: Requesting distance from Google API - Outlet: ' . $outlet_id . ', Customer: ' . $latitude . ',' . $longitude);
|
||||
|
||||
// Make the API request using the helper function
|
||||
$response = send_api_request('GET', $url, [], null, true);
|
||||
|
||||
if ($response === false) {
|
||||
log_message('error', 'CalculateService: Failed to get response from Google Distance Matrix API');
|
||||
return 0;
|
||||
}
|
||||
|
||||
$data = $response;
|
||||
|
||||
// Check for API errors
|
||||
if ($data['status'] !== 'OK') {
|
||||
$error_message = $data['error_message'] ?? $data['status'] ?? 'Unknown error';
|
||||
log_message('error', 'CalculateService: Google Distance Matrix API error: ' . $error_message);
|
||||
|
||||
// Log additional error details if available
|
||||
if (isset($data['error_message'])) {
|
||||
log_message('error', 'CalculateService: Google API Error Message: ' . $data['error_message']);
|
||||
}
|
||||
if (isset($data['status'])) {
|
||||
log_message('error', 'CalculateService: Google API Status: ' . $data['status']);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Extract distance from response
|
||||
if (isset($data['rows'][0]['elements'][0]['distance']['value'])) {
|
||||
$distance_meters = $data['rows'][0]['elements'][0]['distance']['value'];
|
||||
$distance_km = $distance_meters / 1000;
|
||||
|
||||
log_message('info', 'CalculateService: Distance calculated successfully - Outlet: ' . $outlet_id . ', Distance: ' . $distance_km . ' km');
|
||||
|
||||
return round($distance_km, 2);
|
||||
} else {
|
||||
// Check if the route is not found
|
||||
if (isset($data['rows'][0]['elements'][0]['status']) && $data['rows'][0]['elements'][0]['status'] === 'NOT_FOUND') {
|
||||
log_message('warning', 'CalculateService: Route not found between outlet and customer location');
|
||||
} else {
|
||||
log_message('warning', 'CalculateService: No distance data in Google API response');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
log_message('error', 'CalculateService: Exception in calculateDistance: ' . $e->getMessage());
|
||||
log_message('error', 'CalculateService: Stack trace: ' . $e->getTraceAsString());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate delivery fee based on distance
|
||||
* @param float $distance Distance in kilometers
|
||||
* @return float Delivery fee in RM
|
||||
*/
|
||||
public function calculateDeliveryFee($distance)
|
||||
{
|
||||
// Validate input
|
||||
if (!is_numeric($distance) || $distance < 0) {
|
||||
log_message('warning', 'CalculateService: Invalid distance provided for fee calculation: ' . $distance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($distance == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Delivery fee structure based on distance
|
||||
$fee = 0;
|
||||
|
||||
//get setting delivery fee
|
||||
$setting_delivery_fee = $this->setting_delivery_fee->findAll();
|
||||
foreach($setting_delivery_fee as $setting){
|
||||
if($distance >= $setting['start_km'] && $distance <= $setting['end_km']){
|
||||
$fee = $setting['price_per_km'];
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure fee is a valid number and round to 2 decimal places
|
||||
$fee = round($fee, 2);
|
||||
|
||||
log_message('info', 'CalculateService: Delivery fee calculated - Distance: ' . $distance . ' km, Fee: RM ' . $fee);
|
||||
|
||||
return $fee;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,426 @@
|
||||
<?
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\CartItems;
|
||||
use App\Models\MenuItemVariations;
|
||||
use App\Models\CartItemOptions;
|
||||
use App\Models\Carts;
|
||||
use App\Models\MenuItems;
|
||||
use App\Models\OutletMenus;
|
||||
use App\Models\OutletTax;
|
||||
use App\Models\SettingTax;
|
||||
use CodeIgniter\Database\Config;
|
||||
use App\Models\MenuItemOptionsGroups;
|
||||
use App\Models\OptionsGroups;
|
||||
use App\Models\VariationOptionsGroups;
|
||||
use App\Services\PromoService;
|
||||
use App\Models\Options;
|
||||
|
||||
class CartService
|
||||
{
|
||||
private $db;
|
||||
private $cart_items;
|
||||
private $variations;
|
||||
private $options;
|
||||
private $carts;
|
||||
private $menu_items;
|
||||
private $outlet_menus;
|
||||
private $outlet_tax;
|
||||
private $setting_tax;
|
||||
private $cart_item_options;
|
||||
private $menu_item_options_groups;
|
||||
private $option_groups;
|
||||
private $menu_item_variations;
|
||||
private $variation_options_groups;
|
||||
|
||||
private $promo_service;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->cart_items = new CartItems();
|
||||
$this->variations = new MenuItemVariations();
|
||||
$this->options = new Options();
|
||||
$this->carts = new Carts();
|
||||
$this->menu_items = new MenuItems();
|
||||
$this->outlet_menus = new OutletMenus();
|
||||
$this->outlet_tax = new OutletTax();
|
||||
$this->setting_tax = new SettingTax();
|
||||
$this->cart_item_options = new CartItemOptions();
|
||||
$this->menu_item_options_groups = new MenuItemOptionsGroups();
|
||||
$this->option_groups = new OptionsGroups();
|
||||
$this->menu_item_variations = new MenuItemVariations();
|
||||
$this->variation_options_groups = new VariationOptionsGroups();
|
||||
|
||||
$this->db = Config::connect();
|
||||
|
||||
}
|
||||
|
||||
public function addCartItem($customer_id, $outlet_id, $existing_cart, $menu_item_id, $variation_id, $option_group, $quantity, $type = null) {
|
||||
// If no cart, create one
|
||||
if (!$existing_cart) {
|
||||
$cartData = [
|
||||
'customer_id' => $customer_id,
|
||||
'outlet_id' => $outlet_id,
|
||||
'status' => 'active',
|
||||
'item_count' => 0,
|
||||
'subtotal_amount' => 0,
|
||||
'discount_amount' => 0,
|
||||
'tax_amount' => 0,
|
||||
'grand_total' => 0,
|
||||
'promo_code_id' => 0,
|
||||
'promo_discount_amount' => 0,
|
||||
'customer_voucher_list_id' => 0,
|
||||
'voucher_discount_amount' => 0,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
$cartId = $this->carts->insert($cartData);
|
||||
if (!$cartId) {
|
||||
return ['status' => 400, 'message' => "Failed to create cart."];
|
||||
}
|
||||
|
||||
$existing_cart = $this->carts->find($cartId);
|
||||
}
|
||||
$cart = $existing_cart;
|
||||
|
||||
$this->db->transStart();
|
||||
try {
|
||||
|
||||
//check outlet menu
|
||||
$menuAvailable = $this->outlet_menus->where([
|
||||
'menu_item_id' => $menu_item_id,
|
||||
'outlet_id' => $outlet_id
|
||||
])->first();
|
||||
|
||||
if (!$menuAvailable) {
|
||||
return ['status' => 400, 'message' => 'This menu is not available for this outlet!'];
|
||||
}
|
||||
|
||||
$menu_variation = $this->menu_item_variations->where('menu_item_id', $menu_item_id)->findAll();
|
||||
|
||||
if(($variation_id == 0 || $variation_id == null) && !empty($menu_variation)){ //make variation required
|
||||
return ['status' => 400, 'message' => 'Please select a variation!'];
|
||||
}
|
||||
|
||||
$required_groups = $this->getRequiredOptionGroups($menu_item_id, $variation_id);
|
||||
|
||||
// Validate that required options are provided
|
||||
if (!empty($required_groups)) {
|
||||
if (empty($option_group)) {
|
||||
return ['status' => 400, 'message' => 'Required options are missing'];
|
||||
}
|
||||
|
||||
$provided_group_ids = array_map(function ($opt) {
|
||||
if (!is_object($opt)) {
|
||||
$opt = (object) $opt;
|
||||
}
|
||||
return $opt->group_id;
|
||||
}, $option_group);
|
||||
|
||||
foreach ($required_groups as $group) {
|
||||
if (!in_array($group['id'], $provided_group_ids)) {
|
||||
return ['status' => 400, 'message' => 'Required option group ' . $group['title'] . ' is missing'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Check if the new item is same with old item then update quantity
|
||||
$options_match = false;
|
||||
$existing_cart_item = $this->cart_items
|
||||
->where('cart_id', $cart['id'])
|
||||
->where('menu_item_id', $menu_item_id)
|
||||
->where('variation_id', $variation_id)
|
||||
->first();
|
||||
if($type != 'free_item'){
|
||||
if ($existing_cart_item) {
|
||||
if(($type == 'pwp' && $existing_cart_item['is_pwp'] == true) || $type != 'pwp'){
|
||||
// Check if options match
|
||||
$options_match = true;
|
||||
|
||||
if (!empty($option_group)) {
|
||||
// Get existing options for this cart item
|
||||
$existing_options = $this->cart_item_options
|
||||
->where('cart_item_id', $existing_cart_item['id'])
|
||||
->findAll();
|
||||
|
||||
// Convert existing options to comparable format
|
||||
$existing_option_ids = [];
|
||||
foreach ($existing_options as $opt) {
|
||||
if (!isset($existing_option_ids[$opt['option_group_id']])) {
|
||||
$existing_option_ids[$opt['option_group_id']] = [];
|
||||
}
|
||||
$existing_option_ids[$opt['option_group_id']][] = $opt['option_id'];
|
||||
}
|
||||
|
||||
// Convert new options to comparable format
|
||||
$new_option_ids = [];
|
||||
foreach ($option_group as $opt) {
|
||||
if (!is_object($opt)) {
|
||||
$opt = (object) $opt;
|
||||
}
|
||||
$new_option_ids[$opt->group_id] = $opt->option_ids;
|
||||
}
|
||||
|
||||
// Compare options
|
||||
if ($existing_option_ids != $new_option_ids) {
|
||||
$options_match = false;
|
||||
}
|
||||
} else {
|
||||
// If no options in request but existing item has options
|
||||
$has_existing_options = $this->cart_item_options
|
||||
->where('cart_item_id', $existing_cart_item['id'])
|
||||
->countAllResults() > 0;
|
||||
|
||||
if ($has_existing_options) {
|
||||
$options_match = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($options_match) {
|
||||
// Update quantity of existing item
|
||||
$new_quantity = $existing_cart_item['quantity'] + $quantity;
|
||||
$new_line_subtotal = $existing_cart_item['unit_price'] * $new_quantity;
|
||||
|
||||
$this->cart_items->update($existing_cart_item['id'], [
|
||||
'quantity' => $new_quantity,
|
||||
'line_subtotal' => $new_line_subtotal
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// echo($variation_id);exit;
|
||||
if (!$options_match) {
|
||||
if ($variation_id == 0) {
|
||||
$menu_item = $this->menu_items->where('status', 'active')->find($menu_item_id);
|
||||
if (empty($menu_item)) {
|
||||
return ['status' => 400, 'message' => "Menu Item not found!"];
|
||||
}
|
||||
|
||||
$basePrice = $menu_item['price'];
|
||||
|
||||
$order_index = $this->cart_items->withDeleted()->where('cart_id', $cart['id'])->countAllResults();
|
||||
|
||||
// Insert cart item
|
||||
$line_subtotal = $basePrice * $quantity;
|
||||
|
||||
$add_cart_detail = [
|
||||
'cart_id' => $cart['id'],
|
||||
'menu_item_id' => $menu_item_id,
|
||||
'variation_id' => 0,
|
||||
'title' => $menu_item['title'],
|
||||
'unit_price' => $basePrice,
|
||||
'quantity' => $quantity,
|
||||
'line_subtotal' => $line_subtotal,
|
||||
'order_index' => $order_index,
|
||||
];
|
||||
|
||||
if($type == 'free_item'){
|
||||
$add_cart_detail = [
|
||||
'cart_id' => $cart['id'],
|
||||
'menu_item_id' => $menu_item_id,
|
||||
'variation_id' => 0,
|
||||
'title' => $menu_item['title'],
|
||||
'unit_price' => 0,
|
||||
'quantity' => $quantity,
|
||||
'line_subtotal' => 0,
|
||||
'order_index' => $order_index,
|
||||
'is_free_item' => 1
|
||||
];
|
||||
}
|
||||
|
||||
if($type == 'pwp'){
|
||||
$add_cart_detail = [
|
||||
'cart_id' => $cart['id'],
|
||||
'menu_item_id' => $menu_item_id,
|
||||
'variation_id' => 0,
|
||||
'title' => $menu_item['title'],
|
||||
'unit_price' => $menu_item['pwp_price'],
|
||||
'quantity' => $quantity,
|
||||
'line_subtotal' => $menu_item['pwp_price'] * $quantity,
|
||||
'order_index' => $order_index,
|
||||
'is_pwp' => true
|
||||
];
|
||||
}
|
||||
|
||||
// print_r($add_cart_detail);exit;
|
||||
|
||||
$cart_item_id = $this->cart_items->insert($add_cart_detail);
|
||||
|
||||
$existingGroupIds = getColumnValues($this->menu_item_options_groups, 'menu_item_id', $menu_item_id, 'option_group_id');
|
||||
|
||||
if (!empty($option_group)) {
|
||||
foreach ($option_group as $opt) {
|
||||
if (!is_object($opt)) {
|
||||
$opt = (object) $opt;
|
||||
}
|
||||
$option_group_id = $opt->group_id;
|
||||
$option_ids = $opt->option_ids;
|
||||
|
||||
if (in_array($option_group_id, $existingGroupIds)) {
|
||||
$option_groups = $this->option_groups->find($option_group_id);
|
||||
$option_num = count($option_ids);
|
||||
|
||||
if (!empty($option_groups)) {
|
||||
if ($option_num >= $option_groups['min_quantity'] && $option_num <= $option_groups['max_quantity']) {
|
||||
foreach ($option_ids as $key => $value) {
|
||||
$option_selected = $this->options->where('option_group_id', $option_group_id)->find($value);
|
||||
if (empty($option_selected)) {
|
||||
return ['status' => 400, 'message' => 'No option selected !'];
|
||||
}
|
||||
$option_data = [
|
||||
'cart_item_id' => $cart_item_id,
|
||||
'option_id' => $value,
|
||||
'option_group_id' => $option_group_id,
|
||||
'option_title' => $option_selected['title'],
|
||||
'price_adjustment' => $option_selected['price_adjustment']
|
||||
];
|
||||
|
||||
$this->cart_item_options->insert($option_data);
|
||||
}
|
||||
} else {
|
||||
return ['status' => 400, 'message' => 'Please select correct quantity for option!'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$menu_item = $this->menu_items->where('status', 'active')->find($menu_item_id);
|
||||
if (empty($menu_item)) {
|
||||
return ['status' => 400, 'message' => "Menu Item not found!"];
|
||||
}
|
||||
|
||||
$menu_variation = $this->menu_item_variations->withDeleted()->find($variation_id);
|
||||
if (empty($menu_variation)) {
|
||||
return ['status' => 400, 'message' => "Menu Variation not found!"];
|
||||
}
|
||||
|
||||
$item_price = $menu_variation['price'];
|
||||
|
||||
$order_index = $this->cart_items->withDeleted()->where('cart_id', $cart['id'])->countAllResults();
|
||||
|
||||
$line_subtotal = $item_price * $quantity;
|
||||
|
||||
$add_cart_detail = [
|
||||
'cart_id' => $cart['id'],
|
||||
'menu_item_id' => $menu_item_id,
|
||||
'variation_id' => $variation_id,
|
||||
'title' => $menu_item['title'],
|
||||
'unit_price' => $item_price,
|
||||
'quantity' => $quantity,
|
||||
'line_subtotal' => $line_subtotal,
|
||||
'order_index' => $order_index,
|
||||
];
|
||||
|
||||
if($type == 'free_item'){
|
||||
$add_cart_detail = [
|
||||
'cart_id' => $cart['id'],
|
||||
'menu_item_id' => $menu_item_id,
|
||||
'variation_id' => $variation_id,
|
||||
'title' => $menu_item['title'],
|
||||
'unit_price' => 0,
|
||||
'quantity' => $quantity,
|
||||
'line_subtotal' => 0,
|
||||
'order_index' => $order_index,
|
||||
'is_free_item' => 1
|
||||
];
|
||||
}
|
||||
|
||||
if($type == 'pwp'){
|
||||
$add_cart_detail = [
|
||||
'cart_id' => $cart['id'],
|
||||
'menu_item_id' => $menu_item_id,
|
||||
'variation_id' => $variation_id,
|
||||
'title' => $menu_item['title'],
|
||||
'unit_price' => $menu_item['pwp_price'],
|
||||
'quantity' => $quantity,
|
||||
'line_subtotal' => $menu_item['pwp_price'] * $quantity,
|
||||
'order_index' => $order_index,
|
||||
'is_pwp' => true
|
||||
];
|
||||
}
|
||||
|
||||
$cart_item_id = $this->cart_items->insert($add_cart_detail);
|
||||
|
||||
if ($menu_variation) {
|
||||
$existing_variation_option_group = getColumnValues($this->variation_options_groups, 'variation_id', $variation_id, 'option_group_id');
|
||||
if (!empty($option_group)) {
|
||||
foreach ($option_group as $opt) {
|
||||
if (!is_object($opt)) {
|
||||
$opt = (object) $opt;
|
||||
}
|
||||
$option_group_id = $opt->group_id;
|
||||
$option_ids = $opt->option_ids;
|
||||
|
||||
if (in_array($option_group_id, $existing_variation_option_group)) {
|
||||
$option_groups = $this->option_groups->find($option_group_id);
|
||||
$option_num = count($option_ids);
|
||||
|
||||
if (!empty($option_groups)) {
|
||||
if ($option_num >= $option_groups['min_quantity'] && $option_num <= $option_groups['max_quantity']) {
|
||||
foreach ($option_ids as $key => $value) {
|
||||
$option_selected = $this->options->where('option_group_id', $option_group_id)->where('id', $value)->first();
|
||||
if (empty($option_selected)) {
|
||||
return ['status' => 400, 'message' => 'No option selected !'];
|
||||
}
|
||||
$option_data = [
|
||||
'cart_item_id' => $cart_item_id,
|
||||
'option_id' => $value,
|
||||
'option_group_id' => $option_group_id,
|
||||
'option_title' => $option_selected['title'],
|
||||
'price_adjustment' => $option_selected['price_adjustment']
|
||||
];
|
||||
|
||||
$this->cart_item_options->insert($option_data);
|
||||
}
|
||||
} else {
|
||||
return ['status' => 400, 'message' => 'Please select correct quantity for option!'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ['status' => 400, 'message' => 'No variation found!'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// $total = $this->calculateCartTotals($cart['id'], $cart['outlet_id']);
|
||||
|
||||
|
||||
$this->db->transComplete();
|
||||
|
||||
return [
|
||||
'status' => 200,
|
||||
'message' => 'Item added to cart successfully.',
|
||||
'data' => ['cart_id' => $cart['id']]
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
$this->db->transRollback();
|
||||
return ['status' => 400, 'message' => "Something went wrong: " . $e];
|
||||
}
|
||||
}
|
||||
|
||||
public function getRequiredOptionGroups($menu_item_id, $variation_id)
|
||||
{
|
||||
if ($variation_id == 0) {
|
||||
$existingGroupIds = getColumnValues($this->menu_item_options_groups, 'menu_item_id', $menu_item_id, 'option_group_id');
|
||||
} else {
|
||||
$existingGroupIds = getColumnValues($this->variation_options_groups, 'variation_id', $variation_id, 'option_group_id');
|
||||
}
|
||||
|
||||
if(!empty($existingGroupIds)){
|
||||
$data = $this->option_groups
|
||||
->whereIn('id', $existingGroupIds)
|
||||
->where('is_required', 1)
|
||||
->findAll();
|
||||
} else {
|
||||
$data = [];
|
||||
}
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\PromoSetting;
|
||||
use App\Models\PromoCode;
|
||||
use App\Models\PromoRedemptionRecord;
|
||||
use App\Models\MenuItems;
|
||||
use App\Models\MenuItemCategories;
|
||||
use App\Models\Carts;
|
||||
|
||||
helper('promo');
|
||||
class PromoService
|
||||
{
|
||||
private $promoSetting;
|
||||
private $promoCode;
|
||||
private $promoRedemptionRecord;
|
||||
private $menuItem;
|
||||
private $menuItemCategories;
|
||||
private $carts;
|
||||
private $cart_service;
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
$this->promoSetting = new PromoSetting();
|
||||
$this->promoCode = new PromoCode();
|
||||
$this->promoRedemptionRecord = new PromoRedemptionRecord();
|
||||
$this->menuItem = new MenuItems();
|
||||
$this->carts = new Carts();
|
||||
$this->menuItemCategories = new MenuItemCategories();
|
||||
$this->cart_service = service('cartService');
|
||||
}
|
||||
|
||||
public function checkPromoAvailability($promo_code_id, $order_type, $customer_id){
|
||||
$promo_code = $this->promoCode->where('deleted_at', null)->find($promo_code_id);
|
||||
|
||||
if (empty($promo_code)) {
|
||||
return ['status' => 400, 'result' => 'Promotion does not exist.'];
|
||||
}
|
||||
|
||||
//Filter by date and time
|
||||
$current_date = date('Y-m-d');
|
||||
$current_time = date('H:i');
|
||||
|
||||
if ($current_date < $promo_code['start_date'] || $current_date > $promo_code['end_date']) {
|
||||
return ['status' => 400, 'result' => 'Promotion already expired.'];
|
||||
}
|
||||
|
||||
// Check day/time validity
|
||||
$validity = json_decode($promo_code['customize_validity'], true);
|
||||
$dayMap = [
|
||||
'mon' => 'mon',
|
||||
'tue' => 'tue',
|
||||
'wed' => 'wed',
|
||||
'thu' => 'thurs',
|
||||
'fri' => 'fri',
|
||||
'sat' => 'sat',
|
||||
'sun' => 'sun'
|
||||
];
|
||||
|
||||
$current_day_key = $dayMap[strtolower(date('D'))];
|
||||
|
||||
if (isset($validity[$current_day_key]) && $validity[$current_day_key]['enabled']) {
|
||||
$startTime = $validity[$current_day_key]['startTime'];
|
||||
$endTime = $validity[$current_day_key]['endTime'];
|
||||
|
||||
if ($current_time < $startTime || $current_time > $endTime) {
|
||||
return ['status' => 400, 'result' => 'Promo code not valid at this time.'];
|
||||
}
|
||||
} else {
|
||||
return ['status' => 400, 'result' => 'Promo Code not valid on this day.'];
|
||||
}
|
||||
|
||||
//Check how many left for the promotion
|
||||
$promotionAlreadyRedeem = $this->promoRedemptionRecord->where('promo_codes_id', $promo_code['id'])->countAllResults();
|
||||
|
||||
if($promotionAlreadyRedeem >= $promo_code['total_redemption_limit'] && $promo_code['total_redemption_limit'] != 0){
|
||||
return ['status' => 400, 'result' => 'Promo Code already fully redeemed.'];
|
||||
}
|
||||
|
||||
//Check how many left for the customer / customer can redeem multiple or one times
|
||||
$customerAlreadyRedeem = $this->promoRedemptionRecord ->where('promo_codes_id', $promo_code['id'])->where('customer_id', $customer_id)->countAllResults();
|
||||
|
||||
if($promo_code['usage_limit_type'] == 'one' && $customerAlreadyRedeem > 0){
|
||||
return ['status' => 400, 'result' => 'Promo Code only can be redeemed once.'];
|
||||
}
|
||||
|
||||
if($promo_code['usage_limit_type'] == 'multiple' && $customerAlreadyRedeem >= $promo_code['usage_limit'] && $promo_code['usage_limit'] != 0){
|
||||
return ['status' => 400, 'result' => 'Promo Code only can be redeemed '. $promo_code['usage_limit'] .' times.'];
|
||||
}
|
||||
|
||||
//Check if the promotion is valid for the order type
|
||||
//order type = delivery, pick-up, dine-in, reservation
|
||||
$allowed_order_types = ['delivery', 'pickup', 'dinein', 'reservation'];
|
||||
if (!in_array($order_type, $allowed_order_types)) {
|
||||
return ['status' => 400, 'result' => 'Invalid order type.'];
|
||||
}
|
||||
|
||||
$promo_code['promo_order_type'] = explode(',', $promo_code['promo_order_type']);
|
||||
// print_r($order_type);exit;
|
||||
|
||||
if (!in_array($order_type, $promo_code['promo_order_type']) && !in_array("all", $promo_code['promo_order_type'])) {
|
||||
return ['status' => 400, 'result' => 'Promo Code only valid for ' . $order_type . ' order.'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// public function checkPromoLogic($promo_setting_id, $cart_items, $subtotal, $customer_id, $outlet_id, $order_type = 'pickup', $delivery_fee = 0){
|
||||
public function checkPromoLogic($promo_setting_id, $cart_items, $subtotal, $customer_id, $outlet_id, $order_type, $delivery_fee) {
|
||||
|
||||
//Check if it has minimum spend requirement
|
||||
$promoSetting = $this->promoSetting->where('status', 'active')->find($promo_setting_id);
|
||||
if(empty($promoSetting)){
|
||||
return ['status' => 400, 'result' => 'Promo Code does not exist.'];
|
||||
}
|
||||
|
||||
$promoSettingData = json_decode($promoSetting['promo_setting'], true);
|
||||
$minimumSpend = $promoSettingData['MinimumSpend'];
|
||||
$promo = $promoSettingData['Promo'];
|
||||
|
||||
|
||||
if ($minimumSpend['type'] !== 'none') {
|
||||
|
||||
//minimum spend of subtotal amount (excluded options price)
|
||||
if($minimumSpend['type'] == 'total' && $minimumSpend['amount_type'] == 'amount'){
|
||||
if ($subtotal < $minimumSpend['amount']) {
|
||||
return ['status' => 400, 'result' => 'Minimum spend requirement not met.'];
|
||||
}
|
||||
}
|
||||
|
||||
//minimum quantity in item / category
|
||||
if(($minimumSpend['type'] == 'item' || $minimumSpend['type'] == 'category') && $minimumSpend['amount_type'] == 'quantity'){
|
||||
|
||||
$minimumQuantity = 0;
|
||||
if($minimumSpend['type'] == 'item'){
|
||||
foreach($cart_items as $item){
|
||||
if(in_array($item['menu_item_id'], $minimumSpend['filter'])){
|
||||
$minimumQuantity += $item['quantity'];
|
||||
}
|
||||
}
|
||||
|
||||
} else if ($minimumSpend['type'] == 'category') {
|
||||
foreach($cart_items as $item){
|
||||
$category_ids = getMenuCategory($item['menu_item_id']);
|
||||
foreach($category_ids as $category_id){
|
||||
if(in_array($category_id['id'], $minimumSpend['filter'])){
|
||||
$minimumQuantity += $item['quantity'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($minimumQuantity < $minimumSpend['amount']){
|
||||
return ['status' => 400, 'result' => 'Minimum quantity requirement for this promo code is not met.'];
|
||||
}
|
||||
}
|
||||
|
||||
//minmum every item / category have certain quantity
|
||||
if (($minimumSpend['type'] == 'item' || $minimumSpend['type'] == 'category') &&$minimumSpend['amount_type'] == 'every_quantity') {
|
||||
$everyQuantity = (int)$minimumSpend['amount'];
|
||||
$validItems = [];
|
||||
|
||||
if ($minimumSpend['type'] == 'item') {
|
||||
// Build a map of menu_item_id to quantity
|
||||
foreach ($cart_items as $item) {
|
||||
if (in_array($item['menu_item_id'], $minimumSpend['filter'])) {
|
||||
$validItems[$item['menu_item_id']] = ($validItems[$item['menu_item_id']] ?? 0) + $item['quantity'];
|
||||
}
|
||||
}
|
||||
|
||||
// Check all required filters are present
|
||||
foreach ($minimumSpend['filter'] as $requiredId) {
|
||||
if (!isset($validItems[$requiredId])) {
|
||||
return ['status' => 400, 'result' => 'Required item(s) is not found in cart.'];
|
||||
}
|
||||
if ((int)$validItems[$requiredId] < $everyQuantity) {
|
||||
return ['status' => 400, 'result' => 'Every quantity requirement for this promo code is not met.'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($minimumSpend['type'] == 'category') {
|
||||
foreach ($cart_items as $item) {
|
||||
$category_ids = getMenuCategory($item['menu_item_id']);
|
||||
foreach ($category_ids as $cat) {
|
||||
$cat_id = $cat['id'];
|
||||
if (in_array($cat_id, $minimumSpend['filter'])) {
|
||||
if (!isset($validItems[$cat_id])) {
|
||||
$validItems[$cat_id] = $item['quantity'];
|
||||
}else{
|
||||
$validItems[$cat_id] += $item['quantity'];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($minimumSpend['filter'] as $requiredCatId) {
|
||||
if (!isset($validItems[$requiredCatId])) {
|
||||
return ['status' => 400, 'result' => 'Required category item(s) is not found in cart.'];
|
||||
}
|
||||
if ((int)$validItems[$requiredCatId] < $everyQuantity) {
|
||||
return ['status' => 400, 'result' => 'Every quantity requirement for this promo code is not met.'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (($minimumSpend['type'] == 'item' || $minimumSpend['type'] == 'category') && $minimumSpend['amount_type'] == 'amount') {
|
||||
$eligibleSubtotal = 0.0;
|
||||
// print_r($minimumSpend);
|
||||
// print_r($cart_items);
|
||||
// exit;
|
||||
foreach ($cart_items as $item) {
|
||||
if ($minimumSpend['type'] === 'item') {
|
||||
if (in_array($item['menu_item_id'], $minimumSpend['filter'])) {
|
||||
$eligibleSubtotal += (float)$item['line_subtotal'];
|
||||
}
|
||||
} else {
|
||||
$category_ids = getMenuCategory($item['menu_item_id']);
|
||||
foreach ($category_ids as $cat) {
|
||||
if (in_array($cat['id'], haystack: $minimumSpend['filter'])) {
|
||||
$eligibleSubtotal += (float)$item['line_subtotal'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// print_r($eligibleSubtotal);exit;
|
||||
if ($eligibleSubtotal < (float)$minimumSpend['amount']) {
|
||||
return ['status' => 400, 'result' => 'Minimum spend on eligible items not met.'];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$afterPromo = [];
|
||||
$promo_discount_total = 0;
|
||||
$afterPromo['promo_discount_total'] = 0;
|
||||
$afterPromo['delivery_fee'] = 0;
|
||||
$afterPromo['free_item_list'] = [];
|
||||
|
||||
//Discount
|
||||
if ($promo['promo_type'] == 'discount') {
|
||||
$promoPrice = 0;
|
||||
$total = 0;
|
||||
// $capped_at = $promo['capped_at'] ?? 0;
|
||||
|
||||
// All
|
||||
if ($promo['filter_type'] == 'total') {
|
||||
$promoPrice = calculatePromoPrice($subtotal, $promo['amount'], $promo['discount_type']);
|
||||
$total = $subtotal;
|
||||
}
|
||||
|
||||
// Item
|
||||
if ($promo['filter_type'] == 'item') {
|
||||
// $total = 0;
|
||||
foreach ($cart_items as $item) {
|
||||
if (in_array($item['menu_item_id'], $promo['filter'])) {
|
||||
$total += $item['line_subtotal'];
|
||||
}
|
||||
}
|
||||
|
||||
switch($promo['discount_type']){
|
||||
case 'amount':
|
||||
$promoPrice = $promo['amount'];
|
||||
break;
|
||||
case 'percentage':
|
||||
$promoPrice = calculatePromoPrice($total, $promo['amount'], $promo['discount_type']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Category
|
||||
if ($promo['filter_type'] == 'category') {
|
||||
foreach ($cart_items as $item) {
|
||||
$category_ids = getMenuCategory($item['menu_item_id']);
|
||||
foreach ($category_ids as $category_id) {
|
||||
if (in_array($category_id['id'], $promo['filter'])) {
|
||||
$total += $item['line_subtotal'];
|
||||
}
|
||||
}
|
||||
}
|
||||
switch($promo['discount_type']){
|
||||
case 'amount':
|
||||
$promoPrice = $promo['amount'];
|
||||
break;
|
||||
case 'percentage':
|
||||
$promoPrice = calculatePromoPrice($total, $promo['amount'], $promo['discount_type']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if($promo['discount_type'] == 'percentage'){
|
||||
// if($promoPrice > $capped_at){
|
||||
// $promoPrice = $capped_at;
|
||||
// }
|
||||
// }
|
||||
|
||||
if($promoPrice > $total){
|
||||
$promoPrice = $total;
|
||||
}
|
||||
|
||||
$afterPromo['promo_discount_total'] = $promoPrice;
|
||||
}
|
||||
|
||||
//Next Item
|
||||
if ($promo['promo_type'] == 'next_item') {
|
||||
if (count($cart_items) < 2) {
|
||||
return ['status' => 400, 'result' => 'At least two items are required for this promo code.'];
|
||||
}
|
||||
|
||||
$eligibleItems = [];
|
||||
|
||||
if ($promo['filter_type'] == 'item') {
|
||||
foreach ($cart_items as $item) {
|
||||
if (in_array($item['menu_item_id'], $promo['filter'])) {
|
||||
$eligibleItems[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($eligibleItems) < 2) {
|
||||
return ['status' => 400, 'result' => 'Two eligible items required for this promo code.'];
|
||||
}
|
||||
|
||||
$lowestItem = $eligibleItems[0];
|
||||
foreach ($eligibleItems as $item) {
|
||||
if ((float)$item['unit_price'] < (float)$lowestItem['unit_price']) {
|
||||
$lowestItem = $item;
|
||||
}
|
||||
}
|
||||
|
||||
$afterPromo['promo_discount_total'] = calculatePromoPrice($lowestItem['unit_price'], $promo['amount'], $promo['discount_type']);
|
||||
}
|
||||
|
||||
if ($promo['filter_type'] == 'category') {
|
||||
foreach ($cart_items as $item) {
|
||||
$category_ids = getMenuCategory($item['menu_item_id']);
|
||||
$category_ids_flat = array_column($category_ids, 'id');
|
||||
$intersect = array_intersect($category_ids_flat, $promo['filter']);
|
||||
if (!empty($intersect)) {
|
||||
$eligibleItems[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($eligibleItems) < 2) {
|
||||
return ['status' => 400, 'result' => 'Two eligible category items required for this promo code.'];
|
||||
}
|
||||
|
||||
$lowestItem = $eligibleItems[0];
|
||||
foreach ($eligibleItems as $item) {
|
||||
if ((float)$item['unit_price'] < (float)$lowestItem['unit_price']) {
|
||||
$lowestItem = $item;
|
||||
}
|
||||
}
|
||||
|
||||
$afterPromo['promo_discount_total'] = calculatePromoPrice($lowestItem['unit_price'], $promo['amount'], $promo['discount_type']);
|
||||
}
|
||||
}
|
||||
|
||||
// print_r($order_type);
|
||||
// print_r($delivery_fee);
|
||||
// print_r($promo);
|
||||
// exit;
|
||||
//Delivery Fee
|
||||
if($delivery_fee > 0 && $order_type == 'delivery') {
|
||||
if($promo['promo_type'] == 'delivery'){
|
||||
if($promo['filter_type'] == 'total'){
|
||||
$afterPromo['delivery_fee'] = calculatePromoPrice($delivery_fee, $promo['amount'], $promo['discount_type']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Free Item
|
||||
if($promo['promo_type'] == 'free_item'){
|
||||
$free_items = [];
|
||||
$isIncluded = true;
|
||||
|
||||
if($promo['filter_type'] == 'item'){
|
||||
foreach($promo['free_item'] as $free_item_id){
|
||||
$menu_item = $this->menuItem
|
||||
->select('menu_items.id, menu_items.title, menu_images.image_url, menu_images.image_url_compressed')
|
||||
->join('menu_images', 'menu_images.menu_item_id = menu_items.id', 'left')
|
||||
->where('menu_items.status', 'active')
|
||||
->find($free_item_id);
|
||||
if($menu_item){
|
||||
$free_items[] = [
|
||||
'id' => $menu_item['id'],
|
||||
'title' => $menu_item['title'],
|
||||
'image_url' => getMenuImage($menu_item['id']),
|
||||
// 'image_url_compressed' => getMenuImage($menu_item['id'])
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($promo['filter_type'] == 'category'){
|
||||
foreach($promo['free_item'] as $free_item_id){
|
||||
$menu_items = $this->menuItemCategories
|
||||
->select('menu_items.id, menu_items.title, menu_item_categories.category_id')
|
||||
->join('menu_items', 'menu_items.id = menu_item_categories.menu_item_id')
|
||||
->where('menu_item_categories.category_id', $free_item_id)
|
||||
->where('menu_items.status', 'active')
|
||||
->findAll();
|
||||
|
||||
if($menu_items){
|
||||
foreach($menu_items as $item){
|
||||
$menu_item = $this->menuItem
|
||||
->select('menu_items.id, menu_items.title, menu_images.image_url, menu_images.image_url_compressed')
|
||||
->join('menu_images', 'menu_images.menu_item_id = menu_items.id', 'left')
|
||||
->where('menu_items.status', 'active')
|
||||
->find($item['id']);
|
||||
|
||||
if($menu_item){
|
||||
$free_items[$item['category_id']][] = [
|
||||
'id' => $menu_item['id'],
|
||||
'category_id' => $item['category_id'],
|
||||
'title' => $menu_item['title'],
|
||||
'image_url' => getMenuImage($menu_item['id']),
|
||||
// 'image_url_compressed' => getMenuImage($menu_item['id'])
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$afterPromo['free_item_list'] = $free_items;
|
||||
}
|
||||
|
||||
$afterPromo['status'] = 200;
|
||||
|
||||
return $afterPromo;
|
||||
}
|
||||
|
||||
public function checkPromoCode($promo_code){
|
||||
if(empty($promo_code)){
|
||||
return ['status' => 400, 'result' => 'Promo code is empty.'];
|
||||
}
|
||||
|
||||
$promo_code_id = $this->promoCode->where('code', $promo_code)->orderBy('id', 'desc')->limit(1)->first();
|
||||
|
||||
if(empty($promo_code_id)){
|
||||
return ['status' => 400, 'result' => 'Promo code not found.'];
|
||||
}
|
||||
|
||||
return $promo_code_id['id'];
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,35 @@
|
||||
<?
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\CustomerVoucherList;
|
||||
|
||||
class VoucherService
|
||||
{
|
||||
private $customer_voucher_list;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->customer_voucher_list = new CustomerVoucherList();
|
||||
}
|
||||
|
||||
public function checkVoucherAvailability($customer_voucher_list_id, $order_type, $customer_id)
|
||||
{
|
||||
$customer_voucher = $this->customer_voucher_list->where('id', $customer_voucher_list_id)->where('customer_id', $customer_id)->first();
|
||||
|
||||
if(empty($customer_voucher)){
|
||||
return ['status' => 400, 'result' => 'Customer voucher not found.'];
|
||||
}
|
||||
|
||||
$current_date = date('Y-m-d');
|
||||
|
||||
|
||||
if($current_date > $customer_voucher['voucher_expiry_date']){
|
||||
return ['status' => 400, 'result' => 'Voucher already expired.'];
|
||||
}
|
||||
|
||||
if($customer_voucher['voucher_status'] != 'active'){
|
||||
return ['status' => 400, 'result' => 'Voucher already ' . $customer_voucher['voucher_status'] . '.'];
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user