<?php declare(strict_types=1);
namespace Acris\RuleSurchargeDiscount\Storefront\Subscriber;
use Acris\RuleSurchargeDiscount\Components\Service\SurchargeDiscountService;
use Acris\RuleSurchargeDiscount\Components\Struct\AdditionalCostStruct;
use Acris\RuleSurchargeDiscount\Core\Checkout\SurchargeDiscount\Cart\AbstractSurchargeDiscountProcessor;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Cart\LineItem\LineItemCollection;
use Shopware\Core\Checkout\Cart\Order\CartConvertedEvent;
use Shopware\Core\Checkout\Cart\Order\OrderConvertedEvent;
use Shopware\Core\Checkout\Cart\Tax\Struct\CalculatedTax;
use Shopware\Core\Checkout\Cart\Tax\Struct\TaxRule;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
use Shopware\Core\Checkout\Order\OrderEntity;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Storefront\Page\Account\Order\AccountOrderPageLoadedEvent;
use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class SurchargeDiscountSubscriber implements EventSubscriberInterface
{
public const SURCHARGE_DISCOUNT_ADDITIONAL_COSTS_DATA_KEY = 'acrisSurchargeDiscountAdditionalCosts';
public const MOLLIE_PAYMENTS_ADDITIONAL_COSTS_DATA_KEY = 'molliePaymentsData';
public const TYPE_DISCOUNT = 'discount';
private SurchargeDiscountService $surchargeDiscountService;
public function __construct(
SurchargeDiscountService $surchargeDiscountService
)
{
$this->surchargeDiscountService = $surchargeDiscountService;
}
public static function getSubscribedEvents(): array
{
return [
CartConvertedEvent::class => [
['onCartConverted', -200]
],
OrderConvertedEvent::class => [
['onOrderConverted', -200]
],
CheckoutFinishPageLoadedEvent::class => [
['onCheckoutFinishPageLoaded', -200]
],
AccountOrderPageLoadedEvent::class => [
['onAccountOrderPageLoaded', -200]
]
];
}
public function onCartConverted(CartConvertedEvent $event): void
{
$additionalCostLineItems = $this->getAdditionalCostLineItems($event->getSalesChannelContext());
$convertedCart = $this->checkLineItemQuantity($event->getConvertedCart(), $event);
if ($additionalCostLineItems->count() === 0) return;
$customFields = !empty($convertedCart) && array_key_exists('customFields', $convertedCart)
&& !empty($convertedCart['customFields'] && is_array($convertedCart['customFields'])) ? $convertedCart['customFields'] : [];
$additionalCostData = [];
foreach ($additionalCostLineItems->getElements() as $lineItem) {
$additionalCostData[] = $this->buildAdditionalCostData($lineItem);
}
if (empty($additionalCostData)) return;
$customFields[self::SURCHARGE_DISCOUNT_ADDITIONAL_COSTS_DATA_KEY] = $additionalCostData;
$convertedCart['customFields'] = $customFields;
$event->setConvertedCart($convertedCart);
}
public function onOrderConverted(OrderConvertedEvent $event): void
{
$cart = $event->getConvertedCart();
if (!empty($event->getOrder()->getCustomFields()) && is_array($event->getOrder()->getCustomFields()) && array_key_exists(self::SURCHARGE_DISCOUNT_ADDITIONAL_COSTS_DATA_KEY, $event->getOrder()->getCustomFields()) && is_array($event->getOrder()->getCustomFields()[self::SURCHARGE_DISCOUNT_ADDITIONAL_COSTS_DATA_KEY])) {
$cart->addExtension(self::SURCHARGE_DISCOUNT_ADDITIONAL_COSTS_DATA_KEY, new AdditionalCostStruct($event->getOrder()->getCustomFields()[self::SURCHARGE_DISCOUNT_ADDITIONAL_COSTS_DATA_KEY]));
} else {
if ($cart->hasExtension(self::SURCHARGE_DISCOUNT_ADDITIONAL_COSTS_DATA_KEY)) {
$cart->removeExtension(self::SURCHARGE_DISCOUNT_ADDITIONAL_COSTS_DATA_KEY);
}
}
}
public function onCheckoutFinishPageLoaded(CheckoutFinishPageLoadedEvent $event): void
{
$this->assignMediaToOrderLineItems($event->getPage()->getOrder(), $event->getSalesChannelContext());
}
public function onAccountOrderPageLoaded(AccountOrderPageLoadedEvent $event): void
{
if ($event->getPage()->getOrders()->count() === 0) return;
foreach ($event->getPage()->getOrders()->getEntities()->getElements() as $order) {
$this->assignMediaToOrderLineItems($order, $event->getSalesChannelContext());
}
}
private function getAdditionalCostLineItems(SalesChannelContext $context): LineItemCollection
{
return $context->hasExtension(AbstractSurchargeDiscountProcessor::LINE_ITEM_COLLECTION) && !empty($context->getExtension(AbstractSurchargeDiscountProcessor::LINE_ITEM_COLLECTION)) && $context->getExtension(AbstractSurchargeDiscountProcessor::LINE_ITEM_COLLECTION) instanceof LineItemCollection ? $context->getExtension(AbstractSurchargeDiscountProcessor::LINE_ITEM_COLLECTION) : new LineItemCollection();
}
private function assignMediaToOrderLineItems(OrderEntity $order, SalesChannelContext $salesChannelContext): void
{
if (empty($order->getLineItems()) || $order->getLineItems()->count() === 0) return;
/** @var OrderLineItemEntity $lineItem */
foreach ($order->getLineItems() as $lineItem) {
if ($lineItem->getType() === AbstractSurchargeDiscountProcessor::LINE_ITEM_TYPE && !empty($lineItem->getPayload()) && is_array($lineItem->getPayload()) && array_key_exists('surchargeDiscountId', $lineItem->getPayload()) && !empty($lineItem->getPayload()['surchargeDiscountId']) && array_key_exists('displayIcon', $lineItem->getPayload()) && !empty($lineItem->getPayload()['displayIcon']) && $lineItem->getPayload()['displayIcon'] === 'image' && array_key_exists('iconMediaId', $lineItem->getPayload()) && !empty($lineItem->getPayload()['iconMediaId'])) {
$this->surchargeDiscountService->loadOrderLineItemExtension($lineItem, $lineItem->getPayload()['surchargeDiscountId'], $salesChannelContext);
}
}
}
private function checkLineItemQuantity(array $convertedCart, CartConvertedEvent $event): array
{
if (empty($convertedCart) || !array_key_exists('lineItems', $convertedCart) || empty($convertedCart['lineItems']) || !is_array($convertedCart['lineItems'])) return $convertedCart;
$lineItems = $convertedCart['lineItems'];
$quantityChanged = false;
foreach ($lineItems as $key => $lineItem) {
if (empty($lineItem) || !is_array($lineItem) || !array_key_exists('type', $lineItem) || empty($lineItem['type']) || $lineItem['type'] !== AbstractSurchargeDiscountProcessor::LINE_ITEM_TYPE || !array_key_exists('quantity', $lineItem) || empty($lineItem['quantity']) || !array_key_exists('payload', $lineItem) || empty($lineItem['payload']) || !is_array($lineItem['payload'])) continue;
$quantity = $lineItem['quantity'];
$payload = $lineItem['payload'];
if (!array_key_exists('calculateQuantityResult', $payload) || empty($payload['calculateQuantityResult']) || !is_array($payload['calculateQuantityResult']) || !array_key_exists('quantity', $payload['calculateQuantityResult']) || empty($payload['calculateQuantityResult']['quantity']) || $payload['calculateQuantityResult']['quantity'] === $quantity) continue;
$lineItems[$key]['quantity'] = $payload['calculateQuantityResult']['quantity'];
$quantityChanged = true;
}
if ($quantityChanged === true) {
$convertedCart['lineItems'] = $lineItems;
$event->setConvertedCart($convertedCart);
}
return $convertedCart;
}
private function buildAdditionalCostData(LineItem $lineItem): array
{
$data = ['label' => $lineItem->getLabel(), 'originalId' => $lineItem->getId(), 'price' => ['totalPrice' => $lineItem->getPrice()->getTotalPrice()]];
// compatibility with the MolliePayments plugin and additional costs
return $this->buildMollieAdditionalCostData($data, $lineItem);
}
private function buildMollieAdditionalCostData(array $originalData, LineItem $lineItem): array
{
try {
$data = [];
if (!empty($lineItem->getPrice())) {
$data['unitPrice'] = $lineItem->getPrice()->getUnitPrice();
$data['totalPrice'] = $lineItem->getPrice()->getTotalPrice();
if (!empty($lineItem->getPrice()->getCalculatedTaxes()) && $lineItem->getPrice()->getCalculatedTaxes()->count() > 0) {
$calculatedTaxes = [];
/** @var CalculatedTax $calculatedTax */
foreach ($lineItem->getPrice()->getCalculatedTaxes()->getElements() as $key => $calculatedTax) {
$calculatedTaxes[$key] = [
'tax' => $calculatedTax->getTax(),
'taxRate' => $calculatedTax->getTaxRate(),
'price' => $calculatedTax->getPrice(),
'extensions' => $calculatedTax->getExtensions()
];
}
if (!empty($calculatedTaxes)) {
$data['calculatedTaxes'] = $calculatedTaxes;
}
}
if (!empty($lineItem->getPrice()->getTaxRules()) && $lineItem->getPrice()->getTaxRules()->count() > 0) {
$taxRules = [];
/** @var TaxRule $taxRule */
foreach ($lineItem->getPrice()->getTaxRules()->getElements() as $key => $taxRule) {
$taxRules[$key] = [
'taxRate' => $taxRule->getTaxRate(),
'percentage' => $taxRule->getPercentage(),
'extensions' => $taxRule->getExtensions()
];
}
if (!empty($taxRules)) {
$data['taxRules'] = $taxRules;
}
}
}
$data = array_merge($data, [
'id' => $lineItem->getId(),
'type' => self::TYPE_DISCOUNT,
'label' => $lineItem->getLabel(),
'quantity' => $lineItem->getQuantity()
]);
$originalData[self::MOLLIE_PAYMENTS_ADDITIONAL_COSTS_DATA_KEY] = $data;
return $originalData;
} catch (\Throwable $e) {
return $originalData;
}
}
}