context = adp_context(); $rule = $contextOrRule instanceof PersistentRule ? $contextOrRule : $deprecated; if ( ! ($rule instanceof PersistentRule)) { $this->context->handleError(new Exception("Wrong rule type")); } $this->rule = clone $rule; $this->originalRule = $rule; $this->conditionsCheckStrategy = new ConditionsCheckStrategy($rule); $this->limitsCheckStrategy = new LimitsCheckStrategy($rule); $this->ruleUsedStock = new ProductStockController(); $this->giftStrategy = Factory::get('Core_RuleProcessor_GiftStrategy', $rule, $this->ruleUsedStock); $this->autoAddStrategy = Factory::get('Core_RuleProcessor_AutoAddStrategy', $rule, $this->ruleUsedStock); $this->activationTriggerStrategy = new ActivationTriggerStrategy($rule); $this->exclusivityStrategy = new ExclusivityAllStrategy($rule); $this->roleDiscountStrategy = new RoleDiscountStrategy($rule); } public function withContext(Context $context) { $this->context = $context; } public function getStatus() { return $this->status; } /** * @return PersistentRule */ public function getRule() { return $this->originalRule; } public function applyToCart($cart) { return false; } /** * @param Cart $cart * @param ICartItem $item * * @return bool */ public function applyToCartItem($cart, $item) { if ($item->hasAttr(CartItemAttributeEnum::IMMUTABLE())) { return true; } global $wp_filter; $currentWpFilter = $wp_filter; $this->process($cart, $item); $wp_filter = $currentWpFilter; return true; } /** * @param Cart $cart * @param ICartItem $item * @param float $price * * @return bool */ public function applyPriceToCartItem($cart, $item, $price) { if ($item->hasAttr(CartItemAttributeEnum::IMMUTABLE())) { return true; } global $wp_filter; $currentWpFilter = $wp_filter; $this->processWithPrice($cart, $item, $price); $wp_filter = $currentWpFilter; return true; } /** * @param Cart $cart * @param ICartItem $item * @param float $price */ protected function processWithPrice($cart, $item, $price) { $this->ruleUsedStock->initFromCart($cart); $this->status = self::STATUS_STARTED; $this->rule = apply_filters('adp_before_apply_rule', $this->rule, $this, $cart); if ( ! apply_filters('adp_is_apply_rule', true, $this->rule, $this, $cart)) { $this->status = self::STATUS_DISABLED_WITH_FORCE; return; } if ( ! $this->activationTriggerStrategy->canBeAppliedByDate($cart)) { $this->status = self::STATUS_DISABLED_BY_DATE; return; } if ( ! $this->activationTriggerStrategy->canBeAppliedUsingCouponCode($cart)) { $this->status = self::STATUS_DISABLED_BY_COUPON_CODE_TRIGGER; return; } if ( ! $this->isRuleMatchedCart($cart)) { return; } $collection = new CartItemsCollection($this->rule->getId()); $collection->add($item); if ( $item->getAddonsAmount() > 0 ) { // recalculate price because the item has changed its price and structure $this->applyProductAdjustment($cart, $collection); } else { $this->applyProductAdjustmentWithPrice($cart, $item, $price); } $this->addFreeProducts($cart, $collection); $this->addGifts($cart, $collection); $this->addAutoAddProducts($cart, $collection); $this->addGifts($cart, $collection); $this->exclusivityStrategy->makeAffectedItemAsExclusive($collection->get_items()); $this->applyChangesToCart($cart, $collection); } /** * @param Cart $cart * @param ICartItem $item * @param float $price */ private function applyProductAdjustmentWithPrice($cart, $item, $price) { if ( $handler = $this->rule->getProductAdjustmentHandler() ) { $isReplaceWithCoupon = $handler->isReplaceWithCartAdjustment(); $replaceCouponName = $handler->getReplaceCartAdjustmentCode(); } elseif ($handler = $this->rule->getProductRangeAdjustmentHandler()) { $isReplaceWithCoupon = $handler->isReplaceWithCartAdjustment(); $replaceCouponName = $handler->getReplaceCartAdjustmentCode(); } else { $isReplaceWithCoupon = false; $replaceCouponName = ''; } $globalContext = $cart->getContext()->getGlobalContext(); $amount = ($item->getPrice() - $price) * $item->getQty(); $priceAdjBuilder = CartItemPriceAdjustment::builder(); $priceAdjBuilder->source(CartItemPriceUpdateSourceEnum::SOURCE_PRODUCT_ONLY_LOAD()) ->type(CartItemPriceUpdateTypeEnum::DEFAULT()) ->ruleId($this->rule->getId()) ->originalPrice($item->getPrice()) ->newPrice($price) ->amount($item->getPrice() - $price); if ($isReplaceWithCoupon) { $adjustmentCode = $replaceCouponName; $priceAdjBuilder->type(CartItemPriceUpdateTypeEnum::REPLACED_BY_CART_ADJUSTMENT()); if ($amount > 0) { $cart->addCoupon( new CouponCartItem( $globalContext, CouponCartItem::TYPE_ITEM_DISCOUNT, $adjustmentCode, $amount / $item->getQty(), $this->rule->getId(), $item->getWcItem() ) ); } elseif ($amount < 0) { $taxClass = $globalContext->getIsPricesIncludeTax() ? "" : "standard"; $cart->addFee( new Fee( $globalContext, Fee::TYPE_ITEM_OVERPRICE, $adjustmentCode, (-1) * $amount, $taxClass, $this->rule->getId() ) ); } } elseif ($globalContext->getOption('item_adjustments_as_coupon', false) && $globalContext->getOption('item_adjustments_coupon_name', false) ) { $adjustmentCode = $globalContext->getOption('item_adjustments_coupon_name'); $priceAdjBuilder->type(CartItemPriceUpdateTypeEnum::REPLACED_BY_CART_ADJUSTMENT()); if ($amount > 0) { $cart->addCoupon( new CouponCartItem( $globalContext, CouponCartItem::TYPE_ITEM_DISCOUNT, $adjustmentCode, $amount / $item->getQty(), $this->rule->getId(), $item->getWcItem() ) ); } elseif ($amount < 0) { $taxClass = $globalContext->getIsPricesIncludeTax() ? "" : "standard"; $cart->addFee( new Fee( $globalContext, Fee::TYPE_ITEM_OVERPRICE, $adjustmentCode, (-1) * $amount, $taxClass, $this->rule->getId() ) ); } } $item->applyPriceAdjustment($priceAdjBuilder->build()); } /** * @param Cart $cart * @param ICartItem $item */ protected function process($cart, $item) { $this->rule = apply_filters('adp_before_apply_rule', $this->rule, $this, $cart); if ( ! apply_filters('adp_is_apply_rule', true, $this->rule, $this, $cart)) { $this->status = self::STATUS_DISABLED_WITH_FORCE; return; } $collection = new CartItemsCollection($this->rule->getId()); $collection->add($item); $this->applyProductAdjustment($cart, $collection); $this->status = self::STATUS_SUCCESSFULLY_COMPLETED; } /** * @param Cart $cart * @param CartItemsCollection $collection */ protected function applyProductAdjustment(&$cart, &$collection) { if ($handler = $this->rule->getProductAdjustmentHandler()) { /** @var PriceCalculator $priceCalculator */ $priceCalculator = Factory::get( "Core_RuleProcessor_PriceCalculator", $this->rule, $handler->getDiscount(), $handler->getMaxAvailableAmount() ); foreach ($collection->get_items() as &$item) { $priceCalculator->applyItemDiscount($item, $cart, $handler); } } // $this->applyRangeDiscounts($cart, $collection); if ( ! $this->rule->getRoleDiscounts() && ! $this->rule->getProductRangeAdjustmentHandler()) { return; } elseif ( ! $this->rule->getRoleDiscounts()) { $this->applyRangeDiscounts($cart, $collection); } elseif ( ! $this->rule->getProductRangeAdjustmentHandler()) { $this->roleDiscountStrategy->processItems($cart, $collection); } } /** * @param Cart $cart * @param CartItemsCollection $collection * * @throws Exception */ protected function applyRangeDiscounts(&$cart, &$collection) { if ( ! ($handler = $this->rule->getProductRangeAdjustmentHandler())) { return; } $ranges = $handler->getRanges(); // Add a "dummy" range, so all always start from 1 $firstRange = reset($ranges); if ($firstRange && $firstRange->getFrom() > 1) { $context = $cart->getContext()->getGlobalContext(); $discount = new Discount($context, Discount::TYPE_PERCENTAGE, 0); $discount = new RangeDiscount(1, $firstRange->getFrom() - 1, $discount); $ranges = array_merge(array($discount), $ranges); // at first position! $handler->setRanges($ranges); } if ($handler::TYPE_BULK === $handler->getType()) { $bulkProcessor = new SingleItemRuleBulkDiscountProcessor(); $groupedItems = $bulkProcessor->calculateItems($this->rule, $collection); foreach ( $groupedItems as $items ) { $value = $bulkProcessor->calculateMeasurementValue($this->rule, $cart, $collection, $items); $bulkProcessor->discountItems($this->rule, $cart, $value, $items); } } elseif ($handler::TYPE_TIER === $handler->getType()) { if ($handler::GROUP_BY_DEFAULT === $handler->getGroupBy()) { $cal = new TierUpItems($this->rule, $cart); $newCollection = new CartItemsCollection($this->rule->getId()); foreach ($cal->executeItems($collection->get_items()) as $item) { $newCollection->add($item); } $collection = $newCollection; } elseif ($handler::GROUP_BY_PRODUCT === $handler->getGroupBy()) { $groupedByProduct = array(); foreach ($collection->get_items() as $item) { $productId = $item->getWcItem()->getProductId(); if ( ! isset($groupedByProduct[$productId])) { $groupedByProduct[$productId] = array(); } $groupedByProduct[$productId][$item->getHash()] = $item; } $cal = new TierUpItems($this->rule, $cart); $newCollection = new CartItemsCollection($this->rule->getId()); foreach ($groupedByProduct as $items) { foreach ($cal->executeItems($items) as $item) { $newCollection->add($item); } } $collection = $newCollection; } elseif ($handler::GROUP_BY_VARIATION === $handler->getGroupBy()) { $groupedByVariation = array(); foreach ($collection->get_items() as $item) { if ($item->getWcItem()->getVariationId()) { $productId = $item->getWcItem()->getVariationId(); } else { $productId = $item->getWcItem()->getProductId(); } if ( ! isset($groupedByVariation[$productId])) { $groupedByVariation[$productId] = array(); } $groupedByVariation[$productId][$item->getHash()] = $item; } $cal = new TierUpItems($this->rule, $cart); $newCollection = new CartItemsCollection($this->rule->getId()); foreach ($groupedByVariation as $items) { foreach ($cal->executeItems($items) as $item) { $newCollection->add($item); } } $collection = $newCollection; } elseif ($handler::GROUP_BY_PRODUCT_SELECTED_PRODUCTS === $handler->getGroupBy()) { $selectedProductIds = $handler->getSelectedProductIds(); $totalQty = floatval(0); if ($selectedProductIds) { foreach (array_merge($collection->get_items(), $cart->getItems()) as $cartItem) { /** @var BasicCartItem $cartItem */ $facade = $cartItem->getWcItem(); if ( ! $facade->isVisible()) { continue; } if (in_array($facade->getProduct()->get_id(), $selectedProductIds)) { $totalQty += $facade->getQty(); } } $cal = new TierUpItems($this->rule, $cart); $newCollection = new CartItemsCollection($this->rule->getId()); foreach ($cal->executeItemsWithCustomQty($collection->get_items(), $totalQty) as $item) { $newCollection->add($item); } $collection = $newCollection; } } elseif ($handler::GROUP_BY_PRODUCT_SELECTED_CATEGORIES === $handler->getGroupBy()) { $selectedCategoryIds = $handler->getSelectedCategoryIds(); $productFiltering = Factory::get("Core_RuleProcessor_ProductFiltering", $this->context); /** @var ProductFiltering $productFiltering */ $productFiltering->prepare(Filter::TYPE_CATEGORY, $selectedCategoryIds, 'in_list'); $totalQty = floatval(0); if ($selectedCategoryIds) { foreach (array_merge($collection->get_items(), $cart->getItems()) as $cartItem) { /** @var BasicCartItem $cartItem */ $facade = $cartItem->getWcItem(); if ( ! $facade->isVisible()) { continue; } if ($productFiltering->checkProductSuitability($facade->getProduct())) { $totalQty += $facade->getQty(); } } $cal = new TierUpItems($this->rule, $cart); $newCollection = new CartItemsCollection($this->rule->getId()); foreach ($cal->executeItemsWithCustomQty($collection->get_items(), $totalQty) as $item) { $newCollection->add($item); } $collection = $newCollection; } } elseif ($handler::GROUP_BY_CART_POSITIONS === $handler->getGroupBy()) { $groupedItems = array(); foreach ($collection->get_items() as $item) { /** * @var BasicCartItem $item */ $facade = $item->getWcItem(); if ( ! isset($groupedItems[$facade->getKey()])) { $groupedItems[$facade->getKey()] = array(); } $groupedItems[$facade->getKey()][] = $item; } $cal = new TierUpItems($this->rule, $cart); $newCollection = new CartItemsCollection($this->rule->getId()); foreach ($groupedItems as $items) { foreach ($cal->executeItems($items) as $item) { $newCollection->add($item); } } $collection = $newCollection; } } } /** * @param Cart $cart * * @return bool */ public function isRuleMatchedCart($cart) { if ( ! $this->checkLimits($cart)) { $this->status = $this::STATUS_LIMITS_NOT_PASSED; return false; } if ( ! $this->checkConditions($cart)) { $this->status = $this::STATUS_CONDITIONS_NOT_PASSED; return false; } if( $this->rule->getRoleDiscounts() && !$this->roleDiscountStrategy->findMatchedRoleDiscounts($cart->getContext()->getCustomer())) { $this->status = $this::STATUS_CONDITIONS_NOT_PASSED; return false; } return true; } /** * @param Cart $cart * * @return bool */ public function isRuleOptionalMatchedCart($cart, $checkLimits = true, $checkConditions = true) { if ($checkLimits && ! $this->checkLimits($cart)) { $this->status = $this::STATUS_LIMITS_NOT_PASSED; return false; } if ($checkConditions && ! $this->checkConditions($cart)) { $this->status = $this::STATUS_CONDITIONS_NOT_PASSED; return false; } return true; } /** * @param Cart $cart * * @return bool */ protected function checkLimits($cart) { return $this->limitsCheckStrategy->check($cart); } /** * @param Cart $cart * * @return bool */ protected function checkConditions($cart) { return $this->conditionsCheckStrategy->check($cart); } /** * @param Cart $cart * * @return bool */ protected function matchConditions($cart) { return $this->conditionsCheckStrategy->match($cart); } /** * @param Cart $cart * @param CartItemsCollection $collection */ protected function applyCartAdjustments($cart, $collection) { $this->cartAdjustmentsApplyStrategy->applyToCartWithItems($cart, $collection); } /** * @param Cart $cart * @param CartItemsCollection $collection */ protected function addFreeProducts($cart, $collection) { if ( ! $this->giftStrategy->canItemGifts()) { return; } // needs for calculate limit $totalQty = floatval(0); foreach ($collection->get_items() as $item) { $totalQty += $item->getQty(); } $this->giftStrategy->addCartItemGifts($cart, $collection); } /** * @param Cart $cart * @param CartItemsCollection $collection */ protected function addGifts($cart, $collection) { if ( ! $this->giftStrategy->canGift()) { return; } $this->giftStrategy->addGifts($cart); } /** * @param Cart $cart * @param CartItemsCollection $collection */ protected function addAutoAddProducts($cart, $collection) { if ( ! $this->autoAddStrategy->canItemAutoAdds()) { return; } // needs for calculate limit $totalQty = floatval(0); foreach ($collection->get_items() as $item) { $totalQty += $item->getQty(); } $this->autoAddStrategy->addCartItemAutoAdds($cart, $collection); } /** * @param Cart $cart * @param CartItemsCollection $collection */ protected function addAutoAdds($cart, $collection) { if ( ! $this->autoAddStrategy->canAutoAdd()) { return; } $this->autoAddStrategy->addAutoAdds($cart); } /** * @return float */ public function getLastExecTime() { return 0.0; } /** * @param Cart $cart * @param WC_Product $product * @param bool $checkConditions * * @return bool */ public function isProductMatched($cart, $product, $checkConditions = false) { return false; } /** * @param Cart $cart * @param int $termId * @param bool $checkConditions * * @return bool */ public function isCategoryMatched($cart, $termId, $checkConditions = false) { if ( ! $termId) { return false; } $termId = intval($termId); if ( ! $this->checkLimits($cart)) { return false; } if ($checkConditions && ! $this->matchConditions($cart)) { return false; } /** * Item must match all filters */ $match = true; foreach ($this->rule->getFilters() as $filter) { if ( ! ($filter->getType() === $filter::TYPE_CATEGORY && in_array($termId, $filter->getValue()))) { $match = false; break; } } return $match; } protected function applyChangesToCart(&$cart, &$collection) { } }