From 969fa850309fd9eaf4eefe711382113793738ade Mon Sep 17 00:00:00 2001 From: Shreesh Arora Date: Mon, 1 Jul 2024 16:57:00 +0530 Subject: [PATCH] Fixed: Partial cart rule not creating when using multiple coupons in a single cart --- classes/PaymentModule.php | 216 +++++++++++++++++++++++--------------- 1 file changed, 132 insertions(+), 84 deletions(-) diff --git a/classes/PaymentModule.php b/classes/PaymentModule.php index d7e75b7d2..9d08b3469 100644 --- a/classes/PaymentModule.php +++ b/classes/PaymentModule.php @@ -246,6 +246,8 @@ abstract class PaymentModuleCore extends Module // Make sure CartRule caches are empty CartRule::cleanCache(); $cart_rules = $this->context->cart->getCartRules(); + $appliedCartRules = array(); + $orderTotals = array(); foreach ($cart_rules as $cart_rule) { if (($rule = new CartRule((int)$cart_rule['obj']->id)) && Validate::isLoadedObject($rule)) { if ($error = $rule->checkValidity($this->context, true, true)) { @@ -260,6 +262,24 @@ abstract class PaymentModuleCore extends Module $error = sprintf(Tools::displayError('CartRule ID %1s (%2s) used in this cart is not valid and has been withdrawn from cart'), (int)$rule->id, $rule_name); PrestaShopLogger::addLog($error, 3, '0000002', 'Cart', (int)$this->context->cart->id); } + } else { + $appliedCartRules[$rule->id] = array( + 'obj' => $cart_rule['obj'], + 'tax' => $cart_rule['obj']->reduction_tax, + 'reduction_amount' => $rule->reduction_amount, + 'available_for_use' => array(), + 'used' => array( + 'excl' => 0, + 'incl' => 0 + ) + ); + if ((int) $rule->reduction_currency !== (int) $this->context->cart->id_currency) { + $appliedCartRules[$rule->id]['reduction_amount'] = Tools::convertPriceFull( + $rule->reduction_amount, + new Currency($rule->reduction_currency), + new Currency($this->context->cart->id_currency) + ); + } } } } @@ -496,6 +516,7 @@ abstract class PaymentModuleCore extends Module /** @var OrderDetail $order_detail */ $order = $order_list[$key]; + $orderTotals[$order->id] = array('incl' => $order->total_products_wt, 'excl' => $order->total_products); if (!$order_creation_failed && isset($order->id)) { // first set if_hotel as 0 and get the hotel id from room type info -> below $idHotel = 0; @@ -619,91 +640,10 @@ abstract class PaymentModuleCore extends Module if (!$values['tax_excl']) { continue; } - - // IF - // The value of the voucher is greater than the total of the order - // Partial use is allowed - // This is an "amount" reduction, not a reduction in % or a gift - // THEN - // The voucher is cloned with a new value corresponding to the remainder - $reduction_amount_converted = $cart_rule['obj']->reduction_amount; - if ((int) $cart_rule['obj']->reduction_currency !== (int) $this->context->cart->id_currency) { - $reduction_amount_converted = Tools::convertPriceFull( - $cart_rule['obj']->reduction_amount, - new Currency($cart_rule['obj']->reduction_currency), - new Currency($this->context->cart->id_currency) - ); - } - if ($cart_rule['obj']->reduction_tax) { - $remaining_amount = $values['tax_incl'] - $order->total_products_wt; - } else { - $remaining_amount = $values['tax_excl'] - $order->total_products; - } - if ($remaining_amount > 0 && $cart_rule['obj']->partial_use == 1 && $reduction_amount_converted > 0) { - // Create a new voucher from the original - $voucher = new CartRule((int)$cart_rule['obj']->id); // We need to instantiate the CartRule without lang parameter to allow saving it - unset($voucher->id); - - // Set a new voucher code - // For a customer, applying voucher ABCD will create partial vouchers ABCD-2, ABCD-3 and so on, and - // applying voucher ABCD-2 will create vouchers ABCD-2-2, ABCD-2-3 and so on - $voucher->code = empty($voucher->code) ? substr(md5($order->id.'-'.$order->id_customer.'-'.$cart_rule['obj']->id), 0, 16) : $voucher->code.'-2'; - while (CartRule::cartRuleExists($voucher->code, $order->id_customer)) { - if (preg_match('/\-([0-9]{1,2})$/', $voucher->code, $matches)) { - $voucher->code = preg_replace('/'.$matches[1].'$/', (intval($matches[1]) + 1), $voucher->code); - } - } - - // Set the new voucher value - $voucher->reduction_amount = $remaining_amount; - if ($voucher->reduction_tax) { - // Add total shipping amout only if reduction amount > total shipping - if ($voucher->free_shipping == 1 && $voucher->reduction_amount >= $order->total_shipping_tax_incl) { - $voucher->reduction_amount -= $order->total_shipping_tax_incl; - } - } else { - // Add total shipping amout only if reduction amount > total shipping - if ($voucher->free_shipping == 1 && $voucher->reduction_amount >= $order->total_shipping_tax_excl) { - $voucher->reduction_amount -= $order->total_shipping_tax_excl; - } - } - if ($voucher->reduction_amount <= 0) { - continue; - } - - if ($this->context->customer->isGuest()) { - $voucher->id_customer = 0; - } else { - $voucher->id_customer = $order->id_customer; - } - - $voucher->quantity = 1; - $voucher->reduction_currency = $order->id_currency; - $voucher->quantity_per_user = 1; - $voucher->free_shipping = 0; - if ($voucher->add()) { - // If the voucher has conditions, they are now copied to the new voucher - CartRule::copyConditions($cart_rule['obj']->id, $voucher->id); - - $params = array( - '{voucher_amount}' => Tools::displayPrice($voucher->reduction_amount, $this->context->currency, false), - '{voucher_num}' => $voucher->code, - '{firstname}' => $this->context->customer->firstname, - '{lastname}' => $this->context->customer->lastname, - '{id_order}' => $order->reference, - '{order_name}' => $order->getUniqReference() - ); - Mail::Send( - (int)$order->id_lang, - 'voucher', - sprintf(Mail::l('New voucher for your order %s', (int)$order->id_lang), $order->reference), - $params, - $this->context->customer->email, - $this->context->customer->firstname.' '.$this->context->customer->lastname, - null, null, null, null, _PS_MAIL_DIR_, false, (int)$order->id_shop - ); - } + if (!isset($appliedCartRules[$cart_rule['obj']->id]['available_for_use'][$order->id])) { + $appliedCartRules[$cart_rule['obj']->id]['available_for_use'][$order->id]; } + $appliedCartRules[$cart_rule['obj']->id]['available_for_use'][$order->id] = $values; $order->addCartRule($cart_rule['obj']->id, $cart_rule['obj']->name, $values, 0, $cart_rule['obj']->free_shipping); @@ -1424,6 +1364,114 @@ abstract class PaymentModuleCore extends Module } } // End foreach $order_detail_list + if (count($appliedCartRules)) { + // check if any cart rules have available reduction_amount remaining and are partiall applied + foreach ($appliedCartRules as $idCartRule => $cartRule) { + if (count($cartRule['available_for_use'])) { + foreach ($cartRule['available_for_use'] as $idOrder => $cartValOrders) { + if (isset($orderTotals[$idOrder])) { + if ($orderTotals[$idOrder]['excl'] > $cartRule['available_for_use'][$idOrder]['tax_excl']) { + $appliedCartRules[$idCartRule]['used']['excl'] += $cartRule['available_for_use'][$idOrder]['tax_excl']; + $orderTotals[$idOrder]['excl'] -= $cartRule['available_for_use'][$idOrder]['tax_excl']; + } else { + $appliedCartRules[$idCartRule]['used']['excl'] += $orderTotals[$idOrder]['excl']; + $orderTotals[$idOrder]['excl'] = 0; + } + if ($orderTotals[$idOrder]['incl'] > $cartRule['available_for_use'][$idOrder]['tax_incl']) { + $appliedCartRules[$idCartRule]['used']['incl'] += $cartRule['available_for_use'][$idOrder]['tax_incl']; + $orderTotals[$idOrder]['incl'] -= $cartRule['available_for_use'][$idOrder]['tax_incl']; + } else { + $appliedCartRules[$idCartRule]['used']['incl'] += $orderTotals[$idOrder]['incl']; + $orderTotals[$idOrder]['incl'] = 0; + } + } + } + } + } + + foreach ($appliedCartRules as $idCartRule => $cartRule) { + $remaining_amount = 0; + if (!$cartRule['tax'] && $cartRule['reduction_amount'] > $cartRule['used']['excl']) { + $remaining_amount = $cartRule['reduction_amount'] - $cartRule['used']['excl']; + } elseif ($cartRule['tax'] && $cartRule['reduction_amount'] > $cartRule['used']['incl']) { + $remaining_amount = $cartRule['reduction_amount'] - $cartRule['used']['incl']; + } + + if ($remaining_amount > 0 && $cartRule['obj']->partial_use == 1) { + // IF + // The value of the voucher is greater than the used value + // Partial use is allowed + // This is an "amount" reduction, not a reduction in % or a gift + // THEN + // The voucher is cloned with a new value corresponding to the remainder + + // Create a new voucher from the original + $voucher = new CartRule((int)$cartRule['obj']->id); // We need to instantiate the CartRule without lang parameter to allow saving it + unset($voucher->id); + + // Set a new voucher code + // For a customer, applying voucher ABCD will create partial vouchers ABCD-2, ABCD-3 and so on, and + // applying voucher ABCD-2 will create vouchers ABCD-2-2, ABCD-2-3 and so on + $voucher->code = empty($voucher->code) ? substr(md5($order->id.'-'.$order->id_customer.'-'.$cartRule['obj']->id), 0, 16) : $voucher->code.'-2'; + while (CartRule::cartRuleExists($voucher->code, $order->id_customer)) { + if (preg_match('/\-([0-9]{1,2})$/', $voucher->code, $matches)) { + $voucher->code = preg_replace('/'.$matches[1].'$/', (intval($matches[1]) + 1), $voucher->code); + } + } + + // Set the new voucher value + $voucher->reduction_amount = $remaining_amount; + if ($voucher->reduction_tax) { + // Add total shipping amout only if reduction amount > total shipping + if ($voucher->free_shipping == 1 && $voucher->reduction_amount >= $order->total_shipping_tax_incl) { + $voucher->reduction_amount -= $order->total_shipping_tax_incl; + } + } else { + // Add total shipping amout only if reduction amount > total shipping + if ($voucher->free_shipping == 1 && $voucher->reduction_amount >= $order->total_shipping_tax_excl) { + $voucher->reduction_amount -= $order->total_shipping_tax_excl; + } + } + if ($voucher->reduction_amount <= 0) { + continue; + } + + if ($this->context->customer->isGuest()) { + $voucher->id_customer = 0; + } else { + $voucher->id_customer = $order->id_customer; + } + + $voucher->quantity = 1; + $voucher->reduction_currency = $order->id_currency; + $voucher->quantity_per_user = 1; + $voucher->free_shipping = 0; + if ($voucher->add()) { + // If the voucher has conditions, they are now copied to the new voucher + CartRule::copyConditions($cartRule['obj']->id, $voucher->id); + + $params = array( + '{voucher_amount}' => Tools::displayPrice($voucher->reduction_amount, $this->context->currency, false), + '{voucher_num}' => $voucher->code, + '{firstname}' => $this->context->customer->firstname, + '{lastname}' => $this->context->customer->lastname, + '{id_order}' => $order->reference, + '{order_name}' => $order->getUniqReference() + ); + Mail::Send( + (int)$order->id_lang, + 'voucher', + sprintf(Mail::l('New voucher for your order %s', (int)$order->id_lang), $order->reference), + $params, + $this->context->customer->email, + $this->context->customer->firstname.' '.$this->context->customer->lastname, + null, null, null, null, _PS_MAIL_DIR_, false, (int)$order->id_shop + ); + } + } + } + } + // Use the last order as currentOrder if (isset($order) && $order->id) { $this->currentOrder = (int)$order->id;