118 lines
3.9 KiB
PHP
118 lines
3.9 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Esun\Cart\Observer;
|
|
|
|
use Esun\Catalog\Model\MixStepPriceProvider;
|
|
use Magento\Catalog\Api\ProductRepositoryInterface;
|
|
use Magento\Catalog\Model\Product;
|
|
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
|
|
use Magento\Framework\Event\Observer;
|
|
use Magento\Framework\Event\ObserverInterface;
|
|
use Magento\Framework\Exception\NoSuchEntityException;
|
|
use Magento\Quote\Model\Quote;
|
|
use Magento\Quote\Model\Quote\Item;
|
|
|
|
class ApplyMixStepPrices implements ObserverInterface
|
|
{
|
|
private const APPLIED_FLAG = 'esun_mix_price_applied';
|
|
|
|
public function __construct(
|
|
private readonly ProductRepositoryInterface $productRepository,
|
|
private readonly MixStepPriceProvider $mixStepPriceProvider
|
|
) {
|
|
}
|
|
|
|
public function execute(Observer $observer): void
|
|
{
|
|
$quote = $observer->getEvent()->getQuote();
|
|
if (!$quote instanceof Quote || !$quote->getStoreId()) {
|
|
return;
|
|
}
|
|
|
|
$groups = [];
|
|
foreach ($quote->getAllVisibleItems() as $item) {
|
|
if ($item->getProductType() !== Configurable::TYPE_CODE) {
|
|
continue;
|
|
}
|
|
$groups[(int)$item->getProductId()][] = $item;
|
|
}
|
|
|
|
foreach ($groups as $parentProductId => $items) {
|
|
try {
|
|
$parentProduct = $this->productRepository->getById(
|
|
$parentProductId,
|
|
false,
|
|
(int)$quote->getStoreId()
|
|
);
|
|
} catch (NoSuchEntityException) {
|
|
continue;
|
|
}
|
|
|
|
$rules = $this->mixStepPriceProvider->getRules(
|
|
$parentProduct,
|
|
(int)$quote->getCustomerGroupId()
|
|
);
|
|
if (!$rules) {
|
|
$this->clearPreviousMixPrices($items);
|
|
continue;
|
|
}
|
|
|
|
$combinedQuantity = array_reduce(
|
|
$items,
|
|
static fn (float $quantity, Item $item): float => $quantity + (float)$item->getQty(),
|
|
0.0
|
|
);
|
|
$activeRule = $this->mixStepPriceProvider->getActiveRule($rules, $combinedQuantity);
|
|
|
|
foreach ($items as $item) {
|
|
$basePrice = $this->getChildBasePrice($item, $quote);
|
|
if ($basePrice === null) {
|
|
continue;
|
|
}
|
|
|
|
$unitPrice = round(
|
|
$this->mixStepPriceProvider->calculateUnitPrice($basePrice, $activeRule),
|
|
4
|
|
);
|
|
$item->setCustomPrice($unitPrice);
|
|
$item->setOriginalCustomPrice($unitPrice);
|
|
$item->setOriginalPrice($basePrice);
|
|
$item->setBaseOriginalPrice($basePrice);
|
|
$item->setData(self::APPLIED_FLAG, 1);
|
|
$item->getProduct()->setIsSuperMode(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The quantity-one price includes Customer Group Price, Special Price and
|
|
* Catalog Price Rules, while deliberately excluding child SKU quantity tiers.
|
|
*/
|
|
private function getChildBasePrice(Item $item, Quote $quote): ?float
|
|
{
|
|
$childProduct = $item->getOptionByCode('simple_product')?->getProduct();
|
|
if (!$childProduct instanceof Product || !(int)$childProduct->getId()) {
|
|
return null;
|
|
}
|
|
|
|
$childProduct->setStoreId((int)$quote->getStoreId());
|
|
$childProduct->setCustomerGroupId((int)$quote->getCustomerGroupId());
|
|
|
|
return (float)$childProduct->getPriceModel()->getFinalPrice(1, $childProduct);
|
|
}
|
|
|
|
/** @param Item[] $items */
|
|
private function clearPreviousMixPrices(array $items): void
|
|
{
|
|
foreach ($items as $item) {
|
|
if (!(bool)$item->getData(self::APPLIED_FLAG)) {
|
|
continue;
|
|
}
|
|
$item->setCustomPrice(null);
|
|
$item->setOriginalCustomPrice(null);
|
|
$item->setData(self::APPLIED_FLAG, 0);
|
|
}
|
|
}
|
|
}
|