src/Elements/Bundle/DemiFrontendBundle/Controller/AdditionalServiceController.php line 265

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 Carbon\Carbon;
  12. use Carbon\Traits\Creator;
  13. use Elements\Bundle\CmsToolsBundle\Tool\Helper\ElementsCustomDateFormat;
  14. use Elements\Bundle\DemiFrontendBundle\Event\DemiDirectInquiryEvent;
  15. use Elements\Bundle\DemiFrontendBundle\Service\AdditionalService;
  16. use Elements\Bundle\DemiFrontendBundle\Service\CartItemInfo;
  17. use Elements\Bundle\SeoHelperBundle\Templating\Helper\Robots;
  18. use Elements\Demi\Frontend\Service\Configuration;
  19. use Elements\Bundle\DemiFrontendBundle\Service\ConnectedEntryProvider;
  20. use Elements\Bundle\DemiFrontendBundle\Service\EcommerceHelper;
  21. use Elements\Bundle\DemiFrontendBundle\Service\PaginatorHelper;
  22. use Elements\Bundle\DemiFrontendBundle\Service\Payment;
  23. use Elements\Demi\AdditionalService\Search\Listing;
  24. use Elements\Demi\AdditionalService\Search\Parameter;
  25. use Elements\Demi\AdditionalService\Search\Parameter\Line;
  26. use Elements\Demi\Deskline\AdditionalService\Search\Adapter;
  27. use Elements\Demi\Deskline\Config;
  28. use Elements\Demi\Deskline\Constant\AddressInterface;
  29. use Elements\Demi\Deskline\Constant\DescriptionInterface;
  30. use Elements\Demi\Deskline\Constant\FacilityGroupTypeInterface;
  31. use Elements\Demi\Deskline\Service\Search\CancellationInformation;
  32. use Elements\Demi\Info\CartItemInfoHolder;
  33. use Elements\Demi\Model\AdditionalProduct;
  34. use Elements\Demi\Model\AdditionalServiceProvider;
  35. use ErrorException;
  36. use Exception;
  37. use InvalidArgumentException;
  38. use Pimcore\Bundle\EcommerceFrameworkBundle\Factory;
  39. use Pimcore\Bundle\EcommerceFrameworkBundle\Tracking\TrackingManager;
  40. use Pimcore\Db;
  41. use Pimcore\Model\DataObject;
  42. use Pimcore\Model\DataObject\AbstractObject;
  43. use Pimcore\Model\DataObject\DemiAdditionalService;
  44. use Pimcore\Model\DataObject\DemiFilterObject;
  45. use Pimcore\Model\Document;
  46. use Pimcore\Model\Document\Editable\Areablock;
  47. use Symfony\Component\HttpFoundation\JsonResponse;
  48. use Symfony\Component\HttpFoundation\Request;
  49. use Symfony\Component\HttpFoundation\Response;
  50. use Symfony\Component\HttpKernel\Exception\GoneHttpException;
  51. use Symfony\Component\Security\Core\User\UserInterface;
  52. use Symfony\Component\DependencyInjection\ContainerInterface;
  53. use Symfony\Component\Validator\Constraints\Email;
  54. use Symfony\Component\Validator\Validator\ValidatorInterface;
  55. class AdditionalServiceController extends AbstractDemiController
  56. {
  57.     public function __construct(protected EcommerceHelper $ecommerceHelper,
  58.                                 protected CartItemInfo $cartItemInfoService,
  59.                                 protected Payment $paymentHelper,
  60.                                 protected array $enabledFeatures,
  61.                                 protected ConnectedEntryProvider $connectedEntryProvider,
  62.                                 protected Listing $additionalServiceListingLive,
  63.                                 protected Listing $additionalServiceListingLocal,
  64.                                 protected TrackingManager $trackingManager,
  65.                                 protected AdditionalService $additionalServiceService,
  66.                                 Configuration $config,
  67.                                 protected PaginatorHelper $paginatorHelper,
  68.                                 protected Adapter $adapter,
  69.                                 ElementsCustomDateFormat $elementsCustomDateFormat,
  70.                                 ContainerInterface $container,
  71.                                 protected SavingRequestController $savingRequestController,
  72.                                 protected ?ValidatorInterface $emailValidator null)
  73.     {
  74.         $this->setElementsCustomDateFormat($elementsCustomDateFormat);
  75.         $this->setConfiguration($config);
  76.         $this->setContainer($container);
  77.         if ($emailValidator === null) {
  78.             $this->emailValidator $this->container->get('validator.email');
  79.         }
  80.     }
  81.     public function overviewAction(Request $request) : Response
  82.     {
  83.         if (!$this->enabledFeatures['additionalServices']) {
  84.             throw new Exception('AdditionalServices are not enabled!');
  85.         }
  86.         $editablePrefix $this->findEditablePrefix($this->document);
  87.         $providerId DataObject\DemiAdditionalServiceProvider::classId();
  88.         $productId DataObject\DemiAdditionalProduct::classId();
  89.         $items = new DemiAdditionalService\Listing();
  90.         $serviceId $items->getClassId();
  91.         $lang $request->getLocale();
  92.         $dateBegin htmlentities($request->get('from'));
  93.         if (empty($dateBegin)) {
  94.             $dateBegin = new Carbon();
  95.             $dateBegin->startOfDay();
  96.         } else {
  97.             $dateBegin $this->elementsCustomDateFormat->stringToCarbon($dateBegin);
  98.         }
  99.         $dateEnd htmlentities($request->get('to'));
  100.         if (empty($dateEnd)) {
  101.             $dateEnd $dateBegin->copy()->addYear();
  102.         } else {
  103.             $dateEnd $this->elementsCustomDateFormat->stringToCarbon($dateEnd);
  104.         }
  105.         $text $request->get('keyword');
  106.         if (!empty($text)) {
  107.             $items->addConditionParam(Db::get()->quoteIdentifier('name') . ' LIKE ' Db::get()->quote('%' $text '%'));
  108.             $viewParams["text"] = htmlentities(nl2br($text));
  109.         }
  110.         $selectedHolidayTheme $request->get('category');
  111.         $predefinedHolidayThemes $this->document->getEditable($editablePrefix 'holidayThemes')->getData();
  112.         if (!empty($selectedHolidayTheme)) {
  113.             $obj AbstractObject::getById($selectedHolidayTheme);
  114.             if ($obj instanceof DataObject\DemiFilterObject) {
  115.                 foreach ($obj->getFilterElements() as $theme) {
  116.                     $themeids[] = $theme->getId();
  117.                 }
  118.             } else {
  119.                 $themeids[] = $selectedHolidayTheme;
  120.             }
  121.             $items->addConditionParam("object_localized_{$serviceId}_{$lang}.o_id in (select o_parentId from object_$productId where object_$productId.o_id in (select src_id from object_relations_$productId where dest_id in ("implode(','$themeids) . ") and fieldname = 'holidaythemes'))");
  122.         } elseif (!empty($predefinedHolidayThemes)) {
  123.             $themeids = [];
  124.             foreach ($predefinedHolidayThemes as $holidayTheme) {
  125.                 $obj AbstractObject::getById($holidayTheme);
  126.                 if ($obj instanceof DataObject\DemiFilterObject) {
  127.                     foreach ($obj->getFilterElements() as $item) {
  128.                         $themeids[] = $item->getId();
  129.                     }
  130.                 } else {
  131.                     $themeids[] = $holidayTheme->getId();
  132.                 }
  133.             }
  134.             $items->addConditionParam("object_localized_{$serviceId}_{$lang}.o_id in (select o_parentId from object_$productId where object_$productId.o_id in (select src_id from object_relations_$productId where dest_id in ("implode(','$themeids).") and fieldname = 'holidaythemes'))");
  135.         }
  136.         $selectedRegion $request->get('region');
  137.         $predefinedRegions $this->document->getEditable($editablePrefix.'regions')->getData();
  138.         if (!empty($selectedRegion)) {
  139.             $townids = [];
  140.             $districtIds = [];
  141.             $region AbstractObject::getById($selectedRegion);
  142.             if ($region instanceof DemiFilterObject) {
  143.                 foreach ($region->getFilterElements() as $filterElement) {
  144.                     if ($filterElement instanceof DataObject\DemiRegion) {
  145.                         foreach ($filterElement->getChildren() as $town) {
  146.                             $townids[] = $town->getId();
  147.                         }
  148.                     } elseif ($filterElement instanceof DataObject\DemiTown) {
  149.                         $townids[] = $filterElement->getId();
  150.                     } elseif ($filterElement instanceof DataObject\DemiDistrict) {
  151.                         $districtIds[] = $filterElement->getId();
  152.                     }
  153.                 }
  154.             } elseif ($region instanceof DataObject\DemiRegion) {
  155.                 foreach ($region->getChildren() as $town) {
  156.                     $townids[] = $town->getId();
  157.                 }
  158.             } elseif ($region instanceof DataObject\DemiTown) {
  159.                 $townids[] = $region->getId();
  160.             } elseif ($region instanceof DataObject\DemiDistrict) {
  161.                 $districtIds[] = $region->getId();
  162.             }
  163.         } elseif (!empty($predefinedRegions)) {
  164.             foreach ($predefinedRegions as $predRegion) {
  165.                 if (!$predRegion instanceof AbstractObject) {
  166.                     $region AbstractObject::getById($predRegion);
  167.                 } else {
  168.                     $region $predRegion;
  169.                 }
  170.                 if ($region instanceof DemiFilterObject) {
  171.                     foreach ($region->getFilterElements() as $filterElement) {
  172.                         if ($filterElement instanceof DataObject\DemiRegion) {
  173.                             foreach ($filterElement->getChildren() as $town) {
  174.                                 $townids[] = $town->getId();
  175.                             }
  176.                         } elseif ($filterElement instanceof DataObject\DemiTown) {
  177.                             $townids[] = $filterElement->getId();
  178.                         } elseif ($filterElement instanceof DataObject\DemiDistrict) {
  179.                             $districtIds[] = $filterElement->getId();
  180.                         }
  181.                     }
  182.                 } elseif ($region instanceof DataObject\DemiRegion) {
  183.                     foreach ($region->getChildren() as $town) {
  184.                         $townids[] = $town->getId();
  185.                     }
  186.                 } elseif ($region instanceof DataObject\DemiTown) {
  187.                     $townids[] = $region->getId();
  188.                 } elseif ($region instanceof DataObject\DemiDistrict) {
  189.                     $districtIds[] = $region->getId();
  190.                 }
  191.             }
  192.         }
  193.         $selectedServiceTypes $request->get('serviceType');
  194.         $serviceTypeIds = [];
  195.         $predefinedServiceTypes $this->document->getEditable($editablePrefix'additionalServiceTypes')->getData();
  196.         if (!empty($selectedServiceTypes)) {
  197.             if (is_array($selectedServiceTypes)) {
  198.                 foreach ($selectedServiceTypes as $selectedServiceType) {
  199.                     $obj DataObject\DemiAdditionalServiceType::getById($selectedServiceType);
  200.                     if ($obj instanceof DataObject\DemiAdditionalServiceType) {
  201.                         $serviceTypeIds[] = $selectedServiceType;
  202.                     }
  203.                 }
  204.             } else {
  205.                 $obj DataObject\DemiAdditionalServiceType::getById($selectedServiceTypes);
  206.                 if ($obj instanceof DataObject\DemiAdditionalServiceType) {
  207.                     $serviceTypeIds[] = $selectedServiceTypes;
  208.                 }
  209.             }
  210.             $items->addConditionParam("object_localized_{$serviceId}_{$lang}.o_id in (select src_id from object_relations_$serviceId where dest_id in (" implode(','$serviceTypeIds) . '))');
  211.         } elseif (!empty($predefinedServiceTypes)) {
  212.             foreach ($predefinedServiceTypes as $serviceType) {
  213.                 $allServiceTypes[] = $serviceType->getId();
  214.             }
  215.             $items->addConditionParam("object_localized_{$items->getClassId()}_{$lang}.o_id in (select src_id from object_relations_$serviceId where dest_id in (" implode(','$allServiceTypes) . '))');
  216.         }
  217.         if (!empty($townids)) {
  218.             $items->addConditionParam("o_parentId in (select o_id from object_$providerId where town__id in (" implode(','$townids) . ') AND o_published = 1)');
  219.         } elseif (!empty($districtIds)) {
  220.             $items->addConditionParam("o_parentId in (select o_id from object_$providerId where district__id in (" implode(','$districtIds) . ') AND o_published = 1)');
  221.         } else {
  222.             $items->addConditionParam("o_parentId in (select o_id from object_$providerId where o_published = 1)");
  223.         }
  224.         $items->addConditionParam('needAcco is null or needAcco = 0');
  225.         $items->addConditionParam("object_localized_{$serviceId}_{$lang}.o_id in (select o_parentid from object_$productId where packageOnly is null or packageOnly = 0)");
  226.         $items->addConditionParam("object_localized_{$serviceId}_{$lang}.o_id in (select o_id from object_collection_demiDate_$serviceId where (object_collection_demiDate_$serviceId.to > {$dateBegin->getTimestamp()} and object_collection_demiDate_$serviceId.from < {$dateEnd->getTimestamp()}))");
  227.         $items $this->additionalServiceService->sortListingPerDefault($items$serviceId$lang);
  228.         $items $this->additionalServiceService->modifyListingForOverview($items);
  229.         $perPage $this->document->getEditable($editablePrefix.'perPage')->getData() ?: 12;
  230.         $paginator $this->paginatorHelper->getPaginator($items$perPage$request->get('page'1), 5);
  231.         $viewParams['paginator'] = $paginator;
  232.         $viewParams['isAjax'] = $request->isXmlHttpRequest();
  233.         $viewParams['additionalServiceNoResultText'] = $this->configuration->getAdditionalServiceNoResultText();
  234.         $viewParams['currencySymbol'] = $this->paymentHelper->getCurrency();
  235.         if ($request->isXmlHttpRequest() && ($request->headers->get('format') === 'json')) {
  236.             $response = [
  237.                 'status' => 'success',
  238.                 'html' => $this->renderTemplate('@ElementsDemiFrontend/AdditionalService/list-result.html.twig'$viewParams)->getContent()
  239.             ];
  240.             return $this->json($response);
  241.         }
  242.         return $this->renderTemplate('@ElementsDemiFrontend/AdditionalService/list-result.html.twig'$viewParams);
  243.     }
  244.     public function detailAction(Request $requestint $idRobots $robotsUserInterface $user null): ?Response
  245.     {
  246.         if (!$this->enabledFeatures['additionalServices']) {
  247.             throw new ErrorException('AdditionalServices are not enabled!');
  248.         }
  249.         $today = new Carbon();
  250.         $productId DataObject\DemiAdditionalProduct::classId();
  251.         $providerId DataObject\DemiAdditionalServiceProvider::classId();
  252.         $object DataObject::getById($request->get('id'));
  253.         if (!$object instanceof DemiAdditionalService) {
  254.             throw new GoneHttpException("The requested Object doesn't exist (anymore)");
  255.         }
  256.         if (!$object->isPublished()) {
  257.             return $this->forward(self::class. '::unavailableAction', [
  258.                 'request' => $request
  259.             ]);
  260.         }
  261.         if (empty($object->getAdditionalProducts())) {
  262.             throw new GoneHttpException('The requested Object does not offer any services!');
  263.         }
  264.         $serviceProvider $object->getParent() instanceof AdditionalServiceProvider $object->getParent() : null;
  265.         if (!$serviceProvider instanceof AdditionalServiceProvider || !$serviceProvider->isPublished()) {
  266.             throw new GoneHttpException("The requested Object doesn't have a published Serviceprovider!");
  267.         }
  268.         $viewParams['user'] = $user;
  269.         if ($request->get('enquire')) {
  270.             $success $this->sendEnquire($request$object$user);
  271.             if ($success) {
  272.                 $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  273.                 $response = [
  274.                     'status' => 'success',
  275.                     'html' => $this->renderTemplate('@ElementsDemiFrontend/AdditionalService/enquiry-success.html.twig', [])->getContent()
  276.                 ];
  277.                 return $this->json($response);
  278.             } else {
  279.                 throw new \Elements\Demi\Deskline\Exception('can not sent enquiry - dsi saving request was not successful');
  280.             }
  281.         }
  282.         if($object->getProperty('seo_noindex')) {
  283.             $robots->setNoIndex(true);
  284.         }
  285.         if ($object->getDates()) {
  286.             $endDate null;
  287.             foreach ($object->getDates()->getItems() as $item) {
  288.                 if ($endDate === null) {
  289.                     $endDate $item->getTo();
  290.                 } elseif ($endDate->lt($item->getTo())) {
  291.                     $endDate $item->getTo();
  292.                 }
  293.             }
  294.             if ($endDate !== null) {
  295.                 $endDate $endDate->endOfDay()->toRfc850String();
  296.                 $this->addResponseHeader('X-Robots-Tag''unavailable_after: ' $endDate);
  297.             }
  298.         }
  299.         $onlyHolidaythemes = new DemiAdditionalService\Listing();
  300.         $serviceId $onlyHolidaythemes->getClassId();
  301.         $themeids = [];
  302.         foreach ($object->getAdditionalProducts() as $product) {
  303.             foreach ($product->getHolidayThemes() as $holidayTheme) {
  304.                 $themeId $holidayTheme->getId();
  305.                 if (in_array($themeId$themeidstrue)) {
  306.                     continue;
  307.                 }
  308.                 $themeids[] = $themeId;
  309.             }
  310.         }
  311.         $onlyHolidaythemes->addConditionParam("o_id in (select o_parentId from object_$productId where holidayThemes like '%".implode(','$themeids)."%')");
  312.         $onlyHolidaythemes->addConditionParam('needAcco is null or needAcco = 0');
  313.         $onlyHolidaythemes->addConditionParam("o_id in (select o_id from object_collection_demiDate_$serviceId where object_collection_demiDate_$serviceId.from > {$today->getTimestamp()} or object_collection_demiDate_$serviceId.to > {$today->getTimestamp()})");
  314.         $onlyHolidaythemes->addConditionParam('o_id != '$object->getId());
  315.         $onlyHolidaythemes->addConditionParam("o_parentId in (select o_id from object_$providerId where o_published = 1 )");
  316.         $onlyHolidaythemes->addConditionParam("o_id in (select o_parentid from object_$productId where packageOnly is null or packageOnly = 0)");
  317.         $onlyHolidaythemes->setOrderKey('order');
  318.         $onlyHolidaythemes $this->additionalServiceService->modifyListingForOverview($onlyHolidaythemes);
  319.         $onlyHolidaythemes->setLimit(4);
  320.         $relatedItems $onlyHolidaythemes->load();
  321.         if (count($relatedItems) < 4) {
  322.             $townid $serviceProvider->getTown() ? $serviceProvider->getTown()->getId() : null;
  323.             $relatedServices = new DemiAdditionalService\Listing();
  324.             $relatedServices->addConditionParam('needAcco is null or needAcco = 0');
  325.             $relatedServices->addConditionParam("o_id in (select o_id from object_collection_demiDate_$serviceId where object_collection_demiDate_$serviceId.from > {$today->getTimestamp()} or object_collection_demiDate_$serviceId.to > {$today->getTimestamp()})");
  326.             $relatedServices->addConditionParam('o_id != '$object->getId());
  327.             if ($townid) {
  328.                 $relatedServices->addConditionParam("o_parentId in (select o_id from object_$providerId where town__id in ($townid) and o_published = 1 )");
  329.             } else {
  330.                 $relatedServices->addConditionParam("o_parentId in (select o_id from object_$providerId where o_published = 1 )");
  331.             }
  332.             if ($onlyHolidaythemes->count() > 0) {
  333.                 $alreadyLoadedIds = [];
  334.                 foreach ($relatedItems as $onlyHolidaytheme) {
  335.                     $alreadyLoadedIds[] = $onlyHolidaytheme->getId();
  336.                 }
  337.                 $relatedServices->addConditionParam('o_id not in (' implode(', '$alreadyLoadedIds). ')');
  338.             }
  339.             $relatedServices->addConditionParam("o_id in (select o_parentid from object_$productId where packageOnly is null or packageOnly = 0)");
  340.             $relatedServices->setOrderKey('order');
  341.             $relatedServices $this->additionalServiceService->modifyListingForOverview($relatedServices);
  342.             $relatedServices->setLimit(count($relatedItems));
  343.             $relatedItems array_merge($relatedItems$relatedServices->load());
  344.         }
  345.         $objectAddress $serviceProvider->getAddress([AddressInterface::ADDRESS_OBJECT]);
  346.         if ($objectAddress &&
  347.             ($this->configuration->getAdditionalServiceShowAddressObjectName()
  348.                 || $this->configuration->getAdditionalServiceShowAddressObjectAddress()
  349.                 || $this->configuration->getAdditionalServiceShowAddressObjectEmail()
  350.                 || $this->configuration->getAdditionalServiceShowAddressObjectPhone()
  351.                 || $this->configuration->getAdditionalServiceShowAddressObjectWebsite())
  352.         ) {
  353.             $viewParams["addressObject"] = $objectAddress;
  354.         }
  355.         $ownerAddress $serviceProvider->getAddress([AddressInterface::ADDRESS_OWNER]);
  356.         if ($ownerAddress &&
  357.             ($this->configuration->getAdditionalServiceShowAddressOwnerName()
  358.                 || $this->configuration->getAdditionalServiceShowAddressOwnerAddress()
  359.                 || $this->configuration->getAdditionalServiceShowAddressOwnerEmail()
  360.                 || $this->configuration->getAdditionalServiceShowAddressOwnerPhone()
  361.                 || $this->configuration->getAdditionalServiceShowAddressOwnerWebsite())
  362.         ) {
  363.             $viewParams["addressOwner"] = $ownerAddress;
  364.         }
  365.         // Facilities
  366.         $facilities $object->getFacilities();
  367.         $sortedHealthMeasures = [];
  368.         if ($facilities !== null) {
  369.             foreach ($facilities as $item) {
  370.                 $facility $item->getFacility();
  371.                 if ($facility) {
  372.                     $facGroup $facility->getFacilityGroup();
  373.                     if ($facGroup && $facGroup->getFacilityGroupType() == FacilityGroupTypeInterface::FACILITY_GROUP_TYPE_HEALTH) {
  374.                         if (!isset($sortedHealthMeasures[$facGroup->getId()])) {
  375.                             $data = [
  376.                                 'name' => $facGroup->getName(),
  377.                                 'keyname' => $facGroup->getDemiIconClass(),
  378.                                 'facilities' => []
  379.                             ];
  380.                             $sortedHealthMeasures[$facGroup->getId()] = $data;
  381.                         }
  382.                         $facName $facility->getName();
  383.                         $sortedHealthMeasures[$facGroup->getId()]['facilities'][] = $facName;
  384.                     }
  385.                 }
  386.             }
  387.         }
  388.         $viewParams["sortedHealthMeasures"] = $sortedHealthMeasures;
  389.         $viewParams["connectedEntries"] = $this->connectedEntryProvider->getConnectedEntriesForAdditional($object$request);
  390.         $viewParams["offeritem"] = $object;
  391.         $viewParams["loadAdditionalServiceJs"] = true;
  392.         $viewParams["relatedItems"] = $relatedItems;
  393.         return $this->renderTemplate('@ElementsDemiFrontend/AdditionalService/detail.html.twig'$viewParams);
  394.     }
  395.     public static function sortAdditionalServiceProducts(AdditionalProduct $aAdditionalProduct $b): int
  396.     {
  397.         return (int) $a->getOrder() - (int) $b->getOrder();
  398.     }
  399.     public function productsAction(Request $request): Response
  400.     {
  401.         $serviceId $request->get('id');
  402.         $additionalService AbstractObject::getById((int) $serviceId);
  403.         /*
  404.         $dateFrom = $request->get('from') ? $this->elementsCustomDateFormat->stringToCarbon($request->get('from')) : new Carbon();
  405.         $dateTo = $request->get('to') ? $this->elementsCustomDateFormat->stringToCarbon($request->get('to')) : new Carbon();
  406. */
  407.         if($request->get('date')){
  408.             $date $this->elementsCustomDateFormat->stringToCarbon($request->get('date'));
  409.         } else if($request->get('earliestServiceDate')){
  410.             $date $this->elementsCustomDateFormat->stringToCarbon($request->get('earliestServiceDate'));
  411.         } else {
  412.             $date = new Carbon();
  413.         }
  414.         $date $request->get('date') ? $this->elementsCustomDateFormat->stringToCarbon($request->get('date')) : new Carbon();
  415.         $dateFrom=$date;
  416.         $dateTo= clone $dateFrom;
  417.         if($additionalService !== null){
  418.             $first true;
  419.             foreach($additionalService->getDates() as $availableDate){
  420.                 if($availableDate->getTo()->gt($date) && $date->lt($availableDate->getFrom())){
  421.                     //set date to first possibly available date
  422.                     if($first || $availableDate->getFrom()->lt($dateFrom)){
  423.                         $dateFrom $availableDate->getFrom();
  424.                     }
  425.                 }
  426.                 if ($availableDate->getTo()->gt($dateTo)) {
  427.                     $dateTo $availableDate->getTo();
  428.                 }
  429.                 $first false;
  430.             }
  431.         }
  432.         if(empty($request->get('date'))){
  433.            //no date given, use now + 1 year
  434.             $dateTo->addYear();
  435.         }
  436.         $period $dateFrom->diffInDays($dateTo) + 1;
  437.         if ($period 999) {
  438.             $period 365;
  439.             $dateTo $dateFrom->clone()->addYear();
  440.         }
  441.         $viewParams = [];
  442.         $products = [];
  443.         $allCalendarProducts = [];
  444.         if ($additionalService !== null) {
  445.             if ($additionalService && $additionalService->getParent() && $additionalService->getParent() instanceof AdditionalServiceProvider) {
  446.                 Config::setClientKeyOverrideFromObject($additionalService->getParent());
  447.             }
  448.             $productsAdditionalService $additionalService->getAdditionalProducts();
  449.             usort($productsAdditionalService, [__CLASS__'sortAdditionalServiceProducts']);
  450.             $additionalServicesList $this->additionalServiceListingLive;
  451.             $additionalServicesList->setAdapter($this->adapter);
  452.             foreach ($productsAdditionalService as $product) {
  453.                 $paramLines = new Line();
  454.                 $today = new Carbon();
  455.                 $aYearLater = (clone  $today)->addYear();
  456.                 $paramLines->setDateFrom($today);
  457.                 $paramLines->setDateTo($aYearLater);
  458.                 $paramLines->setBookOnly(false);
  459.                 $period $today->diffInDays($aYearLater) + 1;
  460.                 $paramLines->setPeriod($period);
  461.                 $paramLines->setProductId($product->getId());
  462.                 $params = new Parameter();
  463.                 $params->setSearchLines([$paramLines]);
  464.                 $params->setPage(1);
  465.                 $params->setPerPage(100);
  466.                 $additionalServicesList->setParameter($params);
  467.                 $allCalendarProducts += $additionalServicesList->getItems(0100);
  468.                 $additionalServicesList->flushObjects();
  469.             }
  470.             $availableDates = [];
  471.             $customDateFormat = new ElementsCustomDateFormat();
  472.             $firstAvailableDate null;
  473.             foreach($allCalendarProducts as $productId => $product){
  474.                 foreach($product->getPrices() as $price){
  475.                     if($price->getAvailability() > 0){
  476.                         $availableDates[$price->getStartDay()->getTimestamp()]=$customDateFormat->dateToString($price->getStartDay());
  477.                         if($firstAvailableDate === null || $price->getStartDay()->getTimestamp() < $firstAvailableDate->getTimestamp()){
  478.                             $firstAvailableDate = clone $price->getStartDay();
  479.                         }
  480.                     }
  481.                 }
  482.             }
  483.             ksort($availableDates);
  484.             $availableDates array_unique($availableDates);
  485.             $firstAvailableDate count($availableDates)>$customDateFormat->stringToCarbon(reset($availableDates)) : null;
  486.             // some sanity checks
  487.             if ($dateTo->lt($firstAvailableDate)) {
  488.                 $dateTo $firstAvailableDate;
  489.             }
  490.             if ($date->lt($firstAvailableDate)) {
  491.                 $date $firstAvailableDate;
  492.             }
  493.             // get products after the from and to sanity checks
  494.             foreach ($productsAdditionalService as $product) {
  495.                 $paramLines = new Line();
  496.                 $paramLines->setDateFrom($dateFrom);
  497.                 $paramLines->setDateTo($dateTo);
  498.                 $paramLines->setPeriod($period);
  499.                 $paramLines->setProductId($product->getId());
  500.                 $paramLines->setBookOnly(false);
  501.                 $params = new Parameter();
  502.                 $params->setBookOnly(false);
  503.                 $params->setSearchLines([$paramLines]);
  504.                 $params->setPage(1);
  505.                 $params->setPerPage(100);
  506.                 $additionalServicesList->setParameter($params);
  507.                 $products += $additionalServicesList->getItems(0100);
  508.                 $additionalServicesList->flushObjects();
  509.             }
  510.             $viewParams["products"] = $products;
  511.             $viewParams["from"] = $firstAvailableDate ?: $dateFrom;
  512.             $viewParams["to"] = $dateTo;
  513.             $viewParams["date"] = $request->get('date') ? $date $firstAvailableDate;
  514.             $viewParams["service"] = $additionalService;
  515.             //if ($request->headers->get('format') === 'json') {
  516.                 $viewParams["isNew"] = true;
  517.                 $viewParams["isAdditionalService"] = true;
  518.                 $viewParams["styleModifier"] = 'package-group--additionalservice';
  519.                 $viewParams["availableDates"] = array_values($availableDates);
  520.                 $response = [
  521.                     'status' => 'success',
  522.                     'html' => $this->renderTemplate('@ElementsDemiFrontend/AdditionalService/products.html.twig'$viewParams)->getContent()
  523.                 ];
  524.                 return $this->json($response);
  525.             //}
  526.         }
  527.         return $this->renderTemplate('@ElementsDemiFrontend/AdditionalService/products.html.twig'$viewParams);
  528.     }
  529.     public function productsAjaxAction(Request $request): ?JsonResponse
  530.     {
  531.         $reqProd $request->get('products');
  532.         $prodid key($reqProd);
  533.         $dateFrom $request->get('from') ? $this->elementsCustomDateFormat->stringToCarbon($request->get('from')) : new Carbon();
  534.         $dateTo $request->get('to') ? $this->elementsCustomDateFormat->stringToCarbon($request->get('to')) : new Carbon();
  535.         $serviceId $request->get('id');
  536.         $dateDiff $dateFrom->diff($dateTo);
  537.         $period = (int) $dateDiff->format('%R%a days') + 1;
  538.         $products = [];
  539.         if ($serviceId && AbstractObject::getById((int) $serviceId)) {
  540.             $additionalService AbstractObject::getById((int) $serviceId);
  541.             if ($additionalService && $additionalService->getParent() && $additionalService->getParent() instanceof AdditionalServiceProvider) {
  542.                 Config::setClientKeyOverrideFromObject($additionalService->getParent());
  543.             }
  544.             $productsAdditionalService $additionalService->getAdditionalProducts();
  545.             usort($productsAdditionalService, [__CLASS__'sortAdditionalServiceProducts']);
  546.             $additionalServicesList $this->additionalServiceListingLive;
  547.             $additionalServicesList->setAdapter($this->adapter);
  548.             foreach ($productsAdditionalService as $product) {
  549.                 if ($product->getId() !== $prodid) {
  550.                     continue;
  551.                 }
  552.                 $paramLines = new Line();
  553.                 $paramLines->setDateFrom($dateFrom);
  554.                 $paramLines->setDateTo($dateTo);
  555.                 $paramLines->setPeriod($period);
  556.                 $paramLines->setProductId($product->getId());
  557.                 $paramLines->setBookOnly(false);
  558.                 $params = new Parameter();
  559.                 $params->setBookOnly(false);
  560.                 $params->setSearchLines([$paramLines]);
  561.                 $params->setPage(1);
  562.                 $params->setPerPage(100);
  563.                 $additionalServicesList->setParameter($params);
  564.                 $products += $additionalServicesList->getItems(0100);
  565.             }
  566.             $viewParams["startdate"] = $reqProd[$prodid]['date'];
  567.             $viewParams["products"] = $products;
  568.             $viewParams["from"] = $dateFrom;
  569.             $viewParams["to"] = $dateTo;
  570.             $viewParams["service"] = $additionalService;
  571.             if ($request->headers->get('format') === 'json') {
  572.                 $response = [
  573.                     'success' => true,
  574.                     'content' => $this->renderTemplate('@ElementsDemiFrontend/AdditionalService/productsAjax.html.twig'$viewParams)->getContent()
  575.                 ];
  576.                 return $this->json($response);
  577.             }
  578.         }
  579.         return null;
  580.     }
  581.     public function addAdditionalItemAction(Request $request): Response
  582.     {
  583.         $items = [];
  584.         Factory::getInstance()->getEnvironment()->removeCustomItem('demi_cartName');
  585.         $productId $request->get('offerId');
  586.         $product AbstractObject::getById($productId);
  587.         $count $request->get('count');
  588.         $cart $this->ecommerceHelper->getCart();
  589.         if ($product instanceof AdditionalProduct) {
  590.             $cartItemInfo $this->cartItemInfoService->fillAdditionalServiceProductFromRequest(new CartItemInfoHolder(), $request);
  591.             $cartItemInfo->setAdditionalInfo(['relatedProduct' => $request->get('relatedProduct'), 'ignoreInCartTemplate' => true]);
  592.             $cancelationService = new CancellationInformation();
  593.             $cancelationInfo $cancelationService->getCancellationInformation($product->getParent()->getParent(),
  594.                 $product,
  595.                 $product->getParent()->getParent()->getDbCode(),
  596.                 Config::getInstance()->getSalesChannel(),
  597.                 $cartItemInfo->getDateFrom(),
  598.                 $cartItemInfo->getDateTo(),
  599.                 $cartItemInfo->getAdultAmount(),
  600.                 $cartItemInfo->getChildrenAges(),
  601.                 $cartItemInfo->getDurationValue());
  602.             $cartItemInfo->setCancellationInformation($cancelationInfo);
  603.             $this->ecommerceHelper->addProductToCart($count$product$cartItemInfo$cart);
  604.             $this->trackingManager->trackCartProductActionAdd($cart$product$count);
  605.             $items[] = ['persons' => $count'product' => $product'price' => $this->paymentHelper->getPriceObject($cartItemInfo->getPrice()), 'count' => $count];
  606.         } else {
  607.             foreach ($request->get('products') as $productId => $productData) {
  608.                 $selectedDay $productData['days'];
  609.                 $selectedDay explode(';'$selectedDay);
  610.                 $price $selectedDay[2];
  611.                 $duration $selectedDay[0];
  612.                 $request->request->set('additionalInformation'$productData);
  613.                 if (!$productData['count'] || !$productData['date'] || !$price) {
  614.                     throw new InvalidArgumentException('some parameters are missing. cannot add product to cart');
  615.                 }
  616.                 $request->request->set('count'$productData['count']);
  617.                 $request->request->set('offerId'$productId);
  618.                 $request->request->set('date'$productData['date']);
  619.                 $request->request->set('price'$price);
  620.                 $request->request->set('totalPrice'$price);
  621.                 $request->request->set('settlerCode'$productData['settlerCode']);
  622.                 $request->request->set('durationType'$productData['durationType']);
  623.                 $request->request->set('durationValue'$duration);
  624.                 $cartItemInfo $this->cartItemInfoService->fillAdditionalServiceProductFromRequest(new CartItemInfoHolder(), $request);
  625.                 $cartItemInfo->setAdditionalInfo(['relatedProduct' => $request->get('relatedProduct')]);
  626.                 $additionalInfo $productData;
  627.                 unset($additionalInfo['date'], $additionalInfo['count'], $additionalInfo['settlerCode'],
  628.                     $additionalInfo['price'], $additionalInfo['totalPrice'], $additionalInfo['durationValue'],
  629.                     $additionalInfo['durationType'], $additionalInfo['days']);
  630.                 $count $request->get('count');
  631.                 if (!empty($additionalInfo)) {
  632.                     //process if any arrays be here
  633.                     foreach ($additionalInfo as $key => $info) {
  634.                         foreach ($info as $k => $v) {
  635.                             if (is_array($v)) {
  636.                                 $additionalInfo[$key][$k] = implode('<br>'$v);
  637.                             }
  638.                         }
  639.                     }
  640.                     $cartItemInfo->setAdditionalInfo($additionalInfo);
  641.                     $persons max(array_keys($additionalInfo));
  642.                     if ($persons <= 0) {
  643.                         $persons $count;
  644.                     }
  645.                     $cartItemInfo->setAdultAmount($persons);
  646.                 }
  647.                 $cancelationService = new CancellationInformation();
  648.                 $cancelationInfo $cancelationService->getCancellationInformation($product->getParent()->getParent(),
  649.                     $product,
  650.                     $product->getParent()->getParent()->getDbCode(),
  651.                     Config::getInstance()->getSalesChannel(),
  652.                     $cartItemInfo->getDateFrom(),
  653.                     $cartItemInfo->getDateTo(),
  654.                     $cartItemInfo->getAdultAmount(),
  655.                     $cartItemInfo->getChildrenAges(),
  656.                     $cartItemInfo->getDurationValue());
  657.                 $cartItemInfo->setCancellationInformation($cancelationInfo);
  658.                 $this->ecommerceHelper->addProductToCart($count$product$cartItemInfo$cart);
  659.                 $items[] = ['persons' => $persons'product' => $product'price' => $this->paymentHelper->getPriceObject($cartItemInfo->getPrice()), 'count' => $count];
  660.             }
  661.         }
  662.         $response = [
  663.             'status' => 'success',
  664.             'html' => $this->renderTemplate('@ElementsDemiFrontend/AdditionalService/package-add-cart.html.twig', [
  665.                 'totalCount' => $cart->getItemAmount(),
  666.                 'cartItems' => $items
  667.             ])->getContent()
  668.         ];
  669.         return $this->json($response);
  670.     }
  671.     public function productDetailAction(Request $request): Response
  672.     {
  673.         $productId $request->get('id');
  674.         $price $request->get('price');
  675.         $viewParams["showPrice"] = true;
  676.         $product AdditionalProduct::getById($productId);
  677.         $additionalService $product->getParent();
  678.         if ($additionalService && $additionalService->getParent() && $additionalService->getParent() instanceof AdditionalServiceProvider) {
  679.             Config::setClientKeyOverrideFromObject($additionalService->getParent());
  680.         }
  681.         if($request->get('date')){
  682.             $dateFrom $this->elementsCustomDateFormat->stringToCarbon($request->get('date'));
  683.             $dateTo = clone  $dateFrom;
  684.         } else {
  685.             //legacy?
  686.             $dateFrom $request->get('from') ? $this->elementsCustomDateFormat->stringToCarbon($request->get('from')) : new Carbon();
  687.             $dateTo $request->get('to') ? $this->elementsCustomDateFormat->stringToCarbon($request->get('to')) : (new Carbon())->addMonth();
  688.         }
  689.         $period $dateFrom->diffInDays($dateTo) + 1;
  690.         $paramLines = new Line();
  691.         $paramLines->setDateFrom($dateFrom);
  692.         $paramLines->setDateTo($dateTo);
  693.         $paramLines->setPeriod($period);
  694.         $paramLines->setProductId($product->getId());
  695.         $paramLines->setBookOnly(false);
  696.         $params = new Parameter();
  697.         $params->setBookOnly(false);
  698.         $params->setSearchLines([$paramLines]);
  699.         $params->setPage(1);
  700.         $params->setPerPage(100);
  701.         $this->additionalServiceListingLocal->setParameter($params);
  702.         $this->additionalServiceListingLocal->load();
  703.         $paramLines->setUnits(1);
  704.         $params->setSearchLines([$paramLines]);
  705.         $this->additionalServiceListingLive->setParameter($params);
  706.         $this->additionalServiceListingLive->load();
  707.         $result $this->additionalServiceListingLive->getItems(1100);
  708.         $bookable false;
  709.         if($result) {
  710.             foreach ($result as $item) {
  711.                 $prices $item->getPrices();
  712.                 if (reset($prices)->getBookable()) {
  713.                     $bookable true;
  714.                     break;
  715.                 }
  716.             }
  717.         }
  718.         $viewParams["id"] = $productId;
  719.         $viewParams["price"] = $price;
  720.         $viewParams["arrivalDate"] = $dateFrom;
  721.         $viewParams["departureDate"] = $dateTo;
  722.         foreach ($product->getAdditionalService()->getDescriptions() as $description) {
  723.             $descriptionTypes[] = $description->getDescriptionType();
  724.             if ($description->getDescriptionType() !== DescriptionInterface::DESCRIPTION_SERVICE_DESCRIPTION) {
  725.                 $tosc5texts[$description->getDescriptionType()] = $description;
  726.             }
  727.         }
  728.         $viewParams["productText"] = $product->getDescription();
  729.         $viewParams["tosc5texts"] = $tosc5texts ?? [];
  730.         $viewParams["text"] = $product->getAdditionalService()->getDescription();
  731.         $viewParams["title"] = $product->getName();
  732.         $viewParams["additionalServiceItem"] = $this->additionalServiceListingLive;
  733.         $viewParams["additionalServiceItems"] = $this->additionalServiceListingLocal;
  734.         $viewParams["isPackage"] = !empty($request->get('packages'));
  735.         $viewParams["searchParam"] = $params;
  736.         $viewParams["maxSelect"] = 20;
  737.         if(!$bookable){
  738.             return $this->renderTemplate('@ElementsDemiFrontend/AdditionalService/includes/offerDetailEnquiry.desktop.html.twig'$viewParams);
  739.         } else {
  740.             return $this->renderTemplate('@ElementsDemiFrontend/AdditionalService/includes/offerDetailContent.desktop.html.twig'$viewParams);
  741.         }
  742.     }
  743.     protected function findEditablePrefix(Document $document): string
  744.     {
  745.         if ($document->getEditables() !== null) {
  746.             $areaBlock current(array_filter($document->getEditables(), static function ($editable) {
  747.                 return $editable instanceof Areablock;
  748.             }));
  749.             if ($areaBlock !== false && !empty($areaBlock->getIndices())) {
  750.                 $correctIndex array_filter($areaBlock->getIndices(), static function ($index) {
  751.                     return $index['type'] === 'demi-additionalservice-list';
  752.                 });
  753.                 return $areaBlock->getName() . ':' current($correctIndex)['key'] . '.';
  754.             }
  755.         }
  756.         // Fallback for overriden documents
  757.         if ($document->getContentMasterDocument() !== null) {
  758.             return $this->findEditablePrefix($document->getContentMasterDocument());
  759.         }
  760.         return '';
  761.     }
  762.     private function sendEnquire(Request $request\Elements\Demi\Model\AdditionalService $additionalServiceUserInterface $user null): bool
  763.     {
  764.         if ($this->configuration->getDisableEnquiry()) {
  765.             return false// Just return since detailEnquireAction is called from detailAction
  766.         }
  767.         // date
  768.         $fromDate $this->elementsCustomDateFormat->stringToCarbon($request->get('date'));
  769.         $request->query->set('from'ElementsCustomDateFormat::toString($fromDate));
  770.         $request->query->set('to'ElementsCustomDateFormat::toString($fromDate));
  771.         if (!empty($this->configuration->getPrivacyPolicyEnquiry())) {
  772.             $ppAccepted = (bool)$request->get('pp');
  773.         } else {
  774.             $ppAccepted true;
  775.         }
  776.         $request->query->set('accoIds', [$additionalService->getParent()->getId()]);
  777.         $request->query->set('pp'$ppAccepted);
  778.         $request->query->set('price'null);
  779.         $request->query->set('u0'$request->get('count') ?? );
  780.         $productIds $request->get('productId');
  781.         $time $request->get('time') ? $this->elementsCustomDateFormat->stringToCarbon($request->get('time')) : '';
  782.         if($time instanceof Carbon){
  783.             $time $time->format('H:i');
  784.         }
  785.         $products=[['id'=>$productIds'units' =>$request->get('count'), 'time' => $time]];
  786.         $request->query->set('comment'$this->getCommentForDSIEnquiry($request$products));
  787.         if($additionalService->getParent()->getTown()){
  788.             $request->query->set('towns', [$additionalService->getParent()->getTown()->getId()]);
  789.         }
  790.         $mailSent $this->savingRequestController->serviceProviderRequestAction($request$user);
  791.         return $mailSent;
  792.     }
  793.     /**
  794.      * @param Request $request
  795.      * @param $package
  796.      * @param $products
  797.      * @param $nights
  798.      *
  799.      * @return mixed|string
  800.      */
  801.     private function getCommentForDSIEnquiry(Request $request,  array $products): mixed
  802.     {
  803.         $comment $request->get('comment');
  804.         foreach ($products as $productInfo) {
  805.             $product \Elements\Demi\AbstractObject::getById($productInfo['id']);
  806.             $units $productInfo['units'];
  807.             $time = isset($productInfo['time']) ? $productInfo['time'] : '';
  808.             if ($product) {
  809.                 $prodStr '';
  810.                 if ($units) {
  811.                     $prodStr .= $units 'x ';
  812.                 }
  813.                 $prodStr .= $product->getName();
  814.                 $comment .= " "$prodStr;
  815.                 if(!empty($time)){
  816.                     $comment .= " ".$time;
  817.                 }
  818.             }
  819.         }
  820.         if ($request->get('tel')) {
  821.             $comment .= ', Tel.: '.$request->get('tel');
  822.         }
  823.         return $comment;
  824.     }
  825. }