diff --git a/classes/Cart.php b/classes/Cart.php index a4a555450..9b0b477e4 100644 --- a/classes/Cart.php +++ b/classes/Cart.php @@ -1331,6 +1331,9 @@ class CartCore extends ObjectModel else $shipping_fees = 0; + if ($type == Cart::ONLY_SHIPPING) + return $shipping_fees; + if ($type == Cart::ONLY_PRODUCTS_WITHOUT_SHIPPING) $type = Cart::ONLY_PRODUCTS; @@ -1455,12 +1458,14 @@ class CartCore extends ObjectModel $wrapping_fees = 0; if ($this->gift) $wrapping_fees = Tools::convertPrice(Tools::ps_round($this->getGiftWrappingPrice($with_taxes), 2), Currency::getCurrencyInstance((int)$this->id_currency)); + if ($type == Cart::ONLY_WRAPPING) + return $wrapping_fees; $order_total_discount = 0; if (!in_array($type, array(Cart::ONLY_SHIPPING, Cart::ONLY_PRODUCTS)) && CartRule::isFeatureActive()) { // First, retrieve the cart rules associated to this "getOrderTotal" - if ($with_shipping) + if ($with_shipping || $type == Cart::ONLY_DISCOUNTS) $cart_rules = $this->getCartRules(CartRule::FILTER_ACTION_ALL); else { @@ -1486,7 +1491,7 @@ class CartCore extends ObjectModel foreach ($cart_rules as $cart_rule) { // If the cart rule offers free shipping, add the shipping cost - if ($with_shipping && $cart_rule['obj']->free_shipping) + if (($with_shipping || $type == Cart::ONLY_DISCOUNTS) && $cart_rule['obj']->free_shipping) $order_total_discount += Tools::ps_round($cart_rule['obj']->getContextualValue($with_taxes, $virtual_context, CartRule::FILTER_ACTION_SHIPPING, ($param_product ? $package : null), $use_cache), 2); // If the cart rule is a free gift, then add the free gift value only if the gift is in this package @@ -1512,12 +1517,6 @@ class CartCore extends ObjectModel $order_total -= $order_total_discount; } - if ($type == Cart::ONLY_SHIPPING) - return $shipping_fees; - - if ($type == Cart::ONLY_WRAPPING) - return $wrapping_fees; - if ($type == Cart::BOTH) $order_total += $shipping_fees + $wrapping_fees; diff --git a/classes/CartRule.php b/classes/CartRule.php index c27525952..2495155e7 100644 --- a/classes/CartRule.php +++ b/classes/CartRule.php @@ -223,7 +223,7 @@ class CartRuleCore extends ObjectModel FROM `'._DB_PREFIX_.'cart_rule` cr LEFT JOIN `'._DB_PREFIX_.'cart_rule_lang` crl ON (cr.`id_cart_rule` = crl.`id_cart_rule` AND crl.`id_lang` = '.(int)$id_lang.') WHERE ( - cr.`id_customer` = '.(int)$id_customer.' + cr.`id_customer` = '.(int)$id_customer.' OR cr.group_restriction = 1 '.($includeGeneric ? 'OR cr.`id_customer` = 0' : '').' ) AND cr.date_from < "'.date('Y-m-d H:i:s').'" @@ -232,20 +232,17 @@ class CartRuleCore extends ObjectModel '.($inStock ? 'AND cr.`quantity` > 0' : '')); // Remove cart rule that does not match the customer groups - if ($includeGeneric) - { - $customerGroups = Customer::getGroupsStatic($id_customer); - foreach ($result as $key => $cart_rule) - if ($cart_rule['group_restriction']) - { - $cartRuleGroups = Db::getInstance()->getValue('SELECT id_group FROM '._DB_PREFIX_.'cart_rule_group WHERE id_cart_rule = '.(int)$cart_rule['id_cart_rule']); - foreach ($cartRuleGroups as $cartRuleGroup) - if (in_array($cartRuleGroups['id_group'], $customerGroups)) - continue 2; + $customerGroups = Customer::getGroupsStatic($id_customer); + foreach ($result as $key => $cart_rule) + if ($cart_rule['group_restriction']) + { + $cartRuleGroups = Db::getInstance()->executeS('SELECT id_group FROM '._DB_PREFIX_.'cart_rule_group WHERE id_cart_rule = '.(int)$cart_rule['id_cart_rule']); + foreach ($cartRuleGroups as $cartRuleGroup) + if (in_array($cartRuleGroup['id_group'], $customerGroups)) + continue 2; - unset($result[$key]); - } - } + unset($result[$key]); + } foreach ($result as &$cart_rule) if ($cart_rule['quantity_per_user']) @@ -867,7 +864,15 @@ class CartRuleCore extends ObjectModel // If it has the same tax application that you need, then it's the right value, whatever the product! if ($this->reduction_tax == $use_tax) + { + // The reduction cannot exceed the products total, except when we do not want it to be limited (for the partial use calculation) + if ($filter != CartRule::FILTER_ACTION_ALL_NOCAP) + { + $cart_amount = $context->cart->getOrderTotal($use_tax, Cart::ONLY_PRODUCTS); + $reduction_amount = min($reduction_amount, $cart_amount); + } $reduction_value += $prorata * $reduction_amount; + } else { if ($this->reduction_product > 0) diff --git a/classes/PaymentModule.php b/classes/PaymentModule.php index 16ef6d1f9..2b56a4846 100644 --- a/classes/PaymentModule.php +++ b/classes/PaymentModule.php @@ -469,7 +469,7 @@ abstract class PaymentModuleCore extends Module $values['tax_excl'] -= $values['tax_excl'] - $order->total_products; } - $order->addCartRule($cart_rule['obj']->id, $cart_rule['obj']->name, $values); + $order->addCartRule($cart_rule['obj']->id, $cart_rule['obj']->name, $values, 0, $cart_rule['obj']->free_shipping); 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)) { diff --git a/classes/order/Order.php b/classes/order/Order.php index 07ac08868..34806bb8f 100644 --- a/classes/order/Order.php +++ b/classes/order/Order.php @@ -1027,7 +1027,7 @@ class OrderCore extends ObjectModel * @param int $id_order_invoice * @return bool */ - public function addCartRule($id_cart_rule, $name, $values, $id_order_invoice = 0) + public function addCartRule($id_cart_rule, $name, $values, $id_order_invoice = 0, $free_shipping = null) { $order_cart_rule = new OrderCartRule(); $order_cart_rule->id_order = $this->id; @@ -1036,6 +1036,12 @@ class OrderCore extends ObjectModel $order_cart_rule->name = $name; $order_cart_rule->value = $values['tax_incl']; $order_cart_rule->value_tax_excl = $values['tax_excl']; + if ($free_shipping === null) + { + $cart_rule = new CartRule($id_cart_rule); + $free_shipping = $cart_rule->free_shipping; + } + $order_cart_rule->free_shipping = (int)$free_shipping; $order_cart_rule->add(); } diff --git a/classes/order/OrderInvoice.php b/classes/order/OrderInvoice.php index fee05a4a1..ea74e877e 100644 --- a/classes/order/OrderInvoice.php +++ b/classes/order/OrderInvoice.php @@ -345,6 +345,11 @@ class OrderInvoiceCore extends ObjectModel // shipping cost are added in the product taxes breakdown if ($this->useOneAfterAnotherTaxComputationMethod()) return $taxes_breakdown; + + // No shipping breakdown if it's free! + foreach ($order->getCartRules() as $cart_rule) + if ($cart_rule['free_shipping']) + return $taxes_breakdown; $shipping_tax_amount = $this->total_shipping_tax_incl - $this->total_shipping_tax_excl; diff --git a/install-dev/data/db_structure.sql b/install-dev/data/db_structure.sql index 572de3a2c..eea6b46a4 100644 --- a/install-dev/data/db_structure.sql +++ b/install-dev/data/db_structure.sql @@ -1182,6 +1182,7 @@ CREATE TABLE `PREFIX_order_cart_rule` ( `name` varchar(254) NOT NULL, `value` decimal(17,2) NOT NULL default '0.00', `value_tax_excl` decimal(17,2) NOT NULL default '0.00', + `free_shipping` BOOLEAN NOT NULL DEFAULT FALSE, PRIMARY KEY (`id_order_cart_rule`), KEY `id_order` (`id_order`), KEY `id_cart_rule` (`id_cart_rule`) diff --git a/install-dev/upgrade/sql/1.5.4.0.sql b/install-dev/upgrade/sql/1.5.4.0.sql index b758dbde7..061c3a001 100644 --- a/install-dev/upgrade/sql/1.5.4.0.sql +++ b/install-dev/upgrade/sql/1.5.4.0.sql @@ -10,3 +10,13 @@ UPDATE `PREFIX_customer` c, `PREFIX_orders` o SET c.id_lang = o.id_lang WHERE c. UPDATE `PREFIX_quick_access` SET `link` = 'index.php?controller=AdminCartRules&addcart_rule' WHERE `link` = 'index.php?tab=AdminDiscounts&adddiscount'; +ALTER TABLE `PREFIX_order_cart_rule` ADD `free_shipping` BOOLEAN NOT NULL DEFAULT FALSE AFTER `value_tax_excl`; + +UPDATE `PREFIX_order_cart_rule` ocr, `PREFIX_cart_rule` cr SET ocr.free_shipping = 1 WHERE ocr.id_cart_rule = cr.id_cart_rule AND cr.free_shipping = 1; + +UPDATE `PREFIX_orders` o, `PREFIX_order_cart_rule` ocr SET + o.`total_discounts` = o.total_discounts + o.`total_shipping_tax_incl`, + o.`total_discounts_tax_incl` = o.`total_discounts_tax_incl` + o.`total_shipping_tax_incl`, + o.`total_discounts_tax_excl` = o.`total_discounts_tax_excl` + o.`total_shipping_tax_excl` +WHERE o.id_order = ocr.id_order AND ocr.free_shipping = 1; + diff --git a/modules/statsforecast/statsforecast.php b/modules/statsforecast/statsforecast.php index ffe86a176..d27f8c756 100644 --- a/modules/statsforecast/statsforecast.php +++ b/modules/statsforecast/statsforecast.php @@ -123,7 +123,8 @@ class StatsForecast extends Module $dataTable[$row['fix_date']] = $row; $this->_html .= '
-

'.$this->displayName.'

+
+

'.$this->displayName.'

'.$this->l('All amounts are without taxes.').'

@@ -300,30 +301,28 @@ class StatsForecast extends Module
-
- '.$this->l('Registered visitors').' - - '.round(100 * $orders / max(1, $customers), 2).' % - - '.$this->l('orders').' -
- -
-
- '.$this->l('Visitors').' - - '.round(100 * $orders / max(1, $visitors), 2).' % - - '.$this->l('orders').' - + '.$this->l('Registered visitors').' + + '.round(100 * $orders / max(1, $customers), 2).' % + + '.$this->l('orders').'
-
-

- '.$this->l('Turn your visitors into money:').' -
'.$this->l('Each visitor yields').' '.Tools::displayPrice($ca['ventil']['total'] / max(1, $visitors), $currency).'. -
'.$this->l('Each registered visitor yields').' '.Tools::displayPrice($ca['ventil']['total'] / max(1, $customers), $currency).'. -

'; +
+
+ '.$this->l('Visitors').' + + '.round(100 * $orders / max(1, $visitors), 2).' % + + '.$this->l('orders').' +
+
+

+ '.$this->l('Turn your visitors into money:').' +
'.$this->l('Each visitor yields').' '.Tools::displayPrice($ca['ventil']['total'] / max(1, $visitors), $currency).'. +
'.$this->l('Each registered visitor yields').' '.Tools::displayPrice($ca['ventil']['total'] / max(1, $customers), $currency).'. +

+
'; $from = strtotime($employee->stats_date_from.' 00:00:00'); $to = strtotime($employee->stats_date_to.' 23:59:59'); @@ -332,7 +331,10 @@ class StatsForecast extends Module $this->_html .= '
'; - $this->_html .= '

'.$this->l('Payment distribution').'

+ $this->_html .= ' +
+

'.$this->l('Payment distribution').'

+

'.$this->l('The amounts are with taxes, so you can get an estimation of the commission due to the payment method.').'

'.$this->l('Zone:').'