<?php declare(strict_types=1);
namespace GoodaheadCart\Subscriber;
use Shopware\Core\Checkout\Payment\Cart\Error\PaymentMethodBlockedError;
use Shopware\Core\Checkout\Payment\PaymentMethodEntity;
use Shopware\Core\Checkout\Promotion\Cart\PromotionItemBuilder;
use Shopware\Core\Checkout\Promotion\Gateway\PromotionGateway;
use Shopware\Core\Checkout\Promotion\Gateway\Template\PermittedGlobalCodePromotions;
use Shopware\Core\Checkout\Promotion\Gateway\Template\PermittedIndividualCodePromotions;
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\Validation\DataBag\DataBag;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Core\Checkout\Cart\Event\BeforeLineItemAddedEvent;
use Shopware\Core\Checkout\Promotion\PromotionCollection;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\System\SalesChannel\SalesChannel\SalesChannelContextSwitcher;
use Shopware\Core\Framework\Context;
class BeforeLineItemAdded implements EventSubscriberInterface
{
protected PromotionGateway $gateway;
protected PromotionItemBuilder $itemBuilder;
protected EntityRepositoryInterface $ruleRepository;
protected SalesChannelContextSwitcher $salesChannelContextSwitcher;
public function __construct(
PromotionGateway $gateway,
PromotionItemBuilder $itemBuilder,
SalesChannelContextSwitcher $salesChannelContextSwitcher,
EntityRepositoryInterface $ruleRepository
) {
$this->gateway = $gateway;
$this->itemBuilder = $itemBuilder;
$this->ruleRepository = $ruleRepository;
$this->salesChannelContextSwitcher = $salesChannelContextSwitcher;
}
public static function getSubscribedEvents(): array
{
// Return the events to listen to as array like this: <event to listen to> => <method to execute>
return [
BeforeLineItemAddedEvent::class => 'onLineItemAdded',
];
}
public function onLineItemAdded(BeforeLineItemAddedEvent $event)
{
// return $this;
$lineItem = $event->getLineItem();
if ($lineItem->getType() != 'promotion') {
return $this;
}
$currentCode = $lineItem->getReferencedId();
$foundPromotions = $this->getMatchedPromotions($currentCode, $event->getSalesChannelContext());
if (!\is_iterable($foundPromotions)) {
return $this;
}
foreach ($foundPromotions->getElements() as $promotion) {
if ($promotion->getCartRules() && \is_iterable($promotion->getCartRules()->getElements())) {
foreach ($promotion->getCartRules()->getElements() as $rule) {
$ruleCriteria = new Criteria([$rule->getId()]);
$ruleCriteria->addAssociation('conditions');
$loadedRule = $this->ruleRepository->search($ruleCriteria, $event->getSalesChannelContext()->getContext())->first();
if ($loadedRule->getConditions() && \is_iterable($loadedRule->getConditions()->getElements())) {
foreach ($loadedRule->getConditions()->getElements() as $element) {
if ($element->getType() == 'paymentMethod' && \is_iterable($element->getValue())) {
foreach ($element->getValue() as $key => $value) {
if ($key == 'operator' && $value === '=') {
$paymentMethodIds = isset($element->getValue()['paymentMethodIds']) ? $element->getValue()['paymentMethodIds'] : [];
if (\is_iterable($paymentMethodIds)) {
foreach ($paymentMethodIds as $paymentMethodId) {
$dataBag = new DataBag(['paymentMethodId' => $paymentMethodId]);
try {
$this->salesChannelContextSwitcher->update($dataBag, $event->getSalesChannelContext());
$this->cleanCartErrors($event->getCart());
return $this;
} catch (\Exception $e) {
// TODO: add logging here
}
}
}
}
}
}
}
}
}
}
}
return $this;
}
public function validatePromotions(\Shopware\Core\Checkout\Cart\Cart $cart, SalesChannelContext $salesChannelContext)
{
foreach ($cart->getLineItems() as $lineItem) {
if ($lineItem->getType() != 'promotion') {
continue;
}
$currentCode = $lineItem->getReferencedId();
$foundPromotions = $this->getMatchedPromotions($currentCode, $salesChannelContext);
if (!\is_iterable($foundPromotions)) {
return $this;
}
foreach ($foundPromotions->getElements() as $promotion) {
if ($promotion->getCartRules() && \is_iterable($promotion->getCartRules()->getElements())) {
foreach ($promotion->getCartRules()->getElements() as $rule) {
$ruleCriteria = new Criteria([$rule->getId()]);
$ruleCriteria->addAssociation('conditions');
$loadedRule = $this->ruleRepository->search($ruleCriteria, $salesChannelContext->getContext())->first();
if ($loadedRule->getConditions() && \is_iterable($loadedRule->getConditions()->getElements())) {
foreach ($loadedRule->getConditions()->getElements() as $element) {
if ($element->getType() == 'paymentMethod' && \is_iterable($element->getValue())) {
foreach ($element->getValue() as $key => $value) {
if ($key == 'operator' && $value === '=') {
$paymentMethodIds = isset($element->getValue()['paymentMethodIds']) ? $element->getValue()['paymentMethodIds'] : [];
if (\is_iterable($paymentMethodIds)) {
foreach ($paymentMethodIds as $paymentMethodId) {
$dataBag = new DataBag(['paymentMethodId' => $paymentMethodId]);
try {
$this->salesChannelContextSwitcher->update($dataBag, $salesChannelContext);
$this->cleanCartErrors($cart);
return $this;
} catch (\Exception $e) {
// TODO: add logging here
}
}
}
}
}
}
}
}
}
}
}
}
return $this;
}
protected function getMatchedPromotions($promoCode, $salesChannelContext): EntityCollection
{
$foundPromotions = $this->getGlobalCodePromotion($promoCode, $salesChannelContext);
if (\count($foundPromotions->getElements()) <= 0) {
// no global code, so try with an individual code instead
$foundPromotions = $this->getIndividualMatchedPromotions($promoCode, $salesChannelContext);
}
return $foundPromotions;
}
protected function getGlobalCodePromotion($promoCode, $salesChannelContext): EntityCollection
{
$globalCriteria = (new Criteria())->addFilter(
new PermittedGlobalCodePromotions([$promoCode], $salesChannelContext->getSalesChannelId())
);
foreach ($this->getPromoAssociations() as $association) {
$globalCriteria->addAssociation($association);
}
return $this->gateway->get($globalCriteria, $salesChannelContext);
}
protected function getIndividualMatchedPromotions($promoCode, $salesChannelContext): EntityCollection
{
$individualCriteria = (new Criteria())->addFilter(
new PermittedIndividualCodePromotions([$promoCode], $salesChannelContext->getSalesChannelId())
);
foreach ($this->getPromoAssociations() as $association) {
$individualCriteria->addAssociation($association);
}
/** @var PromotionCollection $foundPromotions */
return $this->gateway->get($individualCriteria, $salesChannelContext);
}
private function getPromoAssociations(): array
{
return [
'personaRules',
'personaCustomers',
'cartRules',
'orderRules',
'discounts.discountRules',
'discounts.promotionDiscountPrices',
'setgroups',
'setgroups.setGroupRules',
];
}
private function cleanCartErrors($cart)
{
foreach ($cart->getErrors()->getElements() as $key => $cartError) {
if ($cartError instanceof PaymentMethodBlockedError) {
$cart->getErrors()->remove($key);
}
}
}
}