Files
2019-11-17 19:14:07 +01:00

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;
}
}
}