<?php declare(strict_types=1);
namespace AlphaplanPriceConnector\Subscriber;
use AlphaplanPriceConnector\DataAbstractionLayer\ApPriceHelper;
use Doctrine\DBAL\Connection;
use Shopware\Core\Checkout\Cart\Price\QuantityPriceCalculator;
use Shopware\Core\Content\Product\Aggregate\ProductPrice\ProductPriceCollection;
use Shopware\Core\Content\Product\Aggregate\ProductPrice\ProductPriceEntity;
use Shopware\Core\Content\Product\SalesChannel\Price\AbstractProductPriceCalculator;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
use Shopware\Core\Framework\DataAbstractionLayer\Pricing\Price;
use Shopware\Core\Framework\DataAbstractionLayer\Pricing\PriceCollection;
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Core\System\SalesChannel\Entity\SalesChannelEntityLoadedEvent;
use Shopware\Core\System\SystemConfig\SystemConfigService;
class ApPriceSubscriber implements EventSubscriberInterface
{
/**
* @var ContainerInterface
*/
protected ContainerInterface $container;
/**
* @var SystemConfigService
*/
private SystemConfigService $systemConfigService;
/**
* @var ApPriceHelper
*/
private ApPriceHelper $apPriceHelper;
private QuantityPriceCalculator $priceCalculator;
/**
* @var AbstractProductPriceCalculator
*/
private $calculator;
public function __construct(
ContainerInterface $container,
QuantityPriceCalculator $priceCalculator,
AbstractProductPriceCalculator $calculator,
SystemConfigService $systemConfigService
)
{
$this->container = $container;
$this->priceCalculator = $priceCalculator;
$this->systemConfigService = $systemConfigService;
$this->calculator = $calculator;
$this->apPriceHelper = new ApPriceHelper($this->container->get(Connection::class), $container);
}
public static function getSubscribedEvents(): array
{
return [
'sales_channel.product.loaded' => 'loaded',
];
}
public function loaded(SalesChannelEntityLoadedEvent $event): void
{
/** @var SalesChannelProductEntity $product */
foreach ($event->getEntities() as $product) {
$this->calculateApPrices($event->getSalesChannelContext(), $product);
}
$this->calculator->calculate($event->getEntities(), $event->getSalesChannelContext());
/** @var SalesChannelProductEntity $product */
foreach ($event->getEntities() as $product) {
$product->setCalculatedMaxPurchase(
$this->calculateMaxPurchase($product, $event->getSalesChannelContext()->getSalesChannel()->getId())
);
$this->markAsNew($event->getSalesChannelContext(), $product);
}
}
private function calculateApPrices(SalesChannelContext $context, SalesChannelProductEntity $product): void
{
$custId = $context->getCustomer() ? $context->getCustomer()->getId() : '0';
$priceSessionIdentifier = $this->apPriceHelper->apGetPriceSessionIdentifier($context, $custId, $product->getId());
if(!isset($_SESSION['ap-tmp-prices'][$priceSessionIdentifier])){
$productApPrice = $this->apPriceHelper->getApPrice($product->getId(), $context, $context->getCustomer());
$_SESSION['ap-tmp-prices'][$priceSessionIdentifier] = $productApPrice;
}else{
$productApPrice = $_SESSION['ap-tmp-prices'][$priceSessionIdentifier];
}
$validRule = $this->apPriceHelper->getApAlwaysValidRule($context);
if (!empty($productApPrice) && $validRule) {
$newApProductPrices = array();
$newApProductPriceCollection = new ProductPriceCollection();
$apTax = $this->apPriceHelper->getApTax($context, $productApPrice[0]['ArtikelID']);
if (!empty($apTax)) {
$product->setTax($context->getTaxRules()->get($apTax['taxId']));
}else{
$apStandardTax = $this->apPriceHelper->getStandardApTax($context, $productApPrice[0]['ArtikelID']);
if (!empty($apStandardTax)) {
$product->setTax($context->getTaxRules()->get($apStandardTax['taxId']));
}
}
foreach ($productApPrice as $index => $apPrice) {
$productApPriceVENettoBase = $productApPriceVENetto = (float)$apPrice['PENetto'];
$productApDiscount = $this->apPriceHelper->getApDiscount($apPrice, $product->getId(), $context, $context->getCustomer());
$apCustomFields = $product->getCustomFields();
if (!empty($productApDiscount)) {
$ApNewDiscountedPrice = $this->apPriceHelper->getApNewDiscountedPrice($productApPriceVENetto, $productApDiscount, $apCustomFields);
$productApPriceVENetto = $ApNewDiscountedPrice['apPrice'];
$newApProductDiscount = new Price($context->getCurrency()->getId(),
$productApPriceVENettoBase,
$productApPriceVENettoBase + (($productApPriceVENettoBase * $product->getTax()->getTaxRate()) / 100),
$product->getPrice()->get($context->getCurrency()->getId())->getLinked(),
$product->getPrice()->get($context->getCurrency()->getId())->getListPrice());
$product->setCustomFields($ApNewDiscountedPrice['apCustomFields']);
} else {
$newApProductDiscount = $product->getPrice()->get($context->getCurrency()->getId())->getListPrice();
}
$newApProductPrice = new Price($context->getCurrency()->getId(),
$productApPriceVENetto,
$productApPriceVENetto + (($productApPriceVENetto * $product->getTax()->getTaxRate()) / 100),
$product->getPrice()->get($context->getCurrency()->getId())->getLinked(),
$newApProductDiscount);
$quantityStart = $apPrice['PEAnzahl'] ? $apPrice['PEAnzahl'] : 1;
$quantityEnd = isset($productApPrice[$index + 1]) ? $productApPrice[$index + 1]['PEAnzahl'] - 1 : null;
$newApProductPriceEntity = (new ProductPriceEntity())
->assign(
[
'id' => Uuid::randomHex(),
'productId' => $product->getId(),
'quantityStart' => (int)$quantityStart,
'quantityEnd' => $quantityEnd,
'ruleId' => $validRule->getRuleId(),
'price' => new PriceCollection([$newApProductPrice]),
]
);
$newApProductPrices[] = $newApProductPrice;
$newApProductPriceCollection->add($newApProductPriceEntity);
}
$newApPriceCollection = new PriceCollection();
$newApPriceCollection->add($newApProductPrices[0]);
$product->setPrice($newApPriceCollection);
if (count($newApProductPrices) > 1) {
$product->setPrices($newApProductPriceCollection);
} else {
$product->setPrices(new ProductPriceCollection());
}
}else{
$product->setAvailable(false);
$product->setActive(false);
$product->setIsCloseout(true);
}
}
private function calculateMaxPurchase(SalesChannelProductEntity $product, string $salesChannelId): int
{
$fallback = $this->systemConfigService->getInt('core.cart.maxQuantity', $salesChannelId);
$max = $product->getMaxPurchase() ?? $fallback;
if ($product->getIsCloseout() && $product->getAvailableStock() < $max) {
$max = (int) $product->getAvailableStock();
}
$max = floor($max / ($product->getPurchaseSteps() ?? 1)) * $product->getPurchaseSteps();
return (int) max($max, 0);
}
private function markAsNew(SalesChannelContext $context, SalesChannelProductEntity $product): void
{
$markAsNewDayRange = $this->systemConfigService->get('core.listing.markAsNew', $context->getSalesChannel()->getId());
$now = new \DateTime();
/* @var SalesChannelProductEntity $product */
$product->setIsNew(
$product->getReleaseDate() instanceof \DateTimeInterface
&& $product->getReleaseDate()->diff($now)->days <= $markAsNewDayRange
);
}
}