Files
2026-09-15 10:35:56 +08:00

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