<?php declare(strict_types=1);
namespace EsmConfigurator\Storefront\Subscriber;
use EsmConfigurator\Core\Content\ConfigurationItem\ConfigurationItemEntity;
use Shopware\Core\Checkout\Cart\Event\BeforeLineItemAddedEvent;
use Shopware\Core\Checkout\Cart\Event\CheckoutOrderPlacedEvent;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Cart\LineItem\LineItemCollection;
use Shopware\Core\Checkout\Cart\Price\QuantityPriceCalculator;
use Shopware\Core\Checkout\Cart\Price\Struct\QuantityPriceDefinition;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemCollection;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepositoryInterface;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Storefront\Controller\StorefrontController;
use Shopware\Storefront\Page\Checkout\Cart\CheckoutCartPageLoadedEvent;
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoadedEvent;
use Shopware\Storefront\Page\Checkout\Offcanvas\OffcanvasCartPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Contracts\Translation\TranslatorInterface;
use Throwable;
use function array_column;
use function array_key_exists;
use function array_shift;
use function count;
use function implode;
use function in_array;
class CheckoutSubscriber implements EventSubscriberInterface
{
protected QuantityPriceCalculator $quantityPriceCalculator;
protected Session $session;
protected TranslatorInterface $translator;
protected EntityRepository $orderRepository;
protected EntityRepository $configurationItemRepository;
protected SalesChannelRepositoryInterface $salesChannelProductRepository;
public function __construct(
QuantityPriceCalculator $quantityPriceCalculator,
Session $session,
TranslatorInterface $translator,
EntityRepository $orderRepository,
EntityRepository $configurationItemRepository,
SalesChannelRepositoryInterface $salesChannelProductRepository
)
{
$this->quantityPriceCalculator = $quantityPriceCalculator;
$this->session = $session;
$this->translator = $translator;
$this->orderRepository = $orderRepository;
$this->configurationItemRepository = $configurationItemRepository;
$this->salesChannelProductRepository = $salesChannelProductRepository;
}
public static function getSubscribedEvents(): array
{
return [
OffcanvasCartPageLoadedEvent::class => 'onOffcanvasCartPageLoadedEvent',
CheckoutCartPageLoadedEvent::class => 'onCheckoutCartPageLoadedEvent',
CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmPageLoadedEvent',
CheckoutFinishPageLoadedEvent::class => 'onCheckoutFinishPageLoadedEvent',
BeforeLineItemAddedEvent::class => 'onBeforeLineItemAddedEvent',
CheckoutOrderPlacedEvent::class => 'onCheckoutOrderPlacedEvent'
];
}
public function onCheckoutOrderPlacedEvent(CheckoutOrderPlacedEvent $event)
{
try {
$lineItems = $event->getOrder()->getLineItems();
$comment = '';
/** @var OrderLineItemEntity $lineItem */
foreach ($lineItems as $lineItem) {
if (array_key_exists('children', $lineItem->getPayload())) {
$childIds = $lineItem->getPayload()['children'];
$comment .= 'Konfiguration für ' . $lineItem->getPayload()['productNumber'] . ' => ';
foreach ($childIds as $childId) {
$child = $lineItems->filterByProperty('productId', $childId)->first();
if (!$child) {
continue;
}
$comment .= $child->getPayload()['productNumber'] . ' (' . $lineItem->getQuantity() . 'x - ' . $child->getPayload()['configurationGroupTitle'] . ') ';
}
}
}
// The customer comment is saved by default, we only need to intervene, if there is an upgrade comment
if($comment) {
// Add our internal upgrade comment
$mergedComment = [$comment];
// Add customer comment, if it exists
if($customerComment = $event->getOrder()->getCustomerComment()) {
$mergedComment[] = $customerComment;
}
$this->orderRepository->update([
[
'id' => $event->getOrder()->getId(),
'customerComment' => implode(' | ', $mergedComment)
]
], $event->getContext());
}
}catch (Throwable $e) {
error_log($e->getMessage());
}
}
public function onBeforeLineItemAddedEvent(BeforeLineItemAddedEvent $event): void
{
try {
// If "upgrade" is already present (upgrade could be a standalone docking station)
if(array_key_exists('lineItems', $_POST) && count($_POST['lineItems']) === 2 && in_array('upgrade', array_column($_POST['lineItems'], 'type'), true)) {
$parentProduct = array_shift($_POST['lineItems']);
$upgrade = array_shift($_POST['lineItems']);
$criteria = new Criteria([$upgrade['id']]);
$criteria->addAssociation('product');
/** @var ConfigurationItemEntity $configurationItem */
$configurationItem = $this->configurationItemRepository->search($criteria, $event->getContext())->first();
if(!$configurationItem || !$configurationItem->getProduct()) {
return;
}
$productId = $configurationItem->getProduct()->getId();
foreach ($event->getCart()->getLineItems() as $cartLineItem) {
if ($cartLineItem->getId() === $productId && !$cartLineItem->getPayloadValue('configurationItemId')) {
$this->session->getFlashBag()->add(StorefrontController::DANGER, $this->translator->trans('checkout.upgrade_already_basket_article'));
$parentLineItem = $event->getCart()->getLineItems()->get($parentProduct['id']);
if($parentLineItem) {
$event->getCart()->remove($parentProduct['id']);
}
}
}
}
// If article with upgrade is already present
$lineItem = $event->getCart()->getLineItems()->get($event->getLineItem()->getId());
if(!$lineItem) {
return;
}
if($event->isMerged() && $lineItem->getPayloadValue('parentLineItems')) {
$this->session->getFlashBag()->add(StorefrontController::DANGER, $this->translator->trans('checkout.article_exists_as_upgrade'));
}
} catch (Throwable $e) {
mail('dev@sicor-kdl.net', 'Fehler im Warenkorb: ' . $e->getLine(), $e->getMessage());
}
}
public function onOffcanvasCartPageLoadedEvent(OffcanvasCartPageLoadedEvent $event): void
{
$this->onCheckoutAction($event);
}
public function onCheckoutCartPageLoadedEvent(CheckoutCartPageLoadedEvent $event): void
{
$this->onCheckoutAction($event);
}
public function onCheckoutConfirmPageLoadedEvent(CheckoutConfirmPageLoadedEvent $event): void
{
$this->onCheckoutAction($event);
}
public function onCheckoutFinishPageLoadedEvent(CheckoutFinishPageLoadedEvent $event): void
{
$lineItems = $event->getPage()->getOrder()->getLineItems();
if($lineItems) {
$event->getPage()->getOrder()->addExtension('lineItems', $this->addOrderLineItemChildrenToParent($lineItems, $event->getSalesChannelContext()));
}
}
protected function onCheckoutAction($event): void
{
$lineItems = $event->getPage()->getCart()->getLineItems();
if($this->session->getFlashBag()->peek('danger')) {
$this->session->getFlashBag()->get('success');
}
$originalLineItems = clone $lineItems;
/** @var LineItemCollection $originalLineItems */
foreach($originalLineItems as $originalLineItem) {
/** @var ProductEntity $product */
$product = $this->salesChannelProductRepository->search(new Criteria([$originalLineItem->getId()]), $event->getSalesChannelContext())->first();
if($product) {
$originalLineItem->setPayload(['productNumber' => $product->getProductNumber()]);
}
}
$event->getPage()->addExtension('originalLineItems', $originalLineItems);
$event->getPage()->getCart()->setLineItems($this->addLineItemChildrenToParent($lineItems, $event->getSalesChannelContext()));
}
private function addLineItemChildrenToParent(LineItemCollection $lineItems, SalesChannelContext $context): LineItemCollection
{
/** @var LineItem $lineItem */
foreach($lineItems as $lineItem) {
if($lineItem->getPayloadValue('parentLineItems')) {
foreach ($lineItem->getPayloadValue('parentLineItems') as $parentLineItemId) {
$parentLineItem = $lineItems->get($parentLineItemId);
if(!$parentLineItem) {
continue;
}
/** @var LineItem $parentLineItem */
if (array_key_exists('productPrice', $lineItem->getPayload())) {
$newUpgradeLineItem = clone $lineItem;
$priceDefinition = new QuantityPriceDefinition($lineItem->getPayloadValue('productPrice'), $context->buildTaxRules($lineItem->getPayloadValue('productTaxId')), $parentLineItem->getQuantity());
$newUpgradeLineItem->setQuantity($parentLineItem->getQuantity());
$newUpgradeLineItem->setPriceDefinition($priceDefinition);
$newUpgradeLineItem->setPrice($this->quantityPriceCalculator->calculate($priceDefinition, $context));
$parentLineItem->getChildren()->add($newUpgradeLineItem);
if($lineItem->getQuantity() - $parentLineItem->getQuantity() <= 0) {
$lineItems->removeElement($lineItem);
}else {
$lineItem->setQuantity($lineItem->getQuantity() - $parentLineItem->getQuantity());
}
}
}
}
}
return $lineItems;
}
private function addOrderLineItemChildrenToParent(OrderLineItemCollection $lineItems, SalesChannelContext $context): OrderLineItemCollection
{
foreach($lineItems as $key => $lineItem) {
if (array_key_exists('parentLineItems', $lineItem->getPayload())) {
foreach ($lineItem->getPayload()['parentLineItems'] as $parentLineItemId) {
$parentLineItem = $lineItems->filterByProperty('productId', $parentLineItemId)->first();
if(!$parentLineItem) {
continue;
}
/** @var OrderLineItemEntity $parentLineItem */
if (array_key_exists('productPrice', $lineItem->getPayload())) {
$newUpgradeLineItem = clone $lineItem;
$priceDefinition = new QuantityPriceDefinition($lineItem->getPayload()['productPrice'], $context->buildTaxRules($lineItem->getPayload()['productTaxId']), $parentLineItem->getQuantity());
$newUpgradeLineItem->setQuantity($parentLineItem->getQuantity());
$newUpgradeLineItem->setPriceDefinition($priceDefinition);
$newUpgradeLineItem->setPrice($this->quantityPriceCalculator->calculate($priceDefinition, $context));
if (!$parentLineItem->getExtension('children') instanceof OrderLineItemCollection) {
$parentLineItem->addExtension('children', new OrderLineItemCollection([]));
}
// Dirty workaround, because $parentLineItem->setChildren() doesn't change items (always empty) -> used in finish.html.twig
$parentLineItem->getExtension('children')->add($newUpgradeLineItem);
if ($lineItem->getQuantity() - $parentLineItem->getQuantity() <= 0) {
$lineItems->remove($key);
} else {
$lineItem->setQuantity($lineItem->getQuantity() - $parentLineItem->getQuantity());
}
}
}
}
}
return $lineItems;
}
}