src/Controller/Shop/Json/TicketJsonController.php line 198

Open in your IDE?
  1. <?php
  2. /**
  3.  * Created by Elements.at New Media Solutions GmbH
  4.  *
  5.  */
  6. namespace App\Controller\Shop\Json;
  7. use App\Controller\AbstractController;
  8. use App\Model\DataObject\Customer;
  9. use App\Model\Shop\Ticket\ShopTicketCatalog;
  10. use App\Model\Shop\Ticket\TicketProduct;
  11. use App\Service\Shop\JsonDataService;
  12. use App\Service\Shop\PricingRulesService;
  13. use App\Service\Shop\ProductService;
  14. use App\Service\Shop\ShopService;
  15. use App\Service\Shop\SmartPricerService;
  16. use App\Service\TicketShopFrameworkBundle\TicketFilter;
  17. use Carbon\Carbon;
  18. use CustomerManagementFrameworkBundle\CustomerProvider\CustomerProviderInterface;
  19. use CustomerManagementFrameworkBundle\Security\Authentication\LoginManager;
  20. use Elements\Bundle\RecurringDatesTypeBundle\Templating\RecurringDatesHelper;
  21. use Elements\Bundle\TicketShopFrameworkBundle\Ecommerce\PricingManager\PriceInfo;
  22. use Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketConsumerCategory;
  23. use Pimcore\Security\Hasher\PasswordFieldHasher;
  24. use Symfony\Component\HttpFoundation\Request;
  25. use Symfony\Component\HttpFoundation\Response;
  26. use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
  27. use Symfony\Component\Routing\Annotation\Route;
  28. use Symfony\Contracts\Translation\TranslatorInterface;
  29. /**
  30.  * @Route("/{_locale}/shop/ticket")
  31.  */
  32. class TicketJsonController extends AbstractController
  33. {
  34.     /**
  35.      * @Route("/calendar-dates", name="json_ticket_calendar_dates", methods={"GET"})
  36.      */
  37.     public function calendarDatesJson(
  38.         Request $request,
  39.         ShopService $shopHelper,
  40.         SmartPricerService $smartPricerService,
  41.         RecurringDatesHelper $recurringDatesHelper,
  42.         ProductService $productService
  43.     ): Response {
  44.         $json = [];
  45.         $id intval($request->get('ticketId'));
  46.         if (empty($id)) {
  47.             return $this->json($json);
  48.         }
  49.         if ($catalog ShopTicketCatalog::getById($id)) {
  50.             $recurringDates $recurringDatesHelper->getCalculatedDates($catalog'getValidityDates');
  51.             $isMobile $request->get('isMobile');
  52.             $today Carbon::today();
  53.             $validDates $catalog->getFirstValidDateRange($today);
  54.             //for timePressure
  55.             $useDemandPressure $catalog->getUseDemandPressure();
  56.             /** @var Carbon $minDate */
  57.             $minDate $validDates['from']->gt($today) ? $validDates['from'] : $today;
  58.             $maxDate $shopHelper->getMaxValidityDate($recurringDates);
  59.             $maxDate $maxDate ?: $validDates['to'];
  60.             if ($request->get('shownStartYear') && $request->get('shownStartMonth')) {
  61.                 $shownStartDate Carbon::create($request->get('shownStartYear'), $request->get('shownStartMonth'));
  62.             } else {
  63.                 $shownStartDate = clone $minDate;
  64.                 $shownStartDate->startOfMonth();
  65.             }
  66.             if ($request->get('shownEndYear') && $request->get('shownEndMonth')) {
  67.                 $shownEndDate Carbon::create($request->get('shownEndYear'), $request->get('shownEndMonth'));
  68.             } else {
  69.                 $shownEndDate = clone $minDate;
  70.                 if ($isMobile === 'false') {
  71.                     $shownEndDate->addMonthWithoutOverflow();
  72.                 }
  73.             }
  74.             //after 15pm
  75.             if ($catalog->getUseNextDay() && $minDate->equalTo($today) && Carbon::now()->hour 15) {
  76.                 $minDate->addDay();
  77.             }
  78.             $calendarData = [];
  79.             $seasonDates $shopHelper->getSeasonDates($catalog);
  80.             if ($useDemandPressure) {
  81.                 $pressureEntries $smartPricerService->getDemandPressureEntries($shownStartDate->copy(), $shownEndDate->copy()->endOfMonth());
  82.                 $pressures = [];
  83.                 foreach ($pressureEntries as $pressureEntry) {
  84.                     $pressures[$pressureEntry['pointInTime']] = $pressureEntry['score'];
  85.                 }
  86.             }
  87.             while ($shownStartDate->lessThanOrEqualTo($shownEndDate)) {
  88.                 $dates = [];
  89.                 $month $shownStartDate->month;
  90.                 $year $shownStartDate->year;
  91.                 $tmpDate = clone $shownStartDate;
  92.                 while ($shownStartDate->lessThanOrEqualTo($tmpDate->endOfMonth())) {
  93.                     $data = [
  94.                         'id' => $id,
  95.                         'date' => $shownStartDate->toDateTimeLocalString(),
  96.                         'season' => $shopHelper->getSeasonNameForDate($seasonDates$shownStartDate),
  97.                     ];
  98.                     foreach ($recurringDates as $dateInfo) {
  99.                         if (!$shownStartDate->betweenIncluded($dateInfo['fromDate'], $dateInfo['toDate'])) {
  100.                             $data['disabled'] = true;
  101.                         } else {
  102.                             $data['disabled'] = false;
  103.                             break;
  104.                         }
  105.                     }
  106.                     //no demand pressure for the past
  107.                     if ($useDemandPressure && $shownStartDate->greaterThanOrEqualTo($today)) {
  108.                         $data['priceTendency'] = $productService->getPriceTendency($shownStartDate$pressures);
  109.                     }
  110.                     $dates[] = $data;
  111.                     $shownStartDate->addDay();
  112.                 }
  113.                 $calendarData[] = [
  114.                     'month' => $month,
  115.                     'year' => $year,
  116.                     'dates' => $dates,
  117.                 ];
  118.             }
  119.             $json = [
  120.                 'success' => true,
  121.                 'calendarConfiguration' => [
  122.                     'minDate' => $minDate->toDateTimeLocalString(),
  123.                     'maxDate' => $maxDate->toDateTimeLocalString(),
  124.                     'minSelectableDays' => $catalog->getMinSelectableDays(),
  125.                     'maxSelectableDays' => $catalog->getMaxSelectableDays(),
  126.                     'selectableValidities' => $catalog->getValidityDaysNumeric(),
  127.                     'minSelectableDates' => $catalog->getMinSelectableDates(),
  128.                     'maxSelectableDates' => $catalog->getMaxSelectableDates(),
  129.                 ],
  130.                 'calendarDates' => $calendarData,
  131.             ];
  132.         }
  133.         if ($json) {
  134.             return $this->json($json);
  135.         } elseif (\Pimcore::inDebugMode()) {
  136.             return $this->redirect('/faker-api/skiticket-configs/dates.json');
  137.         } else {
  138.             return $this->json([]);
  139.         }
  140.     }
  141.     /**
  142.      * @Route("/price", name="json_ticket_price", methods={"GET"})
  143.      *
  144.      * @param Request $request
  145.      * @param ShopService $shopHelper
  146.      * @param PricingRulesService $pricingRulesService
  147.      * @param TranslatorInterface $translator
  148.      *
  149.      * @return Response
  150.      */
  151.     public function priceJson(
  152.         Request $request,
  153.         ShopService $shopHelper,
  154.         PricingRulesService $pricingRulesService,
  155.         TranslatorInterface $translator
  156.     ): Response {
  157.         $json = [];
  158.         $priceInfo null;
  159.         if ($catalog ShopTicketCatalog::getById(intval($request->get('ticketId')))) {
  160.             $wrongRange false;
  161.             if (($selectedStartDate $request->get('selectedDateStart')) && ($selectedEndDate $request->get('selectedDateEnd'))) {
  162.                 $startDate Carbon::parse($selectedStartDate);
  163.                 $endDate Carbon::parse($selectedEndDate);
  164.                 $ticketAvailability $catalog->getAvailabilityForDateRange($startDate$endDate);
  165.                 if ($ticketAvailability->hasAvailability()) {
  166.                     $priceInfo $catalog->getLowestPriceInfoForAvailability($ticketAvailability);
  167.                 } else {
  168.                     $wrongRange true;
  169.                 }
  170.             } elseif ($selectedStartDate $request->get('selectedDateStart')) {
  171.                 $startDate Carbon::parse($selectedStartDate);
  172.                 $product $catalog->getProduct($startDate);
  173.                 if ($product) {
  174.                     $priceInfo $catalog->getPriceInfoForProduct($product$startDate);
  175.                 } else {
  176.                     $wrongRange true;
  177.                 }
  178.             } else {
  179.                 //initial price when opening the calendar
  180.                 $startDate Carbon::now();
  181.                 $product $catalog->getProduct();
  182.                 if ($product) {
  183.                     $priceInfo $catalog->getPriceInfoForProduct($product$startDate);
  184.                 } else {
  185.                     $wrongRange true;
  186.                 }
  187.             }
  188.             $json = [
  189.                 'success' => true,
  190.                 'data' => [
  191.                     'hideServiceView' => $shopHelper->isServiceViewHidden($catalog$startDate$endDate ?? nulltrue),
  192.                     'pricePerAdult' => $priceInfo $priceInfo->getPrice()->getGrossAmount()->asNumeric() : 0,
  193.                     'noAvailableDates' => $wrongRange,
  194.                 ],
  195.             ];
  196.             if ($priceInfo instanceof PriceInfo) {
  197.                 $product $priceInfo->getOriginalPriceInfo()->getProduct();
  198.                 if ($product instanceof TicketProduct && $presalesExpiredMessage $pricingRulesService->getPresaleExpiredMessage($startDate,
  199.                     $product$priceInfo)) {
  200.                     $json['data']['presalesExpiredMessage'] = $translator->trans($presalesExpiredMessage);
  201.                 } else {
  202.                     $json['data']['presalesExpiredMessage'] = '';
  203.                 }
  204.             }
  205.         }
  206.         return $this->json($json);
  207.     }
  208.     /**
  209.      * @Route("/price_tendency", name="json_ticket_price_tendency", methods={"GET"})
  210.      *
  211.      * @param Request $request
  212.      * @param SmartPricerService $smartPricerService
  213.      * @param TranslatorInterface $translator
  214.      *
  215.      * @return Response
  216.      */
  217.     public function priceTendencyJson(
  218.         Request $request,
  219.         SmartPricerService $smartPricerService,
  220.         TranslatorInterface $translator
  221.     ): Response {
  222.         $json = [];
  223.         if ($catalog ShopTicketCatalog::getById(intval($request->get('ticketId')))) {
  224.             $data = [];
  225.             if ($catalog->getPriceType() !== 'dynamicPrice') {
  226.                 $data['hidePriceTendency'] = true;
  227.             }
  228.             if (!$catalog->getUseTimePressure()) {
  229.                 return $this->json([
  230.                     'success' => true,
  231.                     'data' => $data,
  232.                 ]);
  233.             }
  234.             if ($selectedDate $request->get('selectedDateStart')) {
  235.                 $startDate Carbon::parse($selectedDate);
  236.             } else {
  237.                 $startDate $catalog->getFirstValidValidityDate(Carbon::now());
  238.             }
  239.             $endDate $request->get('selectedDateEnd') == null null Carbon::parse($request->get('selectedDateEnd'));
  240.             $filter = new TicketFilter($startDate$endDate);
  241.             $filter->setExactDuration(true);
  242.             $filter->setIgnorePrice(true);
  243.             $priceTendencyScore null;
  244.             if ($calendarConsumer $catalog->getCalendarPriceConsumerCategory()) {
  245.                 /** @var TicketConsumerCategory $calendarConsumer */
  246.                 $filter->setConsumers([$calendarConsumer->getId()]);
  247.                 $availabilities $filter->getTicketCatalogAvailability($catalogfalse);
  248.                 $skidataProduct null;
  249.                 if ($availabilities->hasAvailability()) {
  250.                     foreach ($availabilities->getTicketAvailability() as $ticketAvailability) {
  251.                         $ticketProduct $ticketAvailability->getTicketProduct();
  252.                         if (!$ticketProduct->getIgnoreTimePressure()) {
  253.                             $skidataProduct $ticketProduct->getSkidataProduct();
  254.                             break;
  255.                         }
  256.                     }
  257.                 }
  258.                 if ($skidataProduct) {
  259.                     $consumer $calendarConsumer->getSkidataConsumerForSkidataProduct($skidataProduct);
  260.                     $priceTendencyScore $smartPricerService->getTimePressure($skidataProduct->getExternalId(), $consumer->getExternalId(), $startDate);
  261.                 }
  262.             }
  263.             $data = [];
  264.             if ($priceTendencyScore) {
  265.                 $allowedLevels = ['low''rather-low''normal'];
  266.                 $pressureText $smartPricerService->getPressureText($priceTendencyScore['textScore']);
  267.                 $pressureDescription $smartPricerService->getPriceAvailabilityText(1.01 $priceTendencyScore['score']);
  268.                 $data['priceTendency'] = $pressureText;
  269.                 $data['percentage'] = 100 - ($priceTendencyScore['score'] * 99.9);
  270.                 $data['percentageDescription'] = $translator->trans('pricing.' $pressureDescription);
  271.                 $data['percentageDescriptionInfo'] = '';
  272.                 $data['additionalPriceInfo'] = in_array($pressureText$allowedLevels) ? $translator->trans('price-tendency.price-info.' $pressureText) : '';
  273.             }
  274.             $json = [
  275.                 'success' => true,
  276.                 'data' => $data,
  277.             ];
  278.         }
  279.         return $this->json($json);
  280.     }
  281.     /**
  282.      * @Route("/pricing", name="json_ticket_pricing", methods={"GET"})
  283.      *
  284.      * @param Request $request
  285.      * @param ShopService $shopHelper
  286.      * @param JsonDataService $jsonDataService
  287.      *
  288.      * @return Response
  289.      */
  290.     public function pricingJson(Request $requestShopService $shopHelperJsonDataService $jsonDataService): Response
  291.     {
  292.         $json $jsonDataService->getPricingJson($request$shopHelper);
  293.         return $this->json($json);
  294.     }
  295.     /**
  296.      * @Route("/services", name="json_services", methods={"GET"})
  297.      *
  298.      * @param Request $request
  299.      * @param TranslatorInterface $translator
  300.      * @param ShopService $shopHelper
  301.      *
  302.      * @return Response
  303.      */
  304.     public function servicesJson(Request $requestTranslatorInterface $translatorShopService $shopHelper): Response
  305.     {
  306.         $json = [];
  307.         if ($catalog ShopTicketCatalog::getById(intval($request->get('ticketId')))) {
  308.             $startDate Carbon::parse($request->get('selectedDateStart'));
  309.             if ($selectedEndDate $request->get('selectedDateEnd')) {
  310.                 $endDate Carbon::parse($selectedEndDate);
  311.             } else {
  312.                 $endDate $startDate->copy();
  313.             }
  314.             $filter = new TicketFilter($startDate$endDate);
  315.             $filter->setExactDuration(true);
  316.             $filter->setIgnorePrice(true);
  317.             $filter->setUseTicketValidity(true);
  318.             $allowedConsumers = [];
  319.             foreach ($catalog->getTicketConsumerCategories() as $allowedConsumer) {
  320.                 $allowedConsumers[] = $allowedConsumer->getId();
  321.             }
  322.             if ($allowedConsumers) {
  323.                 $filter->setConsumers($allowedConsumers);
  324.             }
  325.             $catalogAvailability $filter->getTicketCatalogAvailability($catalogtrue);
  326.             $options = [];
  327.             $insurances = [];
  328.             $upgrades = [];
  329.             $upgradeCatalog $catalog->getUpgrades();
  330.             if ($catalogAvailability->hasAvailability()) {
  331.                 $insuranceData $shopHelper->getInsurancesForProduct($catalogAvailability);
  332.                 $insuranceObjects $insuranceData['insurances'] ?? [];
  333.                 foreach ($catalogAvailability->getTicketAvailability() as $availability) {
  334.                     /** @var TicketProduct $ticketProduct */
  335.                     $ticketProduct $availability->getTicketProduct();
  336.                     $metaProduct $ticketProduct->getMetaProduct();
  337.                     $insuranceIds = [];
  338.                     $optionUpgrades = [];
  339.                     if ($insuranceObjects) {
  340.                         /** @var TicketProduct $insurance */
  341.                         foreach ($insuranceObjects[$ticketProduct->getId()] as $insurance) {
  342.                             $mappingObject $insuranceData['mappingObject'][$insurance->getId()];
  343.                             $insuranceIds[] = (string)$insurance->getId();
  344.                             $insurances[] = [
  345.                                 'id' => (string)$insurance->getId(),
  346.                                 'label' => (string)$mappingObject->getName(),
  347.                                 'labelSubText' => (string)$insurance->getTextDateSelectOverlay(),
  348.                                 'info' => (string)($mappingObject->getDescription() ?: $insurance->getDescription()),
  349.                             ];
  350.                         }
  351.                     }
  352.                     foreach ($catalog->getUpgrades() as $upgrade) {
  353.                         if (!str_contains($upgrade->getData()['name'], ',')) {
  354.                             /** @var ShopTicketCatalog $upgradeObject */
  355.                             $upgradeObject $upgrade->getObject();
  356.                             $upgradeFilter = new TicketFilter($startDate$endDate);
  357.                             $upgradeFilter->setExactDuration(true);
  358.                             $upgradeFilter->setIgnorePrice(true);
  359.                             $upgradeFilter->setUseTicketValidity(true);
  360.                             $upgradeCatalogAvailability $upgradeFilter->getTicketCatalogAvailability($upgradeObjecttrue);
  361.                             if ($upgradeCatalogAvailability->hasAvailability() && !$upgradeObject->getIsNotBookable()) {
  362.                                 //upgrade insurances
  363.                                 if ($upgradeInsuranceData $shopHelper->getInsurancesForProduct($upgradeCatalogAvailability)) {
  364.                                     if ($upgradeInsuranceObjects $upgradeInsuranceData['insurances'] ?? []) {
  365.                                         foreach ($upgradeCatalogAvailability->getTicketAvailability() as $upgradeTicketAvailability) {
  366.                                             $upgradeTicketProduct $upgradeTicketAvailability->getTicketProduct();
  367.                                             /** @var TicketProduct $insurance */
  368.                                             foreach ($upgradeInsuranceObjects[$upgradeTicketProduct->getId()] as $insurance) {
  369.                                                 $mappingObject $upgradeInsuranceData['mappingObject'][$insurance->getId()];
  370.                                                 $insuranceIds[] = (string)$insurance->getId();
  371.                                                 $insurances[] = [
  372.                                                     'id' => (string)$insurance->getId(),
  373.                                                     'label' => (string)$mappingObject->getName(),
  374.                                                     'labelSubText' => (string)$insurance->getTextDateSelectOverlay(),
  375.                                                     'info' => (string)($mappingObject->getDescription() ?: $insurance->getDescription()),
  376.                                                 ];
  377.                                             }
  378.                                         }
  379.                                     }
  380.                                 }
  381.                                 //special logic for flex passes where there is no shuttle
  382.                                 if ($metaProduct) {
  383.                                     if (array_intersect($metaProduct->getRelatedTo(), $upgradeObject->getTicketProducts())) {
  384.                                         $optionUpgrades[] = (string)$upgradeObject->getId();
  385.                                     }
  386.                                 } else {
  387.                                     $optionUpgrades[] = (string)$upgradeObject->getId();
  388.                                 }
  389.                             }
  390.                         }
  391.                     }
  392.                     if ($insuranceIds) {
  393.                         $insuranceIds array_values(array_unique($insuranceIds));
  394.                     }
  395.                     $options[] = [
  396.                         'id' => (string)$ticketProduct->getId(),
  397.                         'label' => (string)$ticketProduct->getName(),
  398.                         'labelSubText' => (string)$ticketProduct->getTextDateSelectOverlay(),
  399.                         'info' => (string)$ticketProduct->getDescription(),
  400.                         'upgrades' => $optionUpgrades,
  401.                         'insurances' => $insuranceIds,
  402.                     ];
  403.                 }
  404.             }
  405.             foreach ($upgradeCatalog as $upgrade) {
  406.                 if (!str_contains($upgrade->getData()['name'], ',')) {
  407.                     /** @var ShopTicketCatalog $upgradeObject */
  408.                     $upgradeObject $upgrade->getObject();
  409.                     $upgrades[] = [
  410.                         'id' => (string)$upgradeObject->getId(),
  411.                         'label' => (string)$upgradeObject->getName(),
  412.                         'labelSubText' => (string)$upgradeObject->getTextDateSelectOverlay(),
  413.                         'info' => (string)$upgradeObject->getInfoBubbleDescription(),
  414.                         'warning' => $upgrade->getData()['warning'] ? $translator->trans('shop.warning-catalog-' $upgrade->getData()['name']) : '',
  415.                     ];
  416.                 }
  417.             }
  418.             $noDuplicateInsurances array_values(array_intersect_key($insurancesarray_unique(array_map('serialize'$insurances))));
  419.             $json = [
  420.                 'success' => true,
  421.                 'serviceDescription' => $catalog->getServicesDescription() ?? '',
  422.                 'options' => $options,
  423.                 'upgrades' => $upgrades,
  424.                 'insurances' => $noDuplicateInsurances,
  425.             ];
  426.         }
  427.         return $this->json($json);
  428.     }
  429.     /**
  430.      * @Route("/login-data", name="json_login_data")
  431.      *
  432.      * @param Request $request
  433.      * @param LoginManager $loginManager
  434.      * @param CustomerProviderInterface $customerProvider
  435.      * @param PasswordHasherFactoryInterface $hasherFactory
  436.      * @param TranslatorInterface $translator
  437.      *
  438.      * @return Response
  439.      */
  440.     public function loginCalendarJson(
  441.         Request $request,
  442.         LoginManager $loginManager,
  443.         CustomerProviderInterface $customerProvider,
  444.         PasswordHasherFactoryInterface $hasherFactory,
  445.         TranslatorInterface $translator
  446.     ): Response {
  447.         //get user if logged in
  448.         $user $this->getUser();
  449.         $redirectData = [
  450.             'ticketId' => $request->get('ticketId'),
  451.             'option' => $request->get('option'),
  452.             'selectedDateStart' => $request->get('selectedDateStart'Carbon::today()->toDateTimeLocalString()),
  453.         ];
  454.         if (!$user instanceof Customer) {
  455.             $email $request->get('email');
  456.             $password $request->get('password');
  457.             if (!$email || !$password) {
  458.                 return $this->getErrorJson($translator->trans('error.login.no-user-or-password'));
  459.             }
  460.             /** @var Customer|null $user */
  461.             $user $customerProvider->getActiveCustomerByEmail($email);
  462.             if ($user) {
  463.                 /** @var PasswordFieldHasher $passwordHasher */
  464.                 $passwordHasher $hasherFactory->getPasswordHasher($user);
  465.                 //check if password is correct and log in user
  466.                 if ($passwordHasher->isPasswordValid(true$password)) {
  467.                     $loginManager->login($user$request);
  468.                     return $this->redirectToRoute('json_person_data'$redirectData);
  469.                 } else {
  470.                     return $this->getErrorJson($translator->trans('error.login.wrong-user-or-password'));
  471.                 }
  472.             } else {
  473.                 return $this->getErrorJson($translator->trans('error.login.wrong-user-or-password'));
  474.             }
  475.         }
  476.         return $this->redirectToRoute('json_person_data'$redirectData);
  477.     }
  478.     /**
  479.      * @Route("/person-data", name="json_person_data")
  480.      *
  481.      * @param Request $request
  482.      *
  483.      * @return Response
  484.      */
  485.     public function personDataJson(Request $request): Response
  486.     {
  487.         /** @var Customer $user */
  488.         $user $this->getUser();
  489.         $ticket TicketProduct::getById((int)$request->get('option'));
  490.         $validityDate Carbon::parse($request->get('selectedDateStart'Carbon::today()));
  491.         $consumerCategories $ticket->getConsumerCategories();
  492.         $persons = [];
  493.         $userAgeGroup null;
  494.         foreach ($consumerCategories as $consumerCategory) {
  495.             /** @phpstan-ignore-next-line */
  496.             if ($user->getBirthdate() && $user->getBirthdate()->between($validityDate->clone()->subYears($consumerCategory->getYearFrom()), $validityDate->clone()->subYears($consumerCategory->getYearTo() + 1)->addDay())) {
  497.                 $userAgeGroup $consumerCategory;
  498.                 break;
  499.             }
  500.         }
  501.         if ($userAgeGroup) {
  502.             $persons[] = [
  503.                 'id' => (string)$user->getId(),
  504.                 'name' => $user->getFullname(),
  505.                 'priceGroup' => (string)$userAgeGroup->getId(),
  506.             ];
  507.         }
  508.         foreach ($user->getContacts() as $contact) {
  509.             $ageGroup null;
  510.             $birthday $contact->getBirthday();
  511.             foreach ($consumerCategories as $consumerCategory) {
  512.                 /** @phpstan-ignore-next-line */
  513.                 if ($birthday && $birthday->between($validityDate->clone()->subYears($consumerCategory->getYearFrom()), $validityDate->clone()->subYears($consumerCategory->getYearTo() + 1)->addDay())) {
  514.                     $ageGroup $consumerCategory;
  515.                     break;
  516.                 }
  517.             }
  518.             if (!empty($ageGroup)) {
  519.                 $persons[] = [
  520.                     'id' => (string)$contact->getId(),
  521.                     'name' => $contact->getFullname(),
  522.                     'priceGroup' => (string)$ageGroup->getId(),
  523.                 ];
  524.             }
  525.         }
  526.         $json = [
  527.             'success' => true,
  528.             'errorMessage' => '',
  529.             'persons' => $persons,
  530.         ];
  531.         return $this->json($json);
  532.     }
  533.     /**
  534.      * @param string $message
  535.      *
  536.      * @return Response
  537.      */
  538.     private function getErrorJson(string $message): Response
  539.     {
  540.         return $this->json([
  541.             'success' => false,
  542.             'errorMessage' => $message,
  543.             'persons' => [],
  544.         ]);
  545.     }
  546. }