175 lines
6.2 KiB
PHP
175 lines
6.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Form\Service;
|
|
|
|
use App\BusProNet\Constants;
|
|
use App\BusProNet\Model\Booking;
|
|
use App\BusProNet\Model\Service;
|
|
use App\BusProNet\Model\Travel;
|
|
use App\Form\Model\BookingDto;
|
|
use App\Form\Model\ParticipantDto;
|
|
use App\Form\Service\ParticipantFieldOptionsProvider;
|
|
use App\Service\BookingPriceCalculator;
|
|
use App\Service\InsuranceManager;
|
|
use App\Service\ServiceAvailabilityCalculator;
|
|
use App\Service\ServiceLabelFormatter;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
|
|
|
/**
|
|
* Edit mode applies the availability rules instead of bypassing them.
|
|
*
|
|
* Edit used to skip availability entirely for every category except ski passes, so a course
|
|
* that had sold out or moved to 'Anfrage' was still offered. Selecting one made BusPro refuse
|
|
* the whole update ("Status der Leistung (A) ist unterschiedlich zum Status des Teilnehmers
|
|
* (F)"), because the participants of an existing booking are fixed at status 'F'.
|
|
*
|
|
* Courses stand in for the categories that render read-only rather than dropping the option.
|
|
*/
|
|
class ParticipantFieldOptionsProviderEditAvailabilityTest extends TestCase
|
|
{
|
|
private ParticipantFieldOptionsProvider $provider;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$translator = $this->createStub(TranslatorInterface::class);
|
|
$translator->method('trans')->willReturnArgument(0);
|
|
|
|
$this->provider = new ParticipantFieldOptionsProvider(
|
|
new ServiceAvailabilityCalculator(),
|
|
$this->createStub(InsuranceManager::class),
|
|
$this->createStub(BookingPriceCalculator::class),
|
|
new ServiceLabelFormatter(),
|
|
$translator
|
|
);
|
|
}
|
|
|
|
public function testOnRequestCourseIsReadonlyInEditMode(): void
|
|
{
|
|
$course = $this->createCourse(id: 1, status: Constants::STATUS_ON_REQUEST);
|
|
$bookingDto = $this->createEditBookingDto([$course]);
|
|
|
|
$attributes = $this->getChoiceAttributes($bookingDto, $course);
|
|
|
|
$this->assertArrayHasKey('readonly', $attributes);
|
|
$this->assertSame(
|
|
'Diese Leistung ist derzeit nur auf Anfrage buchbar. Bitte kontaktiere uns.',
|
|
$attributes['data-tooltip']
|
|
);
|
|
}
|
|
|
|
public function testSoldOutCourseIsReadonlyInEditMode(): void
|
|
{
|
|
$course = $this->createCourse(id: 1, available: 0);
|
|
$bookingDto = $this->createEditBookingDto([$course]);
|
|
|
|
$attributes = $this->getChoiceAttributes($bookingDto, $course);
|
|
|
|
$this->assertArrayHasKey('readonly', $attributes);
|
|
$this->assertSame('ausgebucht', $attributes['data-tooltip']);
|
|
}
|
|
|
|
public function testOnRequestCourseStaysSelectableWhenAlreadyBooked(): void
|
|
{
|
|
$course = $this->createCourse(id: 1, status: Constants::STATUS_ON_REQUEST);
|
|
$bookingDto = $this->createEditBookingDto([$course], heldServiceIds: [1]);
|
|
|
|
$attributes = $this->getChoiceAttributes($bookingDto, $course);
|
|
|
|
$this->assertArrayNotHasKey('readonly', $attributes);
|
|
$this->assertArrayNotHasKey('data-tooltip', $attributes);
|
|
}
|
|
|
|
public function testUnlimitedCourseIsNotReadonlyInEditMode(): void
|
|
{
|
|
// A null contingent means unlimited. The previous edit branch inverted this and made
|
|
// such a service read-only for anyone who did not already hold it.
|
|
$course = $this->createCourse(id: 1, available: null);
|
|
$bookingDto = $this->createEditBookingDto([$course]);
|
|
|
|
$attributes = $this->getChoiceAttributes($bookingDto, $course);
|
|
|
|
$this->assertArrayNotHasKey('readonly', $attributes);
|
|
}
|
|
|
|
public function testOnRequestCourseStaysSelectableInCreateMode(): void
|
|
{
|
|
$course = $this->createCourse(id: 1, status: Constants::STATUS_ON_REQUEST);
|
|
$bookingDto = $this->createBookingDto([$course]);
|
|
|
|
$this->assertSame(BookingDto::MODE_CREATE, $bookingDto->getMode());
|
|
|
|
$attributes = $this->getChoiceAttributes($bookingDto, $course);
|
|
|
|
$this->assertArrayNotHasKey('readonly', $attributes);
|
|
}
|
|
|
|
/** @return array<string, mixed> */
|
|
private function getChoiceAttributes(BookingDto $bookingDto, Service $service): array
|
|
{
|
|
$options = $this->provider->getFieldOptions('courses', $bookingDto, 0);
|
|
|
|
return $options['choice_attr']($service);
|
|
}
|
|
|
|
/** @param Service[] $courses */
|
|
private function createBookingDto(array $courses): BookingDto
|
|
{
|
|
$travel = new Travel();
|
|
$travel->dateFrom = new \DateTimeImmutable('2027-01-09');
|
|
$travel->dateTo = new \DateTimeImmutable('2027-01-16');
|
|
|
|
foreach ($courses as $course) {
|
|
$travel->additionalServices[$course->id] = $course;
|
|
}
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
|
|
$participant = new ParticipantDto();
|
|
$participant->index = 0;
|
|
$participant->dateOfBirth = new \DateTimeImmutable('1990-01-01');
|
|
$bookingDto->participants[0] = $participant;
|
|
|
|
return $bookingDto;
|
|
}
|
|
|
|
/**
|
|
* @param Service[] $courses
|
|
* @param list<int> $heldServiceIds IDs participant 0 already holds in the booking
|
|
*/
|
|
private function createEditBookingDto(array $courses, array $heldServiceIds = []): BookingDto
|
|
{
|
|
$bookingDto = $this->createBookingDto($courses);
|
|
|
|
$heldServices = [];
|
|
foreach ($heldServiceIds as $serviceId) {
|
|
$held = clone $bookingDto->travel->additionalServices[$serviceId];
|
|
$held->mapping = [0];
|
|
$heldServices[$serviceId] = $held;
|
|
}
|
|
|
|
// A non-null booking is what puts the DTO into edit mode
|
|
$booking = new Booking();
|
|
$booking->additionalServices = $heldServices;
|
|
$bookingDto->booking = $booking;
|
|
|
|
return $bookingDto;
|
|
}
|
|
|
|
private function createCourse(int $id, ?int $available = 10, string $status = Constants::STATUS_AVAILABLE): Service
|
|
{
|
|
$service = new Service();
|
|
$service->id = $id;
|
|
$service->label = sprintf('Kurs %d', $id);
|
|
$service->subType = Constants::TOKEN_COURSES;
|
|
$service->category = Constants::CATEGORY_ADDITIONAL;
|
|
$service->price = (float) $id;
|
|
$service->available = $available;
|
|
$service->status = $status;
|
|
|
|
return $service;
|
|
}
|
|
}
|