custom/static-plugins/GlobusSW6/src/Service/StoreLocator/StoreSwitchService.php line 477

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace GlobusSW6\Service\StoreLocator;
  3. use Doctrine\DBAL\Connection;
  4. use GlobusSW6\Core\Content\Attributes\Product\ProductAttributesEntity;
  5. use GlobusSW6\Core\IaneoDefaults;
  6. use GlobusSW6\Service\App\AppService;
  7. use GlobusSW6\Service\CheckoutVisibility\CheckoutVisibilityStruct;
  8. use Psr\Log\LoggerInterface;
  9. use Shopware\Core\Checkout\Cart\LineItem\LineItem;
  10. use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
  11. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  13. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  14. use Symfony\Component\HttpFoundation\JsonResponse;
  15. use Shopware\Core\Checkout\Cart\Cart;
  16. use Shopware\Core\Checkout\Cart\LineItem\LineItemCollection;
  17. use Shopware\Core\Framework\Uuid\Uuid;
  18. use Shopware\Core\Framework\Context;
  19. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  20. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
  21. use Symfony\Component\HttpFoundation\Request;
  22. use GlobusSW6\Storefront\Controller\IaneoCartLineItemControllerExtension;
  23. use Shopware\Core\Content\Product\ProductEntity;
  24. use Symfony\Component\HttpFoundation\Session\Session;
  25. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  26. use Shopware\Core\Framework\Adapter\Translation\Translator;
  27. class StoreSwitchService
  28. {
  29.     /** @var Translator */
  30.     protected $translator;
  31.     /** @var StoreLocatorService */
  32.     protected $storeService;
  33.     /** @var StockService */
  34.     protected $stockService;
  35.     /** @var Connection */
  36.     protected $connection;
  37.     /** @var EntityRepositoryInterface */
  38.     protected $productRepository;
  39.     /** @var IaneoCartLineItemControllerExtension */
  40.     private $cartLineItemController;
  41.     /** @var SessionInterface */
  42.     private $session;
  43.     /** @var CartService */
  44.     private $cartService;
  45.     /** @var LoggerInterface */
  46.     private $logger;
  47.     /** @var AppService */
  48.     private $appService;
  49.     const COOKIE_TIME 360000;
  50.     /**
  51.      * @param Translator $translator
  52.      * @param StoreLocatorService $storeService
  53.      * @param StockService $stockService
  54.      * @param Connection $connection
  55.      * @param EntityRepositoryInterface $productRepository
  56.      * @param IaneoCartLineItemControllerExtension $cartLineItemController
  57.      * @param SessionInterface $session
  58.      * @param CartService $cartService
  59.      * @param LoggerInterface $logger
  60.      * @param AppService $appService
  61.      */
  62.     public function __construct(Translator $translatorStoreLocatorService $storeServiceStockService $stockServiceConnection $connectionEntityRepositoryInterface $productRepositoryIaneoCartLineItemControllerExtension $cartLineItemControllerSessionInterface $sessionCartService $cartServiceLoggerInterface $loggerAppService $appService)
  63.     {
  64.         $this->translator $translator;
  65.         $this->storeService $storeService;
  66.         $this->stockService $stockService;
  67.         $this->connection $connection;
  68.         $this->productRepository $productRepository;
  69.         $this->cartLineItemController $cartLineItemController;
  70.         $this->session $session;
  71.         $this->cartService $cartService;
  72.         $this->logger $logger;
  73.         $this->appService $appService;
  74.     }
  75.     /**
  76.      * @param Cart $cart
  77.      * @param array $formattedStores
  78.      * @param string|null $salesChannelId
  79.      * @param string|null $languageId
  80.      * @return JsonResponse|null
  81.      *
  82.      * TODO-NGS: Description
  83.      */
  84.     public function handleReservedItemsInCart(Cart $cart, array $formattedStores, ?string $salesChannelId null, ?string $languageId null, ?bool $enrichInformation null )
  85.     {
  86.         $lineItems $cart->getLineItems();
  87.         // detect lineItems in cart that are reserved in store
  88.         $reservedItems $this->findReservationLineItems($lineItems);
  89.         if (count($reservedItems) !== 0) {
  90.             $storeStockArray $this->storeService->getMaxPurchaseForCertainProductsInCertainStores(array_keys($reservedItems), $formattedStores);
  91.             // compares the max. available stock of a product in a store to the desired amount in cart
  92.             $availability $this->compareCartQuantityToStoreMaxPurchase($reservedItems$storeStockArray$salesChannelId$languageId);
  93.             // getProductNumber and Description
  94.             $additionalProductData $this->addProductData($availability$reservedItems$enrichInformation);
  95.             return new JsonResponse([
  96.                 'success' => true,
  97.                 'total' => count($additionalProductData),
  98.                 'data' => $this->storeService->indexAscending($additionalProductData)
  99.             ]);
  100.         }
  101.         return null;
  102.     }
  103.     /**
  104.      * @param array $productIds
  105.      * @return array
  106.      *
  107.      * Used to get additional product information like product number or description.
  108.      * Also detects it the product can be ordered online.
  109.      * Adds this information to the availabilityArray in order to gather all information necessary for the switch-store modal
  110.      */
  111.     private function addProductData(array $availabilityArray, array $reservedProductsInCart, ?bool $enrichInformation null): array
  112.     {
  113.         $productIds array_keys($reservedProductsInCart);
  114.         $criteria = new Criteria();
  115.         if ($enrichInformation) {
  116.             $criteria->addAssociation('ianeoAttributes');
  117.             $criteria->addAssociation('media');
  118.         }
  119.         $criteria->addFilter(new EqualsAnyFilter('id'$productIds));
  120.         $productCollection $this->productRepository->search($criteriaContext::createDefaultContext());
  121.         foreach ($availabilityArray as $storeLegacyId => $storeEntry) {
  122.             foreach ($storeEntry['products'] as $productId => $productEntry) {
  123.                 /** @var ProductEntity $product */
  124.                 $product $productCollection->get($productId);
  125.                 if ($enrichInformation) {
  126.                     $subtitle null;
  127.                     /** @var ProductAttributesEntity $ianeoAttributes */
  128.                     $ianeoAttributes $product->getExtensions()['ianeoAttributes'];
  129.                     if (!is_null($ianeoAttributes)) {
  130.                         $subtitle $ianeoAttributes->getSubtitle();
  131.                     }
  132.                     try {
  133.                         $mediaUrl $product->getMedia()->first()->getMedia()->getUrl();
  134.                     } catch (\Throwable $t) {
  135.                         $mediaUrl null;
  136.                     }
  137.                     $availabilityArray[$storeLegacyId]['products'][$productId]['mediaUrl'] = $mediaUrl;
  138.                     $availabilityArray[$storeLegacyId]['products'][$productId]['subtitle'] = $subtitle;
  139.                 }
  140.                 $amountInCart $reservedProductsInCart[$productId];
  141.                 $maxOnlinePurchase $this->stockService->getArticleSpecificMaxPurchaseById($productId);
  142.                 $buyOnlineBtn = ($amountInCart <= $maxOnlinePurchase);
  143.                 $availabilityArray[$storeLegacyId]['products'][$productId]['name'] = $product->getName();
  144.                 $availabilityArray[$storeLegacyId]['products'][$productId]['description'] = $product->getDescription();
  145.                 $availabilityArray[$storeLegacyId]['products'][$productId]['productNumber'] = $product->getProductNumber();
  146.                 $availabilityArray[$storeLegacyId]['products'][$productId]['maxOnlinePurchase'] = $maxOnlinePurchase;
  147.                 $availabilityArray[$storeLegacyId]['products'][$productId]['amountInCart'] = $amountInCart;
  148.                 $availabilityArray[$storeLegacyId]['products'][$productId]['buyOnlineBtn'] = $buyOnlineBtn;
  149.             }
  150.         }
  151.         return $availabilityArray;
  152.     }
  153.     /**
  154.      * @param LineItemCollection $lineItems
  155.      * @return array
  156.      *
  157.      * Given the line items from the cart, this function searches for reservation items and returns them
  158.      */
  159.     private function findReservationLineItems(LineItemCollection $lineItems)
  160.     {
  161.         $reservedItems = [];
  162.         /** @var LineItem $lineItem */
  163.         foreach ($lineItems as $lineItem) {
  164.             $extensions $lineItem->getExtensions();
  165.             if (array_key_exists('ianeoOrderType'$extensions)) {
  166.                 if ($extensions['ianeoOrderType']['orderType'] == 'reserveInStore' && $lineItem->getType() == 'product') {
  167.                     // special case custprod / holzzuschnitt article -> here referencedId is normal productId
  168.                     if (array_key_exists('ianeoAttributes'$extensions)
  169.                         && ($extensions['ianeoAttributes']->getCustprodParamsCount() > 0) )
  170.                     {
  171.                         $reservedItems[$lineItem->getReferencedId()] = $lineItem->getQuantity();
  172.                     } else { // normal case
  173.                         $reservedItems[$lineItem->getId()] = $lineItem->getQuantity();
  174.                     }
  175.                 }
  176.             }
  177.         }
  178.         return $reservedItems;
  179.     }
  180.     /**
  181.      * @param string $languageId
  182.      * @return false|mixed
  183.      * @throws \Doctrine\DBAL\Exception
  184.      *
  185.      * Helper function that collects the locale id required to configure the translator
  186.      */
  187.     private function getLocale(string $languageId)
  188.     {
  189.         $localeId $this->connection->fetchOne("
  190.             SELECT LOWER(HEX(locale_id))
  191.                 FROM language l
  192.                 WHERE l.id = :lId;
  193.         ", ['lId' => Uuid::fromHexToBytes($languageId)]);
  194.         return $localeId;
  195.     }
  196.     /**
  197.      * @param array $cartArray
  198.      * Has the form:
  199.      * [
  200.      *  'd373db18a3134ab096f7d12ddc0fd56f' => 4, // productId
  201.      * ...
  202.      * ]
  203.      *
  204.      * @param array $storeStockArray
  205.      * Compare: StockService->maxPurchaseForReservationInStore(...) Has the form:
  206.      * [
  207.      *  0 => [
  208.      *      "store" => "1155"
  209.      *      "product_id" => 'd373db18a3134ab096f7d12ddc0fd56f'
  210.      *      ...
  211.      *      "maxStock" => 5
  212.      * ], ...
  213.      *
  214.      * '1155' => [ // storeLegacyId
  215.      *      ...,
  216.      *      "products" => [
  217.      *          'd373db18a3134ab096f7d12ddc0fd56f' => [ // productId
  218.      *              'maxStock' => 5
  219.      *          ]
  220.      *      ],
  221.      *      ...
  222.      * ], ...
  223.      *
  224.      * ]
  225.      *
  226.      * Compares the desired stock to the available stock in store. The storeStockArray is enriched with
  227.      * availability information.
  228.      *
  229.      */
  230.     private function compareCartQuantityToStoreMaxPurchase(array $cartArray, array $storeStockArray, ?string $salesChannelId, ?string $languageId)
  231.     {
  232.         $result = [];
  233.         $localId null;
  234.         if (!is_null($languageId)) {
  235.             $localId $this->getLocale($languageId);
  236.         }
  237.         if (!is_null($localId) && !is_null($languageId)) {
  238.             // allows to use snippets even if we are not in a storefront controller
  239.             $this->translator->injectSettings(
  240.                 $salesChannelId,
  241.                 $languageId,
  242.                 $localId,
  243.                 Context::createDefaultContext()
  244.             );
  245.         }
  246.         foreach ($storeStockArray as $storeLegacyId => $storeStockEntry) {
  247.             $availabilityCounter 0;
  248.             foreach ($cartArray as $productId => $quantity) {
  249.                 if (!array_key_exists($productId$storeStockEntry['products'])) { // store does not contain product
  250.                     $storeStockArray[$storeLegacyId]['products'][$productId] = [
  251.                         'maxStock' => 0,
  252.                         'availability' => false
  253.                     ];
  254.                     continue;
  255.                 }
  256.                 if ($storeStockEntry['products'][$productId]['maxStock'] >= $quantity) { // available
  257.                     $storeStockArray[$storeLegacyId]['products'][$productId]['availability'] = true;
  258.                     $availabilityCounter++;
  259.                 } else { // not available
  260.                     $storeStockArray[$storeLegacyId]['products'][$productId]['availability'] = false;
  261.                 }
  262.             }
  263.             // set overall reserved-product availability in this store: all available/none available/partially available
  264.             if ($availabilityCounter == count($cartArray)) {
  265.                 $storeStockArray[$storeLegacyId]['availability'] = $this->translator->trans('store.product.available');
  266.                 $storeStockArray[$storeLegacyId]['modal'] = false;
  267.             } else if ($availabilityCounter == 0) {
  268.                 $storeStockArray[$storeLegacyId]['availability'] = $this->translator->trans('store.product.unavailable');
  269.                 $storeStockArray[$storeLegacyId]['modal'] = true;
  270.                 $storeStockArray[$storeLegacyId]['availableCount'] = $availabilityCounter;
  271.                 $storeStockArray[$storeLegacyId]['totalCount'] = count($cartArray);
  272.             } else {
  273.                 $storeStockArray[$storeLegacyId]['availability'] = $this->translator->trans('store.product.partiallyAvailable', ['%share%' => $availabilityCounter'%total%' => count($cartArray)]);
  274.                 $storeStockArray[$storeLegacyId]['modal'] = true;
  275.                 $storeStockArray[$storeLegacyId]['availableCount'] = $availabilityCounter;
  276.                 $storeStockArray[$storeLegacyId]['totalCount'] = count($cartArray);
  277.             }
  278.         }
  279.         return $storeStockArray;
  280.     }
  281.     // TODO-NGS: use CartService->getCart instead
  282.     public function getCart(string $token)
  283.     {
  284.         $content $this->connection->fetchColumn(
  285.             'SELECT cart FROM cart WHERE token = :token',
  286.             ['token' => $token]
  287.         );
  288.         /** @var Cart $cart */
  289.         $cart unserialize((string)$content);
  290.         if (!$cart instanceof Cart) {
  291.             return null;
  292.         }
  293.         return $cart;
  294.     }
  295.     /**
  296.      * @param array $products
  297.      * @param string $storeId
  298.      * @param Cart $cart
  299.      * @param SalesChannelContext $salesChannelContext
  300.      *
  301.      * Change the 'store' in the lineItem extensions of 'products' to the id 'storeId' and update the cart accordingly
  302.      */
  303.     public function changeStoreForLineItems(array $productsstring $storeIdCart $cartSalesChannelContext $salesChannelContext)
  304.     {
  305.         if (count($products) === 0) {
  306.             return;
  307.         }
  308.         /** @var LineItem $lineItem */
  309.         foreach ($cart->getLineItems() as $lineItem) {
  310.             $extensions $lineItem->getExtensions();
  311.             // Reservieren Artikel
  312.             if (array_key_exists('ianeoOrderType'$extensions) && $extensions['ianeoOrderType']['orderType'] === 'reserveInStore') {
  313.                 if (in_array($lineItem->getId(), $products) || in_array($lineItem->getReferencedId(), $products)) {
  314.                     $this->switchExtensions($lineItem$storeId);
  315.                     $modifiedLineItems [] = $lineItem;
  316.                     // remove old lineItem from cart
  317.                     $this->cartLineItemController->deleteLineItem($cart$lineItem->getId(), new Request(), $salesChannelContext);
  318.                     continue;
  319.                 }
  320.                 $this->logger->info'Problems to switch store for lineitem: ' $lineItem->getId() . ' in cart ' $cart->getToken());
  321.             }
  322.         }
  323.         // add line item with adjusted store
  324.         $this->cartLineItemController->addCertainLineItems($cart$modifiedLineItems, new Request(), $salesChannelContext);
  325.     }
  326.     /**
  327.      * @param array $products
  328.      * @param Cart $cart
  329.      * @param SalesChannelContext $salesChannelContext
  330.      *
  331.      * Remove line items specified in products-array
  332.      */
  333.     public function removeLineItems(array $productsCart $cartSalesChannelContext $salesChannelContext)
  334.     {
  335.         foreach ($products as $productId) {
  336.             // remove old lineItem from cart
  337.             $this->cartLineItemController->deleteLineItem($cart$productId, new Request(), $salesChannelContext);
  338.         }
  339.     }
  340.     /**
  341.      * @param LineItem $lineItem
  342.      * @param string $storeId
  343.      *
  344.      * If an item is reserved in another store and the quantity does not change,
  345.      * it is sufficient to exchange the storeId in the lineItem extensions.
  346.      * Also, the acidSplitCartCheckoutIaneoData extension is removed in case it contains outdated information.
  347.      */
  348.     private function switchExtensions(LineItem $lineItemstring $storeId)
  349.     {
  350.         $extensions $lineItem->getExtensions();
  351.         if (!array_key_exists('ianeoOrderType'$extensions) && !array_key_exists('store'$extensions['ianeoOrderType'])) {
  352.             throw new \InvalidArgumentException('Expected line item for reservation in store');
  353.         }
  354.         $extensions['ianeoOrderType']['store'] = (int)$storeId;
  355.         if (array_key_exists('acidSplitCartCheckoutIaneoData'$extensions)) {
  356.             unset($extensions['acidSplitCartCheckoutIaneoData']);
  357.         }
  358.         $lineItem->setExtensions($extensions);
  359.     }
  360.     public function getProductIdByNumber(string $productNumber)
  361.     {
  362.         $criteria = new Criteria();
  363.         $criteria->addFilter(new EqualsFilter('productNumber'$productNumber));
  364.         /** @var ProductEntity $product */
  365.         $product $this->productRepository->search($criteriaContext::createDefaultContext())->first();
  366.         if (is_null($product)) {
  367.             return null;
  368.         } else {
  369.             return $product->getId();
  370.         }
  371.     }
  372.     public function setStoreInSession(Context $contextstring $storeId): array
  373.     {
  374.         $store $this->storeService->getStore($storeId$context);
  375.         if (is_null($store)) {
  376.             throw new \InvalidArgumentException('Store ' $storeId ' not found');
  377.         }
  378.         $storeFormatted $this->storeService->formatStore([$store]);
  379.         $this->session->set('ianeoCurrentStore'$store->getLegacyId());
  380.         $this->session->set('isFallbackStore'false);
  381.         // TODO-NGS: Only fallback! Cookie validness could become a problem.
  382.         // Only use this if session does not have value. E.g. session->getVariable = -1 (in case session died for some reason)
  383.         setcookie('ianeoCurrentStore', (string)$store->getLegacyId(), time() + self::COOKIE_TIME'/');
  384.         return $storeFormatted;
  385.     }
  386.     public function setStoreInSessionWithFallbackStoreFlag(Context $contextstring $storeIdbool $setFallback): array
  387.     {
  388.         $store $this->storeService->getStore($storeId$context);
  389.         if (is_null($store)) {
  390.             throw new \InvalidArgumentException('Store ' $storeId ' not found');
  391.         }
  392.         $storeFormatted $this->storeService->formatStore([$store]);
  393.         $this->session->set('ianeoCurrentStore'$store->getLegacyId());
  394.         $this->session->set('isFallbackStore'$setFallback);
  395.         // TODO-NGS: Only fallback! Cookie validness could become a problem.
  396.         // Only use this if session does not have value. E.g. session->getVariable = -1 (in case session died for some reason)
  397.         setcookie('ianeoCurrentStore', (string)$store->getLegacyId(), time() + self::COOKIE_TIME'/');
  398.         return $storeFormatted;
  399.     }
  400.     public function changeStoreAndAdjustCart(string $storeIdCart $cartSalesChannelContext $salesChannelContext)
  401.     {
  402.         // test if cart contains products that were reserved in a different store
  403.         $reserveProducts = [];
  404.         $removeProducts = [];
  405.         $store $this->storeService->getStore($storeIdContext::createDefaultContext());
  406.         $formattedStore $this->storeService->formatStore([$store]);
  407.         /** @var JsonResponse $availabilityInformation */
  408.         $availabilityInformation $this->handleReservedItemsInCart($cart$formattedStore);
  409.         if (!empty($availabilityInformation)) {
  410.             $products json_decode($availabilityInformation->getContent(), true)['data'][0]['products'];
  411.             foreach ($products as $key => $product) {
  412.                 $product['availability'] == true $reserveProducts [] = $key $removeProducts [] = $key;
  413.             }
  414.         }
  415.         $this->changeStoreForLineItems($reserveProducts$storeId$cart$salesChannelContext);
  416.         $this->removeLineItems($removeProducts$cart$salesChannelContext);
  417.         $cartMessage "";
  418.         $reservedNumber count($reserveProducts);
  419.         $removedNumber count($removeProducts);
  420.         $token $this->session->get('sw-context-token');
  421.         $this->setStoreInSalesChannelApiPayload($token, (int)$storeId);
  422.         if ($this->appService->isAppFromSession($this->session)) {
  423.             $this->appService->saveStoreForDevice($token, (int)$storeId$this->session->get('x-globus-device-id'));
  424.         }
  425.         if ($reservedNumber === && $removedNumber === 0) { // no change in cart
  426.             $cartMessage "";
  427.         } else if ($reservedNumber >= && $removedNumber === 0) { // all reservation items were reserved in newly selected store
  428.             $cartMessage $this->translator->trans('switchStore.switchedAll');
  429.         } else if ($reservedNumber === && $removedNumber >= 0) { // all reservation items were removed
  430.             $cartMessage $this->translator->trans('switchStore.removedAll');
  431.         } else { // Some reservation items were removed, others were reserved in newly selected store
  432.             $cartMessage $this->translator->trans('switchStore.switchedPartially');
  433.         }
  434.         return new JsonResponse([
  435.             'success' => true,
  436.             'data' => $formattedStore,
  437.             'cartMessage' => $cartMessage
  438.         ]);
  439.     }
  440.     /**
  441.      * @param string $contextToken
  442.      * @param SalesChannelContext $salesChannelContext
  443.      *
  444.      * Is called if no store is set for session, as we always need a store set.
  445.      * If the cart associated with the session does not contain reservation line items, the fallback store is selected.
  446.      * If there are reservation line items contained, the function checks which store was used for these items and selects this store for the session.
  447.      * If the cart contains reservation line items from multiple stores, the reservation line items are removed from the cart and the fallback store
  448.      * is selected, as we only allow reservations for one store at a time.
  449.      */
  450.     public function setFallbackStore(Session $sessionSalesChannelContext $salesChannelContext): void
  451.     {
  452.         $salesChannelId = !empty($salesChannelContext->getSalesChannelId()) ? $salesChannelContext->getSalesChannelId() : IaneoDefaults::SALES_CHANNEL_BAUMARKT;
  453.         $contextToken $session->get('sw-context-token');
  454.         $cart $this->cartService->getCart($contextToken$salesChannelContext);
  455.         if (is_null($cart)) {
  456.             if($salesChannelId !== IaneoDefaults::SALES_CHANNEL_ALPHATECC){
  457.                     $this->setStoreInSessionWithFallbackStoreFlag(Context::createDefaultContext(), '62',true); // TODO-NGS: remove hardcoded fallback store ID
  458.             } else {
  459.                     $this->setStoreInSessionWithFallbackStoreFlag(Context::createDefaultContext(), '305'true);
  460.             }
  461.             return;
  462.         }
  463.         $lineItems $cart->getLineItems();
  464.         if (is_null($lineItems) || count($lineItems) === 0) {
  465.             if($salesChannelId !== IaneoDefaults::SALES_CHANNEL_ALPHATECC){
  466.                 $this->setStoreInSessionWithFallbackStoreFlag(Context::createDefaultContext(), '62',true); // TODO-NGS: remove hardcoded fallback store ID
  467.             } else {
  468.                 $this->setStoreInSessionWithFallbackStoreFlag(Context::createDefaultContext(), '305'true);
  469.             }
  470.             return;
  471.         }
  472.         $reservationLIs $this->findReservationLineItems($lineItems);
  473.         if (count($reservationLIs) === 0) {
  474.             if($salesChannelId !== IaneoDefaults::SALES_CHANNEL_ALPHATECC){
  475.                 $this->setStoreInSessionWithFallbackStoreFlag(Context::createDefaultContext(), '62'true); // TODO-NGS: remove hardcoded fallback store ID
  476.             } else {
  477.                 $this->setStoreInSessionWithFallbackStoreFlag(Context::createDefaultContext(), '305'true);
  478.             }
  479.             return;
  480.         }
  481.         // check which stores are in cart
  482.         $stores $this->getStores($reservationLIs$lineItems);
  483.         if (count($stores) !== 1) {
  484.             $this->logger->error(__CLASS__ ":" __FUNCTION__ ":" __LINE__ ": Cart with token " $contextToken " contains " count($stores) . " stores");
  485.             // remove reservation lineItems in this case
  486.             foreach ($reservationLIs as $id => $quantity) {
  487.                 $this->cartLineItemController->deleteLineItem($cart$id, new Request(), $salesChannelContext);
  488.             }
  489.             if($salesChannelId !== IaneoDefaults::SALES_CHANNEL_ALPHATECC){
  490.                 $this->setStoreInSessionWithFallbackStoreFlag(Context::createDefaultContext(), '62'true); // TODO-NGS: remove hardcoded fallback store ID
  491.             } else {
  492.                 $this->setStoreInSessionWithFallbackStoreFlag(Context::createDefaultContext(), '305'true);
  493.             }
  494.             return;
  495.         }
  496.         // set store to store already used in cart
  497.         $fallbackStore $stores[0];
  498.         $this->changeStoreAndAdjustCart($fallbackStore$cart$salesChannelContext);
  499.         $session->set('isFallbackStore'false);
  500.     }
  501.     /**
  502.      * @param array $reservationLIs
  503.      * @param LineItemCollection $cartLineItems
  504.      * @return array
  505.      *
  506.      * Get all stores from reservation lineItems $reservationLIs in the cart lineItems $cartLineItems.
  507.      * Note: We do not want to mix reservations from different stores, so usually this method should return an array of length 1.
  508.      * This method can be used to verify if the cart contains a single store as expected
  509.      */
  510.     private function getStores(array $reservationLIsLineItemCollection $cartLineItems)
  511.     {
  512.         $storeArray = []; // only expect one store
  513.         foreach ($reservationLIs as $key => $quantity) {
  514.             /** @var LineItem $lineItem */
  515.             $lineItem $cartLineItems->get($key);
  516.             $lineItem->getExtension('orderType');
  517.             $extensions $lineItem->getExtensions();
  518.             if (array_key_exists('store'$extensions)) {
  519.                 $storeArray $extensions['store'];
  520.             }
  521.         }
  522.         return array_unique($storeArray);
  523.     }
  524.     private function setStoreInSalesChannelApiPayload(string $tokenint $storeLegacyId)
  525.     {
  526.         $payload $this->connection->fetchAssoc('
  527.             SELECT payload
  528.             FROM sales_channel_api_context scac
  529.             WHERE scac.token = :token;
  530.         ', [
  531.             'token' => $token
  532.         ]);
  533.         if (!empty($payload)) {
  534.             // TODO-NGS: Is there a more efficient way to do this?
  535.             $payload json_decode($payload['payload'], true);
  536.             $payload['ianeoCurrentStore'] = $storeLegacyId;
  537.             $payload json_encode($payload);
  538.             $this->connection->update('sales_channel_api_context', ['payload' => $payload], ['token' => $token]);
  539.         }
  540.     }
  541.     public function getSalesChannelId(string $token)
  542.     {
  543.         $salesChannelId $this->connection->fetchOne(
  544.             'SELECT LOWER(HEX(sales_channel_id))FROM cart WHERE token = :token',
  545.             ['token' => $token]
  546.         );
  547.         return $salesChannelId;
  548.     }
  549. }