src/Elements/Bundle/DemiFrontendBundle/Controller/CartController.php line 1343

Open in your IDE?
  1. <?php
  2. /**
  3.  * Elements DeMI
  4.  *
  5.  * This source file is available under the elements DeMI license version 1
  6.  *
  7.  * @copyright  Copyright (c) elements.at New Media Solutions GmbH (https://www.elements.at/)
  8.  * @license    elements DeMI Lizenz Version 1 (https://www.elements.at/de/demi-lizenz)
  9.  */
  10. namespace Elements\Bundle\DemiFrontendBundle\Controller;
  11. use Elements\Bundle\CmsToolsBundle\Tool\Helper\ElementsCustomDateFormat;
  12. use Elements\Bundle\DemiFrontendBundle\Service\AccommodationResultSet;
  13. use Elements\Bundle\DemiFrontendBundle\Service\AdditionalService;
  14. use Elements\Bundle\DemiFrontendBundle\Service\CartItemInfo;
  15. use Elements\Bundle\DemiFrontendBundle\Service\Checkout\PrePaymentHandler;
  16. use Elements\Demi\Frontend\Service\Configuration;
  17. use Elements\Bundle\DemiFrontendBundle\Service\DemiUrl;
  18. use Elements\Bundle\DemiFrontendBundle\Service\EcommerceHelper;
  19. use Elements\Bundle\DemiFrontendBundle\Service\Redirect;
  20. use Elements\Bundle\DemiFrontendBundle\Service\SearchFrontend;
  21. use Elements\Bundle\DemiFrontendBundle\Service\Ticketing;
  22. use Elements\Bundle\DemiFrontendBundle\Service\Validation;
  23. use Elements\Bundle\TicketingBundle\Model\OrderItem;
  24. use Elements\Bundle\TicketingBundle\OrderManager;
  25. use Elements\Demi\AbstractObject;
  26. use Elements\Demi\Accommodation\Search\Parameter;
  27. use Elements\Demi\AdditionalService\Search\Listing;
  28. use Elements\Demi\AdditionalService\Search\Parameter\Line;
  29. use Elements\Demi\Deskline\Config;
  30. use Elements\Demi\Deskline\Constant\DescriptionInterface;
  31. use Elements\Demi\Deskline\Constant\ShoppingCart\DurationTypeInterface;
  32. use Elements\Demi\Deskline\Constant\ShoppingCart\ItemTypeInterface;
  33. use Elements\Demi\Deskline\DSI\Service\DynamicService;
  34. use Elements\Demi\Deskline\Package\Search\Listing\Live;
  35. use Elements\Demi\Deskline\Service\Search\CancellationInformation;
  36. use Elements\Demi\EcommerceFramework\Checkout\SessionCart;
  37. use Elements\Demi\Info\CartItemInfoHolder;
  38. use Elements\Demi\Info\DestinationPackageCartItemInfoHolder;
  39. use Elements\Demi\Info\DestinationPackageTicketSubCartItemInfoHolder;
  40. use Elements\Demi\Info\TicketCartItemInfoHolder;
  41. use Elements\Demi\Model\AccommodationProduct;
  42. use Elements\Demi\Model\AdditionalProduct;
  43. use Elements\Demi\Model\HousePackageMaster;
  44. use Elements\Demi\Model\PackageSection;
  45. use Elements\Demi\Model\ShopItem;
  46. use Elements\Demi\Package\Search\Detail\ResultSet\Package;
  47. use Exception;
  48. use HttpEncodingException;
  49. use JetBrains\PhpStorm\Pure;
  50. use Pimcore\Bundle\AdminBundle\HttpFoundation\JsonResponse;
  51. use Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\CartInterface;
  52. use Pimcore\Bundle\EcommerceFrameworkBundle\Factory;
  53. use Pimcore\Bundle\EcommerceFrameworkBundle\Tracking\TrackingManager;
  54. use Pimcore\Log\ApplicationLogger;
  55. use Pimcore\Model\DataObject\DemiAccommodationProduct;
  56. use Pimcore\Model\DataObject\DemiAdditionalProduct;
  57. use Pimcore\Model\DataObject\DemiPackageSection;
  58. use Pimcore\Tool\DeviceDetector;
  59. use Pimcore\Translation\Translator;
  60. use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
  61. use Symfony\Component\HttpFoundation\RedirectResponse;
  62. use Symfony\Component\HttpFoundation\Request;
  63. use Symfony\Component\HttpFoundation\Response;
  64. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  65. class CartController extends BookingController
  66. {
  67.     /**
  68.      * @var SearchFrontend|null
  69.      */
  70.     protected $searchFrontend null;
  71.     /**
  72.      * @var Listing|null
  73.      */
  74.     protected $additionalServiceListingLive null;
  75.     /**
  76.      * @var Listing|null
  77.      */
  78.     protected $additionalServiceListingLocal null;
  79.     /**
  80.      * @var AccommodationResultSet|null
  81.      */
  82.     protected $accommodationResultSet null;
  83.     /**
  84.      * @var DemiUrl|null
  85.      */
  86.     protected $demiUrl null;
  87.     /**
  88.      * @var null|Translator
  89.      */
  90.     protected $translator null;
  91.     /**
  92.      * @var null|TrackingManager
  93.      */
  94.     protected $trackingManager null;
  95.     /**
  96.      * @var Ticketing|null
  97.      */
  98.     protected $ticketing null;
  99.     /**
  100.      * @var OrderManager|null
  101.      */
  102.     protected $orderManager null;
  103.     /**
  104.      * @var AdditionalService
  105.      */
  106.     protected $additionalServiceCartHandler null;
  107.     /**
  108.      * @var PrePaymentHandler
  109.      */
  110.     protected $prePaymentHandler null;
  111.     public function __construct(
  112.         EcommerceHelper                                                               $ecommerceHelper,
  113.         Validation                                                                    $validationService,
  114.         Redirect                                                                      $redirectService,
  115.         CartItemInfo                                                                  $cartItemInfoService,
  116.         Configuration                                                                 $configuration,
  117.         SearchFrontend                                                                $searchFrontend,
  118.         Listing                                                                       $additionalServiceListingLive,
  119.         Listing                                                                       $additionalServiceListingLocal,
  120.         AccommodationResultSet                                                        $accommodationResultSet,
  121.         DemiUrl                                                                       $demiUrl,
  122.         Translator                                                                    $translator,
  123.         TrackingManager                                                               $trackingManager,
  124.         AdditionalService                                                             $additionalServiceCartHandler,
  125.         Ticketing                                                                     $ticketing,
  126.         OrderManager                                                                  $orderManager null,
  127.         PrePaymentHandler                                                             $prePaymentHandler,
  128.         protected ContainerBagInterface                                               $parameterBag,
  129.         protected \Elements\Demi\Deskline\Accommodation\Search\Service\Vacancies\Live $vacanciesLive,
  130.         protected \Elements\Demi\Deskline\Package\Search\Service\Live                 $searchServiceLive,
  131.     )
  132.     {
  133.         parent::__construct($ecommerceHelper$validationService$redirectService$cartItemInfoService,
  134.             $configuration);
  135.         $this->searchFrontend $searchFrontend;
  136.         $this->additionalServiceListingLive $additionalServiceListingLive;
  137.         $this->additionalServiceListingLocal $additionalServiceListingLocal;
  138.         $this->accommodationResultSet $accommodationResultSet;
  139.         $this->demiUrl $demiUrl;
  140.         $this->translator $translator;
  141.         $this->trackingManager $trackingManager;
  142.         $this->additionalServiceCartHandler $additionalServiceCartHandler;
  143.         $this->ticketing $ticketing;
  144.         $this->orderManager $orderManager;
  145.         $this->prePaymentHandler $prePaymentHandler;
  146.     }
  147.     public function step1Action(Request $request): RedirectResponse|Response
  148.     {
  149.         $cart $this->ecommerceHelper->getCart();
  150.         // if items in cart, but destination package - redirect to packagestep1
  151.         if (count($cart->getItems()) > && count($cart->getDestinationPackageItems()) > 0) {
  152.             return $this->redirect($this->demiUrl->getCheckoutUrl('cart''packagestep1',
  153.                 ['_locale' => $request->getLocale()]));
  154.         }
  155.         $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  156.         Factory::getInstance()->getEnvironment()->removeCustomItem('demi_cartName');
  157.         $env Factory::getInstance()->getEnvironment();
  158.         $error $this->ecommerceHelper->getError();
  159.         $this->ecommerceHelper->setError('');
  160.         if (is_array($cart->getAccommodationItems()) && count($cart->getAccommodationItems()) > 0) {
  161.             $accommodationItems $cart->getAccommodationItems();
  162.             $isOrgOffer reset($accommodationItems)->getCartItemInfo()->getIsOrganisationOffer();
  163.             if ($isOrgOffer) {
  164.                 $env Factory::getInstance()->getEnvironment();
  165.                 $env->clearEnvironment();
  166.                 $this->ecommerceHelper->clear();
  167.                 if ($error) {
  168.                     $this->ecommerceHelper->setError($error);
  169.                 }
  170.             }
  171.         }
  172.         $originator $this->document->getProperty('demi_customOriginator');
  173.         $this->handleRecommendedServices($cart$originator);
  174.         if ($request->get('clear') || $request->get('add')) {
  175.             $error null;
  176.             $env->removeCustomItem('deskline_bookableOnRequest');
  177.             $env->save();
  178.             $tvbId $request->get('tvbPackage');
  179.             if ($request->get('clear')) {
  180.                 $cart $this->ecommerceHelper->clear();
  181.             }
  182.             if (!$this->validationService->checkRequestForCartParameters($request)) {
  183.                 throw new Exception('some parameters are missing. cannot add product to cart');
  184.             }
  185.             $bookableOnRequest false;
  186.             foreach ($request->get('roomRow') as $key => $roomRow) {
  187.                 $accommodationLiveAdapter $tvbId $this->searchServiceLive $this->vacanciesLive;
  188.                 $id $request->get('productId')[$key];
  189.                 $mealCode $request->get('mealCode')[$key];
  190.                 $units $request->get('count')[$key];
  191.                 $searchParam $tvbId $this->searchFrontend->getPackageSearchParam() : $this->searchFrontend->getSearchParam($roomRow,
  192.                     true);
  193.                 $searchParam->setOmitExtraFeesInMinPrice(false);
  194.                 $searchParam->setOmitVisitorTaxMinPrice(false);
  195.                 if ($searchParam instanceof Parameter) {
  196.                     $searchParam->setIsCorridor(false);
  197.                 }
  198.                 $tmproomrows $searchParam->getRoomrows();
  199.                 $tmproomrows[0]->setUnits($units);
  200.                 $searchParam->setRoomrows($tmproomrows);
  201.                 $searchParam->setMealTypeId($mealCode);
  202.                 $tvbId $accommodationLiveAdapter->setParams($searchParam) : $accommodationLiveAdapter->setSearchParameter($searchParam);
  203.                 $resultSetProduct $this->accommodationResultSet->getProductByProductIdAndMealType($accommodationLiveAdapter,
  204.                     $id$mealCodetrue);
  205.                 if ($resultSetProduct === null) {
  206.                     throw new Exception("Serviceprovider not actually available. Return");
  207.                 }
  208.                 $cartItemInfo $this->cartItemInfoService->fillPriceInfoFromResultSetProduct(new CartItemInfoHolder(),
  209.                     $resultSetProduct);
  210.                 $cartItemInfo $this->cartItemInfoService->fillOtherFromRequest($cartItemInfo$request$roomRow,
  211.                     $key);
  212.                 $bookableOnRequest $resultSetProduct->getBookableOnRequest();
  213.                 $product AccommodationProduct::getById($id);
  214.                 $cart $this->ecommerceHelper->addProductToCart($units$product$cartItemInfo$cart);
  215.                 $this->trackingManager->trackCartProductActionAdd($cart$product$units);
  216.             }
  217.             $env->setCustomItem('demi_hpmId'$request->get('hpmId'));
  218.             $env->setCustomItem('deskline_bookableOnRequest'$bookableOnRequest);
  219.             $env->save();
  220.         }
  221.         if ($cart->getLastAddedId()) {
  222.             $viewParams['lastAddedId'] = $cart->getLastAddedId();
  223.             $cart->setLastAddedId(0);
  224.             $cart->save();
  225.         }
  226.         if ($cart->getLastRemovedId()) {
  227.             $viewParams['lastRemovedId'] = $cart->getLastRemovedId();
  228.             $cart->setLastRemovedId(0);
  229.             $cart->save();
  230.         }
  231.         $acco $this->ecommerceHelper->getAccoFromCart($cart);
  232.         $additionalProvider $this->ecommerceHelper->getAddProviderFromCart($cart);
  233.         if (!$this->validationService->validateBookableOnRequestCart($cart)) {
  234.             throw new Exception('a cart with a product that is bookable on request cannot contain non bookable on request products');
  235.         }
  236.         $hpmId $env->getCustomItem('demi_hpmId');
  237.         if ($acco !== null) {
  238.             $additionalServiceParams $this->searchFrontend->getAdditionalServiceParam($acco$cart);
  239.             $this->additionalServiceListingLocal->setParameter($additionalServiceParams);
  240.             $this->additionalServiceListingLocal->load();
  241.             $this->additionalServiceListingLocal $this->additionalServiceCartHandler->modifyListingForCart($this->additionalServiceListingLocal$cart);
  242.             $viewParams['additionalServiceItems'] = $this->additionalServiceListingLocal;
  243.         }
  244.         $arrivalDate $acco === null && $additionalProvider === null null $cart->getArrivalDate();
  245.         $departureDate $acco === null && $additionalProvider === null null $cart->getDepartureDate();
  246.         $bookOnRequest $env->getCustomItem('deskline_bookableOnRequest');
  247.         $viewParams['isBookOnRequest'] = $bookOnRequest;
  248.         //viewpoint is only important for mobile
  249.         $viewpoint = ($acco !== null && !$bookOnRequest && $this->additionalServiceListingLocal && $this->additionalServiceListingLocal->count() > && $request->get('viewpoint') != 2) ? 2;
  250.         $viewParams['viewpoint'] = $viewpoint;
  251.         if ($error) {
  252.             //in case of error skip display of any additional offers
  253.             $viewParams['viewpoint'] = 2;
  254.         }
  255.         $viewParams['acco'] = $acco;
  256.         $viewParams['additionalProvider'] = $additionalProvider;
  257.         $viewParams['cart'] = $cart;
  258.         $viewParams['isPackage'] = !empty($hpmId);
  259.         $viewParams['pId'] = !empty($hpmId) ? $hpmId null;
  260.         $viewParams['pName'] = !empty($hpmId) ? HousePackageMaster::getById($hpmId)->getName() : null;
  261.         $viewParams['searchParam'] = $this->searchFrontend->getSearchParam(nulltrue);
  262.         $viewParams['error'] = $error 'demi.booking.error.' $error null;
  263.         $viewParams['emptyCart'] = $acco === null && $additionalProvider === null;
  264.         $hasTickets = !empty($this->ticketing->getDummyTickets($arrivalDate$departureDate,
  265.                 $acco$cart)) && empty($cart->getDestinationPackageItems());
  266.         $viewParams['hasTickets'] = $hasTickets;
  267.         $viewParams['isMerchandise'] = $env->getCustomItem('demi_isMerchandiseCart');
  268.         $viewParams['hasAdditionalPrePaymentStep'] = $this->prePaymentHandler->hasAdditionalPrePaymentStep();
  269.         if ($this->parameterBag->get('elements_demi.checkout') === 'long') {
  270.             $viewParams['checkoutSteps'] = 'long';
  271.         } else {
  272.             $viewParams['checkoutSteps'] = 'short';
  273.         }
  274.         if (!$request->get('viewpoint') && !empty($cart->getItems()) && $hasTickets && DeviceDetector::getInstance()->isPhone() && $this->additionalServiceListingLocal->getParameter() && $this->additionalServiceListingLocal->count() <= 0) {
  275.             //don't skip the tickets just because viewport is set to 2 already
  276.             return $this->redirect($this->demiUrl->getCheckoutUrl('ticket''overview',
  277.                 ['_locale' => $request->getLocale()]));
  278.         }
  279.         if (!$this->configuration->getNeverSkipCart() && (!$request->get('viewpoint') || $request->get('viewpoint') != 2) && !empty($cart->getItems()) && $this->additionalServiceListingLocal->getParameter() &&
  280.             $this->additionalServiceListingLocal->count() <= && $request->get('clear')) {
  281.             return $this->redirect($this->demiUrl->getCheckoutUrl($hasTickets 'ticket' 'checkout'$hasTickets 'overview' 'step2',
  282.                 ['_locale' => $request->getLocale()], 'customer'));
  283.         }
  284.         $deviceDetector DeviceDetector::getInstance();
  285.         $device $deviceDetector->isPhone() ? 'mobile' 'desktop';
  286.         return $this->renderTemplate('@ElementsDemiFrontend/Cart/step1.' $device '.html.twig'$viewParams);
  287.     }
  288.     public function packagestep1Action(Request $request): Response
  289.     {
  290.         $cart $this->ecommerceHelper->getCart();
  291.         // if items in cart, but no destination packages - redirect to step1
  292.         if (count($cart->getItems()) > && count($cart->getDestinationPackageItems()) == 0) {
  293.             return $this->redirect($this->demiUrl->getCheckoutUrl('cart''step1',
  294.                 ['_locale' => $request->getLocale()]));
  295.         }
  296.         $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  297.         Factory::getInstance()->getEnvironment()->removeCustomItem('demi_cartName');
  298.         $env Factory::getInstance()->getEnvironment();
  299.         $error $this->ecommerceHelper->getError();
  300.         $this->ecommerceHelper->setError('');
  301.         if (is_array($cart->getAccommodationItems()) && count($cart->getAccommodationItems()) > 0) {
  302.             $accommodationItems $cart->getAccommodationItems();
  303.             $isOrgOffer reset($accommodationItems)->getCartItemInfo()->getIsOrganisationOffer();
  304.             if ($isOrgOffer) {
  305.                 $env Factory::getInstance()->getEnvironment();
  306.                 $env->clearEnvironment();
  307.                 $this->ecommerceHelper->clear();
  308.                 if ($error) {
  309.                     $this->ecommerceHelper->setError($error);
  310.                 }
  311.             }
  312.         }
  313.         $tvbId $request->get('tvbPackage');
  314.         if (!$tvbId) {
  315.             $cartItems $cart->getItems();
  316.             $cartItem reset($cartItems);
  317.             $destinationPackageItems $cart->getDestinationPackageItems();
  318.             $tvbId $cart->getDestinationPackageItems() && count($cart->getDestinationPackageItems()) > reset($destinationPackageItems)->getProduct()->getId() : null;
  319.             $tvbPackage $cart->getDestinationPackageItems() && count($cart->getDestinationPackageItems()) > reset($destinationPackageItems)->getProduct() : null;
  320.         } else {
  321.             $tvbPackage AbstractObject::getById($tvbId) ?: null;
  322.         }
  323.         $error null;
  324.         $env->removeCustomItem('deskline_bookableOnRequest');
  325.         $env->save();
  326.         if ($request->get('clear') || $request->get('add')) {
  327.             if ($request->get('clear')) {
  328.                 $cart $this->ecommerceHelper->clear();
  329.             }
  330.             if (!$this->validationService->checkRequestForCartParameters($request) && $tvbPackage->hasAccommodationSection()) {
  331.                 throw new Exception('some parameters are missing. cannot add product to cart');
  332.             }
  333.         }
  334.         Config::setClientKeyOverrideFromObject($tvbPackage);
  335.         $roomRows $request->get('roomRow');
  336.         if (!$roomRows) {
  337.             $roomRows[0] = 0;
  338.         }
  339.         $accommodationLiveAdapter $tvbId $this->searchServiceLive $this->vacanciesLive;
  340.         foreach ($roomRows as $key => $roomRow) {
  341.             if ($request->get('clear') || $request->get('add')) {
  342.                 $searchParam $this->searchFrontend->getPackageSearchParam();
  343.                 $searchParam->setOmitExtraFeesInMinPrice(false);
  344.                 $searchParam->setOmitVisitorTaxMinPrice(false);
  345.             } else {
  346.                 if (!$tvbId) {
  347.                     //empty package cart
  348.                     break;
  349.                 }
  350.                 $searchParam $this->searchFrontend->getPackageSearchParam(nullfalse$tvbId);
  351.                 $searchParam->setPackageId($tvbId);
  352.                 $searchParam $this->cartItemInfoService->fillPackageSearchParamsFromCartItemInfo($cartItem->getCartItemInfo(),
  353.                     $searchParam);
  354.             }
  355.             $tvbId $accommodationLiveAdapter->setParams($searchParam) : $accommodationLiveAdapter->setSearchParameter($searchParam);
  356.             $id $request->get('productId')[$key];
  357.             $mealCode $request->get('mealCode')[$key];
  358.             $units $request->get('count')[$key];
  359.             $packageResultSet Package::getById($tvbId$accommodationLiveAdapter);
  360.             if ($id && $mealCode) {
  361.                 $packageCategory $packageResultSet->getCategoryForProductAndMealCode($id$mealCode);
  362.                 $searchParam->setMealTypeId($mealCode);
  363.             } elseif ($cartItem) {
  364.                 $packageCategory $cartItem->getCartItemInfo()->getPackageCategory();
  365.             } else {
  366.                 //fallback for packages without acco, just take the first category
  367.                 $resultsetCategory $packageResultSet->getCategory()[0];
  368.                 $packageCategory $resultsetCategory->getObject();
  369.             }
  370.             if ($request->get('clear') || $request->get('add')) {
  371.                 $listing = new Live($packageResultSet$searchParam);
  372.                 $test $listing->getItems(01000);
  373.                 foreach ($test as $resultSetAcco) {
  374.                     $acco AccommodationProduct::getById($id)->getParent()->getParent();
  375.                     if ($resultSetAcco->getAccommodationId() == $acco->getId()) {
  376.                         break;
  377.                     }
  378.                 }
  379.                 if ($resultSetAcco) {
  380.                     foreach ($resultSetAcco->getProducts() as $resultSetProduct) {
  381.                         if ($resultSetProduct->getProductId() == $id) {
  382.                             break;
  383.                         }
  384.                     }
  385.                 }
  386.                 if ($resultSetProduct) {
  387.                     $cartItemInfo $this->cartItemInfoService->fillPriceInfoFromResultSetProduct(new DestinationPackageCartItemInfoHolder(),
  388.                         $resultSetProduct);
  389.                 } else {
  390.                     //tvb package without acco
  391.                     $cartItemInfo = new DestinationPackageCartItemInfoHolder();
  392.                     $cartItemInfo->setCurrency(Factory::getInstance()->getEnvironment()->getDefaultCurrency()->getShortName());
  393.                 }
  394.                 $cartItemInfo $this->cartItemInfoService->fillOtherFromRequest($cartItemInfo$request$roomRow,
  395.                     $key);
  396.                 $env->save();
  397.             } else {
  398.                 foreach ($cartItem->getSubItems() as $subItem) {
  399.                     if ($subItem->getProduct() instanceof AccommodationProduct) {
  400.                         $acco $subItem->getProduct()->getParent()->getParent();
  401.                     }
  402.                 }
  403.             }
  404.             $product \Elements\Demi\Model\Package::getById($tvbId);
  405.             if ($request->get('clear') || $request->get('add')) {
  406.                 //add package to cart
  407.                 $cart $this->ecommerceHelper->addProductToCart($units$product$cartItemInfo$carttrue,
  408.                     []);
  409.                 $cartItemInfo $this->ecommerceHelper->handlePackageCategoryPrices($product$cartItemInfo$packageCategory$packageResultSet);
  410.                 $this->trackingManager->trackCartProductActionAdd($cart$product$units);
  411.             }
  412.             //add subproducts to cart
  413.             $accommodationProduct AccommodationProduct::getById($id);
  414.             if ($accommodationProduct && ($request->get('clear') || $request->get('add'))) {
  415.                 if (!$packageCategory) {
  416.                     throw new NotFoundHttpException('Category for meal code id ' $mealCode ' not found');
  417.                 }
  418.                 $subProducts[] = $this->ecommerceHelper->addSubProductToCart(1$packageResultSet->getObject(),
  419.                     $accommodationProduct$packageResultSet->getAccommodationSection($packageCategory),
  420.                     $packageResultSet$cart$packageCategory);
  421.                 //reset the price to that of acco product, prices of additionals will be added later.
  422.                 if($product->getPackagePrice() === 'Dynamic'){
  423.                     $cartItemInfo->setPrice($subProducts[0]->getPrice());
  424.                     $cartItemInfo->setTotalPrice($subProducts[0]->getPrice());
  425.                 }
  426.             }
  427.             $mandatoryProductsAlreadyInCart = [];
  428.             foreach ($packageResultSet->getSectionData() as $section) {
  429.                 if ($section->getProductType() == ItemTypeInterface::ADDITIONAL_SERVICE && $section->getIsMandatory() && $section->getObject()->getProducts() && count($section->getObject()->getProducts()->getItems()) == 1) {
  430.                     //exactly one product in mandatory section and preselected -> add to cart automatically if preselect activated
  431.                     $amount 1;
  432.                     if ($section->getUnitType() === 'PerPerson') {
  433.                         $amount = (int)$request->get('a' $roomRow) + (int)$request->get('c' $roomRow);
  434.                     }
  435.                     $additionalProducts $section->getObject()->getProducts();
  436.                     foreach ($additionalProducts->getItems() as $additionalProduct) {
  437.                         if ($prod $additionalProduct->getProduct()) {
  438.                             //check ist preselected or not relevant for totalprice and mandatory - in both cases we add it to shopping cart
  439.                             if ($additionalProduct->getPreselected() || (!$section->getObject()->getSelectionRulesAddPrice() && $section->getIsMandatory())) {
  440.                                 if ($request->get('clear') || $request->get('add')) {
  441.                                     $service $prod->getParent();
  442.                                     $hasMandatoryCheckoutQuestions false;
  443.                                     if ($service instanceof \Elements\Demi\Model\AdditionalService && $service->getCheckoutQuestions()) {
  444.                                         foreach ($service->getCheckoutQuestions()->getItems() as $checkoutQuestion) {
  445.                                             if ($checkoutQuestion->getAnswerMandatory()) {
  446.                                                 //skip automatic adding to cart - mandatory questions have to be answered
  447.                                                 $hasMandatoryCheckoutQuestions true;
  448.                                                 break;
  449.                                             }
  450.                                         }
  451.                                     }
  452.                                     if ($hasMandatoryCheckoutQuestions) {
  453.                                         continue;
  454.                                     }
  455.                                     $newSubProduct $this->ecommerceHelper->addSubProductToCart($amount$packageResultSet->getObject(), $prod$section->getObject(), $packageResultSet$cart$packageCategory$cartItemInfo->getChildrenAges());
  456.                                     //add price to overall price
  457.                                     $cartItemInfo->setPrice($cartItemInfo->getPrice() + $newSubProduct->getPrice());
  458.                                     $cartItemInfo->setTotalPrice($cartItemInfo->getPrice() + $newSubProduct->getPrice());
  459.                                     $subProducts[] = $newSubProduct;
  460.                                     if (!$resultSetProduct) {
  461.                                         //hack for package without acco - cartitemInfo was newly created in line 420, set price to that instance
  462.                                         $cartItemInfo->setTotalPrice($cartItemInfo->getTotalPrice() + $newSubProduct->getPrice());
  463.                                     }
  464.                                     if (!$cartItemInfo->getDbCode()) {
  465.                                         $cartItemInfo->setDbCode($prod->getParent()->getParent()->getDbcode());
  466.                                     }
  467.                                     if (!$cartItemInfo->getPackageCategory()) {
  468.                                         $cartItemInfo->setPackageCategory($packageCategory);
  469.                                     }
  470.                                 }
  471.                             }
  472.                         }
  473.                     }
  474.                 }
  475.             }
  476.             if ($request->get('clear') || $request->get('add')) {
  477.                 $cartItemInfo $this->cartItemInfoService->fillDestinationPackageInfosFromCart($cartItemInfo$cart,
  478.                     $packageResultSet);
  479.                 $destinationPackageItems $cart->getDestinationPackageItems();
  480.                 $existingItem reset($destinationPackageItems);
  481.                 $existingItem->setCartItemInfo($cartItemInfo);
  482.                 if ($this->orderManager && $this->orderManager->getType() === 'Dta') {
  483.                     foreach ($existingItem->getSubItems() as $subItem) {
  484.                         $isSkiticket $subItem->getProduct()->getDemiExtension()
  485.                             && $subItem->getProduct()->getDemiExtension()->getDesklineDummyProduct()
  486.                             && $subItem->getProduct()->getDemiExtension()->getDesklineDummyProduct()->getIsSkiticket();
  487.                         if ($isSkiticket
  488.                             && $subItem->getCartItemInfo() instanceof DestinationPackageTicketSubCartItemInfoHolder
  489.                             && !$subItem->getCartItemInfo()->getInterfaceTicketId()) {
  490.                             //autodetect skiticket because it was added as mandatory item
  491.                             $dummyExtension $subItem->getProduct()->getDemiExtension()->getDesklineDummyProduct();
  492.                             //use first possible duration value from service - the user did not get the option to select it
  493.                             $possibleDurationValues $subItem->getProduct()->getParent()->getDurationValues();
  494.                             $subItem->getCartItemInfo()->setDurationValue(is_array($possibleDurationValues) && $possibleDurationValues[0] ? $possibleDurationValues[0][0] : null);
  495.                             if (!$subItem->getCartItemInfo()->getDurationValue()) {
  496.                                 throw new Exception('can not auto detect ticket duration value');
  497.                             }
  498.                             $subItem->getCartItemInfo()->setDurationValue(DurationTypeInterface::DAY);
  499.                             if (count($dummyExtension->getDtaConsumerCategory()) > 1) {
  500.                                 throw new Exception('Can not detect consumer category for skiticket mandatory product - multiple consumer categories assigned');
  501.                             }
  502.                             $consumerCategory = !empty($dummyExtension->getDtaConsumerCategory()) ? $dummyExtension->getDtaConsumerCategory()[0]->getUuid() : null;
  503.                             $detectedTickets = [];
  504.                             foreach ($dummyExtension->getTickets() as $ticket) {
  505.                                 if ($ticket->getValidityCategory()->getValidityValue() == $subItem->getCartItemInfo()->getDurationValue()) {
  506.                                     $detectedTickets[$ticket->getUuid()] = $ticket;
  507.                                 }
  508.                             }
  509.                             if (count($detectedTickets) > || count($detectedTickets) == 0) {
  510.                                 throw new Exception('Can not autodetect ticket for mandatory product - zero or multiple options found');
  511.                             }
  512.                             $ticketArray array_keys($detectedTickets);
  513.                             $subItem->getCartItemInfo()->setInterfaceTicketId(reset($ticketArray));
  514.                             $subItem->getCartItemInfo()->setInterfaceTicketTypeId($consumerCategory);
  515.                         }
  516.                     }
  517.                 }
  518.             }
  519.         }
  520.         $additionalProvider $this->ecommerceHelper->getAddProviderFromCart($cart);
  521.         if (!$this->validationService->validateBookableOnRequestCart($cart)) {
  522.             throw new Exception('a cart with a product that is bookable on request cannot contain non bookable on request products');
  523.         }
  524.         $productIds = [];
  525.         if ($packageResultSet) {
  526.             foreach ($packageResultSet->getSectionData() as $section) {
  527.                 if ($section->getProductType() == ItemTypeInterface::ADDITIONAL_SERVICE) {
  528.                     $additionalProducts $section->getObject()->getProducts();
  529.                     if ($additionalProducts) {
  530.                         foreach ($additionalProducts->getItems() as $additionalProduct) {
  531.                             if ($additionalProduct->getProduct()) {
  532.                                 $sectionObject DemiPackageSection::getById($section->getSectionId());
  533.                                 if ($sectionObject && $sectionObject->getAssignedProducts() === 'perCategory') {
  534.                                     if (!$additionalProduct->getCategory() || ($packageCategory && $additionalProduct->getCategory()
  535.                                             && $additionalProduct->getCategory()->getFid() == $packageCategory->getFid())) {
  536.                                         $productIds[] = $additionalProduct->getProduct()->getId();
  537.                                     }
  538.                                 } else {
  539.                                     $productIds[] = $additionalProduct->getProduct()->getId();
  540.                                 }
  541.                             }
  542.                         }
  543.                     }
  544.                 }
  545.             }
  546.         }
  547.         if (count($productIds) > 0) {
  548.             $additionalServiceParams $this->searchFrontend->getAdditionalServiceParam($acco$cart);
  549.             $additionalServiceParams->setExcludeServicesOfAdditionalServiceProviders(false);
  550.             $additionalServiceParams->setIgnorePackageOnly(true);
  551.             $additionalServiceParams->setIgnoreAvailabilities(true);
  552.             $additionalServiceParams->setProductIds($productIds);
  553.             $this->additionalServiceListingLocal->setParameter($additionalServiceParams);
  554.             $this->additionalServiceListingLocal->load();
  555.             $viewParams['additionalServiceItems'] = $this->additionalServiceListingLocal;
  556.         }
  557.         $viewParams['packageResultSet'] = $packageResultSet;
  558.         if (!$accommodationProduct && $cartItem) {
  559.             foreach ($cartItem->getSubItems() as $subItem) {
  560.                 if ($subItem->getProduct() instanceof AccommodationProduct) {
  561.                     $accommodationProduct $subItem->getProduct();
  562.                 }
  563.             }
  564.         }
  565.         if ($packageResultSet && $accommodationProduct) {
  566.             $addNightPrice $packageResultSet->getPriceForAdditionalNight($accommodationProduct,
  567.                 $packageCategory$packageResultSet->getAccommodationSection($packageCategory));
  568.             $viewParams['addNightPrice'] = (float)$addNightPrice $addNightPrice 0;
  569.             $viewParams['accommodationProduct'] = $accommodationProduct;
  570.         }
  571.         $viewParams['packageCategory'] = $packageCategory;
  572.         $viewParams['isBookOnRequest'] = $env->getCustomItem('deskline_bookableOnRequest');
  573.         //viewpoint is only relevant for mobile view
  574.         $viewParams['viewpoint'] = count($productIds) > && $packageResultSet && $this->additionalServiceListingLocal && $this->additionalServiceListingLocal->count() > && $request->get('viewpoint') != 2;
  575.         if ($error) {
  576.             //in case of error skip display of any additional offers
  577.             $viewParams['viewpoint'] = 2;
  578.         }
  579.         $destinationPackageItems $cart->getDestinationPackageItems();
  580.         $existingItem reset($destinationPackageItems);
  581.         if ($packageResultSet && $existingItem) {
  582.             $allSectionsSelected true;
  583.             foreach ($packageResultSet->getSectionData() as $section) {
  584.                 $sectionSelected false;
  585.                 foreach ($existingItem->getSubItems() as $subItem) {
  586.                     if ($subItem->getCartItemInfo()->getSection()->getId() == $section->getSectionId()) {
  587.                         $sectionSelected true;
  588.                     }
  589.                 }
  590.                 if (!$sectionSelected) {
  591.                     $allSectionsSelected false;
  592.                     break;
  593.                 }
  594.             }
  595.             $viewParams['allSectionsSelected'] = $allSectionsSelected;
  596.             if ($allSectionsSelected) {
  597.                 $viewParams['viewpoint'] = 2;
  598.             }
  599.         }
  600.         $viewParams['acco'] = $acco;
  601.         $viewParams['additionalProvider'] = $additionalProvider;
  602.         $viewParams['cart'] = $cart;
  603.         $viewParams['isPackage'] = !empty($hpmId) || !empty($tvbId);
  604.         $pid = !empty($hpmId) ? $hpmId null;
  605.         $viewParams['pId'] = $pid ?: (!empty($tvbId) ? $tvbId null);
  606.         $pname = !empty($hpmId) ? HousePackageMaster::getById($hpmId)->getName() : null;
  607.         $viewParams['pName'] = $pname ?: ($tvbPackage $tvbPackage->getName() : null);
  608.         $viewParams['searchParam'] = $this->searchFrontend->getSearchParam(nulltrue);
  609.         $viewParams['error'] = $error 'demi.booking.error.' $error null;
  610.         $viewParams['emptyCart'] = $acco === null && $additionalProvider === null && $tvbPackage === null;
  611.         $viewParams['tvbPackage'] = $tvbPackage;
  612.         $arrivalDate $acco === null && $additionalProvider === null null $cart->getArrivalDate();
  613.         $departureDate $acco === null && $additionalProvider === null null $cart->getDepartureDate();
  614.         $hasTickets = !empty($this->ticketing->getDummyTickets($arrivalDate$departureDate$acco$cart))
  615.             && empty($cart->getDestinationPackageItems());
  616.         $viewParams['hasTickets'] = $hasTickets;
  617.         $viewParams['lastAddedAdditionalNights'] = $request->get('added') === 'addNights';
  618.         $viewParams['lastRemovedAdditionalNights'] = $request->get('removed') === 'addNights';
  619.         $viewParams['hasAdditionalPrePaymentStep'] = $this->prePaymentHandler->hasAdditionalPrePaymentStep();
  620.         if ($cart->getLastAddedId()) {
  621.             $viewParams['lastAddedId'] = $cart->getLastAddedId();
  622.             $cart->setLastAddedId(0);
  623.             $cart->save();
  624.         }
  625.         if ($cart->getLastRemovedId()) {
  626.             $viewParams['lastRemovedId'] = $cart->getLastRemovedId();
  627.             $cart->setLastRemovedId(0);
  628.             $cart->save();
  629.         }
  630.         if (!$this->configuration->getNeverSkipCart()
  631.             && !empty($cart->getItems())
  632.             && count($productIds) > 0
  633.             && $this->additionalServiceListingLocal->getParameter()
  634.             && $this->additionalServiceListingLocal->count() <= 0
  635.             && $request->get('clear')) {
  636.             return $this->redirect($this->demiUrl->getCheckoutUrl($hasTickets 'ticket' 'checkout'$hasTickets 'overview' 'step2',
  637.                 ['_locale' => $request->getLocale()], 'customer'));
  638.         }
  639.         $deviceDetector DeviceDetector::getInstance();
  640.         $device $deviceDetector->isPhone() ? 'mobile' 'desktop';
  641.         return $this->renderTemplate('@ElementsDemiFrontend/Cart/packagestep1.' $device '.html.twig'$viewParams);
  642.     }
  643.     /**
  644.      * @param Request $request
  645.      *
  646.      * @return RedirectResponse
  647.      *
  648.      * @throws Exception
  649.      * removes a product from the cart and forwards to the overview action
  650.      */
  651.     public function removeAdditionalNightsFromCartAction(Request $request): RedirectResponse
  652.     {
  653.         $cart $this->ecommerceHelper->getCart();
  654.         if (count($cart->getItems()) === 0) {
  655.             // session ran out
  656.             return $this->redirect($this->redirectService->redirectToList($request->request->all(),
  657.                 'demi.booking.error.session'));
  658.         }
  659.         $itemKey $request->query->get('itemKey');
  660.         if (empty($itemKey)) {
  661.             throw new Exception('cannot remove product since there is no itemKey given');
  662.         }
  663.         $env Factory::getInstance()->getEnvironment();
  664.         $env->setCustomItem('useExistingDesklineCart'false);
  665.         $env->save();
  666.         $destinationPackageItems $cart->getDestinationPackageItems();
  667.         $destinationPackageCartItem reset($destinationPackageItems);
  668.         if ($destinationPackageCartItem && $destinationPackageCartItem->getItemKey() != $itemKey) {
  669.             $subItems $destinationPackageCartItem->getSubItems();
  670.             foreach ($subItems as $subitem) {
  671.                 if ($subitem->getProduct() instanceof AccommodationProduct) {
  672.                     $nightPrice $subitem->getCartItemInfo()->getAdditionalNightsPrice();
  673.                     $oldAddPrice $subitem->getCartItemInfo()->getAdditionalNights() * $nightPrice;
  674.                     if ($oldAddPrice) {
  675.                         $destinationPackageCartItem->getCartItemInfo()->setTotalPrice($destinationPackageCartItem->getCartItemInfo()->getTotalPrice() - $oldAddPrice);
  676.                     }
  677.                     $subitem->getCartItemInfo()->setAdditionalNights(0);
  678.                     $subitem->getCartItemInfo()->setAdditionalNightsPrice($nightPrice);
  679.                     $cart->save();
  680.                     break;
  681.                 }
  682.             }
  683.         }
  684.         $urlParams = ['_locale' => $request->getLocale()];
  685.         if (DeviceDetector::getInstance()->isPhone()) {
  686.             $urlParams['viewpoint'] = 2;
  687.             $urlParams['removed'] = 'addNights';
  688.         }
  689.         return $this->redirect($this->demiUrl->getCheckoutUrl('cart''packagestep1'$urlParams));
  690.     }
  691.     /**
  692.      * @param Request $request
  693.      *
  694.      * @return RedirectResponse
  695.      *
  696.      * @throws Exception
  697.      * removes a product from the cart and forwards to the overview action
  698.      */
  699.     public function removeFromCartAction(Request $request): RedirectResponse
  700.     {
  701.         $cart $this->ecommerceHelper->getCart();
  702.         $controller 'cart';
  703.         if (count($cart->getItems()) === 0) {
  704.             // session ran out
  705.             return $this->redirect($this->redirectService->redirectToList($request->request->all(),
  706.                 'demi.booking.error.session'));
  707.         }
  708.         $itemKey $request->query->get('itemKey');
  709.         if (empty($itemKey)) {
  710.             throw new Exception('cannot remove product since there is no itemKey given');
  711.         }
  712.         $destinationPackageItems $cart->getDestinationPackageItems();
  713.         $destinationPackageCartItem reset($destinationPackageItems);
  714.         if ($destinationPackageCartItem && $destinationPackageCartItem->getItemKey() != $itemKey) {
  715.             //TODO: trackingmanager track remove subitem?
  716.             $this->ecommerceHelper->removeSubProductFromCart($destinationPackageCartItem->getProductId(), $itemKey);
  717.             // TODO fix price of package!
  718.             $action 'packagestep1';
  719.             $viewpoint $destinationPackageCartItem->getCartItemInfo()->getMandatoryMissing() ? 2;
  720.         } else {
  721.             $this->trackingManager->trackCartProductActionRemove($cart$cart->getItem($itemKey)->getProduct(),
  722.                 $cart->getItem($itemKey)->getCount());
  723.             $this->ecommerceHelper->removeProductFromCart($itemKey);
  724.             if (!empty($cart->getItems())) {
  725.                 foreach ($cart->getItems() as $remainingItem) {
  726.                     if (isset($remainingItem->getCartItemInfo()->getAdditionalInfo()['relatedProduct']) && $remainingItem->getCartItemInfo()->getAdditionalInfo()['relatedProduct'] === $itemKey) {
  727.                         $keyToRemove $remainingItem->getItemKey();
  728.                         $this->trackingManager->trackCartProductActionRemove($cart$cart->getItem($keyToRemove)->getProduct(),
  729.                             $cart->getItem($keyToRemove)->getCount());
  730.                         $this->ecommerceHelper->removeProductFromCart($keyToRemove);
  731.                     }
  732.                 }
  733.             }
  734.             $action 'step1';
  735.             $viewpoint 2;
  736.         }
  737.         if ($request->get('ticket')) {
  738.             $controller 'ticket';
  739.             $action 'overview';
  740.         }
  741.         $urlParams = ['_locale' => $request->getLocale()];
  742.         if (DeviceDetector::getInstance()->isPhone()) {
  743.             $urlParams['viewpoint'] = $viewpoint;
  744.         }
  745.         return $this->redirect($this->demiUrl->getCheckoutUrl($controller$action$urlParams));
  746.     }
  747.     /**
  748.      * @param Request $request
  749.      *
  750.      * @return RedirectResponse
  751.      *
  752.      * @throws Exception
  753.      * adds an product to the cart and forwards to the overview (step1)
  754.      * it also sets the cancellationInfo of the additionalProduct with the cancellationInfo of the referring acco
  755.      */
  756.     public function addAdditionalNightsToCartAction(Request $request): RedirectResponse
  757.     {
  758.         $count $request->get('count');
  759.         $cart $this->ecommerceHelper->getCart();
  760.         $destinationPackageItems $cart->getDestinationPackageItems();
  761.         $firstDestinationPackageProduct reset($destinationPackageItems);
  762.         if (!$firstDestinationPackageProduct) {
  763.             // session ran out
  764.             return $this->redirect($this->redirectService->redirectToList($request->request->all(),
  765.                 'demi.booking.error.session'));
  766.         }
  767.         $env Factory::getInstance()->getEnvironment();
  768.         $env->setCustomItem('useExistingDesklineCart'false);
  769.         $env->save();
  770.         $subItems $firstDestinationPackageProduct->getSubItems();
  771.         foreach ($subItems as $subitem) {
  772.             if ($subitem->getProduct() instanceof AccommodationProduct) {
  773.                 $nightPrice $subitem->getCartItemInfo()->getAdditionalNightsPrice();
  774.                 $oldAddPrice $subitem->getCartItemInfo()->getAdditionalNights() * $nightPrice;
  775.                 if ($oldAddPrice) {
  776.                     $firstDestinationPackageProduct->getCartItemInfo()->setTotalPrice($firstDestinationPackageProduct->getCartItemInfo()->getTotalPrice() - $oldAddPrice);
  777.                 }
  778.                 $addPrice $nightPrice $count;
  779.                 $subitem->getCartItemInfo()->setAdditionalNights($count);
  780.                 $subitem->getCartItemInfo()->setAdditionalNightsPrice($nightPrice);
  781.                 $firstDestinationPackageProduct->getCartItemInfo()->setTotalPrice($firstDestinationPackageProduct->getCartItemInfo()->getTotalPrice() + $addPrice);
  782.                 $cart->save();
  783.                 break;
  784.             }
  785.         }
  786.         $url $this->demiUrl->getCheckoutUrl('cart''packagestep1',
  787.             ['_locale' => $request->getLocale(), 'added' => 'addNights']);
  788.         return $this->redirect($url);
  789.     }
  790.     protected function getTicketsByPrice(TicketCartItemInfoHolder $cartItemInfo$countSessionCart $cartAbstractObject $product): array
  791.     {
  792.         $alreadyAdded 0;
  793.         foreach ($cart->getItems() as $item) {
  794.             if ($item->getProductId() == $product->getId()
  795.                 && $item->getCartItemInfo()->getDateFrom()->getTimestamp() == $cartItemInfo->getDateFrom()->getTimestamp()
  796.                 && $item->getCartItemInfo()->getDurationValue() == $cartItemInfo->getDurationValue()
  797.                 && $item->getCartItemInfo()->getDurationType() == $cartItemInfo->getDurationType()) {
  798.                 $alreadyAdded += $item->getCount();
  799.             }
  800.         }
  801.         $ticketOrder $this->orderManager->create([
  802.             'product_id' => $cartItemInfo->getInterfaceTicketId(),
  803.             'purchaser_first_name' => 'Max',
  804.             'purchaser_last_name' => 'Mustermann',
  805.             'currency' => Factory::getInstance()->getEnvironment()->getDefaultCurrency()->getShortName(),
  806.             'email' => 'max.mustermann@test.at',
  807.             'date' => $cartItemInfo->getDateFrom(),
  808.             'ticket_types' => [
  809.                 [
  810.                     'id' => $cartItemInfo->getInterfaceTicketTypeId(),
  811.                     'count' => $alreadyAdded $count
  812.                 ]
  813.             ]
  814.         ]);
  815.         $ticketsByPrice = [];
  816.         /** @var OrderItem $item */
  817.         $items array_slice($ticketOrder->getItems(), $count * -1nulltrue);
  818.         foreach ($items as $item) {
  819.             if (!isset($ticketsByPrice[$item->getSubTotal()])) {
  820.                 $ticketsByPrice[$item->getSubTotal()] = [];
  821.             }
  822.             $ticketsByPrice[$item->getSubTotal()][] = $item;
  823.         }
  824.         return $ticketsByPrice;
  825.     }
  826.     /**
  827.      * @param Request $request
  828.      *
  829.      * @return RedirectResponse
  830.      *
  831.      * @throws Exception
  832.      * adds an product to the cart and forwards to the overview (step1)
  833.      * it also sets the cancellationInfo of the additionalProduct with the cancellationInfo of the referring acco
  834.      */
  835.     public function addToCartAction(Request $request): RedirectResponse
  836.     {
  837.         if ($request->get('tid')) {
  838.             if (!$request->get('date') || !$request->get('offerId') || !$request->get('count')) {
  839.                 throw new Exception('some parameters are missing. cannot add product to cart');
  840.             }
  841.         } elseif (!$this->validationService->checkRequestForAddToCartParameters($request)) {
  842.             throw new Exception('some parameters are missing. cannot add product to cart');
  843.         }
  844.         $productId $request->get('offerId');
  845.         /** @var AdditionalProduct|ShopItem $product */
  846.         $product AbstractObject::getById($productId);
  847.         $count $request->get('count');
  848.         $cart $this->ecommerceHelper->getCart();
  849.         $controller 'cart';
  850.         if ($destinationPackageId $request->get('tid')) {
  851.             $section PackageSection::getById($request->get('sectionId'));
  852.             if (!$section) {
  853.                 throw new NotFoundHttpException('Could not determine package section');
  854.             }
  855.             $destinationPackageItems $cart->getDestinationPackageItems();
  856.             $firstDestinationPackageProduct reset($destinationPackageItems);
  857.             if (!$firstDestinationPackageProduct) {
  858.                 // session ran out
  859.                 return $this->redirect($this->redirectService->redirectToList($request->request->all(),
  860.                     'demi.booking.error.session'));
  861.             }
  862.             $childrenAges = [];
  863.             foreach ($firstDestinationPackageProduct->getCartItemInfo()->getChildrenAges() as $item) {
  864.                 $c = (int)$request->get('c' $item);
  865.                 for ($i 0$i $c$i++) {
  866.                     $childrenAges[] = (string)$item;
  867.                 }
  868.             }
  869.             $count += count($childrenAges);
  870.             $searchParam $this->searchFrontend->getPackageSearchParam(nullfalse$destinationPackageId);
  871.             $searchParam->setPackageId($destinationPackageId);
  872.             $searchParam $this->cartItemInfoService->fillPackageSearchParamsFromCartItemInfo($firstDestinationPackageProduct->getCartItemInfo(),
  873.                 $searchParam);
  874.             $this->searchServiceLive->setParams($searchParam);
  875.             $packageResultSet Package::getById($destinationPackageId$this->searchServiceLive);
  876.             $this->ecommerceHelper->addSubProductToCart($count$packageResultSet->getObject(), $product$section,
  877.                 $packageResultSet$cartnull$childrenAges);
  878.             $cartSubItem $firstDestinationPackageProduct->getSubItems()[$product->getid() . '_' $section->getId()];
  879.             if ($product instanceof AdditionalProduct && $cartSubItem) {
  880.                 $this->cartItemInfoService->fillDestinationPackageSubItemInfosFromRequest($cartSubItem->getCartItemInfo(), $request);
  881.             }
  882.             $this->cartItemInfoService->fillDestinationPackageInfosFromCart($firstDestinationPackageProduct->getCartItemInfo(),
  883.                 $cart,
  884.                 $packageResultSet);
  885.             if (!$firstDestinationPackageProduct->getCartItemInfo()->getPackageCategory() && is_array($packageResultSet->getCategory())) {
  886.                 !$firstDestinationPackageProduct->getCartItemInfo()->setPackageCategory($packageResultSet->getCategory()[0]->getObject());
  887.             }
  888.             $cart->save();
  889.             $action 'packagestep1';
  890.         } else {
  891.             $accommodationItems $cart->getAccommodationItems();
  892.             $firstAccoProduct reset($accommodationItems);
  893.             if (!$firstAccoProduct) {
  894.                 // session ran out
  895.                 return $this->redirect($this->redirectService->redirectToList($request->request->all(),
  896.                     'demi.booking.error.session'));
  897.             }
  898.             $infoHolder $request->get('ticket') ? new TicketCartItemInfoHolder() : new CartItemInfoHolder();
  899.             $cartItemInfo $this->cartItemInfoService->fillAdditionalServiceProductFromRequest($infoHolder$request);
  900.             $cancelationService = new CancellationInformation();
  901.             $cancelationInfo $cancelationService->getCancellationInformation($product->getParent()->getParent(),
  902.                 $product,
  903.                 $product->getParent()->getParent()->getDbCode(),
  904.                 Config::getInstance()->getSalesChannel(),
  905.                 $cartItemInfo->getDateFrom(),
  906.                 $cartItemInfo->getDateTo(),
  907.                 $cartItemInfo->getAdultAmount(),
  908.                 $cartItemInfo->getChildrenAges(),
  909.                 $cartItemInfo->getDurationValue());
  910.             $cartItemInfo->setCancellationInformation($cancelationInfo);
  911.             if ($this->orderManager && $this->orderManager->getType() === 'Liftopia' && $cartItemInfo instanceof TicketCartItemInfoHolder) {
  912.                 $ticketsByPrice $this->getTicketsByPrice($cartItemInfo$count$cart$product);
  913.                 foreach ($ticketsByPrice as $price => $ticketByPrice) {
  914.                     $tmpCartItemInfo = clone $cartItemInfo;
  915.                     $tmpCartItemInfo->setPrice($price);
  916.                     $tmpCartItemInfo->setTotalPrice($price);
  917.                     $this->ecommerceHelper->addProductToCart(count($ticketByPrice), $product$tmpCartItemInfo$cart);
  918.                     $this->trackingManager->trackCartProductActionAdd($cart$productcount($ticketByPrice));
  919.                 }
  920.             } else {
  921.                 $this->ecommerceHelper->addProductToCart($count$product$cartItemInfo$cart);
  922.                 $this->trackingManager->trackCartProductActionAdd($cart$product$count);
  923.             }
  924.             $action 'step1';
  925.         }
  926.         if ($request->get('ticket') || $request->get('ticketOverview')) {
  927.             $controller 'ticket';
  928.             $action 'overview';
  929.         }
  930.         $url $this->demiUrl->getCheckoutUrl($controller$action, ['_locale' => $request->getLocale()]);
  931.         return $this->redirect($url);
  932.     }
  933.     /**
  934.      * @param Request $request
  935.      *
  936.      * @return Response
  937.      *
  938.      * @throws Exception
  939.      * ajax action for additional offers in step1
  940.      */
  941.     public function destinationpackageOfferDetailContentAction(Request $request): Response
  942.     {
  943.         if (!$this->validationService->checkRequestForOfferDetailContent($request) || !$request->get('tid')) {
  944.             throw new Exception('some parameters are missing. cannot add product to cart');
  945.         }
  946.         $productId $request->get('id');
  947.         $destinationPackageId $request->get('tid');
  948.         $sectionId $request->get('sectionId');
  949.         $cart $this->ecommerceHelper->getCart();
  950.         $packageItems $cart->getDestinationPackageItems();
  951.         $cartItem reset($packageItems);
  952.         $destinationPackage \Elements\Demi\Model\Package::getById($destinationPackageId);
  953.         foreach ($destinationPackage->getSections() as $section) {
  954.             if ($section->getId() == $sectionId) {
  955.                 foreach ($section->getProducts()->getItems() as $item) {
  956.                     if ($item->getProduct() && $item->getProduct()->getId() == $productId) {
  957.                         //found section for product
  958.                         $viewParams['showPrice'] = $section->getSelectionRulesShowPrice();
  959.                         $viewParams['sectionId'] = $section->getId();
  960.                         break 2;
  961.                     }
  962.                 }
  963.             }
  964.         }
  965.         $product AdditionalProduct::getById($productId);
  966.         $acco $this->ecommerceHelper->getAccoFromCart($cart);
  967.         if (($acco === null) && $destinationPackage->hasAccommodationSection()) {
  968.             // session ran out
  969.             return $this->redirect($this->redirectService->redirectToList($request->request->all(),
  970.                 'demi.booking.error.session'));
  971.         }
  972.         Config::setClientKeyOverrideFromObject($destinationPackage);
  973.         $searchParam $this->searchFrontend->getPackageSearchParam(nullfalse$destinationPackageId);
  974.         $searchParam->setPackageId($destinationPackageId);
  975.         $searchParam $this->cartItemInfoService->fillPackageSearchParamsFromCartItemInfo($cartItem->getCartItemInfo(),
  976.             $searchParam);
  977.         $this->searchServiceLive->setParams($searchParam);
  978.         $packageResultSet Package::getById($destinationPackageId$this->searchServiceLive);
  979.         //does product have own availabilities?
  980.         $destinationPackageItems $cart->getDestinationPackageItems();
  981.         $packageItem reset($destinationPackageItems);
  982.         $category $packageItem->getCartItemInfo()->getPackageCategory();
  983.         foreach ($packageResultSet->getCategory() as $resultSetCategory) {
  984.             if (!$category || $resultSetCategory->getCategoryFid() == $category->getFid()) {
  985.                 foreach ($resultSetCategory->getSections() as $resultsetSection) {
  986.                     if ($resultsetSection->getSectionObject()->getSectionId() == $section->getId()) {
  987.                         foreach ($resultsetSection->getAssignmentItems() as $assignmentItem) {
  988.                             if ($assignmentItem->getProductId() == $productId) {
  989.                                 $additionalOfferItem $assignmentItem;
  990.                                 $viewParams['bookablePer'] = $section->getBookingBookablePer();
  991.                                 if ($section->getBookingBookablePer() === 'perPerson') {
  992.                                     $maxSelect $cartItem->getCartItemInfo()->getAdultAmount() < $assignmentItem->getAvailability() ? $cartItem->getCartItemInfo()->getAdultAmount() : $assignmentItem->getAvailability();
  993.                                 } elseif ($section->getBookingBookablePer() === 'perPackage') {
  994.                                     $maxSelect 1;
  995.                                 } else {
  996.                                     $maxSelect $assignmentItem->getAvailability();
  997.                                 }
  998.                                 $viewParams['maxSelect'] = $maxSelect;
  999.                                 $viewParams['pricePerAdult'] = $assignmentItem->getPriceTotalAdults() / $cartItem->getCartItemInfo()->getAdultAmount();
  1000.                                 $viewParams['childrenPrices'] = $assignmentItem->getPriceChildren();
  1001.                                 //TODO: use info preselected?
  1002.                                 break 3;
  1003.                             }
  1004.                         }
  1005.                     }
  1006.                 }
  1007.             }
  1008.         }
  1009.         foreach ($product->getAdditionalService()->getDescriptions() as $description) {
  1010.             $descriptionTypes[] = $description->getDescriptionType();
  1011.             if ($description->getDescriptionType() != DescriptionInterface::DESCRIPTION_SERVICE_DESCRIPTION) {
  1012.                 $tosc5texts[$description->getDescriptionType()] = $description;
  1013.             }
  1014.         }
  1015.         $viewParams['destinationPackageId'] = $destinationPackageId;
  1016.         $viewParams['id'] = $productId;
  1017.         $viewParams['price'] = $additionalOfferItem $additionalOfferItem->getPriceTotalPrice() : '';
  1018.         $viewParams['cart'] = $cart;
  1019.         $viewParams['arrivalDate'] = $cart->getArrivalDate();
  1020.         $viewParams['departureDate'] = $cart->getDepartureDate();
  1021.         $viewParams['text'] = $product->getAdditionalService()->getDescription();
  1022.         $viewParams['tosc5texts'] = $tosc5texts;
  1023.         $viewParams['title'] = $product->getName();
  1024.         $viewParams['sectionId'] = $sectionId;
  1025.         $viewParams['categoryId'] = $category?->getId();
  1026.         $viewParams['searchParam'] = $this->searchFrontend->getSearchParam();
  1027.         $viewParams['additionalOfferItem'] = $additionalOfferItem;
  1028.         $deviceDetector DeviceDetector::getInstance();
  1029.         $device $deviceDetector->isPhone() ? 'mobile' 'desktop';
  1030.         return $this->renderTemplate('@ElementsDemiFrontend/Cart/destinationpackageOfferDetailContent.' $device '.html.twig'$viewParams);
  1031.     }
  1032.     /**
  1033.      * ajax action for additional offers in step1
  1034.      */
  1035.     public function offerDetailContentAction(Request $request): Response
  1036.     {
  1037.         if (!$this->validationService->checkRequestForOfferDetailContent($request)) {
  1038.             throw new Exception('some parameters are missing. cannot add product to cart');
  1039.         }
  1040.         $productId $request->get('id');
  1041.         $price $request->get('price');
  1042.         $cart $this->ecommerceHelper->getCart();
  1043.         $viewParams['showPrice'] = true;
  1044.         $product AdditionalProduct::getById($productId);
  1045.         $acco $this->ecommerceHelper->getAccoFromCart($cart);
  1046.         if ($acco === null) {
  1047.             // session ran out
  1048.             return $this->redirect($this->redirectService->redirectToList($request->request->all(),
  1049.                 'demi.booking.error.session'));
  1050.         }
  1051.         Config::setClientKeyOverrideFromObject($acco);
  1052.         $additionalServiceParams $this->searchFrontend->getAdditionalServiceParam($acco$cart);
  1053.         $this->additionalServiceListingLocal->setParameter($additionalServiceParams);
  1054.         $this->additionalServiceListingLocal->load();
  1055.         $additionalServiceParams->setSearchLines([
  1056.             $this->searchFrontend->getAdditionalServiceParamLineLive($cart$productId)
  1057.         ]);
  1058.         $tosc5texts = [];
  1059.         foreach ($product->getAdditionalService()->getDescriptions() as $description) {
  1060.             $descriptionTypes[] = $description->getDescriptionType();
  1061.             if ($description->getDescriptionType() != DescriptionInterface::DESCRIPTION_SERVICE_DESCRIPTION) {
  1062.                 $tosc5texts[$description->getDescriptionType()] = $description;
  1063.             }
  1064.         }
  1065.         $additionalServiceParams->setPage(1);
  1066.         $additionalServiceParams->setPerPage(10);
  1067.         $env Factory::getInstance()->getEnvironment();
  1068.         $bookOnRequest $env->getCustomItem('deskline_bookableOnRequest');
  1069.         $additionalServiceParams->setBookOnRequestAsBookable($bookOnRequest);
  1070.         $this->additionalServiceListingLive->setParameter($additionalServiceParams);
  1071.         $this->additionalServiceListingLive->load();
  1072.         $viewParams['id'] = $productId;
  1073.         $viewParams['price'] = $price;
  1074.         $viewParams['arrivalDate'] = $cart->getArrivalDate();
  1075.         $viewParams['departureDate'] = $cart->getDepartureDate();
  1076.         $viewParams['productText'] = $product->getDescription();
  1077.         $viewParams['tosc5texts'] = $tosc5texts;
  1078.         $viewParams['text'] = $product->getAdditionalService()->getDescription();
  1079.         $viewParams['title'] = $product->getName();
  1080.         $viewParams['additionalServiceItem'] = $this->additionalServiceListingLive;
  1081.         $viewParams['additionalServiceItems'] = $this->additionalServiceListingLocal;
  1082.         $viewParams['isPackage'] = !empty($request->get('packages'));
  1083.         $viewParams['cart'] = $cart;
  1084.         $viewParams['searchParam'] = $this->searchFrontend->getSearchParam();
  1085.         $viewParams['ticket'] = (bool)$request->get('ticket');
  1086.         //TODO where do we get that value from?
  1087.         $viewParams['maxSelect'] = $viewParams['ticket'] ? ($cart->getAdultCount() + $cart->getChildrenCount()) : 20;
  1088.         $deviceDetector DeviceDetector::getInstance();
  1089.         $device $deviceDetector->isPhone() ? 'mobile' 'desktop';
  1090.         return $this->renderTemplate('@ElementsDemiFrontend/Cart/offerDetailContent.' $device '.html.twig'$viewParams);
  1091.     }
  1092.     /**
  1093.      * ajax action for additional offers in step1
  1094.      */
  1095.     public function additionalNightsDetailContentAction(Request $request): Response
  1096.     {
  1097.         if (!$this->validationService->checkRequestForOfferDetailContent($request)) {
  1098.             throw new Exception('some parameters are missing. cannot add product to cart');
  1099.         }
  1100.         $productId $request->get('id');
  1101.         $price $request->get('price');
  1102.         $destinationPackageId $request->get('tid');
  1103.         $sectionId $request->get('sectionId');
  1104.         $cart $this->ecommerceHelper->getCart();
  1105.         $destionationPackage \Elements\Demi\Model\Package::getById($destinationPackageId);
  1106.         if (!$destionationPackage) {
  1107.             throw new HttpEncodingException('Destination package missing in additionalNightsDetailContentAction');
  1108.         }
  1109.         $acco $this->ecommerceHelper->getAccoFromCart($cart);
  1110.         if ($acco === null) {
  1111.             // session ran out
  1112.             return $this->redirect($this->redirectService->redirectToList($request->request->all(),
  1113.                 'demi.booking.error.session'));
  1114.         }
  1115.         $accommodationProduct AccommodationProduct::getById($request->get('id'));
  1116.         $currentlySelected 1;
  1117.         $destinationPackageItems $cart->getDestinationPackageItems();
  1118.         $cartItem reset($destinationPackageItems);
  1119.         foreach ($cartItem->getSubItems() as $subitem) {
  1120.             if ($subitem->getProduct() instanceof AccommodationProduct) {
  1121.                 $currentlySelected $subitem->getCartItemInfo()->getAdditionalNights();
  1122.                 break;
  1123.             }
  1124.         }
  1125.         $viewParams['currentlySelected'] = $currentlySelected;
  1126.         $viewParams['accommodationProduct'] = $accommodationProduct;
  1127.         $viewParams['showPrice'] = true;
  1128.         $viewParams['maxSelect'] = 7;
  1129.         $viewParams['sectionId'] = $sectionId;
  1130.         $viewParams['title'] = $accommodationProduct->getName();
  1131.         $viewParams['destinationPackageId'] = $destinationPackageId;
  1132.         $viewParams['id'] = $productId;
  1133.         $viewParams['tid'] = $destinationPackageId;
  1134.         $viewParams['price'] = $price;
  1135.         $viewParams['arrivalDate'] = $cart->getArrivalDate();
  1136.         $viewParams['departureDate'] = $cart->getDepartureDate();
  1137.         return $this->renderTemplate('@ElementsDemiFrontend/Cart/additionalNightsDetailContent.twig'$viewParams);
  1138.     }
  1139.     public function hasAccoInCartAction(Request $request): JsonResponse
  1140.     {
  1141.         /** @var SessionCart $cart */
  1142.         $cart $this->ecommerceHelper->getCart();
  1143.         $hasAcco $canBeAdded $hasAdditional false;
  1144.         $cartUrl $this->demiUrl->getCheckoutUrl('cart''step1');
  1145.         if ($request->get('productId')) {
  1146.             $product DemiAccommodationProduct::getById($request->get('productId'));
  1147.             $acco $product->getParent()->getParent();
  1148.         }
  1149.         foreach ($cart->getItems() as $item) {
  1150.             $prod $item->getProduct();
  1151.             if ($prod instanceof AccommodationProduct) {
  1152.                 if ($acco && $prod->getParent()->getParent()->getId() === $acco->getId() && $product->getProductType() === $prod->getProductType()
  1153.                     && ($request->get('dateFrom') && $request->get('dateTo') && $cart->getArrivalDate()->getTimestamp() === (ElementsCustomDateFormat::parseDateString($request->get('dateFrom')))->getTimestamp() &&
  1154.                         $cart->getDepartureDate()->getTimestamp() === (ElementsCustomDateFormat::parseDateString($request->get('dateTo')))->getTimestamp())) {
  1155.                     $canBeAdded true;
  1156.                 }
  1157.                 $hasAcco true;
  1158.                 $urlParams = [];
  1159.                 if (DeviceDetector::getInstance()->isPhone()) {
  1160.                     $urlParams['viewpoint'] = 2;
  1161.                 }
  1162.                 $cartUrl $this->demiUrl->getCheckoutUrl('cart''step1'$urlParams);
  1163.             } elseif ($prod instanceof DemiAdditionalProduct) {
  1164.                 $hasAdditional true;
  1165.             }
  1166.         }
  1167.         foreach ($cart->getDestinationPackageItems() as $item) {
  1168.             $prod $item->getProduct();
  1169.             if ($prod instanceof \Elements\Demi\Model\Package) {
  1170.                 $hasAcco true;
  1171.                 $cartUrl $this->demiUrl->getCheckoutUrl('cart''packagestep1');
  1172.             }
  1173.         }
  1174.         return new JsonResponse([
  1175.             'hasAcco' => $hasAcco,
  1176.             'cartUrl' => $cartUrl,
  1177.             'canBeAdded' => $hasAcco === false true $canBeAdded,
  1178.             'hasAdditional' => $hasAdditional,
  1179.             'totalItemAmount' => (count($cart->getItems()) + count($cart->getGiftItems()))
  1180.         ]);
  1181.     }
  1182.     public function amountInCartAction(Request $request): JsonResponse
  1183.     {
  1184.         /** @var SessionCart $cart */
  1185.         $cart $this->ecommerceHelper->getCart();
  1186.         $subtractShipping = !empty($this->ecommerceHelper->getShippingProductFromCart($cart));
  1187.         if ($request->get('detailed')) {
  1188.             $giftAdditionals $giftAccoProducts $additionals $accoProducts 0;
  1189.             foreach ($cart->getItems() as $item) {
  1190.                 $item->getProduct() instanceof DemiAccommodationProduct $accoProducts++ : $additionals++;
  1191.             }
  1192.             foreach ($cart->getGiftItems() as $item) {
  1193.                 $item->getProduct() instanceof DemiAccommodationProduct $giftAccoProducts++ : $giftAdditionals++;
  1194.             }
  1195.             if ($subtractShipping) {
  1196.                 $additionals--;
  1197.             }
  1198.             return new JsonResponse([
  1199.                 'items' => [
  1200.                     'additionalProducts' => $additionals,
  1201.                     'accommodationProducts' => $accoProducts
  1202.                 ],
  1203.                 'gifts' => [
  1204.                     'additionalProducts' => $giftAdditionals,
  1205.                     'accommodationProducts' => $giftAccoProducts
  1206.                 ],
  1207.                 'total' => $additionals $accoProducts $giftAdditionals $giftAccoProducts
  1208.             ]);
  1209.         }
  1210.         $items count($cart->getItems());
  1211.         $gifts count($cart->getGiftItems());
  1212.         if ($subtractShipping) {
  1213.             $items--;
  1214.         }
  1215.         return new JsonResponse([
  1216.             'items' => $items,
  1217.             'gifts' => $gifts,
  1218.             'total' => $items $gifts
  1219.         ]);
  1220.     }
  1221.     public function clearAction(Request $request): RedirectResponse
  1222.     {
  1223.         $this->ecommerceHelper->clear();
  1224.         return $this->redirect($this->demiUrl->getCheckoutUrl('cart''step1'));
  1225.     }
  1226.     protected function handleRecommendedServices(CartInterface $cart, ?string $originator null)
  1227.     {
  1228.         foreach ($cart->getItems() as $item) {
  1229.             $cartItemInfo $item->getCartItemInfo();
  1230.             if ($cartItemInfo->getIsMerchandise()) {
  1231.                 continue;
  1232.             }
  1233.             if (empty($originator)) {
  1234.                 $originator $cartItemInfo->getSettlerCode();
  1235.             }
  1236.             $addAdditionalServiceService = new DynamicService();
  1237.             $dateFrom $cartItemInfo->getDateFrom();
  1238.             $dateTo $cartItemInfo->getDateTo();
  1239.             $units $cartItemInfo->getAvailableUnits();
  1240.             $adults $cartItemInfo->getAdultAmount();
  1241.             $children count($cartItemInfo->getChildrenAges());
  1242.             $productId $item->getProduct()->getFId();
  1243.             //$recommendedServices = $addAdditionalServiceService->getUpsellingAdditionalServices($originator, null, $dateFrom, $dateTo, $units, $adults, $children, $productId);
  1244.             if (empty($recommendedServices)) {
  1245.                 continue;
  1246.             }
  1247.             foreach ($recommendedServices as $recommendedService) {
  1248.                 $recommendedProductId $recommendedService["ProductId"];
  1249.                 $recommendedProduct AdditionalProduct::getByFid($recommendedProductId);
  1250.                 if ($recommendedProduct === null) {
  1251.                     throw new \Elements\Demi\Deskline\Checkout\ShoppingCart\Exception("Required Product not Found. Aborting!");
  1252.                 }
  1253.                 $price $recommendedProduct->getPriceTemplates()[0]?->getPrices()?->getItems()[0]?->getPrice();
  1254.                 if ($price === null) {
  1255.                     continue;
  1256.                 }
  1257.                 if (empty($recommendedService["AutoAddRule"])) {
  1258.                     continue;
  1259.                 }
  1260.                 //defaultvalue = AutoAddRule === "perPerson"
  1261.                 $count $adults;
  1262.                 if ($recommendedService["AutoAddRule"] === "perBooking") {
  1263.                     if (count(array_filter($cart->getItems(), static function ($a) use ($recommendedProduct) {
  1264.                             return $a->getProductId() === $recommendedProduct->getId();
  1265.                         })) > 0) {
  1266.                         return;
  1267.                     }
  1268.                     $count 1;
  1269.                 }
  1270.                 $addToCartParams['offerId'] = $recommendedProduct->getId();
  1271.                 $addToCartParams['price'] = $price;
  1272.                 $addToCartParams['count'] = $count;
  1273.                 $addToCartParams['date'] = $dateFrom->toIso8601String();
  1274.                 $addToCartParams['settlerCode'] = $originator;
  1275.                 $addToCartParams['removable'] = !$recommendedService["AutoAddItemNotRemovable"];
  1276.                 $addToCartParams['relatedProduct'] = $item->getItemKey();
  1277.                 $this->forward(AdditionalServiceController::class . "::addAdditionalItemAction", [], $addToCartParams);
  1278.             }
  1279.         }
  1280.     }
  1281. }