src/Service/Shop/JsonDataService.php line 871

Open in your IDE?
  1. <?php
  2. /**
  3.  * Created by Elements.at New Media Solutions GmbH
  4.  *
  5.  */
  6. namespace App\Service\Shop;
  7. use App\Ecommerce\AvailabilitySystem\Event\AvailabilitySystem;
  8. use App\Model\Shop\Event\EventProduct;
  9. use App\Model\Shop\Event\ShopDynamicPrice;
  10. use App\Model\Shop\Ticket\ShopTicketCatalog;
  11. use App\Model\Shop\Ticket\TicketConsumerCategory;
  12. use App\Service\TicketShopFrameworkBundle\TicketFilter;
  13. use App\Twig\LinkGeneratorExtension;
  14. use Carbon\Carbon;
  15. use Elements\Bundle\RecurringDatesTypeBundle\Templating\RecurringDatesHelper;
  16. use Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketCatalog;
  17. use Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketConsumerCategory as TSFTicketConsumerCategory;
  18. use Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketProduct;
  19. use Elements\Bundle\TicketShopFrameworkBundle\Model\Shop\Ticketing\TicketCatalogAvailability;
  20. use Elements\Bundle\TicketShopFrameworkBundle\Model\Shop\Ticketing\TicketProductAvailability;
  21. use Pimcore\Cache\RuntimeCache;
  22. use Pimcore\Model\DataObject\AbstractObject;
  23. use Symfony\Component\HttpFoundation\Request;
  24. use Symfony\Contracts\Translation\TranslatorInterface;
  25. class JsonDataService
  26. {
  27.     private ?Carbon $startDate;
  28.     public function __construct(
  29.         private ShopService $shopService,
  30.         private TranslatorInterface $translator,
  31.         private LinkGeneratorExtension $linkGenerator,
  32.         protected SmartPricerService $smartPricerService,
  33.         private RecurringDatesHelper $datesHelper
  34.     ) {
  35.     }
  36.     public function getStartDate(): ?Carbon
  37.     {
  38.         return $this->startDate;
  39.     }
  40.     public function setStartDate(Carbon $startDate): void
  41.     {
  42.         $this->startDate $startDate;
  43.     }
  44.     private ?Carbon $endDate;
  45.     public function getEndDate(): ?Carbon
  46.     {
  47.         return $this->endDate;
  48.     }
  49.     public function setEndDate(Carbon $endDate): void
  50.     {
  51.         $this->endDate $endDate;
  52.     }
  53.     //--------TICKETS--------
  54.     /**
  55.      * @param TicketCatalogAvailability $ticketCatalogAvailability
  56.      * @param array<mixed> $insurances
  57.      * @param array<mixed> $originalConsumerGroups
  58.      * @param TicketProduct|null $ticket
  59.      * @param array<mixed> $upgradeIds
  60.      *
  61.      * @return array<mixed>
  62.      */
  63.     private function getCatalogAvailabilityConfig(
  64.         TicketCatalogAvailability $ticketCatalogAvailability,
  65.         array $insurances,
  66.         array $originalConsumerGroups,
  67.         TicketProduct $ticket null,
  68.         array $upgradeIds = []
  69.     ): array {
  70.         $configuration = [];
  71.         $upgrades = [];
  72.         $catalog $ticketCatalogAvailability->getTicketCatalog();
  73.         if ($upgradeIds) {
  74.             foreach ($upgradeIds as $upgradeId) {
  75.                 $upgrades[] = (string)$upgradeId;
  76.             }
  77.         }
  78.         $isUpgrade = !empty($ticket);
  79.         foreach ($ticketCatalogAvailability->getTicketAvailability() as $ticketAvailability) {
  80.             $originalTicket $ticketAvailability->getTicketProduct();
  81.             if (!$isUpgrade) {
  82.                 $ticket $originalTicket;
  83.             } else {
  84.                 /** @var \App\Model\Shop\Ticket\TicketProduct $ticket */
  85.                 $metaProduct $ticket->getMetaProduct();
  86.                 if ($metaProduct && !in_array($originalTicket$metaProduct->getRelatedTo(), true)) {
  87.                     continue;
  88.                 }
  89.             }
  90.             if (empty($upgradeIds)) {
  91.                 if ($upgradeConfig $this->getUpgradeConfigurations($catalog$ticket$originalConsumerGroups)) {
  92.                     $configuration array_merge($configuration$upgradeConfig);
  93.                 }
  94.             }
  95.             $consumerMapping $this->getConsumerMapping($ticketAvailability$originalConsumerGroups);
  96.             $flipped array_flip($consumerMapping);
  97.             if ($insurances) {
  98.                 foreach ($insurances[$originalTicket->getId()] as $insurance) {
  99.                     $priceByPriceGroup = [];
  100.                     foreach ($ticketAvailability->getSortedConsumerAvailabilities() as $consumerAvailability) {
  101.                         $product $consumerAvailability->getTicketProductAvailability()->getTicketProduct();
  102.                         $consumer $consumerAvailability->getTicketConsumer();
  103.                         $insurancePriceInfo $insurance->getOSPriceInfo(
  104.                             1,
  105.                             [],
  106.                             $ticketAvailability->getValidFrom(),
  107.                             $consumerAvailability->getTicketConsumer(),
  108.                             catalog$catalog
  109.                         );
  110.                         $insurancePrice $insurancePriceInfo->getTotalPrice()->getGrossAmount()->asNumeric();
  111.                         $priceInfo $product->getOSPriceInfo(
  112.                             1,
  113.                             [],
  114.                             $ticketAvailability->getValidFrom(),
  115.                             $consumerAvailability->getTicketConsumer(),
  116.                             catalog$catalog
  117.                         );
  118.                         if(isset($flipped[$consumer->getId()])) {
  119.                             $priceData = [
  120.                                 'id' => (string)$flipped[$consumer->getId()],
  121.                                 'price' => $priceInfo?->getTotalPrice()->getGrossAmount()->add($insurancePrice)->asNumeric(),
  122.                                 'priceGroupWarning' => (string)$consumer->getInfoText(),
  123.                             ];
  124.                             $this->setPriceData($priceData$priceInfo);
  125.                             if ($priceInfo?->getPriceGroup() instanceof TicketConsumerCategory &&
  126.                                 $priceInfo->getPriceGroup()->getOriginalPriceConsumerCategory() instanceof TicketConsumerCategory
  127.                             ) {
  128.                                 // need to be done for trade platform otherwise strike price adds commission value
  129.                                 RuntimeCache::save(true'disabledCommissionCalc');
  130.                                 $strikePriceInfo $product->getOSPriceInfo(
  131.                                     1,
  132.                                     [],
  133.                                     $ticketAvailability->getValidFrom(),
  134.                                     $priceInfo->getPriceGroup()->getOriginalPriceConsumerCategory(),
  135.                                     $catalog
  136.                                 );
  137.                                 if ($priceInfo->getTotalPrice()->getGrossAmount()->lessThan(
  138.                                     $strikePriceInfo->getTotalPrice()->getGrossAmount())
  139.                                 ) {
  140.                                     $priceData['priceBeforeDiscount'] = $strikePriceInfo?->getTotalPrice()->getGrossAmount()->add($insurancePrice)->asNumeric();
  141.                                 }
  142.                                 RuntimeCache::save(false'disabledCommissionCalc');
  143.                             }
  144.                             $this->setChildSaturdayInfo($priceData$consumer$ticketAvailability);
  145.                             $priceByPriceGroup[] = $priceData;
  146.                         }
  147.                     }
  148.                     $configuration[] = [
  149.                         'option' => (string)$ticket->getId(),
  150.                         'upgradeOption' => $originalTicket->getId(),
  151.                         'upgradeCatalog' => $catalog->getId(),
  152.                         'priceGroupMap' => $consumerMapping,
  153.                         'upgrades' => $upgrades,
  154.                         'insurance' => (string)$insurance->getId(),
  155.                         'priceByPriceGroup' => $priceByPriceGroup,
  156.                     ];
  157.                 }
  158.             }
  159.             //always add "no_insurance" option
  160.             $configuration[] = [
  161.                 'option' => (string)$ticket->getId(),
  162.                 'upgradeOption' => $originalTicket->getId(),
  163.                 'upgradeCatalog' => $catalog->getId(),
  164.                 'priceGroupMap' => $consumerMapping,
  165.                 'upgrades' => $upgrades,
  166.                 'insurance' => 'no_insurance',
  167.                 'priceByPriceGroup' => $this->getNoInsuranceConfiguration($ticketAvailability$originalConsumerGroups$catalog),
  168.             ];
  169.         }
  170.         return $configuration;
  171.     }
  172.     /**
  173.      * @param TicketCatalogAvailability $ticketCatalogAvailability
  174.      * @param array<mixed> $insurances
  175.      * @param array<mixed> $originalConsumerGroups
  176.      *
  177.      * @return array<mixed>
  178.      */
  179.     public function getPriceForCatalogAvailability(TicketCatalogAvailability $ticketCatalogAvailability, array $insurances, array $originalConsumerGroupsCarbon $startDateCarbon $endDate): array
  180.     {
  181.         $this->smartPricerService->setPriceMatrix($ticketCatalogAvailability$insurances$originalConsumerGroups$startDate$endDate);
  182.         $this->smartPricerService->setLivePrices($this->smartPricerService->cat1Array$this->smartPricerService->cat3Array$ticketCatalogAvailability->getStartDate(), $ticketCatalogAvailability->getEndDate());
  183.         return $this->getCatalogAvailabilityConfig($ticketCatalogAvailability$insurances$originalConsumerGroups);
  184.     }
  185.     /**
  186.      * @param array<mixed> $priceData
  187.      * @param TicketConsumerCategory|TSFTicketConsumerCategory $consumer
  188.      * @param TicketProductAvailability $availability
  189.      *
  190.      * @return void
  191.      */
  192.     private function setChildSaturdayInfo(
  193.         array &$priceData,
  194.         TicketConsumerCategory|TSFTicketConsumerCategory $consumer,
  195.         TicketProductAvailability $availability
  196.     ): void {
  197.         $catalogAvailability $availability->getTicketCatalogAvailability();
  198.         /** @var \App\Model\Shop\Ticket\TicketProduct $ticket */
  199.         $ticket $availability->getTicketProduct();
  200.         /** @var ShopTicketCatalog $catalog */
  201.         $catalog $catalogAvailability->getTicketCatalog();
  202.         /** @phpstan-ignore-next-line */
  203.         if ($consumer->useSaturdaySpecial($ticket$catalogAvailability->getStartDate(), $catalog)) {
  204.             $priceData['priceGroupInfo'] = $this->translator->trans('shop.ticket.child.saturday.free');
  205.         }
  206.     }
  207.     /**
  208.      * @param TicketCatalog $catalog
  209.      * @param TicketProduct $ticket
  210.      * @param array<mixed> $originalConsumerGroups
  211.      *
  212.      * @return array<mixed>
  213.      */
  214.     private function getUpgradeConfigurations(TicketCatalog $catalogTicketProduct $ticket, array $originalConsumerGroups): array
  215.     {
  216.         $configuration = [];
  217.         $combinationIds = [];
  218.         foreach ($catalog->getUpgrades() as $upgrade) {
  219.             /** @var TicketCatalog $upgradeCatalog */
  220.             $upgradeCatalog $upgrade->getObject();
  221.             $upgradeIds = [];
  222.             if (str_contains($upgrade->getData()['name'], ',')) {
  223.                 $upgradeIds $combinationIds;
  224.             } else {
  225.                 $upgradeIds[] = $upgradeCatalog->getId();
  226.                 $combinationIds[] = $upgradeCatalog->getId();
  227.             }
  228.             $upgradeFilter = new TicketFilter($this->startDate$this->endDate);
  229.             $upgradeFilter->setExactDuration(true);
  230.             $upgradeFilter->setIgnorePrice(true);
  231.             $allowedConsumers = [];
  232.             foreach ($upgradeCatalog->getTicketConsumerCategories() as $allowedConsumer) {
  233.                 $allowedConsumers[] = $allowedConsumer->getId();
  234.             }
  235.             if ($allowedConsumers) {
  236.                 $upgradeFilter->setConsumers($allowedConsumers);
  237.             }
  238.             $catalogAvailability $upgradeFilter->getTicketCatalogAvailability($upgradeCatalogtrue);
  239.             $insuranceData $this->shopService->getInsurancesForProduct($catalogAvailability);
  240.             $insurances $insuranceData['insurances'] ?? [];
  241.             if ($catalogAvailability->hasAvailability() && !$upgradeCatalog->getIsNotBookable()) {
  242.                 $configuration array_merge(
  243.                     $configuration,
  244.                     $this->getCatalogAvailabilityConfig(
  245.                         $catalogAvailability,
  246.                         $insurances,
  247.                         $originalConsumerGroups,
  248.                         $ticket,
  249.                         $upgradeIds
  250.                     )
  251.                 );
  252.             }
  253.         }
  254.         return $configuration;
  255.     }
  256.     /**
  257.      * Gets the data for one ticket in a specific catalog in a specific date range (for PriceCalculator)
  258.      *
  259.      * @param ShopTicketCatalog $originalCatalog
  260.      * @param TicketProductAvailability $originalTicket
  261.      * @param Carbon $startDate
  262.      * @param Carbon $endDate
  263.      * @param bool $isUpgrade
  264.      * @param ShopTicketCatalog|null $upgradeCatalog
  265.      * @param TicketProductAvailability|null $upgradeTicket
  266.      * @param array<mixed> $upgradeIds
  267.      *
  268.      * @return array<mixed>
  269.      */
  270.     public function getTicketData(ShopTicketCatalog $originalCatalog,
  271.         TicketProductAvailability $originalTicket,
  272.         Carbon $startDate,
  273.         Carbon $endDate,
  274.         bool $isUpgrade false,
  275.         ?ShopTicketCatalog $upgradeCatalog null,
  276.         ?TicketProductAvailability $upgradeTicket null,
  277.         ?array $upgradeIds = []
  278.     ): array {
  279.         $output = [];
  280.         $originalTicketProduct $originalTicket->getTicketProduct();
  281.         $output['ticketTitle'] = $originalCatalog->getName();
  282.         $output['selectedOption'] = strval($originalTicketProduct->getId());
  283.         $output['startDate'] = $startDate->toDateTimeLocalString();
  284.         $output['endDate'] = $endDate->toDateTimeLocalString();
  285.         $output['ticketId'] = strval($originalCatalog->getId());
  286.         $output['isAnnualPass'] = $originalCatalog->getIsAnnualCatalog() ?? false;
  287.         $output['ticketComposition'] = $upgradeTicket $upgradeTicket->getTicketProduct()->getName() : $originalTicketProduct->getName();
  288.         if ($originalCatalog->getIsNotBookable()) {
  289.             $output['isNotBookable'] = true;
  290.             $output['pathToDetailPage'] = $this->linkGenerator->getDetailLink($originalCatalog);
  291.         }
  292.         if ($isUpgrade) {
  293.             $output['selectedUpgrades'] = array_map('strval'$upgradeIds);
  294.         }
  295.         $output['data'] = [];
  296.         $correctAvailabilities $isUpgrade $upgradeTicket->getConsumerAvailabilities() : $originalTicket->getConsumerAvailabilities();
  297.         foreach ($correctAvailabilities as $consumerAvailability) {
  298.             $output['data'][] = [
  299.                 'priceGroup' => $consumerAvailability->getTicketConsumer()->getName(),
  300.                 'price' => $consumerAvailability->getPriceInfo()->getTotalPrice()->getGrossAmount()->asNumeric(),
  301.                 'priceUnit' => $consumerAvailability->getPriceInfo()->getTotalPrice()->getCurrency()->getShortName(),
  302.             ];
  303.         }
  304.         return $output;
  305.     }
  306.     /**
  307.      * Gets an array of all upgrade Options of a specific ticket in a specific catalog in a specific date range (for PriceCalculator)
  308.      *
  309.      * @param ShopTicketCatalog $catalog
  310.      * @param Carbon $startDate
  311.      * @param Carbon $endDate
  312.      * @param TicketProductAvailability $ticket
  313.      * @param array<int> $originalPriceGroupIds
  314.      *
  315.      * @return array<mixed>
  316.      */
  317.     public function getTicketUpgradeData(ShopTicketCatalog $catalog,
  318.         Carbon $startDate,
  319.         Carbon $endDate,
  320.         TicketProductAvailability $ticket,
  321.         array $originalPriceGroupIds,
  322.     ): array {
  323.         $output = [];
  324.         $combinationIds = []; // e.g. international + shuttle
  325.         foreach ($catalog->getUpgrades() as $upgrade) {
  326.             /** @var ShopTicketCatalog $upgradeCatalog */
  327.             $upgradeCatalog $upgrade->getObject();
  328.             $upgradeIds = [];
  329.             if (str_contains($upgrade->getData()['name'], ',')) {
  330.                 $upgradeIds $combinationIds;
  331.             } else {
  332.                 $upgradeIds[] = $upgradeCatalog->getId();
  333.                 $combinationIds[] = $upgradeCatalog->getId();
  334.             }
  335.             $allowedConsumerIds $this->shopService->getCorrectAllowedConsumerIds($upgradeCatalog$originalPriceGroupIds);
  336.             $upgradeAvailability $upgradeCatalog->getAvailabilityForDateRangeWithPriceGroup($allowedConsumerIds$startDate$endDatetrue);
  337.             if (!$upgradeAvailability->hasAvailability()) {
  338.                 continue;
  339.             }
  340.             foreach ($upgradeAvailability->getTicketAvailability() as $upgradeTicket) {
  341.                 // @phpstan-ignore-next-line
  342.                 $metaProduct $ticket->getTicketProduct()->getMetaProduct(); // for skipping upgradeTickets which are not similar to the originalTicket
  343.                 if ($metaProduct && !in_array($upgradeTicket->getTicketProduct(), $metaProduct->getRelatedTo(), true)) {
  344.                     continue;
  345.                 }
  346.                 $output[] = $this->getTicketData($catalog$ticket$startDate$endDatetrue$upgradeCatalog$upgradeTicket$upgradeIds);
  347.             }
  348.         }
  349.         return $output;
  350.     }
  351.     /**
  352.      * @param TicketProductAvailability $availability
  353.      * @param array<mixed> $originalConsumerGroups
  354.      *
  355.      * @return array<mixed>
  356.      */
  357.     private function getNoInsuranceConfiguration(TicketProductAvailability $availability, array $originalConsumerGroups, ?TicketCatalog $catalog): array
  358.     {
  359.         $noInsuranceConfiguration = [];
  360.         foreach ($availability->getSortedConsumerAvailabilities() as $consumerAvailability) {
  361.             $consumer $consumerAvailability->getTicketConsumer();
  362.             $product $consumerAvailability->getTicketProductAvailability()->getTicketProduct();
  363.             //set warning text before mapping
  364.             $warningText = (string)$consumer->getInfoText();
  365.             $this->mapConsumerGroups($consumer$originalConsumerGroupstrue);
  366.             $priceInfo $product->getOSPriceInfo(
  367.                 1,
  368.                 [],
  369.                 $availability->getValidFrom(),
  370.                 $consumerAvailability->getTicketConsumer(),
  371.                 catalog$catalog
  372.             );
  373.             $dataArray = [
  374.                 'id' => (string)$consumer->getId(),
  375.                 'price' => $priceInfo?->getTotalPrice()->getGrossAmount()->asNumeric(),
  376.                 'priceGroupWarning' => $warningText,
  377.             ];
  378.             $this->setPriceData($dataArray$priceInfo);
  379.             if ($priceInfo->getPriceGroup() instanceof TicketConsumerCategory &&
  380.                 $priceInfo->getPriceGroup()->getOriginalPriceConsumerCategory() instanceof TicketConsumerCategory
  381.             ) {
  382.                 // need to be done for trade platform otherwise strike price adds commission value
  383.                 RuntimeCache::save(true'disabledCommissionCalc');
  384.                 $strikePriceInfo $product->getOSPriceInfo(
  385.                     1,
  386.                     [],
  387.                     $availability->getValidFrom(),
  388.                     $priceInfo->getPriceGroup()->getOriginalPriceConsumerCategory(),
  389.                     catalog$catalog
  390.                 );
  391.                 if ($priceInfo->getTotalPrice()->getGrossAmount()->lessThan(
  392.                     $strikePriceInfo->getTotalPrice()->getGrossAmount())
  393.                 ) {
  394.                     $dataArray['priceBeforeDiscount'] = $strikePriceInfo?->getTotalPrice()->getGrossAmount()->asNumeric();
  395.                 }
  396.                 RuntimeCache::save(false'disabledCommissionCalc');
  397.             }
  398.             $this->setChildSaturdayInfo($dataArray$consumer$availability);
  399.             $noInsuranceConfiguration[] = $dataArray;
  400.         }
  401.         return $noInsuranceConfiguration;
  402.     }
  403.     /**
  404.      * @param TSFTicketConsumerCategory $consumer
  405.      * @param array<mixed> $originalConsumerGroups
  406.      * @param bool $onlyConsumer
  407.      * @param array<mixed> $consumerMapping
  408.      *
  409.      * @return array<mixed>
  410.      */
  411.     private function mapConsumerGroups(
  412.         TSFTicketConsumerCategory &$consumer,
  413.         array $originalConsumerGroups,
  414.         bool $onlyConsumer false,
  415.         array $consumerMapping = []
  416.     ): array {
  417.         $tmpConsumer $consumer;
  418.         if (in_array($consumer$originalConsumerGroupstrue)) {
  419.             $consumerMapping[$consumer->getId()] = $consumer->getId();
  420.         } else {
  421.             if (!$consumer->hasChildren() && $consumer->hasSiblings([AbstractObject::OBJECT_TYPE_OBJECT])) {
  422.                 $siblingMapping $consumer->getSiblings([AbstractObject::OBJECT_TYPE_OBJECT]);
  423.                 foreach ($originalConsumerGroups as $originalConsumerGroup) {
  424.                     if (in_array($originalConsumerGroup$siblingMappingtrue)) {
  425.                         $consumerMapping[$originalConsumerGroup->getId()] = $tmpConsumer->getId();
  426.                         $consumer $originalConsumerGroup;
  427.                         if ($onlyConsumer) {
  428.                             break;
  429.                         }
  430.                     } else {
  431.                         if (!isset($consumerMapping[$originalConsumerGroup->getId()])) {
  432.                             $consumerMapping[$originalConsumerGroup->getId()] = $originalConsumerGroup->getId();
  433.                         }
  434.                     }
  435.                 }
  436.             } else {
  437.                 //consumer can be only in upgrade catalog
  438.                 $consumerMapping[$consumer->getId()] = $consumer->getId();
  439.                 foreach ($originalConsumerGroups as $originalConsumerGroup) {
  440.                     if (!isset($consumerMapping[$originalConsumerGroup->getId()])) {
  441.                         $consumerMapping[$originalConsumerGroup->getId()] = $originalConsumerGroup->getId();
  442.                     }
  443.                 }
  444.             }
  445.         }
  446.         return $consumerMapping;
  447.     }
  448.     /**
  449.      * @param TicketProductAvailability $ticketAvailability
  450.      * @param array<mixed> $originalConsumerGroups
  451.      *
  452.      * @return array<mixed>
  453.      */
  454.     private function getConsumerMapping(TicketProductAvailability $ticketAvailability, array $originalConsumerGroups): array
  455.     {
  456.         $consumerMapping = [];
  457.         foreach ($ticketAvailability->getSortedConsumerAvailabilities() as $consumerAvailability) {
  458.             $consumer $consumerAvailability->getTicketConsumer();
  459.             $consumerMapping $this->mapConsumerGroups($consumer$originalConsumerGroupsfalse$consumerMapping);
  460.         }
  461.         return $consumerMapping;
  462.     }
  463.     /**
  464.      * @param TicketCatalogAvailability $ticketCatalogAvailability
  465.      *
  466.      * @return array<mixed>
  467.      */
  468.     public function getPriceGroupJson(TicketCatalogAvailability $ticketCatalogAvailability): array
  469.     {
  470.         $priceGroups = [];
  471.         foreach ($ticketCatalogAvailability->getTicketAvailability() as $ticketAvailability) {
  472.             foreach ($ticketAvailability->getConsumerAvailabilities() as $consumerAvailability) {
  473.                 $consumer $consumerAvailability->getTicketConsumer();
  474.                 $fromYear $consumer->getYearTo();
  475.                 $priceGroups[$consumer->getId()] = [
  476.                     'id' => (string)$consumer->getId(),
  477.                     'label' => $consumer->getName(),
  478.                     'labelPlural' => $consumer->getName(),
  479.                     'subtitle' => '',
  480.                     'info' => '',
  481.                     'sort' => $fromYear,
  482.                 ];
  483.             }
  484.         }
  485.         /** @phpstan-ignore-next-line */
  486.         usort($priceGroups, function ($a$b) {
  487.             return $a['sort'] < $b['sort'];
  488.         });
  489.         array_walk($priceGroups, function (&$a) {
  490.             unset($a['sort']);
  491.         });
  492.         return $priceGroups;
  493.     }
  494.     /**
  495.      * @param TicketCatalogAvailability $ticketCatalogAvailability
  496.      *
  497.      * @return array<mixed>
  498.      */
  499.     public function getSpecialPriceModals(TicketCatalogAvailability $ticketCatalogAvailability): array
  500.     {
  501.         $priceGroups = [];
  502.         foreach ($ticketCatalogAvailability->getTicketAvailability() as $ticketAvailability) {
  503.             $product $ticketAvailability->getTicketProduct();
  504.             foreach ($ticketAvailability->getConsumerAvailabilities() as $consumerAvailability) {
  505.                 $consumer $consumerAvailability->getTicketConsumer();
  506.                 //only when selected date is one day ticket + saturday
  507.                 if ($this->getStartDate()?->isDayOfWeek(Carbon::SATURDAY)
  508.                     && $this->getEndDate()?->isDayOfWeek(Carbon::SATURDAY)
  509.                     && $consumer->getInfoModalProduct()?->getId() == $product->getId()
  510.                 ) {
  511.                     $priceGroups[] = [
  512.                         'ticketId' => $product->getId(),
  513.                         'priceGroup' => $consumer->getId(),
  514.                         'topTitle' => $this->translator->trans('shop.modal-title-child-ticket.top-title'),
  515.                         'title' => $this->translator->trans('shop.modal-title-child-ticket'),
  516.                         'text' => $this->translator->trans('shop.modal-content-child-ticket'),
  517.                     ];
  518.                 } elseif($consumer->getIsSwisspassHTAConsumer()) {
  519.                     $priceGroups[] = [
  520.                         'priceGroup' => $consumer->getId(),
  521.                         'topTitle' => $this->translator->trans('shop.ticket.specialPriceModal.top-title'),
  522.                         'title' => $this->translator->trans('shop.ticket.specialPriceModal.title'),
  523.                         'text' => (string)$consumer->getInfoText(),
  524.                     ];
  525.                 }
  526.             }
  527.         }
  528.         return $priceGroups;
  529.     }
  530.     /**
  531.      * @param TicketCatalogAvailability $ticketCatalogAvailability
  532.      *
  533.      * @return array<mixed>
  534.      */
  535.     public function getPriceGroupObjects(TicketCatalogAvailability $ticketCatalogAvailability): array
  536.     {
  537.         $priceGroups = [];
  538.         foreach ($ticketCatalogAvailability->getTicketAvailability() as $ticketAvailability) {
  539.             foreach ($ticketAvailability->getConsumerAvailabilities() as $consumerAvailability) {
  540.                 $priceGroups[] = $consumerAvailability->getTicketConsumer();
  541.             }
  542.         }
  543.         return $priceGroups;
  544.     }
  545.     /**
  546.      * @param array<mixed> $priceData
  547.      * @param mixed $priceInfo
  548.      *
  549.      * @return void
  550.      */
  551.     private function setPriceData(array &$priceDatamixed $priceInfo): void
  552.     {
  553.         $price $priceInfo->getTotalPrice()->getGrossAmount();
  554.         $priceBeforeDiscount $priceInfo->getOriginalPriceInfo()->getTotalPrice()->getGrossAmount();
  555.         if ($priceBeforeDiscount->greaterThan($price)) {
  556.             $priceData['priceBeforeDiscount'] = $priceBeforeDiscount->asNumeric();
  557.             $priceData['priceDiscountInfo'] = $this->shopService->getPricingRulesLabel($priceInfo) ?? '';
  558.         }
  559.     }
  560.     //------ EVENTS -----------
  561.     /**
  562.      * @param ShopDynamicPrice[] $productGroup
  563.      *
  564.      * @return array<mixed>
  565.      */
  566.     private function getPriceByGroupEventJson(array $productGroupEventProduct $productCarbon $date): array
  567.     {
  568.         $priceByPriceGroup = [];
  569.         foreach ($productGroup as $price) {
  570.             $orderableProduct $product->getOrCreateOrderableObject($date$price);
  571.             $orderPriceInfo $orderableProduct->getOSPriceInfo(1null$date);
  572.             $priceData = [
  573.                 'id' => (string)$price->getId(),
  574.                 'price' => $orderPriceInfo->getTotalPrice()->getGrossAmount()->asNumeric(),
  575.                 'personsPerItem' => $price->getParticipantNumber(),
  576.             ];
  577.             $this->setPriceData($priceData$orderPriceInfo);
  578.             $priceByPriceGroup[] = $priceData;
  579.         }
  580.         return $priceByPriceGroup;
  581.     }
  582.     /**
  583.      * @param EventProduct $product
  584.      *
  585.      * @return array<mixed>
  586.      */
  587.     private function getPriceGroupEventJson(EventProduct $product): array
  588.     {
  589.         $priceGroups = [];
  590.         $priceObjects $product->getPriceObjects();
  591.         foreach ($priceObjects as $price) {
  592.             $priceGroups[] = [
  593.                 'id' => (string)$price->getId(),
  594.                 'label' => $price->getPriceLabel(),
  595.                 'info' => '<p>Info</p>',
  596.             ];
  597.         }
  598.         return $priceGroups;
  599.     }
  600.     /**
  601.      * @param EventProduct $event
  602.      * @param Carbon|null $shownStartDate
  603.      * @param Carbon|null $shownEndDate
  604.      *
  605.      * @return array<mixed>
  606.      */
  607.     private function getEventCalendarDates(EventProduct $eventCarbon $shownStartDate nullCarbon $shownEndDate null): array
  608.     {
  609.         /** @var AvailabilitySystem $availabilitySystem */
  610.         $availabilitySystem $event->getAvailabilitySystemImplementation();
  611.         $availableDates $availabilitySystem->getAvailableList($event$shownStartDate$shownEndDate);
  612.         $calendarData = [];
  613.         if (!$shownStartDate) {
  614.             $shownStartDate Carbon::now();
  615.         }
  616.         while ($shownStartDate->lessThanOrEqualTo($shownEndDate)) {
  617.             $dates = [];
  618.             $month $shownStartDate->month;
  619.             $year $shownStartDate->year;
  620.             $tmpDate = clone $shownStartDate;
  621.             while ($shownStartDate->lessThanOrEqualTo($tmpDate->endOfMonth())) {
  622.                 $timestamps array_keys($availableDates);
  623.                 $maxQuota 0;
  624.                 $currentQuota 0;
  625.                 foreach ($timestamps as $timestamp) {
  626.                     if ($shownStartDate->getTimestamp() <= $timestamp && $shownStartDate->copy()->endOfDay()->getTimestamp() >= $timestamp) {
  627.                         $availability $availableDates[$timestamp];
  628.                         $maxQuota += $availability->getTotalQuota();
  629.                         $currentQuota += $availability->getAvailableQuota();
  630.                     }
  631.                 }
  632.                 if ($maxQuota 0) {
  633.                     if (($currentQuota $maxQuota 100) <= 10) {
  634.                         $status 'danger';
  635.                     } elseif (($currentQuota $maxQuota 100) <= 20) {
  636.                         $status 'warning';
  637.                     } else {
  638.                         $status 'success';
  639.                     }
  640.                 }
  641.                 $dates[] = [
  642.                     'disabled' => $maxQuota == || $currentQuota == 0,
  643.                     'soldOut' => $maxQuota && $currentQuota == 0,
  644.                     'date' => $shownStartDate->toDateTimeLocalString(),
  645.                     'availability' => [
  646.                         'status' => $status ?? 'danger',
  647.                         'total' => $currentQuota,
  648.                     ],
  649.                 ];
  650.                 $shownStartDate->addDay();
  651.             }
  652.             $calendarData[] = [
  653.                 'month' => $month,
  654.                 'year' => $year,
  655.                 'dates' => $dates,
  656.             ];
  657.         }
  658.         return $calendarData;
  659.     }
  660.     /**
  661.      * @param int $activityId
  662.      * @param string $selectedDateEnd
  663.      *
  664.      * @return array<mixed>
  665.      */
  666.     public function getTimeListDatesJson(int $activityIdstring $selectedDateEnd): array
  667.     {
  668.         $json = [];
  669.         if ($product EventProduct::getById($activityId)) {
  670.             $selectedDateTime Carbon::parse($selectedDateEnd);
  671.             $priceGroups $this->getPriceGroupEventJson($product);
  672.             /** @var AvailabilitySystem $availabilitySystem */
  673.             $availabilitySystem $product->getAvailabilitySystemImplementation();
  674.             $availabilities $availabilitySystem->getAvailableList($product$selectedDateTime$selectedDateTime->copy()->endOfDay());
  675.             $timeList = [];
  676.             $quota 0;
  677.             if (count($availabilities) == 1) {
  678.                 $availability array_pop($availabilities);
  679.                 $quota $availability->getAvailableQuota();
  680.                 $groupedProductGroups $product->getPriceObjectGrouped();
  681.                 foreach ($groupedProductGroups as $translationKey => $productGroup) {
  682.                     $priceByPriceGroup $this->getPriceByGroupEventJson($productGroup$product$availability->getDate());
  683.                     $timeList[] = [
  684.                         'title' => $translationKey $this->translator->trans($translationKey) : $product->getName(),
  685.                         'time' => $availability->getDate()->toTimeString('minute'),
  686.                         'isAvailable' => $availability->isAvailable(),
  687.                         'max' => $availability->getMaxAvailableBooking(),
  688.                         'priceByPriceGroup' => $priceByPriceGroup,
  689.                     ];
  690.                 }
  691.             } else {
  692.                 // more than one availability means Recurring Date/Time was used
  693.                 foreach ($availabilities as $availability) {
  694.                     $quota += $availability->getAvailableQuota();
  695.                     $priceByPriceGroup $this->getPriceByGroupEventJson($product->getPriceObjects(), $product$availability->getDate());
  696.                     $timeList[] = [
  697.                         'title' => $product->getName(),
  698.                         'time' => $availability->getDate()->toTimeString('minute'),
  699.                         'isAvailable' => $availability->isAvailable(),
  700.                         'max' => $availability->getMaxAvailableBooking(),
  701.                         'priceByPriceGroup' => $priceByPriceGroup,
  702.                         'showTime' => true,
  703.                     ];
  704.                 }
  705.             }
  706.             usort($timeList, function ($a$b) {
  707.                 if ($a['isAvailable'] && !$b['isAvailable']) {
  708.                     return -1;
  709.                 }
  710.                 if (!$a['isAvailable'] && $b['isAvailable']) {
  711.                     return 1;
  712.                 }
  713.                 return 0;
  714.             });
  715.             $availabilityData = [
  716.                 'total' => $quota,
  717.                 'status' => 'success',
  718.             ];
  719.             $json = [
  720.                 'success' => true,
  721.                 'hasAgeGroups' => false,
  722.                 'id' => (string)$product->getId(),
  723.                 'priceGroups' => $priceGroups,
  724.                 'availability' => $availabilityData,
  725.                 'timeList' => $timeList,
  726.             ];
  727.         }
  728.         return $json;
  729.     }
  730.     /**
  731.      * @param Request $request
  732.      *
  733.      * @return array<mixed>
  734.      */
  735.     public function getCalendarDates(Request $request): array
  736.     {
  737.         $json = [];
  738.         $id intval($request->get('ticketId'));
  739.         if (empty($id)) {
  740.             return $json;
  741.         }
  742.         if ($event EventProduct::getById($id)) {
  743.             $isMobile $request->get('isMobile');
  744.             $startDate $event->getFirstValidValidityDate(Carbon::now());
  745.             $eventDates $this->datesHelper->getCalculatedDates($event'getValidityDates');
  746.             $lastDate end($eventDates);
  747.             if ($request->get('shownStartYear') && $request->get('shownStartMonth')) {
  748.                 $shownStartDate Carbon::create($request->get('shownStartYear'), $request->get('shownStartMonth'));
  749.             } else {
  750.                 $shownStartDate = clone $startDate;
  751.                 $shownStartDate->startOfMonth();
  752.             }
  753.             if ($request->get('shownEndYear') && $request->get('shownEndMonth')) {
  754.                 $shownEndDate Carbon::create($request->get('shownEndYear'), $request->get('shownEndMonth'))->endOfMonth();
  755.             } else {
  756.                 $shownEndDate = clone $startDate;
  757.                 if ($isMobile === 'false') {
  758.                     $shownEndDate->addMonth()->endOfMonth();
  759.                 } else {
  760.                     $shownEndDate->endOfMonth();
  761.                 }
  762.             }
  763.             $maxDate $lastDate['toDate'] ?? clone $shownEndDate;
  764.             $calendarData $this->getEventCalendarDates($event$shownStartDate$shownEndDate);
  765.             $json = [
  766.                 'success' => true,
  767.                 'id' => (string)$event->getId(),
  768.                 'calendarConfiguration' => [
  769.                     'minDate' => $startDate->startOfDay()->toDateTimeLocalString(),
  770.                     'maxDate' => $maxDate->toDateTimeLocalString(),
  771.                     'minSelectableDays' => 1,
  772.                     'maxSelectableDays' => 1,
  773.                     'minSelectableDates' => 1,
  774.                     'maxSelectableDates' => 1,
  775.                 ],
  776.                 'basePriceInfo' => [
  777.                     'label' => $this->translator->trans('shop.configuration.Price-per-Adult'),
  778.                     'price' => $event->getTeaserPrice()->getGrossAmount()->asNumeric(),
  779.                 ],
  780.                 'calendarDates' => $calendarData,
  781.             ];
  782.         }
  783.         if ($json) {
  784.             return $json;
  785.         } else {
  786.             return [];
  787.         }
  788.     }
  789.     /**
  790.      * @param int $activityId
  791.      *
  792.      * @return array<mixed>
  793.      */
  794.     public function getListDatesJson(int $activityId): array
  795.     {
  796.         $json = [];
  797.         if ($product EventProduct::getById($activityId)) {
  798.             $priceGroups $this->getPriceGroupEventJson($product);
  799.             /** @var AvailabilitySystem $availabilitySystem */
  800.             $availabilitySystem $product->getAvailabilitySystemImplementation();
  801.             $availableDates $availabilitySystem->getAvailableList($product);
  802.             $dateList = [];
  803.             foreach ($availableDates as $availability) {
  804.                 $priceByPriceGroup $this->getPriceByGroupEventJson($product->getPriceObjects(), $product$availability->getDate());
  805.                 $availableQuota $availability->getAvailableQuota();
  806.                 $dateList[] = [
  807.                     'title' => $product->getName(),
  808.                     'date' => $availability->getDate()->toDateString(),
  809.                     'availability' => [
  810.                         'total' => $availableQuota,
  811.                         'status' => $availableQuota == 'danger' : ($availability->getCapacityInPercent() <= 20 'warning' 'success'),
  812.                     ],
  813.                     'max' => $availability->getMaxAvailableBooking(),
  814.                     'priceByPriceGroup' => $priceByPriceGroup,
  815.                 ];
  816.             }
  817.             usort($dateList, function ($a$b) {
  818.                 if ($a['availability']['total'] == || $b['availability']['total'] == 0) {
  819.                     return -1;
  820.                 }
  821.                 return 0;
  822.             });
  823.             $json = [
  824.                 'success' => true,
  825.                 'hasAgeGroups' => true,
  826.                 'id' => (string)$product->getId(),
  827.                 'priceGroups' => $priceGroups,
  828.                 'dateList' => $dateList,
  829.             ];
  830.         }
  831.         return $json;
  832.     }
  833. }