chore: cgl fixes
This commit is contained in:
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace App\BusProNet\Model;
|
namespace App\BusProNet\Model;
|
||||||
|
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
use function Symfony\Component\String\u;
|
use function Symfony\Component\String\u;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -334,10 +334,10 @@ class ImportGroupsLegacyDataCommand extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param list<array<string, string>> $rows
|
* @param list<array<string, string>> $rows
|
||||||
* @param array<int, Accommodation> $accommodationByUid
|
* @param array<int, Accommodation> $accommodationByUid
|
||||||
* @param list<int> $knownUids
|
* @param list<int> $knownUids
|
||||||
* @param list<array{season: Season, dateFrom: \DateTimeImmutable, dateTo: \DateTimeImmutable}> $seasonRanges
|
* @param list<array{season: Season, dateFrom: \DateTimeImmutable, dateTo: \DateTimeImmutable}> $seasonRanges
|
||||||
*
|
*
|
||||||
* @return array{0: int, 1: list<int>, 2: int}
|
* @return array{0: int, 1: list<int>, 2: int}
|
||||||
*/
|
*/
|
||||||
@@ -526,11 +526,11 @@ class ImportGroupsLegacyDataCommand extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<int, Accommodation> $accommodationByUid
|
* @param array<int, Accommodation> $accommodationByUid
|
||||||
* @param list<AdditionalService> $additionalServicesNeedingDefaultRange only these
|
* @param list<AdditionalService> $additionalServicesNeedingDefaultRange only these
|
||||||
* (titles without a leading year) get the accommodation-
|
* (titles without a leading year) get the accommodation-
|
||||||
* wide fallback range; year-prefixed ones already have
|
* wide fallback range; year-prefixed ones already have
|
||||||
* their scoped range set in importAdditionalServices().
|
* their scoped range set in importAdditionalServices()
|
||||||
*/
|
*/
|
||||||
private function applyDateRangeToServices(array $accommodationByUid, array $additionalServicesNeedingDefaultRange): void
|
private function applyDateRangeToServices(array $accommodationByUid, array $additionalServicesNeedingDefaultRange): void
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use App\BusProNet\ApiClient;
|
|||||||
use App\BusProNet\Exception\ApiClientException;
|
use App\BusProNet\Exception\ApiClientException;
|
||||||
use App\BusProNet\Model\Notification;
|
use App\BusProNet\Model\Notification;
|
||||||
use App\BusProNet\Model\PersonalData;
|
use App\BusProNet\Model\PersonalData;
|
||||||
use App\BusProNet\Model\PersonalDataUpdateResponse;
|
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Exception\NewsletterProviderException;
|
use App\Exception\NewsletterProviderException;
|
||||||
use App\Form\PersonalDataType;
|
use App\Form\PersonalDataType;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class CalendarController extends AbstractController
|
|||||||
{
|
{
|
||||||
return $this->render('admin/accommodation/_calendar_section.html.twig', [
|
return $this->render('admin/accommodation/_calendar_section.html.twig', [
|
||||||
'accommodation' => $accommodation,
|
'accommodation' => $accommodation,
|
||||||
'startMonth' => $request->query->get('month'),
|
'startMonth' => $request->query->get('month'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class CreateController extends AbstractController
|
|||||||
'date_range' => [
|
'date_range' => [
|
||||||
'from' => $accommodationPrice->getDateFrom()->format('Y-m-d'),
|
'from' => $accommodationPrice->getDateFrom()->format('Y-m-d'),
|
||||||
'to' => $accommodationPrice->getDateTo()->format('Y-m-d'),
|
'to' => $accommodationPrice->getDateTo()->format('Y-m-d'),
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$returnUrl = $this->generateUrl('app_admin_accommodation_edit', [
|
$returnUrl = $this->generateUrl('app_admin_accommodation_edit', [
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class EditController extends AbstractController
|
|||||||
'date_range' => [
|
'date_range' => [
|
||||||
'from' => $accommodationPrice->getDateFrom()->format('Y-m-d'),
|
'from' => $accommodationPrice->getDateFrom()->format('Y-m-d'),
|
||||||
'to' => $accommodationPrice->getDateTo()->format('Y-m-d'),
|
'to' => $accommodationPrice->getDateTo()->format('Y-m-d'),
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$returnUrl = $this->generateUrl('app_admin_accommodation_edit', [
|
$returnUrl = $this->generateUrl('app_admin_accommodation_edit', [
|
||||||
|
|||||||
@@ -4,14 +4,11 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Controller\Admin\AdditionalService;
|
namespace App\Controller\Admin\AdditionalService;
|
||||||
|
|
||||||
use App\Entity\Groups\Accommodation;
|
|
||||||
use App\Entity\Groups\AdditionalService;
|
use App\Entity\Groups\AdditionalService;
|
||||||
use App\Form\Admin\Groups\AdditionalServiceType;
|
use App\Form\Admin\Groups\AdditionalServiceType;
|
||||||
use App\Htmx\HxRedirectResponse;
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\Form\FormInterface;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ class Step2Controller extends AbstractController
|
|||||||
$selectedAdditionalServices = $this->resolveSelectedAdditionalServices($dto, $services['additionalServices']);
|
$selectedAdditionalServices = $this->resolveSelectedAdditionalServices($dto, $services['additionalServices']);
|
||||||
|
|
||||||
$priceBreakdown = null;
|
$priceBreakdown = null;
|
||||||
if ($dto->dateFrom !== null && $dto->dateTo !== null) {
|
if (null !== $dto->dateFrom && null !== $dto->dateTo) {
|
||||||
$priceBreakdown = $this->priceCalculator->calculate(
|
$priceBreakdown = $this->priceCalculator->calculate(
|
||||||
$dto->paxCount,
|
$dto->paxCount,
|
||||||
$dto->minorsCount,
|
$dto->minorsCount,
|
||||||
@@ -168,7 +168,7 @@ class Step2Controller extends AbstractController
|
|||||||
|
|
||||||
return array_values(array_filter(
|
return array_values(array_filter(
|
||||||
$additionalServices,
|
$additionalServices,
|
||||||
fn(AdditionalService $s) => isset($selectedIds[$s->getId()]),
|
fn (AdditionalService $s) => isset($selectedIds[$s->getId()]),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ enum PriceType: string
|
|||||||
|
|
||||||
public function priority(): int
|
public function priority(): int
|
||||||
{
|
{
|
||||||
return match($this) {
|
return match ($this) {
|
||||||
self::OVERRIDE => 1,
|
self::OVERRIDE => 1,
|
||||||
self::DISCOUNT => 2,
|
self::DISCOUNT => 2,
|
||||||
};
|
};
|
||||||
@@ -17,7 +17,7 @@ enum PriceType: string
|
|||||||
|
|
||||||
public function label(): string
|
public function label(): string
|
||||||
{
|
{
|
||||||
return match($this) {
|
return match ($this) {
|
||||||
self::OVERRIDE => 'Override',
|
self::OVERRIDE => 'Override',
|
||||||
self::DISCOUNT => 'Rabatt',
|
self::DISCOUNT => 'Rabatt',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ use App\Service\AdditionalServiceExclusionResolver;
|
|||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Form\FormEvent;
|
use Symfony\Component\Form\FormEvent;
|
||||||
use Symfony\Component\Form\FormEvents;
|
use Symfony\Component\Form\FormEvents;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,6 +96,6 @@ class AccommodationStep2Type extends AbstractType
|
|||||||
'max_adolescent_age' => 0,
|
'max_adolescent_age' => 0,
|
||||||
]);
|
]);
|
||||||
$resolver->setAllowedTypes('max_adolescent_age', 'int');
|
$resolver->setAllowedTypes('max_adolescent_age', 'int');
|
||||||
$resolver->setAllowedTypes('additional_services', AdditionalService::class . '[]');
|
$resolver->setAllowedTypes('additional_services', AdditionalService::class.'[]');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,12 +37,12 @@ class AccommodationPriceType extends AbstractType
|
|||||||
'class' => PriceType::class,
|
'class' => PriceType::class,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'placeholder' => 'Standard',
|
'placeholder' => 'Standard',
|
||||||
'choice_label' => fn($item) => $item->label(),
|
'choice_label' => fn ($item) => $item->label(),
|
||||||
])
|
])
|
||||||
->add('season', EnumType::class, [
|
->add('season', EnumType::class, [
|
||||||
'label' => 'Saison',
|
'label' => 'Saison',
|
||||||
'class' => Season::class,
|
'class' => Season::class,
|
||||||
'choice_label' => fn($item) => $item->token(),
|
'choice_label' => fn ($item) => $item->token(),
|
||||||
])
|
])
|
||||||
->add('includedPax', IntegerType::class, [
|
->add('includedPax', IntegerType::class, [
|
||||||
'label' => 'Inklusiv-Personen',
|
'label' => 'Inklusiv-Personen',
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class AdditionalServiceType extends AbstractType
|
|||||||
->add('type', EnumType::class, [
|
->add('type', EnumType::class, [
|
||||||
'label' => 'Preistyp',
|
'label' => 'Preistyp',
|
||||||
'class' => AdditionalServiceTypeEnum::class,
|
'class' => AdditionalServiceTypeEnum::class,
|
||||||
'choice_label' => fn($item) => $item->label(),
|
'choice_label' => fn ($item) => $item->label(),
|
||||||
])
|
])
|
||||||
->add('price', MoneyType::class, [
|
->add('price', MoneyType::class, [
|
||||||
'label' => 'Preis',
|
'label' => 'Preis',
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class AccommodationBookingDto
|
|||||||
#[Assert\NotNull(message: 'required', groups: ['step_2'])]
|
#[Assert\NotNull(message: 'required', groups: ['step_2'])]
|
||||||
public ?int $selectedBoardServiceId = null;
|
public ?int $selectedBoardServiceId = null;
|
||||||
|
|
||||||
/** @var list<int>*/
|
/** @var list<int> */
|
||||||
public array $selectedAdditionalServiceIds = [];
|
public array $selectedAdditionalServiceIds = [];
|
||||||
|
|
||||||
public bool $isInquiry = false;
|
public bool $isInquiry = false;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class BookingEditContext
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param array<int, ParticipantCardDataDto>|null $cardsData
|
* @param array<int, ParticipantCardDataDto>|null $cardsData
|
||||||
* @param array<int, list<string>> $missingValueLabelsByParticipantIndex
|
* @param array<int, list<string>> $missingValueLabelsByParticipantIndex
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public readonly BookingDto $bookingDto,
|
public readonly BookingDto $bookingDto,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace App\Form\Model;
|
|||||||
use App\Validator\Constraints as AppAssert;
|
use App\Validator\Constraints as AppAssert;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
|
||||||
use function Symfony\Component\String\u;
|
use function Symfony\Component\String\u;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ class ParticipantTransportationDiscountReplacementFieldHandler extends AbstractP
|
|||||||
$services[$oldService->id]->mapping = array_values(
|
$services[$oldService->id]->mapping = array_values(
|
||||||
array_filter(
|
array_filter(
|
||||||
$services[$oldService->id]->mapping,
|
$services[$oldService->id]->mapping,
|
||||||
fn(int $idx): bool => $idx !== $participantIndex,
|
fn (int $idx): bool => $idx !== $participantIndex,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class AdditionalServiceExclusionResolver
|
|||||||
|
|
||||||
$submittedIds = array_filter(
|
$submittedIds = array_filter(
|
||||||
$submittedIds,
|
$submittedIds,
|
||||||
fn(int $id) => !$this->conflicts($rules, $addedId, $id),
|
fn (int $id) => !$this->conflicts($rules, $addedId, $id),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -510,5 +510,4 @@ class BpnXmlAnonymizer
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class CalendarGridBuilder
|
|||||||
$months = [];
|
$months = [];
|
||||||
$base = $from->modify('first day of this month')->setTime(0, 0, 0);
|
$base = $from->modify('first day of this month')->setTime(0, 0, 0);
|
||||||
|
|
||||||
for ($i = 0; $i < $count; $i++) {
|
for ($i = 0; $i < $count; ++$i) {
|
||||||
$monthStart = $base->modify("+{$i} months");
|
$monthStart = $base->modify("+{$i} months");
|
||||||
$months[] = [
|
$months[] = [
|
||||||
'month' => $monthStart,
|
'month' => $monthStart,
|
||||||
@@ -54,7 +54,7 @@ class CalendarGridBuilder
|
|||||||
|
|
||||||
while ($current <= $gridEnd) {
|
while ($current <= $gridEnd) {
|
||||||
$week = [];
|
$week = [];
|
||||||
for ($d = 0; $d < 7; $d++) {
|
for ($d = 0; $d < 7; ++$d) {
|
||||||
$week[] = [
|
$week[] = [
|
||||||
'date' => $current,
|
'date' => $current,
|
||||||
'inMonth' => $current->format('Y-m') === $monthKey,
|
'inMonth' => $current->format('Y-m') === $monthKey,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class GroupsPriceCalculator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, int|float> $config
|
* @param array<string, int|float> $config
|
||||||
|
*
|
||||||
* @return array<string, int|float>
|
* @return array<string, int|float>
|
||||||
*/
|
*/
|
||||||
private function resolveConfig(array $config): array
|
private function resolveConfig(array $config): array
|
||||||
@@ -47,8 +48,8 @@ class GroupsPriceCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param AccommodationPrice[] $prices all prices overlapping the booking window
|
* @param AccommodationPrice[] $prices all prices overlapping the booking window
|
||||||
* @param AdditionalService[] $additionalServices only the selected services
|
* @param AdditionalService[] $additionalServices only the selected services
|
||||||
*
|
*
|
||||||
* @return array{
|
* @return array{
|
||||||
* effectivePax: int,
|
* effectivePax: int,
|
||||||
@@ -88,7 +89,7 @@ class GroupsPriceCalculator
|
|||||||
$prices,
|
$prices,
|
||||||
$boardService?->getPrice(),
|
$boardService?->getPrice(),
|
||||||
array_map(
|
array_map(
|
||||||
fn(AdditionalService $s) => [
|
fn (AdditionalService $s) => [
|
||||||
'label' => $s->getLabel() ?? '',
|
'label' => $s->getLabel() ?? '',
|
||||||
'price' => $s->getPrice() ?? 0,
|
'price' => $s->getPrice() ?? 0,
|
||||||
'type' => $s->getType(),
|
'type' => $s->getType(),
|
||||||
@@ -103,7 +104,7 @@ class GroupsPriceCalculator
|
|||||||
* Same calculation but using frozen snapshot data from an AccommodationBooking entity
|
* Same calculation but using frozen snapshot data from an AccommodationBooking entity
|
||||||
* instead of live catalog entities.
|
* instead of live catalog entities.
|
||||||
*
|
*
|
||||||
* @param AccommodationPrice[] $prices all prices overlapping the booking window
|
* @param AccommodationPrice[] $prices all prices overlapping the booking window
|
||||||
* @param list<array{label: string, price: int, type: string, originalServiceId: int|null}> $additionalServiceSnapshots
|
* @param list<array{label: string, price: int, type: string, originalServiceId: int|null}> $additionalServiceSnapshots
|
||||||
*
|
*
|
||||||
* @return array{
|
* @return array{
|
||||||
@@ -136,7 +137,7 @@ class GroupsPriceCalculator
|
|||||||
string $currency,
|
string $currency,
|
||||||
): array {
|
): array {
|
||||||
$items = array_map(
|
$items = array_map(
|
||||||
fn(array $s) => [
|
fn (array $s) => [
|
||||||
'label' => $s['label'],
|
'label' => $s['label'],
|
||||||
'price' => $s['price'],
|
'price' => $s['price'],
|
||||||
'type' => AdditionalServiceType::tryFrom($s['type']) ?? AdditionalServiceType::Flat,
|
'type' => AdditionalServiceType::tryFrom($s['type']) ?? AdditionalServiceType::Flat,
|
||||||
@@ -158,7 +159,7 @@ class GroupsPriceCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param AccommodationPrice[] $prices
|
* @param AccommodationPrice[] $prices
|
||||||
* @param list<array{label: string, price: int, type: AdditionalServiceType}> $additionalItems
|
* @param list<array{label: string, price: int, type: AdditionalServiceType}> $additionalItems
|
||||||
*
|
*
|
||||||
* @return array{
|
* @return array{
|
||||||
@@ -193,7 +194,7 @@ class GroupsPriceCalculator
|
|||||||
// Derive effectivePax: children 0–3 don't count, but never drop below includedPax
|
// Derive effectivePax: children 0–3 don't count, but never drop below includedPax
|
||||||
$firstCandidates = array_values(array_filter(
|
$firstCandidates = array_values(array_filter(
|
||||||
$prices,
|
$prices,
|
||||||
fn(AccommodationPrice $p) => $p->getDateFrom() <= $dateFrom && $p->getDateTo() >= $dateFrom,
|
fn (AccommodationPrice $p) => $p->getDateFrom() <= $dateFrom && $p->getDateTo() >= $dateFrom,
|
||||||
));
|
));
|
||||||
$firstWinner = $this->priceTimelineBuilder->resolveWinner($firstCandidates);
|
$firstWinner = $this->priceTimelineBuilder->resolveWinner($firstCandidates);
|
||||||
$includedPaxFloor = $firstWinner?->getIncludedPax() ?? 1;
|
$includedPaxFloor = $firstWinner?->getIncludedPax() ?? 1;
|
||||||
@@ -224,11 +225,11 @@ class GroupsPriceCalculator
|
|||||||
|
|
||||||
$candidates = array_values(array_filter(
|
$candidates = array_values(array_filter(
|
||||||
$prices,
|
$prices,
|
||||||
fn(AccommodationPrice $p) => $p->getDateFrom() <= $segStart && $p->getDateTo() >= $segStart,
|
fn (AccommodationPrice $p) => $p->getDateFrom() <= $segStart && $p->getDateTo() >= $segStart,
|
||||||
));
|
));
|
||||||
$winner = $this->priceTimelineBuilder->resolveWinner($candidates) ?? $lastWinner;
|
$winner = $this->priceTimelineBuilder->resolveWinner($candidates) ?? $lastWinner;
|
||||||
|
|
||||||
if ($winner !== null) {
|
if (null !== $winner) {
|
||||||
$lastWinner = $winner;
|
$lastWinner = $winner;
|
||||||
$basePrice += ($winner->getPricePerNight() ?? 0) * $segNights;
|
$basePrice += ($winner->getPricePerNight() ?? 0) * $segNights;
|
||||||
$includedPax = $winner->getIncludedPax() ?? 0;
|
$includedPax = $winner->getIncludedPax() ?? 0;
|
||||||
@@ -254,7 +255,7 @@ class GroupsPriceCalculator
|
|||||||
// Rule 4: undersubscription surcharge (only when a paid board is selected)
|
// Rule 4: undersubscription surcharge (only when a paid board is selected)
|
||||||
$undersubscriptionSurcharge = 0;
|
$undersubscriptionSurcharge = 0;
|
||||||
$undersubscriptionThreshold = null;
|
$undersubscriptionThreshold = null;
|
||||||
if ($paidBoardPricePerPersonNight !== null) {
|
if (null !== $paidBoardPricePerPersonNight) {
|
||||||
$surcharge30 = (int) round(('CHF' === $currency ? $this->config['undersubscription30Chf'] : $this->config['undersubscription30Eur']) * 100);
|
$surcharge30 = (int) round(('CHF' === $currency ? $this->config['undersubscription30Chf'] : $this->config['undersubscription30Eur']) * 100);
|
||||||
$surcharge40 = (int) round(('CHF' === $currency ? $this->config['undersubscription40Chf'] : $this->config['undersubscription40Eur']) * 100);
|
$surcharge40 = (int) round(('CHF' === $currency ? $this->config['undersubscription40Chf'] : $this->config['undersubscription40Eur']) * 100);
|
||||||
|
|
||||||
@@ -269,7 +270,7 @@ class GroupsPriceCalculator
|
|||||||
|
|
||||||
// Rule 5: board price
|
// Rule 5: board price
|
||||||
$boardPrice = 0;
|
$boardPrice = 0;
|
||||||
if ($paidBoardPricePerPersonNight !== null) {
|
if (null !== $paidBoardPricePerPersonNight) {
|
||||||
$boardPrice = $paidBoardPricePerPersonNight * $effectivePax * $nights;
|
$boardPrice = $paidBoardPricePerPersonNight * $effectivePax * $nights;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,10 +115,10 @@ class InsuranceManager
|
|||||||
* participant's price - this splits the given insurances by type and evaluates
|
* participant's price - this splits the given insurances by type and evaluates
|
||||||
* each group against the appropriate price before merging the results back together.
|
* each group against the appropriate price before merging the results back together.
|
||||||
*
|
*
|
||||||
* @param array<Insurance> $insurances Available insurances to filter
|
* @param array<Insurance> $insurances Available insurances to filter
|
||||||
* @param ParticipantDto $participant The participant to match insurances for
|
* @param ParticipantDto $participant The participant to match insurances for
|
||||||
* @param BookingDto $booking The booking context for additional criteria
|
* @param BookingDto $booking The booking context for additional criteria
|
||||||
* @param float $individualPrice The participant's individual travel price (excluding insurance)
|
* @param float $individualPrice The participant's individual travel price (excluding insurance)
|
||||||
* @param float $totalBookingPrice The total booking price across all participants (excluding insurance)
|
* @param float $totalBookingPrice The total booking price across all participants (excluding insurance)
|
||||||
*
|
*
|
||||||
* @return array<Insurance> Filtered array of eligible insurances, sorted by price
|
* @return array<Insurance> Filtered array of eligible insurances, sorted by price
|
||||||
|
|||||||
@@ -39,8 +39,7 @@ class ParticipantCardAssembler
|
|||||||
BookingDto $bookingDto,
|
BookingDto $bookingDto,
|
||||||
int $index,
|
int $index,
|
||||||
?array $precomputedPrices = null,
|
?array $precomputedPrices = null,
|
||||||
): ParticipantCardDataDto
|
): ParticipantCardDataDto {
|
||||||
{
|
|
||||||
$participant = $bookingDto->participants[$index] ?? null;
|
$participant = $bookingDto->participants[$index] ?? null;
|
||||||
|
|
||||||
if (null === $participant) {
|
if (null === $participant) {
|
||||||
@@ -142,8 +141,7 @@ class ParticipantCardAssembler
|
|||||||
BookingDto $bookingDto,
|
BookingDto $bookingDto,
|
||||||
int $index,
|
int $index,
|
||||||
?array $precomputedPrices = null,
|
?array $precomputedPrices = null,
|
||||||
): ParticipantCardPriceDto
|
): ParticipantCardPriceDto {
|
||||||
{
|
|
||||||
// Check if canceled (only possible in edit mode when booking property is set)
|
// Check if canceled (only possible in edit mode when booking property is set)
|
||||||
$isCanceled = ($bookingDto->booking?->participantsStatus[$index] ?? null) === 'S';
|
$isCanceled = ($bookingDto->booking?->participantsStatus[$index] ?? null) === 'S';
|
||||||
|
|
||||||
@@ -189,8 +187,7 @@ class ParticipantCardAssembler
|
|||||||
BookingDto $bookingDto,
|
BookingDto $bookingDto,
|
||||||
int $index,
|
int $index,
|
||||||
bool $forceStrictRequired = false,
|
bool $forceStrictRequired = false,
|
||||||
): ParticipantCardDataDto
|
): ParticipantCardDataDto {
|
||||||
{
|
|
||||||
return $this->getCardDataWithValidationInternal(
|
return $this->getCardDataWithValidationInternal(
|
||||||
$bookingDto,
|
$bookingDto,
|
||||||
$index,
|
$index,
|
||||||
@@ -262,7 +259,7 @@ class ParticipantCardAssembler
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string> $validationGroups
|
* @param array<string> $validationGroups
|
||||||
* @param array<int, float>|null $precomputedPrices
|
* @param array<int, float>|null $precomputedPrices
|
||||||
*/
|
*/
|
||||||
private function getCardDataWithValidationInternal(
|
private function getCardDataWithValidationInternal(
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use App\BusProNet\ApiClient;
|
|||||||
use App\BusProNet\Model\Address;
|
use App\BusProNet\Model\Address;
|
||||||
use App\BusProNet\Model\Notification;
|
use App\BusProNet\Model\Notification;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Form\Model\BookingDto;
|
|
||||||
use App\Form\Model\ParticipantDto;
|
use App\Form\Model\ParticipantDto;
|
||||||
use App\Security\Crypt;
|
use App\Security\Crypt;
|
||||||
use Carbon\CarbonImmutable;
|
use Carbon\CarbonImmutable;
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ class ParticipantFormSupport
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, int> $options
|
* @param array<string, int> $options
|
||||||
|
*
|
||||||
* @return array<string, int>
|
* @return array<string, int>
|
||||||
*/
|
*/
|
||||||
private function resolveOptions(array $options): array
|
private function resolveOptions(array $options): array
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class PriceTimelineBuilder
|
|||||||
$lastDefault = null;
|
$lastDefault = null;
|
||||||
|
|
||||||
// Step 2: walk each segment [start, end) and resolve the effective price.
|
// Step 2: walk each segment [start, end) and resolve the effective price.
|
||||||
for ($i = 0, $count = count($boundaries) - 1; $i < $count; $i++) {
|
for ($i = 0, $count = count($boundaries) - 1; $i < $count; ++$i) {
|
||||||
$segStart = $boundaries[$i];
|
$segStart = $boundaries[$i];
|
||||||
$segEndNext = $boundaries[$i + 1];
|
$segEndNext = $boundaries[$i + 1];
|
||||||
|
|
||||||
@@ -81,12 +81,12 @@ class PriceTimelineBuilder
|
|||||||
|
|
||||||
$candidates = array_filter(
|
$candidates = array_filter(
|
||||||
$prices,
|
$prices,
|
||||||
fn($p) => $p->getDateFrom() <= $segStart && $p->getDateTo() >= $segStart,
|
fn ($p) => $p->getDateFrom() <= $segStart && $p->getDateTo() >= $segStart,
|
||||||
);
|
);
|
||||||
|
|
||||||
$winner = $this->resolveWinner($candidates);
|
$winner = $this->resolveWinner($candidates);
|
||||||
|
|
||||||
if ($winner === null) {
|
if (null === $winner) {
|
||||||
// Gap: reset merge state so the next winner always opens a new row.
|
// Gap: reset merge state so the next winner always opens a new row.
|
||||||
$lastWinner = null;
|
$lastWinner = null;
|
||||||
$lastDefault = null;
|
$lastDefault = null;
|
||||||
@@ -94,8 +94,8 @@ class PriceTimelineBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
$defaultWinner = null;
|
$defaultWinner = null;
|
||||||
if ($winner->getType() === PriceType::DISCOUNT) {
|
if (PriceType::DISCOUNT === $winner->getType()) {
|
||||||
$defaults = array_filter($candidates, fn($p) => $p->getType() === null);
|
$defaults = array_filter($candidates, fn ($p) => null === $p->getType());
|
||||||
$defaultWinner = $this->resolveWinner($defaults);
|
$defaultWinner = $this->resolveWinner($defaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,8 +125,8 @@ class PriceTimelineBuilder
|
|||||||
includedPax: $winner->getIncludedPax(),
|
includedPax: $winner->getIncludedPax(),
|
||||||
pricePerNight: round($winner->getPricePerNight() / 100, 2),
|
pricePerNight: round($winner->getPricePerNight() / 100, 2),
|
||||||
priceAdditionalPerson: round($winner->getPriceAdditionalPerson() / 100, 2),
|
priceAdditionalPerson: round($winner->getPriceAdditionalPerson() / 100, 2),
|
||||||
defaultPricePerNight: $defaultWinner !== null ? round($defaultWinner->getPricePerNight() / 100, 2) : null,
|
defaultPricePerNight: null !== $defaultWinner ? round($defaultWinner->getPricePerNight() / 100, 2) : null,
|
||||||
defaultPriceAdditionalPerson: $defaultWinner !== null ? round($defaultWinner->getPriceAdditionalPerson() / 100, 2) : null,
|
defaultPriceAdditionalPerson: null !== $defaultWinner ? round($defaultWinner->getPriceAdditionalPerson() / 100, 2) : null,
|
||||||
currency: $currency,
|
currency: $currency,
|
||||||
type: $winner->getType()?->value,
|
type: $winner->getType()?->value,
|
||||||
);
|
);
|
||||||
@@ -150,7 +150,7 @@ class PriceTimelineBuilder
|
|||||||
{
|
{
|
||||||
$winner = null;
|
$winner = null;
|
||||||
foreach ($candidates as $candidate) {
|
foreach ($candidates as $candidate) {
|
||||||
if ($winner === null) {
|
if (null === $winner) {
|
||||||
$winner = $candidate;
|
$winner = $candidate;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ use App\Model\CalendarMonth;
|
|||||||
use App\Service\AccommodationPriceCoverage;
|
use App\Service\AccommodationPriceCoverage;
|
||||||
use Carbon\CarbonImmutable;
|
use Carbon\CarbonImmutable;
|
||||||
use Psr\Cache\InvalidArgumentException;
|
use Psr\Cache\InvalidArgumentException;
|
||||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
|
||||||
use Symfony\Contracts\Cache\CacheInterface;
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
use Symfony\Contracts\Cache\ItemInterface;
|
use Symfony\Contracts\Cache\ItemInterface;
|
||||||
|
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||||
|
|
||||||
#[AsTwigComponent('calendar', template: 'components/calendar/index.html.twig')]
|
#[AsTwigComponent('calendar', template: 'components/calendar/index.html.twig')]
|
||||||
class Calendar
|
class Calendar
|
||||||
@@ -55,8 +55,8 @@ class Calendar
|
|||||||
$this->accommodation = $accommodation;
|
$this->accommodation = $accommodation;
|
||||||
$this->startMonth = $startMonth;
|
$this->startMonth = $startMonth;
|
||||||
|
|
||||||
if ($startMonth !== null && preg_match('/^\d{4}-\d{2}$/', $startMonth)) {
|
if (null !== $startMonth && preg_match('/^\d{4}-\d{2}$/', $startMonth)) {
|
||||||
$start = CarbonImmutable::createFromFormat('Y-m-d', $startMonth . '-01');
|
$start = CarbonImmutable::createFromFormat('Y-m-d', $startMonth.'-01');
|
||||||
} else {
|
} else {
|
||||||
$allDates = $this->collectAllDates();
|
$allDates = $this->collectAllDates();
|
||||||
$start = empty($allDates)
|
$start = empty($allDates)
|
||||||
@@ -73,7 +73,7 @@ class Calendar
|
|||||||
|
|
||||||
$months = [];
|
$months = [];
|
||||||
$current = $start;
|
$current = $start;
|
||||||
for ($i = 0; $i < 3; $i++) {
|
for ($i = 0; $i < 3; ++$i) {
|
||||||
$months[] = $this->buildMonth($current, $map, $blockedDates);
|
$months[] = $this->buildMonth($current, $map, $blockedDates);
|
||||||
$current = $current->addMonth();
|
$current = $current->addMonth();
|
||||||
}
|
}
|
||||||
@@ -81,22 +81,22 @@ class Calendar
|
|||||||
$this->months = $months;
|
$this->months = $months;
|
||||||
|
|
||||||
$prevStart = $start->subMonths(3);
|
$prevStart = $start->subMonths(3);
|
||||||
$prevEnd = $prevStart->addMonths(2);
|
$prevEnd = $prevStart->addMonths(2);
|
||||||
$this->prevMonth = $prevStart->format('Y-m');
|
$this->prevMonth = $prevStart->format('Y-m');
|
||||||
$this->prevLabel = self::MONTHS[$prevStart->month] . ' – ' . self::MONTHS[$prevEnd->month] . ' ' . $prevEnd->year;
|
$this->prevLabel = self::MONTHS[$prevStart->month].' – '.self::MONTHS[$prevEnd->month].' '.$prevEnd->year;
|
||||||
|
|
||||||
$nextStart = $start->addMonths(3);
|
$nextStart = $start->addMonths(3);
|
||||||
$nextEnd = $nextStart->addMonths(2);
|
$nextEnd = $nextStart->addMonths(2);
|
||||||
$this->nextMonth = $nextStart->format('Y-m');
|
$this->nextMonth = $nextStart->format('Y-m');
|
||||||
$this->nextLabel = self::MONTHS[$nextStart->month] . ' – ' . self::MONTHS[$nextEnd->month] . ' ' . $nextEnd->year;
|
$this->nextLabel = self::MONTHS[$nextStart->month].' – '.self::MONTHS[$nextEnd->month].' '.$nextEnd->year;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string, true> keyed by Y-m-d, only BLOCKED dates present
|
* @return array<string, true> keyed by Y-m-d, only BLOCKED dates present
|
||||||
*/
|
*/
|
||||||
private function fetchBlockedDates(string $hotelCode, string $dateFrom, string $dateTo): array
|
private function fetchBlockedDates(string $hotelCode, string $dateFrom, string $dateTo): array
|
||||||
{
|
{
|
||||||
if ($hotelCode === '') {
|
if ('' === $hotelCode) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ class Calendar
|
|||||||
|
|
||||||
$blocked = [];
|
$blocked = [];
|
||||||
foreach ($response->data as $entry) {
|
foreach ($response->data as $entry) {
|
||||||
if ($entry->status === ContingentStatus::Blocked) {
|
if (ContingentStatus::Blocked === $entry->status) {
|
||||||
$blocked[(new \DateTimeImmutable($entry->date))->format('Y-m-d')] = true;
|
$blocked[(new \DateTimeImmutable($entry->date))->format('Y-m-d')] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,26 +144,26 @@ class Calendar
|
|||||||
$dates = [];
|
$dates = [];
|
||||||
|
|
||||||
foreach ($this->accommodation->getAccommodationPrices() as $price) {
|
foreach ($this->accommodation->getAccommodationPrices() as $price) {
|
||||||
if ($price->getDateFrom() !== null) {
|
if (null !== $price->getDateFrom()) {
|
||||||
$dates[] = CarbonImmutable::instance($price->getDateFrom());
|
$dates[] = CarbonImmutable::instance($price->getDateFrom());
|
||||||
}
|
}
|
||||||
if ($price->getDateTo() !== null) {
|
if (null !== $price->getDateTo()) {
|
||||||
$dates[] = CarbonImmutable::instance($price->getDateTo());
|
$dates[] = CarbonImmutable::instance($price->getDateTo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($this->accommodation->getBoardServices() as $service) {
|
foreach ($this->accommodation->getBoardServices() as $service) {
|
||||||
if ($service->getDateFrom() !== null) {
|
if (null !== $service->getDateFrom()) {
|
||||||
$dates[] = CarbonImmutable::instance($service->getDateFrom());
|
$dates[] = CarbonImmutable::instance($service->getDateFrom());
|
||||||
}
|
}
|
||||||
if ($service->getDateTo() !== null) {
|
if (null !== $service->getDateTo()) {
|
||||||
$dates[] = CarbonImmutable::instance($service->getDateTo());
|
$dates[] = CarbonImmutable::instance($service->getDateTo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($this->accommodation->getAdditionalServices() as $service) {
|
foreach ($this->accommodation->getAdditionalServices() as $service) {
|
||||||
if ($service->getDateFrom() !== null) {
|
if (null !== $service->getDateFrom()) {
|
||||||
$dates[] = CarbonImmutable::instance($service->getDateFrom());
|
$dates[] = CarbonImmutable::instance($service->getDateFrom());
|
||||||
}
|
}
|
||||||
if ($service->getDateTo() !== null) {
|
if (null !== $service->getDateTo()) {
|
||||||
$dates[] = CarbonImmutable::instance($service->getDateTo());
|
$dates[] = CarbonImmutable::instance($service->getDateTo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,11 +198,11 @@ class Calendar
|
|||||||
$map = [];
|
$map = [];
|
||||||
|
|
||||||
foreach ($this->accommodation->getAccommodationPrices() as $price) {
|
foreach ($this->accommodation->getAccommodationPrices() as $price) {
|
||||||
if ($price->getDateFrom() === null || $price->getDateTo() === null) {
|
if (null === $price->getDateFrom() || null === $price->getDateTo()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$priceFormatted = number_format($price->getPricePerNight() / 100, 2, ',', '.') . ' €';
|
$priceFormatted = number_format($price->getPricePerNight() / 100, 2, ',', '.').' €';
|
||||||
$label = sprintf('Preis: %s/Nacht (min. %d Nächte)', $priceFormatted, $price->getMinNights());
|
$label = sprintf('Preis: %s/Nacht (min. %d Nächte)', $priceFormatted, $price->getMinNights());
|
||||||
|
|
||||||
$day = CarbonImmutable::instance($price->getDateFrom());
|
$day = CarbonImmutable::instance($price->getDateFrom());
|
||||||
@@ -216,7 +216,7 @@ class Calendar
|
|||||||
$key = $day->format('Y-m-d');
|
$key = $day->format('Y-m-d');
|
||||||
$map[$key] ??= ['price' => null, 'board' => null, 'additional' => null];
|
$map[$key] ??= ['price' => null, 'board' => null, 'additional' => null];
|
||||||
$existing = $map[$key]['price'];
|
$existing = $map[$key]['price'];
|
||||||
$replace = $existing === null
|
$replace = null === $existing
|
||||||
|| $priority > $existing['priority']
|
|| $priority > $existing['priority']
|
||||||
|| ($priority === $existing['priority'] && $span < $existing['span']);
|
|| ($priority === $existing['priority'] && $span < $existing['span']);
|
||||||
if ($replace) {
|
if ($replace) {
|
||||||
@@ -227,11 +227,11 @@ class Calendar
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->accommodation->getBoardServices() as $service) {
|
foreach ($this->accommodation->getBoardServices() as $service) {
|
||||||
if ($service->getDateFrom() === null || $service->getDateTo() === null) {
|
if (null === $service->getDateFrom() || null === $service->getDateTo()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$label = 'Verpflegung: ' . $service->getLabel();
|
$label = 'Verpflegung: '.$service->getLabel();
|
||||||
|
|
||||||
$day = CarbonImmutable::instance($service->getDateFrom());
|
$day = CarbonImmutable::instance($service->getDateFrom());
|
||||||
$end = CarbonImmutable::instance($service->getDateTo());
|
$end = CarbonImmutable::instance($service->getDateTo());
|
||||||
@@ -241,7 +241,7 @@ class Calendar
|
|||||||
while ($day <= $end && $day <= $limit) {
|
while ($day <= $end && $day <= $limit) {
|
||||||
$key = $day->format('Y-m-d');
|
$key = $day->format('Y-m-d');
|
||||||
$map[$key] ??= ['price' => null, 'board' => null, 'additional' => null];
|
$map[$key] ??= ['price' => null, 'board' => null, 'additional' => null];
|
||||||
if ($map[$key]['board'] === null || $span < $map[$key]['board']['span']) {
|
if (null === $map[$key]['board'] || $span < $map[$key]['board']['span']) {
|
||||||
$map[$key]['board'] = ['label' => $label, 'span' => $span];
|
$map[$key]['board'] = ['label' => $label, 'span' => $span];
|
||||||
}
|
}
|
||||||
$day = $day->addDay();
|
$day = $day->addDay();
|
||||||
@@ -249,14 +249,14 @@ class Calendar
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->accommodation->getAdditionalServices() as $service) {
|
foreach ($this->accommodation->getAdditionalServices() as $service) {
|
||||||
if ($service->getDateFrom() === null || $service->getDateTo() === null) {
|
if (null === $service->getDateFrom() || null === $service->getDateTo()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$typeLabel = $service->getType() !== null
|
$typeLabel = null !== $service->getType()
|
||||||
? self::TYPE_LABELS[$service->getType()->value]
|
? self::TYPE_LABELS[$service->getType()->value]
|
||||||
: '';
|
: '';
|
||||||
$label = sprintf('Zusatz: %s%s', $service->getLabel(), $typeLabel !== '' ? ' (' . $typeLabel . ')' : '');
|
$label = sprintf('Zusatz: %s%s', $service->getLabel(), '' !== $typeLabel ? ' ('.$typeLabel.')' : '');
|
||||||
|
|
||||||
$day = CarbonImmutable::instance($service->getDateFrom());
|
$day = CarbonImmutable::instance($service->getDateFrom());
|
||||||
$end = CarbonImmutable::instance($service->getDateTo());
|
$end = CarbonImmutable::instance($service->getDateTo());
|
||||||
@@ -266,7 +266,7 @@ class Calendar
|
|||||||
while ($day <= $end && $day <= $limit) {
|
while ($day <= $end && $day <= $limit) {
|
||||||
$key = $day->format('Y-m-d');
|
$key = $day->format('Y-m-d');
|
||||||
$map[$key] ??= ['price' => null, 'board' => null, 'additional' => null];
|
$map[$key] ??= ['price' => null, 'board' => null, 'additional' => null];
|
||||||
if ($map[$key]['additional'] === null || $span < $map[$key]['additional']['span']) {
|
if (null === $map[$key]['additional'] || $span < $map[$key]['additional']['span']) {
|
||||||
$map[$key]['additional'] = ['label' => $label, 'span' => $span];
|
$map[$key]['additional'] = ['label' => $label, 'span' => $span];
|
||||||
}
|
}
|
||||||
$day = $day->addDay();
|
$day = $day->addDay();
|
||||||
@@ -293,8 +293,8 @@ class Calendar
|
|||||||
$firstDow = ($start->dayOfWeek + 6) % 7;
|
$firstDow = ($start->dayOfWeek + 6) % 7;
|
||||||
|
|
||||||
$days = [];
|
$days = [];
|
||||||
for ($d = 1; $d <= $start->daysInMonth; $d++) {
|
for ($d = 1; $d <= $start->daysInMonth; ++$d) {
|
||||||
$dateStr = $month->format('Y-m') . '-' . str_pad((string) $d, 2, '0', STR_PAD_LEFT);
|
$dateStr = $month->format('Y-m').'-'.str_pad((string) $d, 2, '0', STR_PAD_LEFT);
|
||||||
$coverage = $map[$dateStr] ?? [];
|
$coverage = $map[$dateStr] ?? [];
|
||||||
|
|
||||||
$parts = [];
|
$parts = [];
|
||||||
@@ -325,7 +325,7 @@ class Calendar
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new CalendarMonth(
|
return new CalendarMonth(
|
||||||
label: self::MONTHS[$month->month] . ' ' . $month->year,
|
label: self::MONTHS[$month->month].' '.$month->year,
|
||||||
firstDow: $firstDow,
|
firstDow: $firstDow,
|
||||||
days: $days,
|
days: $days,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user