391 lines
18 KiB
PHP
391 lines
18 KiB
PHP
<?php
|
|
/**
|
|
* 2007-2017 PrestaShop
|
|
*
|
|
* NOTICE OF LICENSE
|
|
*
|
|
* This source file is subject to the Open Software License (OSL 3.0)
|
|
* that is bundled with this package in the file LICENSE.txt.
|
|
* It is also available through the world-wide-web at this URL:
|
|
* https://opensource.org/licenses/OSL-3.0
|
|
* If you did not receive a copy of the license and are unable to
|
|
* obtain it through the world-wide-web, please send an email
|
|
* to license@prestashop.com so we can send you a copy immediately.
|
|
*
|
|
* DISCLAIMER
|
|
*
|
|
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
|
* versions in the future. If you wish to customize PrestaShop for your
|
|
* needs please refer to http://www.prestashop.com for more information.
|
|
*
|
|
* @author PrestaShop SA <contact@prestashop.com>
|
|
* @copyright 2007-2017 PrestaShop SA
|
|
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
|
* International Registered Trademark & Property of PrestaShop SA
|
|
*/
|
|
|
|
/**
|
|
* Class CartRuleCore
|
|
*/
|
|
class CartRule extends CartRuleCore
|
|
{
|
|
|
|
|
|
/**
|
|
* Get CartRules for the given Customer
|
|
*
|
|
* @param int $id_lang Language ID
|
|
* @param int $id_customer Customer ID
|
|
* @param bool $active Active vouchers only
|
|
* @param bool $includeGeneric Include generic AND highlighted vouchers, regardless of highlight_only setting
|
|
* @param bool $inStock Vouchers in stock only
|
|
* @param Cart|null $cart Cart
|
|
* @param bool $free_shipping_only Free shipping only
|
|
* @param bool $highlight_only Highlighted vouchers only
|
|
* @return array
|
|
* @throws PrestaShopDatabaseException
|
|
*/
|
|
public static function getCustomerCartRules(
|
|
$id_lang,
|
|
$id_customer,
|
|
$active = false,
|
|
$includeGeneric = true,
|
|
$inStock = false,
|
|
Cart $cart = null,
|
|
$free_shipping_only = false,
|
|
$highlight_only = false
|
|
) {
|
|
|
|
if (!CartRule::isFeatureActive()
|
|
// || !CartRule::haveCartRuleToday($id_customer) Empeche les reductions d'apparaitre...
|
|
) {
|
|
return array();
|
|
}
|
|
|
|
$sql_part1 = '* 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 . ')';
|
|
|
|
$sql_part2 = ' AND cr.date_from < "' . date('Y-m-d H:i:59') . '"
|
|
AND cr.date_to > "' . date('Y-m-d H:i:59') . '"
|
|
' . ($active ? 'AND cr.`active` = 1' : '') . '
|
|
' . ($inStock ? 'AND cr.`quantity` > 0' : '');
|
|
|
|
if ($free_shipping_only) {
|
|
$sql_part2 .= ' AND free_shipping = 1 AND carrier_restriction = 1';
|
|
}
|
|
|
|
if ($highlight_only) {
|
|
$sql_part2 .= ' AND highlight = 1 AND code NOT LIKE "' . pSQL(CartRule::BO_ORDER_CODE_PREFIX) . '%"';
|
|
}
|
|
|
|
$sql = '(SELECT SQL_NO_CACHE ' . $sql_part1 . ' WHERE cr.`id_customer` = ' . (int) $id_customer . ' ' . $sql_part2 . ')';
|
|
$sql .= ' UNION (SELECT ' . $sql_part1 . ' WHERE cr.`group_restriction` = 1 ' . $sql_part2 . ')';
|
|
if ($includeGeneric && (int) $id_customer != 0) {
|
|
$sql .= ' UNION (SELECT ' . $sql_part1 . ' WHERE cr.`id_customer` = 0 ' . $sql_part2 . ')';
|
|
}
|
|
|
|
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql, true, false);
|
|
|
|
if (empty($result)) {
|
|
return array();
|
|
}
|
|
|
|
// Remove cart rule that does not match the customer groups
|
|
$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]);
|
|
}
|
|
}
|
|
|
|
foreach ($result as &$cart_rule) {
|
|
if ($cart_rule['quantity_per_user']) {
|
|
$quantity_used = Order::getDiscountsCustomer((int) $id_customer, (int) $cart_rule['id_cart_rule']);
|
|
if (isset($cart) && isset($cart->id)) {
|
|
$quantity_used += $cart->getDiscountsCustomer((int) $cart_rule['id_cart_rule']);
|
|
}
|
|
$cart_rule['quantity_for_user'] = $cart_rule['quantity_per_user'] - $quantity_used;
|
|
} else {
|
|
$cart_rule['quantity_for_user'] = 0;
|
|
}
|
|
}
|
|
unset($cart_rule);
|
|
|
|
foreach ($result as $key => $cart_rule) {
|
|
if ($cart_rule['shop_restriction']) {
|
|
$cartRuleShops = Db::getInstance()->executeS('SELECT id_shop FROM ' . _DB_PREFIX_ . 'cart_rule_shop WHERE id_cart_rule = ' . (int) $cart_rule['id_cart_rule']);
|
|
foreach ($cartRuleShops as $cartRuleShop) {
|
|
if (Shop::isFeatureActive() && ($cartRuleShop['id_shop'] == Context::getContext()->shop->id)) {
|
|
continue 2;
|
|
}
|
|
}
|
|
unset($result[$key]);
|
|
}
|
|
}
|
|
|
|
if (isset($cart) && isset($cart->id)) {
|
|
foreach ($result as $key => $cart_rule) {
|
|
if ($cart_rule['product_restriction']) {
|
|
$cr = new CartRule((int) $cart_rule['id_cart_rule']);
|
|
$r = $cr->checkProductRestrictions(Context::getContext(), false, false);
|
|
if ($r !== false) {
|
|
continue;
|
|
}
|
|
unset($result[$key]);
|
|
}
|
|
}
|
|
}
|
|
$result_bak = $result;
|
|
$result = array();
|
|
$country_restriction = false;
|
|
foreach ($result_bak as $key => $cart_rule) {
|
|
if ($cart_rule['country_restriction']) {
|
|
$country_restriction = true;
|
|
$countries = Db::getInstance()->ExecuteS('
|
|
SELECT `id_country`
|
|
FROM `' . _DB_PREFIX_ . 'address`
|
|
WHERE `id_customer` = ' . (int) $id_customer . '
|
|
AND `deleted` = 0'
|
|
);
|
|
|
|
if (is_array($countries) && count($countries)) {
|
|
foreach ($countries as $country) {
|
|
$id_cart_rule = (bool) Db::getInstance()->getValue('
|
|
SELECT crc.id_cart_rule
|
|
FROM ' . _DB_PREFIX_ . 'cart_rule_country crc
|
|
WHERE crc.id_cart_rule = ' . (int) $cart_rule['id_cart_rule'] . '
|
|
AND crc.id_country = ' . (int) $country['id_country']);
|
|
if ($id_cart_rule) {
|
|
$result[] = $result_bak[$key];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
$result[] = $result_bak[$key];
|
|
}
|
|
}
|
|
|
|
if (!$country_restriction) {
|
|
$result = $result_bak;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Check if this CartRule can be applied
|
|
*
|
|
* @param Context $context Context instance
|
|
* @param bool $alreadyInCart Check if the voucher is already on the cart
|
|
* @param bool $display_error Display error
|
|
*
|
|
* @return bool|mixed|string
|
|
*/
|
|
public function checkValidity(Context $context, $alreadyInCart = false, $display_error = true, $check_carrier = true)
|
|
{
|
|
if (!CartRule::isFeatureActive()) {
|
|
return false;
|
|
}
|
|
|
|
if (!$this->active) {
|
|
return (!$display_error) ? false : $this->trans('This voucher is disabled', array(), 'Shop.Notifications.Error');
|
|
}
|
|
if (!$this->quantity) {
|
|
return (!$display_error) ? false : $this->trans('This voucher has already been used', array(), 'Shop.Notifications.Error');
|
|
}
|
|
if (strtotime($this->date_from) > time()) {
|
|
return (!$display_error) ? false : $this->trans('This voucher is not valid yet', array(), 'Shop.Notifications.Error');
|
|
}
|
|
if (strtotime($this->date_to) < time()) {
|
|
return (!$display_error) ? false : $this->trans('This voucher has expired', array(), 'Shop.Notifications.Error');
|
|
}
|
|
|
|
if ($context->cart->id_customer) {
|
|
$quantityUsed = Db::getInstance()->getValue('
|
|
SELECT count(*)
|
|
FROM ' . _DB_PREFIX_ . 'orders o
|
|
LEFT JOIN ' . _DB_PREFIX_ . 'order_cart_rule od ON o.id_order = od.id_order
|
|
WHERE o.id_customer = ' . $context->cart->id_customer . '
|
|
AND od.id_cart_rule = ' . (int) $this->id . '
|
|
AND ' . (int) Configuration::get('PS_OS_ERROR') . ' != o.current_state
|
|
');
|
|
if ($quantityUsed + 1 > $this->quantity_per_user) {
|
|
return (!$display_error) ? false : $this->trans('You cannot use this voucher anymore (usage limit reached)', array(), 'Shop.Notifications.Error');
|
|
}
|
|
}
|
|
|
|
// Get an intersection of the customer groups and the cart rule groups (if the customer is not logged in, the default group is Visitors)
|
|
if ($this->group_restriction) {
|
|
$id_cart_rule = (int) Db::getInstance()->getValue('
|
|
SELECT crg.id_cart_rule
|
|
FROM ' . _DB_PREFIX_ . 'cart_rule_group crg
|
|
WHERE crg.id_cart_rule = ' . (int) $this->id . '
|
|
AND crg.id_group ' . ($context->cart->id_customer ? 'IN (SELECT cg.id_group FROM ' . _DB_PREFIX_ . 'customer_group cg WHERE cg.id_customer = ' . (int) $context->cart->id_customer . ')' : '= ' . (int) Configuration::get('PS_UNIDENTIFIED_GROUP')));
|
|
if (!$id_cart_rule) {
|
|
return (!$display_error) ? false : $this->trans('You cannot use this voucher', array(), 'Shop.Notifications.Error');
|
|
}
|
|
}
|
|
|
|
// Check if the customer delivery address is usable with the cart rule
|
|
if ($this->country_restriction) {
|
|
if (!$context->cart->id_address_delivery) {
|
|
return (!$display_error) ? false : $this->trans('You must choose a delivery address before applying this voucher to your order', array(), 'Shop.Notifications.Error');
|
|
}
|
|
$id_cart_rule = (int) Db::getInstance()->getValue('
|
|
SELECT crc.id_cart_rule
|
|
FROM ' . _DB_PREFIX_ . 'cart_rule_country crc
|
|
WHERE crc.id_cart_rule = ' . (int) $this->id . '
|
|
AND crc.id_country = (SELECT a.id_country FROM ' . _DB_PREFIX_ . 'address a WHERE a.id_address = ' . (int) $context->cart->id_address_delivery . ' LIMIT 1)');
|
|
if (!$id_cart_rule) {
|
|
return (!$display_error) ? false : $this->trans('You cannot use this voucher in your country of delivery', array(), 'Shop.Notifications.Error');
|
|
}
|
|
}
|
|
|
|
// Check if the carrier chosen by the customer is usable with the cart rule
|
|
if ($this->carrier_restriction && $check_carrier) {
|
|
if (!$context->cart->id_carrier) {
|
|
return (!$display_error) ? false : $this->trans('You must choose a carrier before applying this voucher to your order', array(), 'Shop.Notifications.Error');
|
|
}
|
|
$id_cart_rule = (int) Db::getInstance()->getValue('
|
|
SELECT crc.id_cart_rule
|
|
FROM ' . _DB_PREFIX_ . 'cart_rule_carrier crc
|
|
INNER JOIN ' . _DB_PREFIX_ . 'carrier c ON (c.id_reference = crc.id_carrier AND c.deleted = 0)
|
|
WHERE crc.id_cart_rule = ' . (int) $this->id . '
|
|
AND c.id_carrier = ' . (int) $context->cart->id_carrier);
|
|
if (!$id_cart_rule) {
|
|
return (!$display_error) ? false : $this->trans('You cannot use this voucher with this carrier', array(), 'Shop.Notifications.Error');
|
|
}
|
|
}
|
|
|
|
if ($this->reduction_exclude_special) {
|
|
$products = $context->cart->getProducts();
|
|
$is_ok = false;
|
|
foreach ($products as $product) {
|
|
if (!$product['reduction_applies']) {
|
|
$is_ok = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!$is_ok) {
|
|
return (!$display_error) ? false : $this->trans('You cannot use this voucher on products on sale', array(), 'Shop.Notifications.Error');
|
|
}
|
|
}
|
|
|
|
// Check if the cart rules appliy to the shop browsed by the customer
|
|
if ($this->shop_restriction && $context->shop->id && Shop::isFeatureActive()) {
|
|
$id_cart_rule = (int) Db::getInstance()->getValue('
|
|
SELECT crs.id_cart_rule
|
|
FROM ' . _DB_PREFIX_ . 'cart_rule_shop crs
|
|
WHERE crs.id_cart_rule = ' . (int) $this->id . '
|
|
AND crs.id_shop = ' . (int) $context->shop->id);
|
|
if (!$id_cart_rule) {
|
|
return (!$display_error) ? false : $this->trans('You cannot use this voucher', array(), 'Shop.Notifications.Error');
|
|
}
|
|
}
|
|
|
|
// Check if the products chosen by the customer are usable with the cart rule
|
|
if ($this->product_restriction) {
|
|
$r = $this->checkProductRestrictions($context, false, $display_error, $alreadyInCart);
|
|
if ($r !== false && $display_error) {
|
|
return $r;
|
|
} elseif (!$r && !$display_error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check if the cart rule is only usable by a specific customer, and if the current customer is the right one
|
|
if ($this->id_customer && $context->cart->id_customer != $this->id_customer) {
|
|
if (!Context::getContext()->customer->isLogged()) {
|
|
return (!$display_error) ? false : ($this->trans('You cannot use this voucher', array(), 'Shop.Notifications.Error') . ' - ' . $this->trans('Please log in first', array(), 'Shop.Notifications.Error'));
|
|
}
|
|
return (!$display_error) ? false : $this->trans('You cannot use this voucher', array(), 'Shop.Notifications.Error');
|
|
}
|
|
|
|
if ($this->minimum_amount && $check_carrier) {
|
|
// Minimum amount is converted to the contextual currency
|
|
$minimum_amount = $this->minimum_amount;
|
|
if ($this->minimum_amount_currency != Context::getContext()->currency->id) {
|
|
$minimum_amount = Tools::convertPriceFull($minimum_amount, new Currency($this->minimum_amount_currency), Context::getContext()->currency);
|
|
}
|
|
|
|
$cartTotal = $context->cart->getOrderTotal($this->minimum_amount_tax, Cart::ONLY_PRODUCTS);
|
|
if ($this->minimum_amount_shipping) {
|
|
$cartTotal += $context->cart->getOrderTotal($this->minimum_amount_tax, Cart::ONLY_SHIPPING);
|
|
}
|
|
$products = $context->cart->getProducts();
|
|
$cart_rules = $context->cart->getCartRules();
|
|
|
|
foreach ($cart_rules as &$cart_rule) {
|
|
if ($cart_rule['gift_product']) {
|
|
foreach ($products as $key => &$product) {
|
|
if (empty($product['gift']) && $product['id_product'] == $cart_rule['gift_product'] && $product['id_product_attribute'] == $cart_rule['gift_product_attribute']) {
|
|
$cartTotal = Tools::ps_round($cartTotal - $product[$this->minimum_amount_tax ? 'price_wt' : 'price'], (int) $context->currency->decimals * _PS_PRICE_COMPUTE_PRECISION_);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($cartTotal < $minimum_amount) {
|
|
return (!$display_error) ? false : $this->trans('You have not reached the minimum amount required to use this voucher', array(), 'Shop.Notifications.Error');
|
|
}
|
|
}
|
|
|
|
/* This loop checks:
|
|
- if the voucher is already in the cart
|
|
- if a non compatible voucher is in the cart
|
|
- if there are products in the cart (gifts excluded)
|
|
Important note: this MUST be the last check, because if the tested cart rule has priority over a non combinable one in the cart, we will switch them
|
|
*/
|
|
$nb_products = Cart::getNbProducts($context->cart->id);
|
|
$otherCartRules = array();
|
|
if ($check_carrier) {
|
|
$otherCartRules = $context->cart->getCartRules();
|
|
}
|
|
if (count($otherCartRules)) {
|
|
foreach ($otherCartRules as $otherCartRule) {
|
|
if ($otherCartRule['id_cart_rule'] == $this->id && !$alreadyInCart) {
|
|
return (!$display_error) ? false : $this->trans('This voucher is already in your cart', array(), 'Shop.Notifications.Error');
|
|
}
|
|
if ($otherCartRule['gift_product'] && $context->cart->containsProduct($otherCartRule['gift_product'], $otherCartRule['gift_product_attribute']) > 0) {
|
|
--$nb_products;
|
|
}
|
|
|
|
if ( ( (!$this->cart_rule_restriction && !$otherCartRule['cart_rule_restriction']) || ($this->cart_rule_restriction && $otherCartRule['cart_rule_restriction']) ) && $otherCartRule['id_cart_rule'] != $this->id) {
|
|
$combinable = Db::getInstance()->getValue('
|
|
SELECT id_cart_rule_1
|
|
FROM ' . _DB_PREFIX_ . 'cart_rule_combination
|
|
WHERE (id_cart_rule_1 = ' . (int) $this->id . ' AND id_cart_rule_2 = ' . (int) $otherCartRule['id_cart_rule'] . ')
|
|
OR (id_cart_rule_2 = ' . (int) $this->id . ' AND id_cart_rule_1 = ' . (int) $otherCartRule['id_cart_rule'] . ')');
|
|
if (!$combinable) {
|
|
$cart_rule = new CartRule((int) $otherCartRule['id_cart_rule'], $context->cart->id_lang);
|
|
// The cart rules are not combinable and the cart rule currently in the cart has priority over the one tested
|
|
if ($cart_rule->priority <= $this->priority) {
|
|
return (!$display_error) ? false : $this->trans('This voucher is not combinable with an other voucher already in your cart: %s', array($cart_rule->name), 'Shop.Notifications.Error');
|
|
} else {
|
|
// But if the cart rule that is tested has priority over the one in the cart, we remove the one in the cart and keep this new one
|
|
$context->cart->removeCartRule($cart_rule->id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$nb_products) {
|
|
return (!$display_error) ? false : $this->trans('Cart is empty', array(), 'Shop.Notifications.Error');
|
|
}
|
|
|
|
if (!$display_error) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
}
|