<?php declare(strict_types=1);
namespace DreiwmBrandstetterPlugin\Subscriber;
use DateTime;
use DateTimeZone;
use DreiwmBrandstetterPlugin\Core\Checkout\Cart\Custom\Error\CustomerTooLateForPackstationError;
use DreiwmBrandstetterPlugin\DreiwmBrandstetterPlugin;
use DreiwmBrandstetterPlugin\Service\DateValidator;
use DreiwmBrandstetterPlugin\Service\PackingStationService;
use DreiwmBrandstetterPlugin\Service\RadbotenService;
use Exception;
use Prophecy\Prophecy\Revealer;
use Psr\Log\LoggerInterface;
use Shopware\Core\Checkout\Cart\Delivery\Struct\Delivery;
use Shopware\Core\Checkout\Cart\Event\CartSavedEvent;
use Shopware\Core\Checkout\Cart\Event\CheckoutOrderPlacedEvent;
use Shopware\Core\Checkout\Cart\Order\CartConvertedEvent;
use Shopware\Core\Checkout\Order\OrderEntity;
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\Log\LoggerFactory;
use Shopware\Core\System\Snippet\SnippetService;
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
class CheckoutSubscriber implements EventSubscriberInterface
{
private RequestStack $requestStack;
private EntityRepository $packstationRepository;
private DateValidator $dateValidator;
private EntityRepository $tagRepository;
private EntityRepository $orderRepository;
private RadbotenService $radbotenService;
private SnippetService $snippetService;
private PackingStationService $packingStationService;
private LoggerInterface $logger;
public function __construct(
RequestStack $requestStack,
EntityRepository $packsatationRepository,
DateValidator $dateValidator,
EntityRepository $tagRepository,
EntityRepository $orderRepository,
RadbotenService $radbotenService,
SnippetService $snippetService,
PackingStationService $packingStationService,
LoggerFactory $loggerFactory
) {
$this->requestStack = $requestStack;
$this->packstationRepository = $packsatationRepository;
$this->dateValidator = $dateValidator;
$this->tagRepository = $tagRepository;
$this->orderRepository = $orderRepository;
$this->radbotenService = $radbotenService;
$this->snippetService = $snippetService;
$this->packingStationService = $packingStationService;
$this->logger = $loggerFactory->createRotating('dreiwm_brandstetter_checkout_subscriber', 7);
}
public static function getSubscribedEvents(): array
{
return [
CartConvertedEvent::class => 'processCheckout',
CheckoutOrderPlacedEvent::class => 'onOrderPlaced',
];
}
/**
* Setzt den Tag "BestelldatumGleichLieferdatum" in die Bestellung, wenn das Bestelldatum gleich dem gewünschten Lieferdatum ist
* Setzt den Tag "BestelldatumIstNextDayKontingent" in die Bestellung, wenn das Bestelldatum größer als das gewünschte Lieferdatum ist
* @param CheckoutOrderPlacedEvent $event
* @throws Exception
*/
public function onOrderPlaced(CheckoutOrderPlacedEvent $event): void
{
// Hole das gewünschte Lieferdatum aus den customFields
$orderCustomFields = $event->getOrder()->getCustomFields();
if ($orderCustomFields === null || !array_key_exists('brandstetter_orders_desired_delivery_date',
$orderCustomFields)) {
return;
}
$desiredDeliveryDate = $orderCustomFields['brandstetter_orders_desired_delivery_date'];
// Erstelle ein DateTime-Objekt aus dem gewünschten Lieferdatum
$desiredDeliveryDate = new DateTime($desiredDeliveryDate, new DateTimeZone('Europe/Berlin'));
// Erstelle ein DateTime-Objekt für das heutige Datum
$today = $this->dateValidator->getServerDateAndTime();
// Prüfe, ob das Bestelldatum heute ist
if ($desiredDeliveryDate->format('Y-m-d') == $today->format('Y-m-d')) {
// Setze den Tag "BestelldatumGleichLieferdatum" in die Bestellung
$this->setTagToOrder($event->getOrder(), 'BestelldatumGleichLieferdatum');
} elseif ($desiredDeliveryDate->format('Y-m-d') > $today->format('Y-m-d')) {
$lineItems = $event->getOrder()->getLineItems();
$stdOptionFound = false;
foreach ($lineItems as $lineItem) {
if ($lineItem->getType() === 'product' && $lineItem->getPayload()['options'] !== null) {
$options = $lineItem->getPayload()['options'];
// Gehe durch alle Optionen
foreach ($options as $option) {
if ($option['option'] === 'std') {
$stdOptionFound = true;
break 2; // Breche beide Schleifen ab, da "std" vorhanden ist
}
}
}
}
if (!$stdOptionFound) {
$this->setTagToOrder($event->getOrder(), 'BestelldatumIstNextDayKontingent');
}
}
}
/**
* Setzt den Tag in die Bestellung
* @param $order OrderEntity
* @param $tagName string
* @return void
*/
private function setTagToOrder(OrderEntity $order, string $tagName): void
{
// Erstelle ein Context-Objekt
$context = Context::createDefaultContext();
// Hole das OrderRepository
$orderRepository = $this->orderRepository;
// Erstelle ein neues Order-Array mit den erforderlichen Daten
$orderData = [
[
'id' => $order->getId(),
'orderNumber' => $order->getOrderNumber(),
'tags' => [
[
'id' => $this->getTagId($tagName)
]
],
]
];
try {
// Speichere die Bestellung
$orderRepository->upsert($orderData, $context);
} catch (Exception $e) {
// Fehlermeldung ausgeben
$this->logger->error('Fehler beim Setzen des Tags in die Bestellung: ' . $e->getMessage()) . 'Bestellnummer:' . $order->getOrderNumber();
}
}
/**
* Gibt die ID des Tags anhand des Tag-Namens zurück
* @param $tagName string
* @return mixed
*/
public function getTagId(string $tagName): mixed
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('name', $tagName));
$context = Context::createDefaultContext();
$result = $this->tagRepository->search($criteria, $context)->first();
return $result->getId();
}
public function onCheckoutConfirmPageLoaded(CheckoutConfirmPageLoadedEvent $event): void
{
// Lieferart Paketstation holen
/** @var $shippingMethod Delivery */
$shippingMethod = $event->getPage()->getCart()->getDeliveries()->first();
$shippingMethodId = $shippingMethod->getShippingMethod()->getId();
// wenn Paketstation gewählt wurde
if ($shippingMethodId == DreiwmBrandstetterPlugin::PAKETSTATION_ID) {
}
}
/**
*
* @param CartConvertedEvent $event
* @throws Exception
*/
public function processCheckout(CartConvertedEvent $event): void
{
$request = $this->requestStack->getCurrentRequest();
// Wenn kein Request vorhanden ist, dann abbrechen
if ($request === null) {
return;
}
// Hole das '_routeScope'-Attribut
$routeScope = $request->attributes->get('_routeScope');
// Wenn $routeScope von api kommt, dann ist es nicht aus dem Frontend, daher abbrechen
if ($routeScope !== null && in_array('api', $routeScope->getScopes())) {
return;
}
$orderData = $event->getConvertedCart();
// bei Post nichts machen, da dort kein Datum übergeben wird
if ($orderData['deliveries'][0]['shippingMethodId'] == DreiwmBrandstetterPlugin::POST_ID) {
return;
}
$lo_serverDateAndTime = new DateTime('now', new DateTimeZone('Europe/Berlin'));
// muss ohne Zeitzone sein, da Shopware nur UTC akzeptiert im CustomField
$desiredDeliveryDateTime = new DateTime($this->requestStack->getCurrentRequest()->cookies->get("customerSelectedDate"));
// Prüfe, ob eine gewünschte Uhrzeit für die Radbotenlieferung übergeben wurde
// kann nur gewählt werden, wenn die Lieferart "Radboten" gewählt wurde
$ls_desiredRadbotenTimeRange = $this->requestStack->getCurrentRequest()->request->get('BrandstetterDesiredRadbotenTimeRange');
if ($ls_desiredRadbotenTimeRange !== null) {
// $ls_desiredRadbotenTimeRange in den customFields des convertedCart speichern
$orderData['customFields']['brandstetter_orders_desired_delivery_timeRange_radboten'] = $ls_desiredRadbotenTimeRange;
// convertedCart mit den neuen Daten zurückgeben
$event->setConvertedCart($orderData);
}
// Abstellort für Radbotenlieferung
$ls_desiredDropOffLocation = $this->requestStack->getCurrentRequest()->request->get('BrandstetterDropOffLocation');
if ($ls_desiredDropOffLocation !== null) {
// $ls_desiredDropOffLocation in den customFields des convertedCart speichern
$orderData['customFields']['brandstetter_orders_desired_dropoff_location'] = $ls_desiredDropOffLocation;
// convertedCart mit den neuen Daten zurückgeben
$event->setConvertedCart($orderData);
}
// prüfe ob Packstation gewählt wurde
$shippingMethodId = $event->getConvertedCart()['deliveries'][0]['shippingMethodId'];
if ($shippingMethodId == DreiwmBrandstetterPlugin::PAKETSTATION_ID) {
$cartToken = $event->getCart()->getToken();
$this->logger->info('Packstation gewählt für Warenkorb ' . $cartToken);
// hole das Cart-Token
// suche nach dem Cart-Token in der Tabelle dreiwm_free_locker
$packstationRepository = $this->packstationRepository;
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('cartToken', $cartToken));
$context = Context::createDefaultContext();
$packstationResult = $packstationRepository->search($criteria, $context)->first();
// wenn das Cart-Token gefunden wurde, dann speichere den CustomerPin und MerchantPin in den customFields des convertedCart
if ($packstationResult !== null) {
// customerPin und merchantPin und box_name in den customFields des convertedCart speichern
if ($packstationResult->getCustomerPin() !== null && $packstationResult->getMerchantPin() !== null && $packstationResult->getBoxName() !== null) {
// kann immer noch empty sein
$this->logger->info('Eintrag in dreiwm_free_locker für Warenkorb ' . $cartToken, [
'customerPin' => $packstationResult->getCustomerPin(),
'merchantPin' => $packstationResult->getMerchantPin(),
'boxName' => $packstationResult->getBoxName()
]);
}
$orderData['customFields']['brandstetter_orders_customer_pin'] = $packstationResult->getCustomerPin();
$orderData['customFields']['brandstetter_orders_merchant_pin'] = $packstationResult->getMerchantPin();
$orderData['customFields']['brandstetter_orders_box_name'] = $packstationResult->getBoxName();
// convertedCart mit den neuen Daten zurückgeben
} else {
$this->logger->info('Kein Eintrag in dreiwm_free_locker für Warenkorb ' . $cartToken);
// Fallback => wir versuchen ein weiteres Fach zu reservieren und holen die PINS
// Copy, weil in reservePaketinLockerOnDate das Objekt verändert wird, weiter unten aber unverändert benötigt wird
$desiredDeliveryDateTimeCopy = clone $desiredDeliveryDateTime;
$reservedLockerData = $this->packingStationService->reservePaketinLockerOnDate($desiredDeliveryDateTimeCopy, $cartToken);
if ($reservedLockerData != null) {
$this->logger->info('Fallback generiert für Warenkorb ' . $cartToken, [
'customerPin' => $reservedLockerData['customerPin'],
'merchantPin' => $reservedLockerData['merchantPin'],
'boxName' => $reservedLockerData['boxName']
]);
$orderData['customFields']['brandstetter_orders_customer_pin'] = $reservedLockerData['customerPin'];
$orderData['customFields']['brandstetter_orders_merchant_pin'] = $reservedLockerData['merchantPin'];
$orderData['customFields']['brandstetter_orders_box_name'] = $reservedLockerData['boxName'];
// speichere das Cart-Token in 3wmfreelocker
$packstationRepository->upsert([
[
'cartToken' => $event->getCart()->getToken(),
'boxId' => $reservedLockerData['boxId'],
'boxName' => $reservedLockerData['boxName'],
'merchantPin' => $reservedLockerData['merchantPin'],
'customerPin' => $reservedLockerData['customerPin'],
'date' => $desiredDeliveryDateTime->format('Y-m-d'),
'freeLocker' => 1,
]
], $context);
} else {
// Fehlermeldung CustomerTooLateForPackstationError
$event->getCart()->addErrors(new CustomerTooLateForPackstationError());
$this->logger->info('Kein weiteres Fach verfügbar für Warenkorb ' . $cartToken);
}
}
$event->setConvertedCart($orderData);
// convertedCart holen
$orderData = $event->getConvertedCart();
// token aus dem Cart holen
$cartToken = $event->getCart()->getToken();
// token in dreiwm_free_locker speichern
$this->packingStationService->markCartTokenAsUsedInFreeLockerRepo($cartToken);
}
$orderData['customFields']['brandstetter_orders_desired_delivery_date'] = $desiredDeliveryDateTime;
// ist die Shipping-Methode "Radboten" gewählt?
if ($this->dateValidator->isRadbotenShippingMethod($shippingMethodId)) {
// desiredDeliveryDate aus dem Cookie holen
$desiredDeliveryDate = $this->requestStack->getCurrentRequest()->cookies->get("customerSelectedDate");
// desiredDeliveryDate in den customFields des convertedCart speichern
$orderData['customFields']['brandstetter_orders_desired_delivery_date'] = $desiredDeliveryDate;
// convertedCart mit den neuen Daten zurückgeben
$event->setConvertedCart($orderData);
}
// convertedCart mit den neuen Daten zurückgeben
$event->setConvertedCart($orderData);
}
public function checkFreeLocker($desiredDeliveryDate)
{
$desiredDeliveryDate = $desiredDeliveryDate->format('Y-m-d');
// durchsuche das dreiwm_free_locker repository nach dem $desiredDeliveryDateParam
$packsatationRepository = $this->packstationRepository;
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('date', $desiredDeliveryDate));
$context = Context::createDefaultContext();
// desiredDeliveryDate in der Tabelle suchen
$packstationResult = $packsatationRepository->search($criteria, $context)->first();
if ($packstationResult) {
// $this->saveOrderToPaketin();
$free_locker = $packstationResult->getFreeLocker();
// Wenn das Datum gefunden wurde, dann schaue ob, free_locker größer als 0 ist
if ($free_locker) {
// free_locker um 1 verringern
$free_locker--;
// free_locker in der Tabelle aktualisieren
$packsatationRepository->update([
[
'id' => $packstationResult->getId(),
'free_locker' => $free_locker
]
], $context);
//@TODO: Datensatz in PAKETIN speichern
} else {
// convertedCart zurückgeben mit Fehlermeldung dass Fach schon voll ist
// $event->setConvertedCart($orderData);
}
} // Wenn das Datum nicht gefunden wurde, dann erstelle das Datum in der Tabelle
else {
// free_locker in der Tabelle aktualisieren
$packsatationRepository->create([
[
'date' => $desiredDeliveryDate,
'free_locker' => DreiwmBrandstetterPlugin::MAXIMUM_FREE_LOCKER - 1
]
], $context);
//TODO: Datensatz in PAKETIN speichern
}
}
}