<?php
declare(strict_types=1);
namespace WbfkExtensions\Subscriber;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception;
use RuntimeException;
use Shopware\Core\Checkout\Cart\Event\CheckoutOrderPlacedEvent;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Cart\Order\CartConvertedEvent;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity;
use Shopware\Core\Defaults;
use Shopware\Core\Framework\Uuid\Uuid;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use WbfkExtensions\Core\Checkout\Cart\Delivery\DeliveryMode;
use WbfkExtensions\Core\Checkout\Cart\Delivery\ExpectedProductDeliveryTimeByQuantity\ExpectedProductDeliveryTimeByQuantityCollection;
use WbfkExtensions\Core\Checkout\Cart\Delivery\ExpectedProductDeliveryTimeByQuantity\ExpectedProductDeliveryTimeByQuantityEntity;
use WbfkExtensions\Core\Checkout\Cart\Delivery\ExpectedProductDeliveryTimeByQuantity\ExpectedProductDeliveryTimeByQuantityService;
class AttachDeliveryTimeDataToOrder implements EventSubscriberInterface
{
public function __construct(
private readonly ExpectedProductDeliveryTimeByQuantityService $expectedProductDeliveryTimeByQuantityService,
private readonly Connection $connection
) {
}
public static function getSubscribedEvents(): array
{
return [
CartConvertedEvent::class => 'onCartConvertedEvent',
CheckoutOrderPlacedEvent::class => 'onCheckoutOrderPlacedEvent',
];
}
public function onCartConvertedEvent(CartConvertedEvent $event)
{
$this->setDeliveryModeCustomField($event);
$this->addDeliveryTimeDataToConvertedCart($event);
}
private function setDeliveryModeCustomField(CartConvertedEvent $event): void
{
$cart = $event->getCart();
$convertedCart = $event->getConvertedCart();
$customFields = $convertedCart['customFields'] ?? [];
$customFields[DeliveryMode::CART_EXTENSION_KEY] = $cart->getExtension(
DeliveryMode::CART_EXTENSION_KEY
);
$convertedCart['customFields'] = $customFields;
$event->setConvertedCart($convertedCart);
}
public function addDeliveryTimeDataToConvertedCart(CartConvertedEvent $event): void
{
$cart = $event->getCart();
$convertedCart = $event->getConvertedCart();
$lineItems = $cart->getLineItems()->filterFlatByType(LineItem::PRODUCT_LINE_ITEM_TYPE);
$extensionKey = ExpectedProductDeliveryTimeByQuantityCollection::LINE_ITEM_EXTENSION_KEY;
foreach ($lineItems as $lineItem) {
$lineItemDataKey = $this->findLineItemDataKey($event->getConvertedCart(), $lineItem);
if ($lineItemDataKey === null) {
throw new RuntimeException('Could not find line item data key');
}
// Store products expected delivery time by quantity in converted cart if available
$convertedCart['lineItems'][$lineItemDataKey]['payload'][$extensionKey] = $lineItem->getExtension($extensionKey);
}
$event->setConvertedCart($convertedCart);
}
private function findLineItemDataKey(array $convertedCart, LineItem $lineItem): int|string|null
{
foreach ($convertedCart['lineItems'] as $key => $lineItemData) {
if ($lineItemData['referencedId'] === $lineItem->getReferencedId()) {
return $key;
}
}
return null;
}
/**
* @throws Exception
*/
public function onCheckoutOrderPlacedEvent(CheckoutOrderPlacedEvent $event)
{
$order = $event->getOrder();
$extensionKey = ExpectedProductDeliveryTimeByQuantityCollection::LINE_ITEM_EXTENSION_KEY;
$orderLineItems = $order->getLineItems()->filter(fn(OrderLineItemEntity $orderLineItem) => $orderLineItem->getType() === LineItem::PRODUCT_LINE_ITEM_TYPE);
/** @var OrderLineItemEntity $orderLineItem */
foreach ($orderLineItems as $orderLineItem) {
$lineItemPayload = $orderLineItem->getPayload();
/** @var ?ExpectedProductDeliveryTimeByQuantityCollection $expectedProductDeliveryTimesByQuantity */
$expectedProductDeliveryTimesByQuantity = $lineItemPayload[$extensionKey] ?? null;
if (!$expectedProductDeliveryTimesByQuantity || empty($expectedProductDeliveryTimesByQuantity)) {
continue;
}
$expectedProductDeliveryTimesByQuantity = $this->restoreExpectedDeliveryByQuantityFromArray($expectedProductDeliveryTimesByQuantity);
if ($expectedProductDeliveryTimesByQuantity->count() === 0) {
continue;
}
// In case the payment method is "Invoice", activate the expected delivery reminder mail right away (no need to wait for the payment)
/** @var ?OrderTransactionEntity $transaction */
$transaction = $order->getTransactions()->last();
if (!!$transaction && in_array($transaction->getPaymentMethod()->getName(), ['Invoice', 'Rechnungskauf'])) {
/** @var ExpectedProductDeliveryTimeByQuantityEntity $expectedDeliveryTime */
foreach ($expectedProductDeliveryTimesByQuantity as $expectedDeliveryTime) {
$expectedDeliveryTime->setIsReminderMailScheduled(true);
}
}
// Save expectedProductDeliveryTimesByQuantity as entities
/** @var ExpectedProductDeliveryTimeByQuantityEntity $expectedDeliveryTime */
foreach ($expectedProductDeliveryTimesByQuantity as $expectedDeliveryTime) {
$expectedDeliveryTime->setOrderLineItemId($orderLineItem->getId());
$expectedDeliveryTime->setOrderId($orderLineItem->getOrderId());
}
$this->expectedProductDeliveryTimeByQuantityService->saveCollection($expectedProductDeliveryTimesByQuantity, $event->getContext());
$orderLineItem->addExtension($extensionKey, $expectedProductDeliveryTimesByQuantity);
// Remove expectedProductDeliveryTimesByQuantity from the line item payload
unset($lineItemPayload[$extensionKey]);
$orderLineItem->setPayload($lineItemPayload);
$updateLineItemPayloadSql = <<<SQL
UPDATE order_line_item SET payload = :payload WHERE id = :id AND version_id = :version_id
SQL;
$this->connection->executeStatement($updateLineItemPayloadSql, [
'payload' => json_encode($lineItemPayload),
'id' => Uuid::fromHexToBytes($orderLineItem->getId()),
'version_id' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION),
]);
}
}
/**
* @throws \Exception
*/
private function restoreExpectedDeliveryByQuantityFromArray(array $items): ExpectedProductDeliveryTimeByQuantityCollection
{
$expectedDeliveryTimeByQuantityCollection = new ExpectedProductDeliveryTimeByQuantityCollection();
foreach ($items as $item) {
$expectedDeliveryTimeByQuantityCollection->add(
ExpectedProductDeliveryTimeByQuantityEntity::create(
$item['orderLineItemId'] ?? null,
$item['orderId'] ?? null,
$item['productId'] ?? null,
$item['productVersionId'] ?? null,
$item['isChild'] ?? false,
$item['quantity'] ?? null,
$item['availableStock'] ?? null,
$item['supplierId'] ?? null,
$item['supplierPriority'] ?? null,
$item['supplierDisplayName'] ?? null,
$item['expectedMinimumReadyToShipDate'] ?? null,
$item['expectedMaximumReadyToShipDate'] ?? null,
$item['expectedMinimumDeliveryDate'] ?? null,
$item['expectedMaximumDeliveryDate'] ?? null,
$item['reminderMailScheduled'] ?? false,
$item['reminderMailSentAt'] ?? null
)
);
}
return $expectedDeliveryTimeByQuantityCollection;
}
}