From d5c20947065c2d18bdd3f15fc4487f01fc2346f7 Mon Sep 17 00:00:00 2001 From: dMetzger Date: Thu, 1 Mar 2012 09:45:39 +0000 Subject: [PATCH] // Temp fix for cart rule and multi-shipping - not fully functionnal yet but still better than before git-svn-id: http://dev.prestashop.com/svn/v1/branches/1.5.x@13771 b9a71923-0436-4b27-9f14-aed3839534dd --- classes/CartRule.php | 207 +++++++++++++++++---------------- classes/PaymentModule.php | 94 ++++++++++++--- modules/blockcart/ajax-cart.js | 2 +- 3 files changed, 188 insertions(+), 115 deletions(-) diff --git a/classes/CartRule.php b/classes/CartRule.php index 279a143ad..c4f20aae9 100644 --- a/classes/CartRule.php +++ b/classes/CartRule.php @@ -27,6 +27,11 @@ class CartRuleCore extends ObjectModel { + const FILTER_ACTION_ALL = 1; + const FILTER_ACTION_SHIPPING = 2; + const FILTER_ACTION_REDUCTION = 3; + const FILTER_ACTION_GIFT = 4; + public $id; public $name; public $id_customer; @@ -630,17 +635,19 @@ class CartRuleCore extends ObjectModel * @param Context $context * @return float|int|string */ - public function getContextualValue($useTax, Context $context = null) + public function getContextualValue($useTax, Context $context = null, $filter = null) { if (!CartRule::isFeatureActive()) return 0; if (!$context) $context = Context::getContext(); + if (!$filter) + $filter = CartRule::FILTER_ACTION_ALL; $reduction_value = 0; // Free shipping on selected carriers - if ($this->free_shipping) + if ($this->free_shipping && ($filter == CartRule::FILTER_ACTION_ALL || $filter == CartRule::FILTER_ACTION_SHIPPING)) { if (!$this->carrier_restriction) $reduction_value += $context->cart->getTotalShippingCost(null, $useTax = true, $context->country); @@ -657,121 +664,125 @@ class CartRuleCore extends ObjectModel $reduction_value += $context->cart->getCarrierCost((int)$cart_rule['id_carrier'], $useTax, $context->country); } } - - // Discount (%) on the whole order - if ($this->reduction_percent && $this->reduction_product == 0) - $reduction_value += $context->cart->getOrderTotal($useTax, Cart::ONLY_PRODUCTS) * $this->reduction_percent / 100; - - // Discount (%) on a specific product - if ($this->reduction_percent && $this->reduction_product > 0) + + if ($filter == CartRule::FILTER_ACTION_ALL || $filter == CartRule::FILTER_ACTION_REDUCTION) { - foreach ($context->cart->getProducts() as $product) - if ($product['id_product'] == $this->reduction_product) - $reduction_value += ($useTax ? $product['total_wt'] : $product['total']) * $this->reduction_percent / 100; - } - // Discount (%) on the cheapest product - if ($this->reduction_percent && $this->reduction_product == -1) - { - $minPrice = false; - foreach ($context->cart->getProducts() as $product) + // Discount (%) on the whole order + if ($this->reduction_percent && $this->reduction_product == 0) + $reduction_value += $context->cart->getOrderTotal($useTax, Cart::ONLY_PRODUCTS) * $this->reduction_percent / 100; + + // Discount (%) on a specific product + if ($this->reduction_percent && $this->reduction_product > 0) { - $price = ($useTax ? $product['price_wt'] : $product['price']); - if ($price > 0 && ($minPrice === false || $minPrice > $price)) - $minPrice = $price; - } - $reduction_value += $minPrice * $this->reduction_percent / 100; - } - - // Discount (%) on the selection of products - if ($this->reduction_percent && $this->reduction_product == -2) - { - $selected_products_reduction = 0; - $selected_products = $this->checkProductRestrictions($context, true); - if (is_array($selected_products)) foreach ($context->cart->getProducts() as $product) - if (in_array($product['id_product'].'-'.$product['id_product_attribute'], $selected_products) - || in_array($product['id_product'].'-0', $selected_products)) - { - $price = ($useTax ? $product['price_wt'] : $product['price']); - $selected_products_reduction += $price * $product['cart_quantity']; - } - $reduction_value += $selected_products_reduction * $this->reduction_percent / 100; - } - - // Discount (¤) - if ($this->reduction_amount) - { - $reduction_amount = $this->reduction_amount; - // If we need to convert the voucher value to the cart currency - if ($this->reduction_currency != $context->currency->id) - { - $voucherCurrency = new Currency($this->reduction_currency); - - // First we convert the voucher value to the default currency - if ($reduction_amount == 0 || $voucherCurrency->conversion_rate == 0) - $reduction_amount = 0; - else - $reduction_amount /= $voucherCurrency->conversion_rate; - - // Then we convert the voucher value in the default currency into the cart currency - $reduction_amount *= $context->currency->conversion_rate; - $reduction_amount = Tools::ps_round($reduction_amount); + if ($product['id_product'] == $this->reduction_product) + $reduction_value += ($useTax ? $product['total_wt'] : $product['total']) * $this->reduction_percent / 100; } - // If it has the same tax application that you need, then it's the right value, whatever the product! - if ($this->reduction_tax == $useTax) - $reduction_value += $reduction_amount; - else + // Discount (%) on the cheapest product + if ($this->reduction_percent && $this->reduction_product == -1) { - if ($this->reduction_product > 0) + $minPrice = false; + foreach ($context->cart->getProducts() as $product) { + $price = ($useTax ? $product['price_wt'] : $product['price']); + if ($price > 0 && ($minPrice === false || $minPrice > $price)) + $minPrice = $price; + } + $reduction_value += $minPrice * $this->reduction_percent / 100; + } + + // Discount (%) on the selection of products + if ($this->reduction_percent && $this->reduction_product == -2) + { + $selected_products_reduction = 0; + $selected_products = $this->checkProductRestrictions($context, true); + if (is_array($selected_products)) foreach ($context->cart->getProducts() as $product) - if ($product['id_product'] == $this->reduction_product) + if (in_array($product['id_product'].'-'.$product['id_product_attribute'], $selected_products) + || in_array($product['id_product'].'-0', $selected_products)) { - $product_price_ti = $product['price_wt']; - $product_price_te = $product['price']; - $product_vat_amount = $product_price_ti - $product_price_te; - - if ($product_vat_amount == 0 || $product_price_te == 0) - $product_vat_rate = 0; - else - $product_vat_rate = $product_vat_amount / $product_price_te; - - if ($this->reduction_tax && !$useTax) - $reduction_value += $reduction_amount / (1 + $product_vat_rate); - elseif (!$this->reduction_tax && $useTax) - $reduction_value += $reduction_amount * (1 + $product_vat_rate); + $price = ($useTax ? $product['price_wt'] : $product['price']); + $selected_products_reduction += $price * $product['cart_quantity']; } - } - // Discount (¤) on the whole order - elseif ($this->reduction_product == 0) + $reduction_value += $selected_products_reduction * $this->reduction_percent / 100; + } + + // Discount (¤) + if ($this->reduction_amount) + { + $reduction_amount = $this->reduction_amount; + // If we need to convert the voucher value to the cart currency + if ($this->reduction_currency != $context->currency->id) { - $cart_amount_ti = $context->cart->getOrderTotal(true, Cart::ONLY_PRODUCTS); - $cart_amount_te = $context->cart->getOrderTotal(false, Cart::ONLY_PRODUCTS); - $cart_vat_amount = $cart_amount_ti - $cart_amount_te; + $voucherCurrency = new Currency($this->reduction_currency); - if ($cart_vat_amount == 0 || $cart_amount_te == 0) - $cart_average_vat_rate = 0; + // First we convert the voucher value to the default currency + if ($reduction_amount == 0 || $voucherCurrency->conversion_rate == 0) + $reduction_amount = 0; else - $cart_average_vat_rate = $cart_vat_amount / $cart_amount_te; + $reduction_amount /= $voucherCurrency->conversion_rate; - if ($this->reduction_tax && !$useTax) - $reduction_value += $reduction_amount / (1 + $cart_average_vat_rate); - elseif (!$this->reduction_tax && $useTax) - $reduction_value += $reduction_amount * (1 + $cart_average_vat_rate); + // Then we convert the voucher value in the default currency into the cart currency + $reduction_amount *= $context->currency->conversion_rate; + $reduction_amount = Tools::ps_round($reduction_amount); + } + + // If it has the same tax application that you need, then it's the right value, whatever the product! + if ($this->reduction_tax == $useTax) + $reduction_value += $reduction_amount; + else + { + if ($this->reduction_product > 0) + { + foreach ($context->cart->getProducts() as $product) + if ($product['id_product'] == $this->reduction_product) + { + $product_price_ti = $product['price_wt']; + $product_price_te = $product['price']; + $product_vat_amount = $product_price_ti - $product_price_te; + + if ($product_vat_amount == 0 || $product_price_te == 0) + $product_vat_rate = 0; + else + $product_vat_rate = $product_vat_amount / $product_price_te; + + if ($this->reduction_tax && !$useTax) + $reduction_value += $reduction_amount / (1 + $product_vat_rate); + elseif (!$this->reduction_tax && $useTax) + $reduction_value += $reduction_amount * (1 + $product_vat_rate); + } + } + // Discount (¤) on the whole order + elseif ($this->reduction_product == 0) + { + $cart_amount_ti = $context->cart->getOrderTotal(true, Cart::ONLY_PRODUCTS); + $cart_amount_te = $context->cart->getOrderTotal(false, Cart::ONLY_PRODUCTS); + $cart_vat_amount = $cart_amount_ti - $cart_amount_te; + + if ($cart_vat_amount == 0 || $cart_amount_te == 0) + $cart_average_vat_rate = 0; + else + $cart_average_vat_rate = $cart_vat_amount / $cart_amount_te; + + if ($this->reduction_tax && !$useTax) + $reduction_value += $reduction_amount / (1 + $cart_average_vat_rate); + elseif (!$this->reduction_tax && $useTax) + $reduction_value += $reduction_amount * (1 + $cart_average_vat_rate); + } + /* + * Reduction on the cheapest or on the selection is not really meaningful and has been disabled in the backend + * Please keep this code, so it won't be considered as a bug + * elseif ($this->reduction_product == -1) + * elseif ($this->reduction_product == -2) + */ } - /* - * Reduction on the cheapest or on the selection is not really meaningful and has been disabled in the backend - * Please keep this code, so it won't be considered as a bug - * elseif ($this->reduction_product == -1) - * elseif ($this->reduction_product == -2) - */ } } - + // Free gift - if ((int)$this->gift_product) + if ((int)$this->gift_product && ($filter == CartRule::FILTER_ACTION_ALL || $filter == CartRule::FILTER_ACTION_GIFT)) { foreach ($context->cart->getProducts() as $product) if ($product['id_product'] == $this->gift_product && $product['id_product_attribute'] == $this->gift_product_attribute) diff --git a/classes/PaymentModule.php b/classes/PaymentModule.php index 52bf09b9c..9b6d71143 100644 --- a/classes/PaymentModule.php +++ b/classes/PaymentModule.php @@ -224,6 +224,8 @@ abstract class PaymentModuleCore extends Module } // Next ! + $only_one_gift = false; + $cart_rule_used = array(); foreach ($order_detail_list as $key => $order_detail) { $order = $order_list[$key]; @@ -298,23 +300,77 @@ abstract class PaymentModuleCore extends Module } // end foreach ($products) $cart_rules_list = ''; - $result = $cart->getCartRules(); - foreach ($result as $cart_rule) + $cart_rules = $cart->getCartRules(); + foreach ($cart_rules as $cart_rule) { - $cart_rule_obj = $cart_rule['obj']; - $values = array( - 'tax_incl' => $cart_rule_obj->getContextualValue(true), - 'tax_excl' => $cart_rule_obj->getContextualValue(false) - ); + $values = array('tax_incl' => 0, 'tax_excl' => 0); + + // If the cart is split in multiple orders, the cart rule must be split too + if (count($order_list) > 1) + { + // If the cart rule is a fee gift, then add the free gift value only if the gift is in this order + if ((int)$cart_rule['obj']->gift_product && !$only_one_gift) + { + $in_order = (bool)Db::getInstance()->getValue(' + SELECT product_id + FROM '._DB_PREFIX_.'order_detail + WHERE product_id = '.(int)$cart_rule['obj']->gift_product.' + AND product_attribute_id = '.(int)$cart_rule['obj']->gift_product_attribute.' + AND id_order = '.(int)$order->id); + + if ($in_order) + { + $values['tax_incl'] += $cart_rule['obj']->getContextualValue(true, null, CartRule::FILTER_ACTION_GIFT); + $values['tax_excl'] += $cart_rule['obj']->getContextualValue(false, null, CartRule::FILTER_ACTION_GIFT); + $only_one_gift = true; + } + } + + // If the cart rule offers free shipping, add the shipping cost + if ($cart_rule['obj']->free_shipping) + { + $values['tax_incl'] += $order->total_shipping_tax_incl; + $values['tax_excl'] += $order->total_shipping_tax_excl; + } + + // If the cart rule offers a reduction, the amount is prorated + if ($cart_rule['obj']->reduction_amount || $cart_rule['obj']->reduction_percent) + { + $prorata = $order->total_paid_tax_incl / $cart_total_paid; + $values['tax_incl'] += Tools::ps_round($prorata * $cart_rule['obj']->getContextualValue(true, null, CartRule::FILTER_ACTION_REDUCTION), 2); + $values['tax_excl'] += Tools::ps_round($prorata * $cart_rule['obj']->getContextualValue(false, null, CartRule::FILTER_ACTION_REDUCTION), 2); + + file_put_contents(dirname(__FILE__).'/../ploplop.txt', print_r(array($order->total_paid_tax_incl, $cart_total_paid, $prorata, $values, $order->id), true), FILE_APPEND); + } + } + else + { + $values = array( + 'tax_incl' => $cart_rule['obj']->getContextualValue(true), + 'tax_excl' => $cart_rule['obj']->getContextualValue(false) + ); + } + + // If the reduction is not applicable to this order (in a multi-shipping case), then try the next + if (!$values['tax_excl']) + continue; - if ($values['tax_incl'] > $order->total_products_wt && $cart_rule_obj->partial_use == 1 && $cart_rule_obj->reduction_amount > 0) + /* IF + ** - This is not multi-shipping + ** - 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 + */ + if (count($order_list) == 1 && $values['tax_incl'] > $order->total_products_wt && $cart_rule['obj']->partial_use == 1 && $cart_rule['obj']->reduction_amount > 0) { // Create a new voucher from the original - $voucher = clone $cart_rule_obj; + $voucher = clone $cart_rule['obj']; unset($voucher->id); // Set a new voucher code - $voucher->code = empty($voucher->code) ? substr(md5($order->id.'-'.$order->id_customer.'-'.$cart_rule_obj->id), 0, 16) : $voucher->code.'-2'; + $voucher->code = empty($voucher->code) ? substr(md5($order->id.'-'.$order->id_customer.'-'.$cart_rule['obj']->id), 0, 16) : $voucher->code.'-2'; if (preg_match('/\-([0-9]{1,2})\-([0-9]{1,2})$/', $voucher->code, $matches) && $matches[1] == $matches[2]) $voucher->code = preg_replace('/'.$matches[0].'$/', '-'.(intval($matches[1]) + 1), $voucher->code); @@ -329,7 +385,7 @@ abstract class PaymentModuleCore extends Module if ($voucher->add()) { // If the voucher has conditions, they are now copied to the new voucher - CartRule::copyConditions($cart_rule_obj->id, $voucher->id); + CartRule::copyConditions($cart_rule['obj']->id, $voucher->id); $params = array( '{voucher_amount}' => Tools::displayPrice($voucher->reduction_amount, $currency, false), @@ -342,19 +398,25 @@ abstract class PaymentModuleCore extends Module } } - $order->addCartRule($cart_rule_obj->id, $cart_rule_obj->name, $values); + $order->addCartRule($cart_rule['obj']->id, $cart_rule['obj']->name, $values); + + $order->total_discounts = $order->total_discounts_tax_incl = $values['tax_incl']; + $order->total_discounts_tax_excl = $values['tax_excl']; + $order->update(); - if ($id_order_state != Configuration::get('PS_OS_ERROR') && $id_order_state != Configuration::get('PS_OS_CANCELED')) + if ($id_order_state != Configuration::get('PS_OS_ERROR') && $id_order_state != Configuration::get('PS_OS_CANCELED') && !in_array($cart_rule['obj']->id, $cart_rule_used)) { + $cart_rule_used[] = $cart_rule['obj']->id; + // Create a new instance of Cart Rule without id_lang, in order to update its quantity - $cart_rule_to_update = new CartRule($cart_rule_obj->id); - $cart_rule_to_update->quantity = $cart_rule_to_update->quantity - 1; + $cart_rule_to_update = new CartRule($cart_rule['obj']->id); + $cart_rule_to_update->quantity = max(0, $cart_rule_to_update->quantity - 1); $cart_rule_to_update->update(); } $cart_rules_list .= ' - '.$this->l('Voucher name:').' '.$cart_rule_obj->name.' + '.$this->l('Voucher name:').' '.$cart_rule['obj']->name.' '.($values['tax_incl'] != 0.00 ? '-' : '').Tools::displayPrice($values['tax_incl'], $currency, false).' '; } diff --git a/modules/blockcart/ajax-cart.js b/modules/blockcart/ajax-cart.js index 84d8e8b5d..7cf5530f4 100644 --- a/modules/blockcart/ajax-cart.js +++ b/modules/blockcart/ajax-cart.js @@ -366,7 +366,7 @@ var ajaxCart = { //refresh display of vouchers (needed for vouchers in % of the total) refreshVouchers : function (jsonData) { - if (jsonData.discounts.length == 0) + if (jsonData.discounts = undefined || jsonData.discounts.length == 0) $('#vouchers').remove(); else {