<?php
/**
* Created by Elements.at New Media Solutions GmbH
*
*/
namespace App\Service\Shop;
use App\Ecommerce\AvailabilitySystem\Event\AvailabilitySystem;
use App\Model\Shop\Event\EventProduct;
use App\Model\Shop\Event\ShopDynamicPrice;
use App\Model\Shop\Ticket\ShopTicketCatalog;
use App\Model\Shop\Ticket\TicketConsumerCategory;
use App\Service\TicketShopFrameworkBundle\TicketFilter;
use App\Twig\LinkGeneratorExtension;
use Carbon\Carbon;
use Elements\Bundle\RecurringDatesTypeBundle\Templating\RecurringDatesHelper;
use Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketCatalog;
use Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketConsumerCategory as TSFTicketConsumerCategory;
use Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketProduct;
use Elements\Bundle\TicketShopFrameworkBundle\Model\Shop\Ticketing\TicketCatalogAvailability;
use Elements\Bundle\TicketShopFrameworkBundle\Model\Shop\Ticketing\TicketProductAvailability;
use Pimcore\Cache\RuntimeCache;
use Pimcore\Model\DataObject\AbstractObject;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Contracts\Translation\TranslatorInterface;
class JsonDataService
{
private ?Carbon $startDate;
public function __construct(
private ShopService $shopService,
private TranslatorInterface $translator,
private LinkGeneratorExtension $linkGenerator,
protected SmartPricerService $smartPricerService,
private RecurringDatesHelper $datesHelper
) {
}
public function getStartDate(): ?Carbon
{
return $this->startDate;
}
public function setStartDate(Carbon $startDate): void
{
$this->startDate = $startDate;
}
private ?Carbon $endDate;
public function getEndDate(): ?Carbon
{
return $this->endDate;
}
public function setEndDate(Carbon $endDate): void
{
$this->endDate = $endDate;
}
//--------TICKETS--------
/**
* @param TicketCatalogAvailability $ticketCatalogAvailability
* @param array<mixed> $insurances
* @param array<mixed> $originalConsumerGroups
* @param TicketProduct|null $ticket
* @param array<mixed> $upgradeIds
*
* @return array<mixed>
*/
private function getCatalogAvailabilityConfig(
TicketCatalogAvailability $ticketCatalogAvailability,
array $insurances,
array $originalConsumerGroups,
TicketProduct $ticket = null,
array $upgradeIds = []
): array {
$configuration = [];
$upgrades = [];
$catalog = $ticketCatalogAvailability->getTicketCatalog();
if ($upgradeIds) {
foreach ($upgradeIds as $upgradeId) {
$upgrades[] = (string)$upgradeId;
}
}
$isUpgrade = !empty($ticket);
foreach ($ticketCatalogAvailability->getTicketAvailability() as $ticketAvailability) {
$originalTicket = $ticketAvailability->getTicketProduct();
if (!$isUpgrade) {
$ticket = $originalTicket;
} else {
/** @var \App\Model\Shop\Ticket\TicketProduct $ticket */
$metaProduct = $ticket->getMetaProduct();
if ($metaProduct && !in_array($originalTicket, $metaProduct->getRelatedTo(), true)) {
continue;
}
}
if (empty($upgradeIds)) {
if ($upgradeConfig = $this->getUpgradeConfigurations($catalog, $ticket, $originalConsumerGroups)) {
$configuration = array_merge($configuration, $upgradeConfig);
}
}
$consumerMapping = $this->getConsumerMapping($ticketAvailability, $originalConsumerGroups);
$flipped = array_flip($consumerMapping);
if ($insurances) {
foreach ($insurances[$originalTicket->getId()] as $insurance) {
$priceByPriceGroup = [];
foreach ($ticketAvailability->getSortedConsumerAvailabilities() as $consumerAvailability) {
$product = $consumerAvailability->getTicketProductAvailability()->getTicketProduct();
$consumer = $consumerAvailability->getTicketConsumer();
$insurancePriceInfo = $insurance->getOSPriceInfo(
1,
[],
$ticketAvailability->getValidFrom(),
$consumerAvailability->getTicketConsumer(),
catalog: $catalog
);
$insurancePrice = $insurancePriceInfo->getTotalPrice()->getGrossAmount()->asNumeric();
$priceInfo = $product->getOSPriceInfo(
1,
[],
$ticketAvailability->getValidFrom(),
$consumerAvailability->getTicketConsumer(),
catalog: $catalog
);
if(isset($flipped[$consumer->getId()])) {
$priceData = [
'id' => (string)$flipped[$consumer->getId()],
'price' => $priceInfo?->getTotalPrice()->getGrossAmount()->add($insurancePrice)->asNumeric(),
'priceGroupWarning' => (string)$consumer->getInfoText(),
];
$this->setPriceData($priceData, $priceInfo);
if ($priceInfo?->getPriceGroup() instanceof TicketConsumerCategory &&
$priceInfo->getPriceGroup()->getOriginalPriceConsumerCategory() instanceof TicketConsumerCategory
) {
// need to be done for trade platform otherwise strike price adds commission value
RuntimeCache::save(true, 'disabledCommissionCalc');
$strikePriceInfo = $product->getOSPriceInfo(
1,
[],
$ticketAvailability->getValidFrom(),
$priceInfo->getPriceGroup()->getOriginalPriceConsumerCategory(),
$catalog
);
if ($priceInfo->getTotalPrice()->getGrossAmount()->lessThan(
$strikePriceInfo->getTotalPrice()->getGrossAmount())
) {
$priceData['priceBeforeDiscount'] = $strikePriceInfo?->getTotalPrice()->getGrossAmount()->add($insurancePrice)->asNumeric();
}
RuntimeCache::save(false, 'disabledCommissionCalc');
}
$this->setChildSaturdayInfo($priceData, $consumer, $ticketAvailability);
$priceByPriceGroup[] = $priceData;
}
}
$configuration[] = [
'option' => (string)$ticket->getId(),
'upgradeOption' => $originalTicket->getId(),
'upgradeCatalog' => $catalog->getId(),
'priceGroupMap' => $consumerMapping,
'upgrades' => $upgrades,
'insurance' => (string)$insurance->getId(),
'priceByPriceGroup' => $priceByPriceGroup,
];
}
}
//always add "no_insurance" option
$configuration[] = [
'option' => (string)$ticket->getId(),
'upgradeOption' => $originalTicket->getId(),
'upgradeCatalog' => $catalog->getId(),
'priceGroupMap' => $consumerMapping,
'upgrades' => $upgrades,
'insurance' => 'no_insurance',
'priceByPriceGroup' => $this->getNoInsuranceConfiguration($ticketAvailability, $originalConsumerGroups, $catalog),
];
}
return $configuration;
}
/**
* @param TicketCatalogAvailability $ticketCatalogAvailability
* @param array<mixed> $insurances
* @param array<mixed> $originalConsumerGroups
*
* @return array<mixed>
*/
public function getPriceForCatalogAvailability(TicketCatalogAvailability $ticketCatalogAvailability, array $insurances, array $originalConsumerGroups, Carbon $startDate, Carbon $endDate): array
{
$this->smartPricerService->setPriceMatrix($ticketCatalogAvailability, $insurances, $originalConsumerGroups, $startDate, $endDate);
$this->smartPricerService->setLivePrices($this->smartPricerService->cat1Array, $this->smartPricerService->cat3Array, $ticketCatalogAvailability->getStartDate(), $ticketCatalogAvailability->getEndDate());
return $this->getCatalogAvailabilityConfig($ticketCatalogAvailability, $insurances, $originalConsumerGroups);
}
/**
* @param array<mixed> $priceData
* @param TicketConsumerCategory|TSFTicketConsumerCategory $consumer
* @param TicketProductAvailability $availability
*
* @return void
*/
private function setChildSaturdayInfo(
array &$priceData,
TicketConsumerCategory|TSFTicketConsumerCategory $consumer,
TicketProductAvailability $availability
): void {
$catalogAvailability = $availability->getTicketCatalogAvailability();
/** @var \App\Model\Shop\Ticket\TicketProduct $ticket */
$ticket = $availability->getTicketProduct();
/** @var ShopTicketCatalog $catalog */
$catalog = $catalogAvailability->getTicketCatalog();
/** @phpstan-ignore-next-line */
if ($consumer->useSaturdaySpecial($ticket, $catalogAvailability->getStartDate(), $catalog)) {
$priceData['priceGroupInfo'] = $this->translator->trans('shop.ticket.child.saturday.free');
}
}
/**
* @param TicketCatalog $catalog
* @param TicketProduct $ticket
* @param array<mixed> $originalConsumerGroups
*
* @return array<mixed>
*/
private function getUpgradeConfigurations(TicketCatalog $catalog, TicketProduct $ticket, array $originalConsumerGroups): array
{
$configuration = [];
$combinationIds = [];
foreach ($catalog->getUpgrades() as $upgrade) {
/** @var TicketCatalog $upgradeCatalog */
$upgradeCatalog = $upgrade->getObject();
$upgradeIds = [];
if (str_contains($upgrade->getData()['name'], ',')) {
$upgradeIds = $combinationIds;
} else {
$upgradeIds[] = $upgradeCatalog->getId();
$combinationIds[] = $upgradeCatalog->getId();
}
$upgradeFilter = new TicketFilter($this->startDate, $this->endDate);
$upgradeFilter->setExactDuration(true);
$upgradeFilter->setIgnorePrice(true);
$allowedConsumers = [];
foreach ($upgradeCatalog->getTicketConsumerCategories() as $allowedConsumer) {
$allowedConsumers[] = $allowedConsumer->getId();
}
if ($allowedConsumers) {
$upgradeFilter->setConsumers($allowedConsumers);
}
$catalogAvailability = $upgradeFilter->getTicketCatalogAvailability($upgradeCatalog, true);
$insuranceData = $this->shopService->getInsurancesForProduct($catalogAvailability);
$insurances = $insuranceData['insurances'] ?? [];
if ($catalogAvailability->hasAvailability() && !$upgradeCatalog->getIsNotBookable()) {
$configuration = array_merge(
$configuration,
$this->getCatalogAvailabilityConfig(
$catalogAvailability,
$insurances,
$originalConsumerGroups,
$ticket,
$upgradeIds
)
);
}
}
return $configuration;
}
/**
* Gets the data for one ticket in a specific catalog in a specific date range (for PriceCalculator)
*
* @param ShopTicketCatalog $originalCatalog
* @param TicketProductAvailability $originalTicket
* @param Carbon $startDate
* @param Carbon $endDate
* @param bool $isUpgrade
* @param ShopTicketCatalog|null $upgradeCatalog
* @param TicketProductAvailability|null $upgradeTicket
* @param array<mixed> $upgradeIds
*
* @return array<mixed>
*/
public function getTicketData(ShopTicketCatalog $originalCatalog,
TicketProductAvailability $originalTicket,
Carbon $startDate,
Carbon $endDate,
bool $isUpgrade = false,
?ShopTicketCatalog $upgradeCatalog = null,
?TicketProductAvailability $upgradeTicket = null,
?array $upgradeIds = []
): array {
$output = [];
$originalTicketProduct = $originalTicket->getTicketProduct();
$output['ticketTitle'] = $originalCatalog->getName();
$output['selectedOption'] = strval($originalTicketProduct->getId());
$output['startDate'] = $startDate->toDateTimeLocalString();
$output['endDate'] = $endDate->toDateTimeLocalString();
$output['ticketId'] = strval($originalCatalog->getId());
$output['isAnnualPass'] = $originalCatalog->getIsAnnualCatalog() ?? false;
$output['ticketComposition'] = $upgradeTicket ? $upgradeTicket->getTicketProduct()->getName() : $originalTicketProduct->getName();
if ($originalCatalog->getIsNotBookable()) {
$output['isNotBookable'] = true;
$output['pathToDetailPage'] = $this->linkGenerator->getDetailLink($originalCatalog);
}
if ($isUpgrade) {
$output['selectedUpgrades'] = array_map('strval', $upgradeIds);
}
$output['data'] = [];
$correctAvailabilities = $isUpgrade ? $upgradeTicket->getConsumerAvailabilities() : $originalTicket->getConsumerAvailabilities();
foreach ($correctAvailabilities as $consumerAvailability) {
$output['data'][] = [
'priceGroup' => $consumerAvailability->getTicketConsumer()->getName(),
'price' => $consumerAvailability->getPriceInfo()->getTotalPrice()->getGrossAmount()->asNumeric(),
'priceUnit' => $consumerAvailability->getPriceInfo()->getTotalPrice()->getCurrency()->getShortName(),
];
}
return $output;
}
/**
* Gets an array of all upgrade Options of a specific ticket in a specific catalog in a specific date range (for PriceCalculator)
*
* @param ShopTicketCatalog $catalog
* @param Carbon $startDate
* @param Carbon $endDate
* @param TicketProductAvailability $ticket
* @param array<int> $originalPriceGroupIds
*
* @return array<mixed>
*/
public function getTicketUpgradeData(ShopTicketCatalog $catalog,
Carbon $startDate,
Carbon $endDate,
TicketProductAvailability $ticket,
array $originalPriceGroupIds,
): array {
$output = [];
$combinationIds = []; // e.g. international + shuttle
foreach ($catalog->getUpgrades() as $upgrade) {
/** @var ShopTicketCatalog $upgradeCatalog */
$upgradeCatalog = $upgrade->getObject();
$upgradeIds = [];
if (str_contains($upgrade->getData()['name'], ',')) {
$upgradeIds = $combinationIds;
} else {
$upgradeIds[] = $upgradeCatalog->getId();
$combinationIds[] = $upgradeCatalog->getId();
}
$allowedConsumerIds = $this->shopService->getCorrectAllowedConsumerIds($upgradeCatalog, $originalPriceGroupIds);
$upgradeAvailability = $upgradeCatalog->getAvailabilityForDateRangeWithPriceGroup($allowedConsumerIds, $startDate, $endDate, true);
if (!$upgradeAvailability->hasAvailability()) {
continue;
}
foreach ($upgradeAvailability->getTicketAvailability() as $upgradeTicket) {
// @phpstan-ignore-next-line
$metaProduct = $ticket->getTicketProduct()->getMetaProduct(); // for skipping upgradeTickets which are not similar to the originalTicket
if ($metaProduct && !in_array($upgradeTicket->getTicketProduct(), $metaProduct->getRelatedTo(), true)) {
continue;
}
$output[] = $this->getTicketData($catalog, $ticket, $startDate, $endDate, true, $upgradeCatalog, $upgradeTicket, $upgradeIds);
}
}
return $output;
}
/**
* @param TicketProductAvailability $availability
* @param array<mixed> $originalConsumerGroups
*
* @return array<mixed>
*/
private function getNoInsuranceConfiguration(TicketProductAvailability $availability, array $originalConsumerGroups, ?TicketCatalog $catalog): array
{
$noInsuranceConfiguration = [];
foreach ($availability->getSortedConsumerAvailabilities() as $consumerAvailability) {
$consumer = $consumerAvailability->getTicketConsumer();
$product = $consumerAvailability->getTicketProductAvailability()->getTicketProduct();
//set warning text before mapping
$warningText = (string)$consumer->getInfoText();
$this->mapConsumerGroups($consumer, $originalConsumerGroups, true);
$priceInfo = $product->getOSPriceInfo(
1,
[],
$availability->getValidFrom(),
$consumerAvailability->getTicketConsumer(),
catalog: $catalog
);
$dataArray = [
'id' => (string)$consumer->getId(),
'price' => $priceInfo?->getTotalPrice()->getGrossAmount()->asNumeric(),
'priceGroupWarning' => $warningText,
];
$this->setPriceData($dataArray, $priceInfo);
if ($priceInfo->getPriceGroup() instanceof TicketConsumerCategory &&
$priceInfo->getPriceGroup()->getOriginalPriceConsumerCategory() instanceof TicketConsumerCategory
) {
// need to be done for trade platform otherwise strike price adds commission value
RuntimeCache::save(true, 'disabledCommissionCalc');
$strikePriceInfo = $product->getOSPriceInfo(
1,
[],
$availability->getValidFrom(),
$priceInfo->getPriceGroup()->getOriginalPriceConsumerCategory(),
catalog: $catalog
);
if ($priceInfo->getTotalPrice()->getGrossAmount()->lessThan(
$strikePriceInfo->getTotalPrice()->getGrossAmount())
) {
$dataArray['priceBeforeDiscount'] = $strikePriceInfo?->getTotalPrice()->getGrossAmount()->asNumeric();
}
RuntimeCache::save(false, 'disabledCommissionCalc');
}
$this->setChildSaturdayInfo($dataArray, $consumer, $availability);
$noInsuranceConfiguration[] = $dataArray;
}
return $noInsuranceConfiguration;
}
/**
* @param TSFTicketConsumerCategory $consumer
* @param array<mixed> $originalConsumerGroups
* @param bool $onlyConsumer
* @param array<mixed> $consumerMapping
*
* @return array<mixed>
*/
private function mapConsumerGroups(
TSFTicketConsumerCategory &$consumer,
array $originalConsumerGroups,
bool $onlyConsumer = false,
array $consumerMapping = []
): array {
$tmpConsumer = $consumer;
if (in_array($consumer, $originalConsumerGroups, true)) {
$consumerMapping[$consumer->getId()] = $consumer->getId();
} else {
if (!$consumer->hasChildren() && $consumer->hasSiblings([AbstractObject::OBJECT_TYPE_OBJECT])) {
$siblingMapping = $consumer->getSiblings([AbstractObject::OBJECT_TYPE_OBJECT]);
foreach ($originalConsumerGroups as $originalConsumerGroup) {
if (in_array($originalConsumerGroup, $siblingMapping, true)) {
$consumerMapping[$originalConsumerGroup->getId()] = $tmpConsumer->getId();
$consumer = $originalConsumerGroup;
if ($onlyConsumer) {
break;
}
} else {
if (!isset($consumerMapping[$originalConsumerGroup->getId()])) {
$consumerMapping[$originalConsumerGroup->getId()] = $originalConsumerGroup->getId();
}
}
}
} else {
//consumer can be only in upgrade catalog
$consumerMapping[$consumer->getId()] = $consumer->getId();
foreach ($originalConsumerGroups as $originalConsumerGroup) {
if (!isset($consumerMapping[$originalConsumerGroup->getId()])) {
$consumerMapping[$originalConsumerGroup->getId()] = $originalConsumerGroup->getId();
}
}
}
}
return $consumerMapping;
}
/**
* @param TicketProductAvailability $ticketAvailability
* @param array<mixed> $originalConsumerGroups
*
* @return array<mixed>
*/
private function getConsumerMapping(TicketProductAvailability $ticketAvailability, array $originalConsumerGroups): array
{
$consumerMapping = [];
foreach ($ticketAvailability->getSortedConsumerAvailabilities() as $consumerAvailability) {
$consumer = $consumerAvailability->getTicketConsumer();
$consumerMapping = $this->mapConsumerGroups($consumer, $originalConsumerGroups, false, $consumerMapping);
}
return $consumerMapping;
}
/**
* @param TicketCatalogAvailability $ticketCatalogAvailability
*
* @return array<mixed>
*/
public function getPriceGroupJson(TicketCatalogAvailability $ticketCatalogAvailability): array
{
$priceGroups = [];
foreach ($ticketCatalogAvailability->getTicketAvailability() as $ticketAvailability) {
foreach ($ticketAvailability->getConsumerAvailabilities() as $consumerAvailability) {
$consumer = $consumerAvailability->getTicketConsumer();
$fromYear = $consumer->getYearTo();
$priceGroups[$consumer->getId()] = [
'id' => (string)$consumer->getId(),
'label' => $consumer->getName(),
'labelPlural' => $consumer->getName(),
'subtitle' => '',
'info' => '',
'sort' => $fromYear,
];
}
}
/** @phpstan-ignore-next-line */
usort($priceGroups, function ($a, $b) {
return $a['sort'] < $b['sort'];
});
array_walk($priceGroups, function (&$a) {
unset($a['sort']);
});
return $priceGroups;
}
/**
* @param TicketCatalogAvailability $ticketCatalogAvailability
*
* @return array<mixed>
*/
public function getSpecialPriceModals(TicketCatalogAvailability $ticketCatalogAvailability): array
{
$priceGroups = [];
foreach ($ticketCatalogAvailability->getTicketAvailability() as $ticketAvailability) {
$product = $ticketAvailability->getTicketProduct();
foreach ($ticketAvailability->getConsumerAvailabilities() as $consumerAvailability) {
$consumer = $consumerAvailability->getTicketConsumer();
//only when selected date is one day ticket + saturday
if ($this->getStartDate()?->isDayOfWeek(Carbon::SATURDAY)
&& $this->getEndDate()?->isDayOfWeek(Carbon::SATURDAY)
&& $consumer->getInfoModalProduct()?->getId() == $product->getId()
) {
$priceGroups[] = [
'ticketId' => $product->getId(),
'priceGroup' => $consumer->getId(),
'topTitle' => $this->translator->trans('shop.modal-title-child-ticket.top-title'),
'title' => $this->translator->trans('shop.modal-title-child-ticket'),
'text' => $this->translator->trans('shop.modal-content-child-ticket'),
];
} elseif($consumer->getIsSwisspassHTAConsumer()) {
$priceGroups[] = [
'priceGroup' => $consumer->getId(),
'topTitle' => $this->translator->trans('shop.ticket.specialPriceModal.top-title'),
'title' => $this->translator->trans('shop.ticket.specialPriceModal.title'),
'text' => (string)$consumer->getInfoText(),
];
}
}
}
return $priceGroups;
}
/**
* @param TicketCatalogAvailability $ticketCatalogAvailability
*
* @return array<mixed>
*/
public function getPriceGroupObjects(TicketCatalogAvailability $ticketCatalogAvailability): array
{
$priceGroups = [];
foreach ($ticketCatalogAvailability->getTicketAvailability() as $ticketAvailability) {
foreach ($ticketAvailability->getConsumerAvailabilities() as $consumerAvailability) {
$priceGroups[] = $consumerAvailability->getTicketConsumer();
}
}
return $priceGroups;
}
/**
* @param array<mixed> $priceData
* @param mixed $priceInfo
*
* @return void
*/
private function setPriceData(array &$priceData, mixed $priceInfo): void
{
$price = $priceInfo->getTotalPrice()->getGrossAmount();
$priceBeforeDiscount = $priceInfo->getOriginalPriceInfo()->getTotalPrice()->getGrossAmount();
if ($priceBeforeDiscount->greaterThan($price)) {
$priceData['priceBeforeDiscount'] = $priceBeforeDiscount->asNumeric();
$priceData['priceDiscountInfo'] = $this->shopService->getPricingRulesLabel($priceInfo) ?? '';
}
}
//------ EVENTS -----------
/**
* @param ShopDynamicPrice[] $productGroup
*
* @return array<mixed>
*/
private function getPriceByGroupEventJson(array $productGroup, EventProduct $product, Carbon $date): array
{
$priceByPriceGroup = [];
foreach ($productGroup as $price) {
$orderableProduct = $product->getOrCreateOrderableObject($date, $price);
$orderPriceInfo = $orderableProduct->getOSPriceInfo(1, null, $date);
$priceData = [
'id' => (string)$price->getId(),
'price' => $orderPriceInfo->getTotalPrice()->getGrossAmount()->asNumeric(),
'personsPerItem' => $price->getParticipantNumber(),
];
$this->setPriceData($priceData, $orderPriceInfo);
$priceByPriceGroup[] = $priceData;
}
return $priceByPriceGroup;
}
/**
* @param EventProduct $product
*
* @return array<mixed>
*/
private function getPriceGroupEventJson(EventProduct $product): array
{
$priceGroups = [];
$priceObjects = $product->getPriceObjects();
foreach ($priceObjects as $price) {
$priceGroups[] = [
'id' => (string)$price->getId(),
'label' => $price->getPriceLabel(),
'info' => '<p>Info</p>',
];
}
return $priceGroups;
}
/**
* @param EventProduct $event
* @param Carbon|null $shownStartDate
* @param Carbon|null $shownEndDate
*
* @return array<mixed>
*/
private function getEventCalendarDates(EventProduct $event, Carbon $shownStartDate = null, Carbon $shownEndDate = null): array
{
/** @var AvailabilitySystem $availabilitySystem */
$availabilitySystem = $event->getAvailabilitySystemImplementation();
$availableDates = $availabilitySystem->getAvailableList($event, $shownStartDate, $shownEndDate);
$calendarData = [];
if (!$shownStartDate) {
$shownStartDate = Carbon::now();
}
while ($shownStartDate->lessThanOrEqualTo($shownEndDate)) {
$dates = [];
$month = $shownStartDate->month;
$year = $shownStartDate->year;
$tmpDate = clone $shownStartDate;
while ($shownStartDate->lessThanOrEqualTo($tmpDate->endOfMonth())) {
$timestamps = array_keys($availableDates);
$maxQuota = 0;
$currentQuota = 0;
foreach ($timestamps as $timestamp) {
if ($shownStartDate->getTimestamp() <= $timestamp && $shownStartDate->copy()->endOfDay()->getTimestamp() >= $timestamp) {
$availability = $availableDates[$timestamp];
$maxQuota += $availability->getTotalQuota();
$currentQuota += $availability->getAvailableQuota();
}
}
if ($maxQuota > 0) {
if (($currentQuota / $maxQuota * 100) <= 10) {
$status = 'danger';
} elseif (($currentQuota / $maxQuota * 100) <= 20) {
$status = 'warning';
} else {
$status = 'success';
}
}
$dates[] = [
'disabled' => $maxQuota == 0 || $currentQuota == 0,
'soldOut' => $maxQuota > 0 && $currentQuota == 0,
'date' => $shownStartDate->toDateTimeLocalString(),
'availability' => [
'status' => $status ?? 'danger',
'total' => $currentQuota,
],
];
$shownStartDate->addDay();
}
$calendarData[] = [
'month' => $month,
'year' => $year,
'dates' => $dates,
];
}
return $calendarData;
}
/**
* @param int $activityId
* @param string $selectedDateEnd
*
* @return array<mixed>
*/
public function getTimeListDatesJson(int $activityId, string $selectedDateEnd): array
{
$json = [];
if ($product = EventProduct::getById($activityId)) {
$selectedDateTime = Carbon::parse($selectedDateEnd);
$priceGroups = $this->getPriceGroupEventJson($product);
/** @var AvailabilitySystem $availabilitySystem */
$availabilitySystem = $product->getAvailabilitySystemImplementation();
$availabilities = $availabilitySystem->getAvailableList($product, $selectedDateTime, $selectedDateTime->copy()->endOfDay());
$timeList = [];
$quota = 0;
if (count($availabilities) == 1) {
$availability = array_pop($availabilities);
$quota = $availability->getAvailableQuota();
$groupedProductGroups = $product->getPriceObjectGrouped();
foreach ($groupedProductGroups as $translationKey => $productGroup) {
$priceByPriceGroup = $this->getPriceByGroupEventJson($productGroup, $product, $availability->getDate());
$timeList[] = [
'title' => $translationKey ? $this->translator->trans($translationKey) : $product->getName(),
'time' => $availability->getDate()->toTimeString('minute'),
'isAvailable' => $availability->isAvailable(),
'max' => $availability->getMaxAvailableBooking(),
'priceByPriceGroup' => $priceByPriceGroup,
];
}
} else {
// more than one availability means Recurring Date/Time was used
foreach ($availabilities as $availability) {
$quota += $availability->getAvailableQuota();
$priceByPriceGroup = $this->getPriceByGroupEventJson($product->getPriceObjects(), $product, $availability->getDate());
$timeList[] = [
'title' => $product->getName(),
'time' => $availability->getDate()->toTimeString('minute'),
'isAvailable' => $availability->isAvailable(),
'max' => $availability->getMaxAvailableBooking(),
'priceByPriceGroup' => $priceByPriceGroup,
'showTime' => true,
];
}
}
usort($timeList, function ($a, $b) {
if ($a['isAvailable'] && !$b['isAvailable']) {
return -1;
}
if (!$a['isAvailable'] && $b['isAvailable']) {
return 1;
}
return 0;
});
$availabilityData = [
'total' => $quota,
'status' => 'success',
];
$json = [
'success' => true,
'hasAgeGroups' => false,
'id' => (string)$product->getId(),
'priceGroups' => $priceGroups,
'availability' => $availabilityData,
'timeList' => $timeList,
];
}
return $json;
}
/**
* @param Request $request
*
* @return array<mixed>
*/
public function getCalendarDates(Request $request): array
{
$json = [];
$id = intval($request->get('ticketId'));
if (empty($id)) {
return $json;
}
if ($event = EventProduct::getById($id)) {
$isMobile = $request->get('isMobile');
$startDate = $event->getFirstValidValidityDate(Carbon::now());
$eventDates = $this->datesHelper->getCalculatedDates($event, 'getValidityDates');
$lastDate = end($eventDates);
if ($request->get('shownStartYear') && $request->get('shownStartMonth')) {
$shownStartDate = Carbon::create($request->get('shownStartYear'), $request->get('shownStartMonth'));
} else {
$shownStartDate = clone $startDate;
$shownStartDate->startOfMonth();
}
if ($request->get('shownEndYear') && $request->get('shownEndMonth')) {
$shownEndDate = Carbon::create($request->get('shownEndYear'), $request->get('shownEndMonth'))->endOfMonth();
} else {
$shownEndDate = clone $startDate;
if ($isMobile === 'false') {
$shownEndDate->addMonth()->endOfMonth();
} else {
$shownEndDate->endOfMonth();
}
}
$maxDate = $lastDate['toDate'] ?? clone $shownEndDate;
$calendarData = $this->getEventCalendarDates($event, $shownStartDate, $shownEndDate);
$json = [
'success' => true,
'id' => (string)$event->getId(),
'calendarConfiguration' => [
'minDate' => $startDate->startOfDay()->toDateTimeLocalString(),
'maxDate' => $maxDate->toDateTimeLocalString(),
'minSelectableDays' => 1,
'maxSelectableDays' => 1,
'minSelectableDates' => 1,
'maxSelectableDates' => 1,
],
'basePriceInfo' => [
'label' => $this->translator->trans('shop.configuration.Price-per-Adult'),
'price' => $event->getTeaserPrice()->getGrossAmount()->asNumeric(),
],
'calendarDates' => $calendarData,
];
}
if ($json) {
return $json;
} else {
return [];
}
}
/**
* @param int $activityId
*
* @return array<mixed>
*/
public function getListDatesJson(int $activityId): array
{
$json = [];
if ($product = EventProduct::getById($activityId)) {
$priceGroups = $this->getPriceGroupEventJson($product);
/** @var AvailabilitySystem $availabilitySystem */
$availabilitySystem = $product->getAvailabilitySystemImplementation();
$availableDates = $availabilitySystem->getAvailableList($product);
$dateList = [];
foreach ($availableDates as $availability) {
$priceByPriceGroup = $this->getPriceByGroupEventJson($product->getPriceObjects(), $product, $availability->getDate());
$availableQuota = $availability->getAvailableQuota();
$dateList[] = [
'title' => $product->getName(),
'date' => $availability->getDate()->toDateString(),
'availability' => [
'total' => $availableQuota,
'status' => $availableQuota == 0 ? 'danger' : ($availability->getCapacityInPercent() <= 20 ? 'warning' : 'success'),
],
'max' => $availability->getMaxAvailableBooking(),
'priceByPriceGroup' => $priceByPriceGroup,
];
}
usort($dateList, function ($a, $b) {
if ($a['availability']['total'] == 0 || $b['availability']['total'] == 0) {
return -1;
}
return 0;
});
$json = [
'success' => true,
'hasAgeGroups' => true,
'id' => (string)$product->getId(),
'priceGroups' => $priceGroups,
'dateList' => $dateList,
];
}
return $json;
}
}