166 lines
5.1 KiB
PHP
166 lines
5.1 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Esun\FreeGift\Model;
|
|
|
|
use Magento\Catalog\Api\ProductRepositoryInterface;
|
|
use Magento\Catalog\Model\Product\Type;
|
|
use Magento\Framework\DataObjectFactory;
|
|
use Magento\Framework\Exception\LocalizedException;
|
|
use Magento\Framework\Serialize\Serializer\Json;
|
|
use Magento\Quote\Model\Quote;
|
|
use Magento\Quote\Model\Quote\Item;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
class GiftSynchronizer
|
|
{
|
|
public function __construct(
|
|
private readonly Config $config,
|
|
private readonly ProductRepositoryInterface $productRepository,
|
|
private readonly DataObjectFactory $dataObjectFactory,
|
|
private readonly Json $json,
|
|
private readonly InternalAddGuard $internalAddGuard,
|
|
private readonly LoggerInterface $logger
|
|
) {
|
|
}
|
|
|
|
public function synchronize(Quote $quote, float $eligibleBaseSubtotal): bool
|
|
{
|
|
$storeId = (int)$quote->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;
|
|
}
|
|
}
|