690 lines
29 KiB
PHP
690 lines
29 KiB
PHP
<?
|
|
|
|
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;
|
|
}
|
|
}
|