<?php
declare(strict_types=1);
namespace WbfkExtensions\Subscriber;
use Shopware\Core\Checkout\Cart\Delivery\Struct\ShippingLocation;
use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryEntity;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionDefinition;
use Shopware\Core\Checkout\Order\Event\OrderStateChangeCriteriaEvent;
use Shopware\Core\Checkout\Payment\PaymentMethodEntity;
use Shopware\Core\Content\Product\Aggregate\ProductVisibility\ProductVisibilityDefinition;
use Shopware\Core\Content\Product\SalesChannel\ProductAvailableFilter;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
use Shopware\Core\Defaults;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
use Shopware\Core\Framework\Util\Random;
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Core\PlatformRequest;
use Shopware\Core\System\Country\CountryEntity;
use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceParameters;
use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepositoryInterface;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\StateMachine\Aggregation\StateMachineTransition\StateMachineTransitionActions;
use Shopware\Core\System\StateMachine\Event\StateMachineTransitionEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use WbfkExtensions\Core\Checkout\Cart\Delivery\ExpectedProductDeliveryTimeByQuantity\ExpectedProductDeliveryTimeByQuantityCollection;
use WbfkExtensions\Core\Checkout\Cart\Delivery\ExpectedProductDeliveryTimeByQuantity\ExpectedProductDeliveryTimeByQuantityEntity;
use WbfkExtensions\Core\Checkout\Cart\Delivery\ExpectedProductDeliveryTimeByQuantity\ExpectedProductDeliveryTimeByQuantityService;
use WbfkExtensions\Core\Checkout\Cart\Delivery\ShippingAndDeliveryInformationService;
class ExpectedLineItemDeliverySubscriber implements EventSubscriberInterface
{
public function __construct(
private readonly EntityRepository $orderLineItemRepository,
private readonly ExpectedProductDeliveryTimeByQuantityService $expectedProductDeliveryTimeByQuantityService,
private readonly SalesChannelRepositoryInterface $salesChannelProductRepository,
private readonly ShippingAndDeliveryInformationService $shippingAndDeliveryInformationService
)
{
}
public static function getSubscribedEvents(): array
{
return [
StateMachineTransitionEvent::class => 'onOrderPaidCreateExpectedLineItemDeliveries',
OrderStateChangeCriteriaEvent::class => 'onOrderStateChangeCriteriaEvent'
];
}
/**
* When an order is paid, update existing order item expected delivery times
* If payment method is ...
* - Invoice: Delivery happens before payment. This is why we don't need to update the expected delivery time,
* because the delivery time, which was calculated during the checkout, is still correct.
* - NOT invoice: Delivery happens after payment. The shipping process starts now (not earlier).
* This is why we have to recalculate the expected delivery time for the line items.
*
* @throws \Exception
*/
public function onOrderPaidCreateExpectedLineItemDeliveries(StateMachineTransitionEvent $event): void
{
if ($event->getEntityName() === OrderTransactionDefinition::ENTITY_NAME
&& $event->getToPlace()->getTechnicalName() === StateMachineTransitionActions::ACTION_PAID
) {
$orderTransactionId = $event->getEntityId();
$orderLineItemCriteria = new Criteria();
$orderLineItemCriteria->addAssociation('expectedProductDeliveryTimeByQuantityCollection');
$expectedDeliveryTimeAssociation = $orderLineItemCriteria->getAssociation('expectedProductDeliveryTimeByQuantityCollection');
$expectedDeliveryTimeAssociation->addFilter(new EqualsFilter('isChild', false));
$orderLineItemCriteria->addAssociation('order.deliveries.shippingOrderAddress.country');
$deliveriesAssociation = $orderLineItemCriteria->getAssociation('order.deliveries');
$deliveriesAssociation->addSorting(new FieldSorting('createdAt', FieldSorting::ASCENDING));
$orderLineItemCriteria->addAssociation('order.transactions.paymentMethod');
$transactionsAssociation = $orderLineItemCriteria->getAssociation('order.transactions');
$transactionsAssociation->addSorting(new FieldSorting('createdAt', FieldSorting::ASCENDING));
$orderLineItemCriteria->addFilter(
new EqualsFilter('order.transactions.id', $orderTransactionId),
new NotFilter(NotFilter::CONNECTION_AND, [new EqualsFilter('productId', null)])
);
$updatedExpectedDeliveryTimes = new ExpectedProductDeliveryTimeByQuantityCollection();
$orderLineItems = $this->orderLineItemRepository->search($orderLineItemCriteria, $event->getContext());
/** @var OrderLineItemEntity $orderLineItem */
foreach ($orderLineItems as $orderLineItem) {
// Skip this line item, if it has no expected delivery times
$expectedDeliveryTimes = $orderLineItem->getExtension(ExpectedProductDeliveryTimeByQuantityCollection::LINE_ITEM_EXTENSION_KEY);
if (!$expectedDeliveryTimes || $expectedDeliveryTimes->count() === 0) continue;
// Recalculate expected delivery times only, when the payment method is not "Invoice" and a delivery country is available
/** @var PaymentMethodEntity $paymentMethod */
$paymentMethod = $orderLineItem->getOrder()->getTransactions()->last()?->getPaymentMethod();
/** @var CountryEntity $country */
$country = $orderLineItem->getOrder()->getDeliveries()->last()?->getShippingOrderAddress()?->getCountry();
if (!in_array($paymentMethod->getName(), ['Rechnungskauf', 'Invoice']) && !!$country) {
/** @var ExpectedProductDeliveryTimeByQuantityEntity $expectedDeliveryTime */
foreach ($expectedDeliveryTimes as $expectedDeliveryTime) {
$this->shippingAndDeliveryInformationService->recalculateExpectedDeliveryDates($expectedDeliveryTime, $country, $event->getContext());
}
}
/** @var ExpectedProductDeliveryTimeByQuantityEntity $expectedDeliveryTime */
foreach ($expectedDeliveryTimes as $expectedDeliveryTime) {
$expectedDeliveryTime->setIsReminderMailScheduled(true);
$updatedExpectedDeliveryTimes->add($expectedDeliveryTime);
}
}
$this->expectedProductDeliveryTimeByQuantityService->updateExpectedDeliveryTimes($updatedExpectedDeliveryTimes, $event->getContext());
}
}
/**
* @param string $productId
* @param SalesChannelContext $salesChannelContext
* @return SalesChannelProductEntity|null
*/
private function fetchSalesChannelProduct(string $productId, SalesChannelContext $salesChannelContext): ?SalesChannelProductEntity
{
$criteria = new Criteria([$productId]);
$criteria->addAssociation('shippingAndDeliveryInformations');
$criteria->addAssociation('productSuppliers.wbfkSupplier');
$results = $this->salesChannelProductRepository->search($criteria, $salesChannelContext);
if ($results->count() > 0) return $results->first();
return null;
}
/**
* @param OrderStateChangeCriteriaEvent $event
* @return void
*/
public function onOrderStateChangeCriteriaEvent(OrderStateChangeCriteriaEvent $event)
{
$criteria = $event->getCriteria();
$criteria->addAssociation('lineItems.' . ExpectedProductDeliveryTimeByQuantityCollection::LINE_ITEM_EXTENSION_KEY);
}
}