custom/plugins/CioBudget/src/Subscriber/HeaderSelectionSubscriber.php line 64

Open in your IDE?
  1. <?php
  2. namespace CioBudget\Subscriber;
  3. use CioBudget\Definition\Budget\BudgetEntity;
  4. use CioBudget\Service\BudgetInfoService;
  5. use CioBudget\Service\BudgetLoaderService;
  6. use CioBudget\Service\SessionService;
  7. use Shopware\Core\Checkout\Cart\Cart;
  8. use Shopware\Core\Checkout\Customer\CustomerEntity;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
  10. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  11. use Shopware\Storefront\Event\StorefrontRenderEvent;
  12. use Shopware\Storefront\Page\Checkout\Cart\CheckoutCartPage;
  13. use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPage;
  14. use Symfony\Component\DependencyInjection\Container;
  15. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  16. use Symfony\Component\HttpFoundation\Session\Session;
  17. use Symfony\Component\HttpFoundation\RedirectResponse;
  18. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  19. use Symfony\Contracts\Translation\TranslatorInterface;
  20. class HeaderSelectionSubscriber implements EventSubscriberInterface
  21. {
  22.     // CIO-AI-Driven: Zahlungsart-IDs mit Budget-Bezug (Budget + ZeroPayment), Deckungspruefung greift nur bei diesen
  23.     private const BUDGET_PAYMENT_METHOD_IDS = ['f261b55b5214496bb30fec99e5f6561c', '8cbfa5840845471ea510d574a947b365'];
  24.     private Container $container;
  25.     private BudgetLoaderService $budgetLoaderService;
  26.     private SessionService $sessionService;
  27.     private BudgetInfoService $budgetInfoService;
  28.     private UrlGeneratorInterface $router;
  29.     private TranslatorInterface $translator;
  30.     public function __construct(Container $container, UrlGeneratorInterface $router, TranslatorInterface $translator)
  31.     {
  32.         $this->container = $container;
  33.         $this->router = $router;
  34.         $this->translator = $translator;
  35.     }
  36.     public function setSessionService(SessionService $sessionService)
  37.     {
  38.         $this->sessionService = $sessionService;
  39.     }
  40.     public function setBudgetLoaderService(BudgetLoaderService $budgetLoaderService)
  41.     {
  42.         $this->budgetLoaderService = $budgetLoaderService;
  43.     }
  44.     public function setBudgetInfoService(BudgetInfoService $budgetInfoService)
  45.     {
  46.         $this->budgetInfoService = $budgetInfoService;
  47.     }
  48.     public static function getSubscribedEvents(): array
  49.     {
  50.         return [
  51.             StorefrontRenderEvent::class => 'onStorefrontRender'
  52.         ];
  53.     }
  54.     public function onStorefrontRender(StorefrontRenderEvent $event)
  55.     {
  56.         $customer = $event->getSalesChannelContext()->getCustomer();
  57.         if (!$customer instanceof CustomerEntity) {
  58.             $event->setParameter('cio_budgets_show', false);
  59.             return;
  60.         }
  61.         if (!$customer || !is_array($customer->getCustomFields())) {
  62.             return;
  63.         }
  64.         /** @var EntitySearchResult $budgets */
  65.         $budgets = $this->budgetLoaderService->getActiveBudgetsByCustomer($customer, $event->getSalesChannelContext()->getContext());
  66.         if (!$budgets) {
  67.             return;
  68.         }
  69.         $currentSelectedBudgetId = $this->sessionService->getBudgetIdFromSession();
  70.         if (!$currentSelectedBudgetId) {
  71.             // CIO-AI-Driven: Fallback - Customer hat aktive Budgets, aber keine Session-Auswahl (z.B. Session vor Budget-Zuweisung).
  72.             // Erstes aktives Budget in die Session schreiben, statt stillschweigend gar kein Dropdown anzuzeigen.
  73.             $firstBudget = $budgets->first();
  74.             if (!$firstBudget instanceof BudgetEntity) {
  75.                 return;
  76.             }
  77.             $this->sessionService->setBudgetIdInSession($firstBudget->getId(), $event->getSalesChannelContext());
  78.             $currentSelectedBudgetId = $firstBudget->getId();
  79.         }
  80.         $currentBudget = $this->budgetLoaderService->getBudgetById($currentSelectedBudgetId);
  81.         $currentBudget = [
  82.             'budget' => $currentBudget,
  83.             'currentState' => $currentBudget ? $this->budgetInfoService->getAvailableBudget($currentBudget->getId(), $event->getSalesChannelContext()) : 0,
  84.             'externalId' => $currentBudget ? str_pad($currentBudget->get('store')->getExtId(), 4, 0, STR_PAD_LEFT) : ''
  85.         ];
  86.         $event->setParameter('cio_budgets_show', true);
  87.         $event->setParameter('cio_currently_selected_budget', $currentBudget);
  88.         if ($budgets) {
  89.             $budgets = $budgets->map(function ($budget) use ($event) {
  90.                 return [
  91.                     'budget' => $budget,
  92.                     'currentState' => $budget instanceof BudgetEntity ? $this->budgetInfoService->getAvailableBudget($budget->getId(), $event->getSalesChannelContext()) : 0,
  93.                     'externalId' => $budget instanceof BudgetEntity ? str_pad($budget->get('store')->getExtId(), 4, 0, STR_PAD_LEFT) : ''
  94.                 ];
  95.             });
  96.             uasort($budgets, function ($a, $b) {
  97.                 if ($a['budget']->getName() == $b['budget']->getName()) return 0;
  98.                 return ($a['budget']->getName() < $b['budget']->getName()) ? -1 : 1;
  99.             });
  100.             $event->setParameter('cio_avaivable_budgets', $budgets);
  101.         }
  102.         $event->setParameter('cio_avaivable_budgets_redirect', $event->getRequest()->attributes->get('_route'));
  103.         $event->setParameter('cio_avaivable_budgets_redirect_querystring', $event->getRequest()->getQueryString());
  104.         $event->setParameter('cio_avaivable_budgets_redirect_params', $event->getRequest()->attributes->get('_route_params'));
  105.         if (key_exists('page', $event->getParameters()) && $event->getParameters()['page'] instanceof CheckoutConfirmPage) {
  106.             $confirmPage = $event->getParameters()['page'];
  107.             if (!$this->isBudgetRelatedPaymentInCart($confirmPage->getCart())) {
  108.                 return;
  109.             }
  110.             $valueBudget = (float) $currentBudget['currentState'];
  111.             if ($confirmPage->getCart()->getPrice()->getTotalPrice() > $valueBudget) {
  112.                 $this->addCartValueExceedsBudgetFlash($valueBudget, $event->getSalesChannelContext());
  113.                 // CIO-AI-Driven: Redirect nur ueber setResponse (kein manuelles send() mehr)
  114.                 $event->setResponse(new RedirectResponse($this->router->generate('frontend.checkout.cart.page')));
  115.             }
  116.         }
  117.         if (key_exists('page', $event->getParameters()) && $event->getParameters()['page'] instanceof CheckoutCartPage) {
  118.             $cartPage = $event->getParameters()['page'];
  119.             if (!$this->isBudgetRelatedPaymentInCart($cartPage->getCart())) {
  120.                 return;
  121.             }
  122.             $valueBudget = (float) $currentBudget['currentState'];
  123.             if ($cartPage->getCart()->getPrice()->getTotalPrice() > $valueBudget) {
  124.                 $this->addCartValueExceedsBudgetFlash($valueBudget, $event->getSalesChannelContext());
  125.             }
  126.         }
  127.     }
  128.     // CIO-AI-Driven: Cart-Transaktion auf Budget-/ZeroPayment-Zahlart pruefen (DRY statt duplizierter Bloecke)
  129.     private function isBudgetRelatedPaymentInCart(Cart $cart): bool
  130.     {
  131.         $currentPaymentMethodId = $cart->getTransactions()->getElements()[0]->getPaymentMethodId() ?? null;
  132.         return $currentPaymentMethodId !== null && in_array($currentPaymentMethodId, self::BUDGET_PAYMENT_METHOD_IDS, true);
  133.     }
  134.     // CIO-AI-Driven: Flash-Meldung mit formatiertem Betrag (vorher unformatiert "125.15")
  135.     private function addCartValueExceedsBudgetFlash(float $valueBudget, SalesChannelContext $context): void
  136.     {
  137.         $valueBudgetDisplay = number_format($valueBudget, 2, ',', '.') . ' ' . $context->getCurrency()->getSymbol();
  138.         /** @var Session $session */
  139.         $session = $this->container->get('session');
  140.         $session->getFlashBag()->set('danger', $this->translator->trans('confirm.cartValueExceedsBudget', ['%value%' => $valueBudgetDisplay]));
  141.     }
  142. }