AMS_Backend/app/Services/PromoService.php
2025-11-06 13:41:06 +08:00

452 lines
19 KiB
PHP

<?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'];
}
}
?>