<?php
namespace CioBudget\Subscriber;
use CioBudget\Definition\Budget\BudgetEntity;
use CioBudget\Service\BudgetInfoService;
use CioBudget\Service\BudgetLoaderService;
use CioBudget\Service\SessionService;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Storefront\Event\StorefrontRenderEvent;
use Shopware\Storefront\Page\Checkout\Cart\CheckoutCartPage;
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPage;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class HeaderSelectionSubscriber implements EventSubscriberInterface
{
// CIO-AI-Driven: Zahlungsart-IDs mit Budget-Bezug (Budget + ZeroPayment), Deckungspruefung greift nur bei diesen
private const BUDGET_PAYMENT_METHOD_IDS = ['f261b55b5214496bb30fec99e5f6561c', '8cbfa5840845471ea510d574a947b365'];
private Container $container;
private BudgetLoaderService $budgetLoaderService;
private SessionService $sessionService;
private BudgetInfoService $budgetInfoService;
private UrlGeneratorInterface $router;
private TranslatorInterface $translator;
public function __construct(Container $container, UrlGeneratorInterface $router, TranslatorInterface $translator)
{
$this->container = $container;
$this->router = $router;
$this->translator = $translator;
}
public function setSessionService(SessionService $sessionService)
{
$this->sessionService = $sessionService;
}
public function setBudgetLoaderService(BudgetLoaderService $budgetLoaderService)
{
$this->budgetLoaderService = $budgetLoaderService;
}
public function setBudgetInfoService(BudgetInfoService $budgetInfoService)
{
$this->budgetInfoService = $budgetInfoService;
}
public static function getSubscribedEvents(): array
{
return [
StorefrontRenderEvent::class => 'onStorefrontRender'
];
}
public function onStorefrontRender(StorefrontRenderEvent $event)
{
$customer = $event->getSalesChannelContext()->getCustomer();
if (!$customer instanceof CustomerEntity) {
$event->setParameter('cio_budgets_show', false);
return;
}
if (!$customer || !is_array($customer->getCustomFields())) {
return;
}
/** @var EntitySearchResult $budgets */
$budgets = $this->budgetLoaderService->getActiveBudgetsByCustomer($customer, $event->getSalesChannelContext()->getContext());
if (!$budgets) {
return;
}
$currentSelectedBudgetId = $this->sessionService->getBudgetIdFromSession();
if (!$currentSelectedBudgetId) {
// CIO-AI-Driven: Fallback - Customer hat aktive Budgets, aber keine Session-Auswahl (z.B. Session vor Budget-Zuweisung).
// Erstes aktives Budget in die Session schreiben, statt stillschweigend gar kein Dropdown anzuzeigen.
$firstBudget = $budgets->first();
if (!$firstBudget instanceof BudgetEntity) {
return;
}
$this->sessionService->setBudgetIdInSession($firstBudget->getId(), $event->getSalesChannelContext());
$currentSelectedBudgetId = $firstBudget->getId();
}
$currentBudget = $this->budgetLoaderService->getBudgetById($currentSelectedBudgetId);
$currentBudget = [
'budget' => $currentBudget,
'currentState' => $currentBudget ? $this->budgetInfoService->getAvailableBudget($currentBudget->getId(), $event->getSalesChannelContext()) : 0,
'externalId' => $currentBudget ? str_pad($currentBudget->get('store')->getExtId(), 4, 0, STR_PAD_LEFT) : ''
];
$event->setParameter('cio_budgets_show', true);
$event->setParameter('cio_currently_selected_budget', $currentBudget);
if ($budgets) {
$budgets = $budgets->map(function ($budget) use ($event) {
return [
'budget' => $budget,
'currentState' => $budget instanceof BudgetEntity ? $this->budgetInfoService->getAvailableBudget($budget->getId(), $event->getSalesChannelContext()) : 0,
'externalId' => $budget instanceof BudgetEntity ? str_pad($budget->get('store')->getExtId(), 4, 0, STR_PAD_LEFT) : ''
];
});
uasort($budgets, function ($a, $b) {
if ($a['budget']->getName() == $b['budget']->getName()) return 0;
return ($a['budget']->getName() < $b['budget']->getName()) ? -1 : 1;
});
$event->setParameter('cio_avaivable_budgets', $budgets);
}
$event->setParameter('cio_avaivable_budgets_redirect', $event->getRequest()->attributes->get('_route'));
$event->setParameter('cio_avaivable_budgets_redirect_querystring', $event->getRequest()->getQueryString());
$event->setParameter('cio_avaivable_budgets_redirect_params', $event->getRequest()->attributes->get('_route_params'));
if (key_exists('page', $event->getParameters()) && $event->getParameters()['page'] instanceof CheckoutConfirmPage) {
$confirmPage = $event->getParameters()['page'];
if (!$this->isBudgetRelatedPaymentInCart($confirmPage->getCart())) {
return;
}
$valueBudget = (float) $currentBudget['currentState'];
if ($confirmPage->getCart()->getPrice()->getTotalPrice() > $valueBudget) {
$this->addCartValueExceedsBudgetFlash($valueBudget, $event->getSalesChannelContext());
// CIO-AI-Driven: Redirect nur ueber setResponse (kein manuelles send() mehr)
$event->setResponse(new RedirectResponse($this->router->generate('frontend.checkout.cart.page')));
}
}
if (key_exists('page', $event->getParameters()) && $event->getParameters()['page'] instanceof CheckoutCartPage) {
$cartPage = $event->getParameters()['page'];
if (!$this->isBudgetRelatedPaymentInCart($cartPage->getCart())) {
return;
}
$valueBudget = (float) $currentBudget['currentState'];
if ($cartPage->getCart()->getPrice()->getTotalPrice() > $valueBudget) {
$this->addCartValueExceedsBudgetFlash($valueBudget, $event->getSalesChannelContext());
}
}
}
// CIO-AI-Driven: Cart-Transaktion auf Budget-/ZeroPayment-Zahlart pruefen (DRY statt duplizierter Bloecke)
private function isBudgetRelatedPaymentInCart(Cart $cart): bool
{
$currentPaymentMethodId = $cart->getTransactions()->getElements()[0]->getPaymentMethodId() ?? null;
return $currentPaymentMethodId !== null && in_array($currentPaymentMethodId, self::BUDGET_PAYMENT_METHOD_IDS, true);
}
// CIO-AI-Driven: Flash-Meldung mit formatiertem Betrag (vorher unformatiert "125.15")
private function addCartValueExceedsBudgetFlash(float $valueBudget, SalesChannelContext $context): void
{
$valueBudgetDisplay = number_format($valueBudget, 2, ',', '.') . ' ' . $context->getCurrency()->getSymbol();
/** @var Session $session */
$session = $this->container->get('session');
$session->getFlashBag()->set('danger', $this->translator->trans('confirm.cartValueExceedsBudget', ['%value%' => $valueBudgetDisplay]));
}
}