getStoreId(); if ($storeId <= 0) { return false; } $targetQuantity = $this->config->getTargetQuantity($eligibleBaseSubtotal, $storeId); $giftItems = $this->getGiftItems($quote); if ($targetQuantity <= 0) { return $this->removeGiftItems($quote, $giftItems); } $changed = false; $configuredGiftItems = []; foreach ($giftItems as $giftItem) { if ($this->config->isGiftSku((string)$giftItem->getSku(), $storeId)) { $configuredGiftItems[] = $giftItem; continue; } $quote->deleteItem($giftItem); $changed = true; } if (!$configuredGiftItems) { return $this->addGiftItem($quote, $targetQuantity, $storeId) || $changed; } $primaryItem = array_shift($configuredGiftItems); if ($primaryItem instanceof Item) { $changed = $this->prepareGiftItem($primaryItem, $targetQuantity) || $changed; } foreach ($configuredGiftItems as $duplicateItem) { $quote->deleteItem($duplicateItem); $changed = true; } return $changed; } /** @return Item[] */ private function getGiftItems(Quote $quote): array { $items = []; foreach ($quote->getAllVisibleItems() as $item) { if ((bool)$item->getData(GiftItem::FLAG)) { $items[] = $item; } } return $items; } /** @param Item[] $giftItems */ private function removeGiftItems(Quote $quote, array $giftItems): bool { foreach ($giftItems as $giftItem) { $quote->deleteItem($giftItem); } return $giftItems !== []; } private function addGiftItem(Quote $quote, int $quantity, int $storeId): bool { $sku = $this->config->getGiftSku($storeId); try { $product = clone $this->productRepository->get($sku, false, $storeId, true); if ($product->getTypeId() !== Type::TYPE_SIMPLE) { throw new LocalizedException(__('The configured free gift must be a simple product.')); } $product->setStoreId($storeId); $product->setIsSuperMode(true); $product->addCustomOption(GiftItem::OPTION_CODE, '1'); $product->addCustomOption( 'additional_options', $this->json->serialize([ [ 'label' => (string)__('Promotion'), 'value' => (string)__('Free Gift') ] ]) ); $request = $this->dataObjectFactory->create(['data' => ['qty' => $quantity]]); $result = $this->internalAddGuard->execute( static fn () => $quote->addProduct($product, $request) ); if (!$result instanceof Item) { throw new LocalizedException(__(is_string($result) ? $result : 'The free gift could not be added.')); } $this->prepareGiftItem($result, $quantity); return true; } catch (\Throwable $exception) { $this->logger->error( 'Unable to add the configured automatic free gift.', [ 'sku' => $sku, 'store_id' => $storeId, 'exception' => $exception ] ); return false; } } private function prepareGiftItem(Item $item, int $quantity): bool { $changed = abs((float)$item->getQty() - $quantity) > 0.0001 || !(bool)$item->getData(GiftItem::FLAG) || $item->getCustomPrice() === null || abs((float)$item->getCustomPrice()) > 0.0001; $item->setQty($quantity); $item->setData(GiftItem::FLAG, 1); $item->setCustomPrice(0.0); $item->setOriginalCustomPrice(0.0); $item->setOriginalPrice(0.0); $item->setBaseOriginalPrice(0.0); $item->setNoDiscount(1); $item->setDiscountAmount(0.0); $item->setBaseDiscountAmount(0.0); $item->setAppliedRuleIds(null); $item->getProduct()->setIsSuperMode(true); return $changed; } }