feat: refactor participant card DTOs and labels
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Model;
|
||||
|
||||
/**
|
||||
* Typed participant card payload for create/edit overview rendering.
|
||||
*/
|
||||
final class ParticipantCardDataDto
|
||||
{
|
||||
/**
|
||||
* @param array<string> $errorMessages
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string $name,
|
||||
public readonly string $email,
|
||||
public readonly string $roomName,
|
||||
public readonly ParticipantCardPriceDto $price,
|
||||
public readonly bool $isCanceled,
|
||||
public readonly bool $isValid = true,
|
||||
public readonly array $errorMessages = [],
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy with validation state applied.
|
||||
*
|
||||
* @param array<string> $errorMessages
|
||||
*/
|
||||
public function withValidation(bool $isValid, array $errorMessages): self
|
||||
{
|
||||
return new self(
|
||||
name: $this->name,
|
||||
email: $this->email,
|
||||
roomName: $this->roomName,
|
||||
price: $this->price,
|
||||
isCanceled: $this->isCanceled,
|
||||
isValid: $isValid,
|
||||
errorMessages: $errorMessages,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Model;
|
||||
|
||||
/**
|
||||
* Typed price display state for a participant card.
|
||||
*/
|
||||
final class ParticipantCardPriceDto
|
||||
{
|
||||
public function __construct(
|
||||
public readonly ?float $amount,
|
||||
public readonly bool $showDash,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ use App\Form\Model\RoomSelectionDto;
|
||||
use App\Form\Service\Abstract\AbstractFieldOptionsProvider;
|
||||
use App\Service\BookingPriceCalculatorService;
|
||||
use App\Service\InsuranceService;
|
||||
use App\Service\ServiceLabelFormatter;
|
||||
use App\Service\ServiceAvailabilityCalculator;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
@@ -42,6 +43,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
private readonly ServiceAvailabilityCalculator $serviceAvailabilityCalculator,
|
||||
private readonly InsuranceService $insuranceService,
|
||||
private readonly BookingPriceCalculatorService $priceCalculatorService,
|
||||
private readonly ServiceLabelFormatter $serviceLabelFormatter,
|
||||
private readonly TranslatorInterface $translator,
|
||||
) {
|
||||
parent::__construct();
|
||||
@@ -303,11 +305,8 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
if (null === $service) {
|
||||
return null;
|
||||
}
|
||||
$priceLabel = (null === $service->price || 0.0 === $service->price)
|
||||
? 'inkl.'
|
||||
: number_format($service->price, 2, ',', '.').' €';
|
||||
|
||||
return $service->label.' ('.$priceLabel.')';
|
||||
return $this->serviceLabelFormatter->formatServiceLabel($service);
|
||||
},
|
||||
'choice_attr' => function (?Service $service) use ($bookingDto, $participantIndex) {
|
||||
if (null === $service) {
|
||||
@@ -405,7 +404,10 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
|
||||
// Rental insurance field provider - provides rental insurance options when rental services are selected
|
||||
$this->fieldOptionProviders['rentalInsurance'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => $this->getRentalInsuranceCheckboxLabel($bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_RENTAL_INSURANCE, true)),
|
||||
'label' => $this->serviceLabelFormatter->formatServiceLabelForServices(
|
||||
Constants::SERVICE_LABELS[Constants::TOKEN_RENTAL_INSURANCE],
|
||||
$bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_RENTAL_INSURANCE, true)
|
||||
),
|
||||
'required' => false,
|
||||
'property_path' => 'rentalInsuranceSelected',
|
||||
'help' => $this->getRentalInsuranceDescription($bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_RENTAL_INSURANCE, true)),
|
||||
@@ -645,7 +647,10 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
}
|
||||
|
||||
return [
|
||||
'label' => $this->getParkingCheckboxLabel($parkingServices),
|
||||
'label' => $this->serviceLabelFormatter->formatServiceLabelForServices(
|
||||
Constants::SERVICE_LABELS[Constants::TOKEN_PARKING],
|
||||
$parkingServices
|
||||
),
|
||||
'required' => false,
|
||||
];
|
||||
};
|
||||
@@ -833,56 +838,6 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats service label with pricing information.
|
||||
*
|
||||
* @param Service|null $service The service to format
|
||||
*
|
||||
* @return string The formatted label
|
||||
*/
|
||||
private function formatServiceLabelWithPrice(?Service $service): string
|
||||
{
|
||||
if (null === $service) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (null === $service->price) {
|
||||
return $service->label;
|
||||
}
|
||||
|
||||
if (0.0 === $service->price) {
|
||||
return sprintf('%s (inkl.)', $service->label);
|
||||
}
|
||||
|
||||
if ($service->price < 0) {
|
||||
// Negative prices are discounts
|
||||
return sprintf('%s (-%s€ Rabatt)', $service->label, number_format(abs($service->price), 2, ',', '.'));
|
||||
}
|
||||
|
||||
return sprintf('%s (€%s)', $service->label, number_format($service->price, 2, ',', '.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parking checkbox label with pricing information.
|
||||
*
|
||||
* Creates a checkbox label for the single parking service including pricing.
|
||||
* Since there's only ever one parking type, we take the first available service.
|
||||
*
|
||||
* @param array $parkingServices Array of available parking services
|
||||
*
|
||||
* @return string The formatted checkbox label with pricing
|
||||
*/
|
||||
private function getParkingCheckboxLabel(array $parkingServices): string
|
||||
{
|
||||
if (empty($parkingServices)) {
|
||||
return Constants::SERVICE_LABELS[Constants::TOKEN_PARKING];
|
||||
}
|
||||
|
||||
$parkingService = reset($parkingServices); // Get the first (and only) parking service
|
||||
|
||||
return $this->formatServiceLabelWithPrice($parkingService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a service should be rendered as read-only due to unavailability.
|
||||
*
|
||||
@@ -1154,19 +1109,6 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates label for rental insurance checkbox including pricing information.
|
||||
*/
|
||||
private function getRentalInsuranceCheckboxLabel(array $rentalInsuranceServices): string
|
||||
{
|
||||
if (empty($rentalInsuranceServices)) {
|
||||
return Constants::SERVICE_LABELS[Constants::TOKEN_RENTAL_INSURANCE];
|
||||
}
|
||||
$rentalInsuranceService = reset($rentalInsuranceServices); // Get the first (and only) rental insurance service
|
||||
|
||||
return $this->formatServiceLabelWithPrice($rentalInsuranceService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rental insurance description for help text.
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,8 @@ declare(strict_types=1);
|
||||
namespace App\Service;
|
||||
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantCardDataDto;
|
||||
use App\Form\Model\ParticipantCardPriceDto;
|
||||
use App\Form\Model\ParticipantEditDto;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
@@ -25,9 +27,9 @@ class ParticipantCardDataService
|
||||
/**
|
||||
* Get card data for a single participant.
|
||||
*
|
||||
* @return array{name: string, email: string, roomName: string, price: string, isCanceled: bool}
|
||||
* @return ParticipantCardDataDto
|
||||
*/
|
||||
public function getCardData(BookingDto $bookingDto, int $index): array
|
||||
public function getCardData(BookingDto $bookingDto, int $index): ParticipantCardDataDto
|
||||
{
|
||||
$participant = $bookingDto->participants[$index] ?? null;
|
||||
|
||||
@@ -44,31 +46,31 @@ class ParticipantCardDataService
|
||||
// Extract room name
|
||||
$roomName = $this->getRoomName($bookingDto, $participant);
|
||||
|
||||
// Calculate and format individual price
|
||||
$price = $this->getFormattedPrice($bookingDto, $index);
|
||||
// Calculate pricing state for display
|
||||
$priceData = $this->getPriceData($bookingDto, $index);
|
||||
|
||||
// Check if participant is canceled
|
||||
$isCanceled = $participant->isCanceled();
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
'email' => $email,
|
||||
'roomName' => $roomName,
|
||||
'price' => $price,
|
||||
'isCanceled' => $isCanceled,
|
||||
];
|
||||
return new ParticipantCardDataDto(
|
||||
name: $name,
|
||||
email: $email,
|
||||
roomName: $roomName,
|
||||
price: $priceData,
|
||||
isCanceled: $isCanceled,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get card data for all participants.
|
||||
*
|
||||
* @return array<int, array{name: string, email: string, roomName: string, price: string, isCanceled: bool}>
|
||||
* @return array<int, ParticipantCardDataDto>
|
||||
*/
|
||||
public function getAllCardsData(BookingDto $bookingDto): array
|
||||
{
|
||||
$cardsData = [];
|
||||
|
||||
foreach ($bookingDto->participants as $index => $participant) {
|
||||
foreach ($bookingDto->participants as $index => $_participant) {
|
||||
$cardsData[$index] = $this->getCardData($bookingDto, $index);
|
||||
}
|
||||
|
||||
@@ -118,12 +120,14 @@ class ParticipantCardDataService
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate and format individual participant price.
|
||||
* Calculate individual participant price display state.
|
||||
*
|
||||
* Returns a dash (-) when the price is zero and no room is assigned,
|
||||
* Returns a dash marker when the price is zero and no room is assigned,
|
||||
* indicating incomplete configuration rather than a zero-cost booking.
|
||||
*
|
||||
* @return ParticipantCardPriceDto
|
||||
*/
|
||||
private function getFormattedPrice(BookingDto $bookingDto, int $index): string
|
||||
private function getPriceData(BookingDto $bookingDto, int $index): ParticipantCardPriceDto
|
||||
{
|
||||
// Check if canceled (only possible in edit mode when booking property is set)
|
||||
$isCanceled = ($bookingDto->booking?->participantsStatus[$index] ?? null) === 'S';
|
||||
@@ -131,7 +135,7 @@ class ParticipantCardDataService
|
||||
if (true === $isCanceled) {
|
||||
// Calculate surcharge total for canceled participant
|
||||
if (null === $bookingDto->booking) {
|
||||
return '-';
|
||||
return new ParticipantCardPriceDto(null, true);
|
||||
}
|
||||
|
||||
$surcharges = $bookingDto->booking->getSurchargesForParticipant($index);
|
||||
@@ -143,10 +147,10 @@ class ParticipantCardDataService
|
||||
|
||||
// Show nothing if no surcharges, otherwise show "x,xx € Stornokosten"
|
||||
if (0.0 === $surchargeTotal) {
|
||||
return '-';
|
||||
return new ParticipantCardPriceDto(null, true);
|
||||
}
|
||||
|
||||
return number_format($surchargeTotal, 2, ',', '.').' € Stornokosten';
|
||||
return new ParticipantCardPriceDto($surchargeTotal, false);
|
||||
}
|
||||
|
||||
// For active participants: existing price calculation logic
|
||||
@@ -157,18 +161,18 @@ class ParticipantCardDataService
|
||||
// Display dash when price is zero and no room assigned (incomplete configuration)
|
||||
$participant = $bookingDto->participants[$index] ?? null;
|
||||
if (0.0 === $price && (null === $participant || null === $participant->assignedRoomId)) {
|
||||
return '-';
|
||||
return new ParticipantCardPriceDto(null, true);
|
||||
}
|
||||
|
||||
return number_format($price, 2, ',', '.').' €';
|
||||
return new ParticipantCardPriceDto($price, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get card data for a single participant with validation state.
|
||||
*
|
||||
* @return array{name: string, email: string, roomName: string, price: string, isCanceled: bool, isValid: bool, errorMessages: array<string>}
|
||||
* @return ParticipantCardDataDto
|
||||
*/
|
||||
public function getCardDataWithValidation(BookingDto $bookingDto, int $index): array
|
||||
public function getCardDataWithValidation(BookingDto $bookingDto, int $index): ParticipantCardDataDto
|
||||
{
|
||||
$participant = $bookingDto->participants[$index] ?? null;
|
||||
|
||||
@@ -199,22 +203,19 @@ class ParticipantCardDataService
|
||||
$errorMessages[] = $violation->getMessage();
|
||||
}
|
||||
|
||||
return array_merge($cardData, [
|
||||
'isValid' => $isValid,
|
||||
'errorMessages' => $errorMessages,
|
||||
]);
|
||||
return $cardData->withValidation($isValid, $errorMessages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get card data for all participants with validation state.
|
||||
*
|
||||
* @return array<int, array{name: string, email: string, roomName: string, price: string, isCanceled: bool, isValid: bool, errorMessages: array<string>}>
|
||||
* @return array<int, ParticipantCardDataDto>
|
||||
*/
|
||||
public function getAllCardsDataWithValidation(BookingDto $bookingDto): array
|
||||
{
|
||||
$cardsData = [];
|
||||
|
||||
foreach ($bookingDto->participants as $index => $participant) {
|
||||
foreach ($bookingDto->participants as $index => $_participant) {
|
||||
$cardsData[$index] = $this->getCardDataWithValidation($bookingDto, $index);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\BusProNet\Model\Service;
|
||||
|
||||
/**
|
||||
* Formats service labels for form and UI display.
|
||||
*/
|
||||
final class ServiceLabelFormatter
|
||||
{
|
||||
public function formatServiceLabel(Service $service): string
|
||||
{
|
||||
$label = $service->label ?? '';
|
||||
|
||||
if (null === $service->price) {
|
||||
return $label;
|
||||
}
|
||||
|
||||
if (0.0 === $service->price) {
|
||||
return sprintf('%s (inkl.)', $label);
|
||||
}
|
||||
|
||||
if ($service->price < 0) {
|
||||
return sprintf('%s (-%s€ Rabatt)', $label, number_format(abs($service->price), 2, ',', '.'));
|
||||
}
|
||||
|
||||
return sprintf('%s (€%s)', $label, number_format($service->price, 2, ',', '.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, Service> $services
|
||||
*/
|
||||
public function formatServiceLabelForServices(string $fallbackLabel, array $services): string
|
||||
{
|
||||
$service = reset($services);
|
||||
if (false === $service) {
|
||||
return $fallbackLabel;
|
||||
}
|
||||
|
||||
if (!($service instanceof Service)) {
|
||||
return $fallbackLabel;
|
||||
}
|
||||
|
||||
return $this->formatServiceLabel($service);
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,13 @@
|
||||
<div class="text-gray-600" {{ qa_attribute('participant-room-name', index) }}>
|
||||
{{ cardData.roomName }}
|
||||
<br>
|
||||
{{ cardData.price }}
|
||||
{% if cardData.price.showDash %}
|
||||
-
|
||||
{% elseif cardData.isCanceled %}
|
||||
{{ cardData.price.amount|format_currency('EUR') }} Stornokosten
|
||||
{% else %}
|
||||
{{ cardData.price.amount|format_currency('EUR') }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ use App\Form\Model\ParticipantDto;
|
||||
use App\Form\Service\ParticipantFieldOptionsProvider;
|
||||
use App\Service\BookingPriceCalculatorService;
|
||||
use App\Service\InsuranceService;
|
||||
use App\Service\ServiceLabelFormatter;
|
||||
use App\Service\ServiceAvailabilityCalculator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
@@ -27,12 +28,14 @@ class ParticipantFieldOptionsProviderBabyTest extends TestCase
|
||||
$this->serviceAvailabilityCalculator = $this->createMock(ServiceAvailabilityCalculator::class);
|
||||
$insuranceService = $this->createMock(InsuranceService::class);
|
||||
$priceCalculatorService = $this->createMock(BookingPriceCalculatorService::class);
|
||||
$serviceLabelFormatter = new ServiceLabelFormatter();
|
||||
$translator = $this->createMock(TranslatorInterface::class);
|
||||
|
||||
$this->provider = new ParticipantFieldOptionsProvider(
|
||||
$this->serviceAvailabilityCalculator,
|
||||
$insuranceService,
|
||||
$priceCalculatorService,
|
||||
$serviceLabelFormatter,
|
||||
$translator
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use App\Form\Model\ParticipantDto;
|
||||
use App\Form\Service\ParticipantFieldOptionsProvider;
|
||||
use App\Service\BookingPriceCalculatorService;
|
||||
use App\Service\InsuranceService;
|
||||
use App\Service\ServiceLabelFormatter;
|
||||
use App\Service\ServiceAvailabilityCalculator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
@@ -30,12 +31,14 @@ class ParticipantFieldOptionsProviderMandatoryServiceTest extends TestCase
|
||||
$serviceAvailabilityCalculator = $this->createMock(ServiceAvailabilityCalculator::class);
|
||||
$insuranceService = $this->createMock(InsuranceService::class);
|
||||
$priceCalculatorService = $this->createMock(BookingPriceCalculatorService::class);
|
||||
$serviceLabelFormatter = new ServiceLabelFormatter();
|
||||
$translator = $this->createMock(TranslatorInterface::class);
|
||||
|
||||
$this->provider = new ParticipantFieldOptionsProvider(
|
||||
$serviceAvailabilityCalculator,
|
||||
$insuranceService,
|
||||
$priceCalculatorService,
|
||||
$serviceLabelFormatter,
|
||||
$translator
|
||||
);
|
||||
}
|
||||
@@ -110,6 +113,28 @@ class ParticipantFieldOptionsProviderMandatoryServiceTest extends TestCase
|
||||
$this->assertArrayNotHasKey('checked', $attributes);
|
||||
}
|
||||
|
||||
public function testParkingLabelIncludesPrice(): void
|
||||
{
|
||||
$parkingService = new Service();
|
||||
$parkingService->id = 3;
|
||||
$parkingService->label = 'Parkplatz';
|
||||
$parkingService->subType = Constants::TOKEN_PARKING;
|
||||
$parkingService->available = 10;
|
||||
$parkingService->price = 12.5;
|
||||
|
||||
$travel = $this->createTravelWithAdditionalServices([$parkingService]);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
|
||||
$participant = new ParticipantDto();
|
||||
$participant->index = 0;
|
||||
$participant->dateOfBirth = $travel->dateFrom->modify('-25 years');
|
||||
$bookingDto->participants[0] = $participant;
|
||||
|
||||
$options = $this->provider->getFieldOptions('parking', $bookingDto, 0);
|
||||
|
||||
$this->assertSame('Parkplatz (€12,50)', $options['label']);
|
||||
}
|
||||
|
||||
public function testNullParticipantReturnsEmptyOptions(): void
|
||||
{
|
||||
$mandatoryService = $this->createMandatoryService(1, 'Ortstaxe');
|
||||
|
||||
@@ -23,7 +23,10 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
{
|
||||
$this->priceCalculator = $this->createMock(BookingPriceCalculatorService::class);
|
||||
$this->validator = $this->createMock(ValidatorInterface::class);
|
||||
$this->service = new ParticipantCardDataService($this->priceCalculator, $this->validator);
|
||||
$this->service = new ParticipantCardDataService(
|
||||
$this->priceCalculator,
|
||||
$this->validator
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetCardDataWithFullParticipantData(): void
|
||||
@@ -55,9 +58,10 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
|
||||
$result = $this->service->getCardData($bookingDto, 0);
|
||||
|
||||
$this->assertEquals('Max Mustermann', $result['name']);
|
||||
$this->assertEquals('Doppelzimmer', $result['roomName']);
|
||||
$this->assertEquals('450,50 €', $result['price']);
|
||||
$this->assertSame('Max Mustermann', $result->name);
|
||||
$this->assertSame('Doppelzimmer', $result->roomName);
|
||||
$this->assertSame(450.50, $result->price->amount);
|
||||
$this->assertFalse($result->price->showDash);
|
||||
}
|
||||
|
||||
public function testGetCardDataWithPartialName(): void
|
||||
@@ -79,7 +83,7 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
|
||||
$result = $this->service->getCardData($bookingDto, 0);
|
||||
|
||||
$this->assertEquals('Max', $result['name']);
|
||||
$this->assertSame('Max', $result->name);
|
||||
}
|
||||
|
||||
public function testGetCardDataWithNoName(): void
|
||||
@@ -101,7 +105,7 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
|
||||
$result = $this->service->getCardData($bookingDto, 0);
|
||||
|
||||
$this->assertEquals('Anmelder:in', $result['name']);
|
||||
$this->assertSame('Anmelder:in', $result->name);
|
||||
}
|
||||
|
||||
public function testGetCardDataWithEmptyName(): void
|
||||
@@ -123,7 +127,7 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
|
||||
$result = $this->service->getCardData($bookingDto, 0);
|
||||
|
||||
$this->assertEquals('Anmelder:in', $result['name']);
|
||||
$this->assertSame('Anmelder:in', $result->name);
|
||||
}
|
||||
|
||||
public function testGetCardDataWithNoRoomAssignment(): void
|
||||
@@ -145,7 +149,7 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
|
||||
$result = $this->service->getCardData($bookingDto, 0);
|
||||
|
||||
$this->assertEquals('Kein Zimmer zugewiesen', $result['roomName']);
|
||||
$this->assertSame('Kein Zimmer zugewiesen', $result->roomName);
|
||||
}
|
||||
|
||||
public function testGetCardDataWithUnknownRoom(): void
|
||||
@@ -167,7 +171,7 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
|
||||
$result = $this->service->getCardData($bookingDto, 0);
|
||||
|
||||
$this->assertEquals('Unbekanntes Zimmer', $result['roomName']);
|
||||
$this->assertSame('Unbekanntes Zimmer', $result->roomName);
|
||||
}
|
||||
|
||||
public function testGetCardDataWithZeroPriceAndNoRoom(): void
|
||||
@@ -190,7 +194,8 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
$result = $this->service->getCardData($bookingDto, 0);
|
||||
|
||||
// When no room assigned and price is zero, display dash (incomplete configuration)
|
||||
$this->assertEquals('-', $result['price']);
|
||||
$this->assertNull($result->price->amount);
|
||||
$this->assertTrue($result->price->showDash);
|
||||
}
|
||||
|
||||
public function testGetCardDataWithZeroPriceButRoomAssigned(): void
|
||||
@@ -219,7 +224,8 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
$result = $this->service->getCardData($bookingDto, 0);
|
||||
|
||||
// When room is assigned but price is zero, display formatted zero price
|
||||
$this->assertEquals('0,00 €', $result['price']);
|
||||
$this->assertSame(0.0, $result->price->amount);
|
||||
$this->assertFalse($result->price->showDash);
|
||||
}
|
||||
|
||||
public function testGetCardDataWithInvalidIndex(): void
|
||||
@@ -279,19 +285,22 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
$this->assertCount(3, $result);
|
||||
|
||||
// First participant
|
||||
$this->assertEquals('Max Mustermann', $result[0]['name']);
|
||||
$this->assertEquals('Einzelzimmer', $result[0]['roomName']);
|
||||
$this->assertEquals('450,00 €', $result[0]['price']);
|
||||
$this->assertSame('Max Mustermann', $result[0]->name);
|
||||
$this->assertSame('Einzelzimmer', $result[0]->roomName);
|
||||
$this->assertSame(450.0, $result[0]->price->amount);
|
||||
$this->assertFalse($result[0]->price->showDash);
|
||||
|
||||
// Second participant
|
||||
$this->assertEquals('Anna Schmidt', $result[1]['name']);
|
||||
$this->assertEquals('Doppelzimmer', $result[1]['roomName']);
|
||||
$this->assertEquals('500,00 €', $result[1]['price']);
|
||||
$this->assertSame('Anna Schmidt', $result[1]->name);
|
||||
$this->assertSame('Doppelzimmer', $result[1]->roomName);
|
||||
$this->assertSame(500.0, $result[1]->price->amount);
|
||||
$this->assertFalse($result[1]->price->showDash);
|
||||
|
||||
// Third participant (no name)
|
||||
$this->assertEquals('Teilnehmer:in', $result[2]['name']);
|
||||
$this->assertEquals('Doppelzimmer', $result[2]['roomName']);
|
||||
$this->assertEquals('480,00 €', $result[2]['price']);
|
||||
$this->assertSame('Teilnehmer:in', $result[2]->name);
|
||||
$this->assertSame('Doppelzimmer', $result[2]->roomName);
|
||||
$this->assertSame(480.0, $result[2]->price->amount);
|
||||
$this->assertFalse($result[2]->price->showDash);
|
||||
}
|
||||
|
||||
public function testGetAllCardsDataWithEmptyParticipants(): void
|
||||
@@ -323,7 +332,8 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
|
||||
$result = $this->service->getCardData($bookingDto, 0);
|
||||
|
||||
$this->assertEquals('1.234,56 €', $result['price']);
|
||||
$this->assertSame(1234.56, $result->price->amount);
|
||||
$this->assertFalse($result->price->showDash);
|
||||
}
|
||||
|
||||
public function testFallbackNameIndexingIsOneBasedNotZeroBased(): void
|
||||
@@ -346,9 +356,9 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
$result2 = $this->service->getCardData($bookingDto, 1);
|
||||
$result3 = $this->service->getCardData($bookingDto, 2);
|
||||
|
||||
$this->assertEquals('Anmelder:in', $result1['name']);
|
||||
$this->assertEquals('Teilnehmer:in', $result2['name']);
|
||||
$this->assertEquals('Teilnehmer:in', $result3['name']);
|
||||
$this->assertSame('Anmelder:in', $result1->name);
|
||||
$this->assertSame('Teilnehmer:in', $result2->name);
|
||||
$this->assertSame('Teilnehmer:in', $result3->name);
|
||||
}
|
||||
|
||||
public function testGetCardDataWithValidationReturnsValidCard(): void
|
||||
@@ -384,13 +394,14 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
->method('validate')
|
||||
->willReturn($violations);
|
||||
|
||||
$result = $this->service->getCardDataWithValidation($bookingDto, 0, ['booking_create']);
|
||||
$result = $this->service->getCardDataWithValidation($bookingDto, 0);
|
||||
|
||||
$this->assertEquals('Max Mustermann', $result['name']);
|
||||
$this->assertEquals('Doppelzimmer', $result['roomName']);
|
||||
$this->assertEquals('450,50 €', $result['price']);
|
||||
$this->assertTrue($result['isValid']);
|
||||
$this->assertEmpty($result['errorMessages']);
|
||||
$this->assertSame('Max Mustermann', $result->name);
|
||||
$this->assertSame('Doppelzimmer', $result->roomName);
|
||||
$this->assertSame(450.50, $result->price->amount);
|
||||
$this->assertFalse($result->price->showDash);
|
||||
$this->assertTrue($result->isValid);
|
||||
$this->assertEmpty($result->errorMessages);
|
||||
}
|
||||
|
||||
public function testGetCardDataWithValidationReturnsInvalidCard(): void
|
||||
@@ -424,12 +435,12 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
->method('validate')
|
||||
->willReturn($violations);
|
||||
|
||||
$result = $this->service->getCardDataWithValidation($bookingDto, 0, ['booking_create']);
|
||||
$result = $this->service->getCardDataWithValidation($bookingDto, 0);
|
||||
|
||||
$this->assertEquals('Max Mustermann', $result['name']);
|
||||
$this->assertFalse($result['isValid']);
|
||||
$this->assertCount(1, $result['errorMessages']);
|
||||
$this->assertEquals('Diese E-Mail Adresse wird bereits von einem anderen Teilnehmer verwendet', $result['errorMessages'][0]);
|
||||
$this->assertSame('Max Mustermann', $result->name);
|
||||
$this->assertFalse($result->isValid);
|
||||
$this->assertCount(1, $result->errorMessages);
|
||||
$this->assertSame('Diese E-Mail Adresse wird bereits von einem anderen Teilnehmer verwendet', $result->errorMessages[0]);
|
||||
}
|
||||
|
||||
public function testGetAllCardsDataWithValidationReturnsAllCards(): void
|
||||
@@ -472,12 +483,12 @@ class ParticipantCardDataServiceTest extends TestCase
|
||||
->method('validate')
|
||||
->willReturn($violations);
|
||||
|
||||
$result = $this->service->getAllCardsDataWithValidation($bookingDto, ['booking_create']);
|
||||
$result = $this->service->getAllCardsDataWithValidation($bookingDto);
|
||||
|
||||
$this->assertCount(2, $result);
|
||||
$this->assertTrue($result[0]['isValid']);
|
||||
$this->assertTrue($result[1]['isValid']);
|
||||
$this->assertEquals('Max Mustermann', $result[0]['name']);
|
||||
$this->assertEquals('Anna Schmidt', $result[1]['name']);
|
||||
$this->assertTrue($result[0]->isValid);
|
||||
$this->assertTrue($result[1]->isValid);
|
||||
$this->assertSame('Max Mustermann', $result[0]->name);
|
||||
$this->assertSame('Anna Schmidt', $result[1]->name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Service;
|
||||
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Service\ServiceLabelFormatter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ServiceLabelFormatterTest extends TestCase
|
||||
{
|
||||
private ServiceLabelFormatter $formatter;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->formatter = new ServiceLabelFormatter();
|
||||
}
|
||||
|
||||
public function testFormatServiceLabelReturnsLabelWithoutPrice(): void
|
||||
{
|
||||
$service = new Service();
|
||||
$service->label = 'Skipass';
|
||||
$service->price = null;
|
||||
|
||||
$this->assertSame('Skipass', $this->formatter->formatServiceLabel($service));
|
||||
}
|
||||
|
||||
public function testFormatServiceLabelFormatsIncludedService(): void
|
||||
{
|
||||
$service = new Service();
|
||||
$service->label = 'Ortstaxe';
|
||||
$service->price = 0.0;
|
||||
|
||||
$this->assertSame('Ortstaxe (inkl.)', $this->formatter->formatServiceLabel($service));
|
||||
}
|
||||
|
||||
public function testFormatServiceLabelFormatsPositivePrice(): void
|
||||
{
|
||||
$service = new Service();
|
||||
$service->label = 'Parkplatz';
|
||||
$service->price = 12.5;
|
||||
|
||||
$this->assertSame('Parkplatz (€12,50)', $this->formatter->formatServiceLabel($service));
|
||||
}
|
||||
|
||||
public function testFormatServiceLabelFormatsNegativePrice(): void
|
||||
{
|
||||
$service = new Service();
|
||||
$service->label = 'Rabatt';
|
||||
$service->price = -8.0;
|
||||
|
||||
$this->assertSame('Rabatt (-8,00€ Rabatt)', $this->formatter->formatServiceLabel($service));
|
||||
}
|
||||
|
||||
public function testFormatServiceLabelForServicesFallsBackWhenEmpty(): void
|
||||
{
|
||||
$this->assertSame(
|
||||
'Verpflegung',
|
||||
$this->formatter->formatServiceLabelForServices('Verpflegung', [])
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user