From b4c03160ca2b2f0c2c6ef32994aed2bd2e5640f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Thu, 11 Dec 2025 11:17:03 +0100 Subject: [PATCH] feat: consistent display of included services and prices --- src/BusProNet/Model/Pickup.php | 6 +++++- src/Form/BookingEditParticipantType.php | 12 +++++++++-- .../ParticipantFieldOptionsProvider.php | 6 +++++- src/Service/ParticipantCardDataService.php | 2 +- src/Service/ServicePricingCalculator.php | 4 ---- src/Twig/AppExtension.php | 1 + src/Twig/AppRuntime.php | 20 +++++++++++++++++++ templates/booking/_form_theme.html.twig | 4 ++-- templates/booking/_participant_form.html.twig | 18 ++++++----------- templates/booking/_summary.html.twig | 2 +- .../ParticipantCardDataServiceTest.php | 6 +++--- 11 files changed, 54 insertions(+), 27 deletions(-) diff --git a/src/BusProNet/Model/Pickup.php b/src/BusProNet/Model/Pickup.php index d4ae1c1..62c6a77 100644 --- a/src/BusProNet/Model/Pickup.php +++ b/src/BusProNet/Model/Pickup.php @@ -76,10 +76,14 @@ class Pickup { $label = $this->getLabel(); - if (null === $this->price || 0.0 === $this->price) { + if (null === $this->price) { return $label; } + if (0.0 === $this->price) { + return sprintf('%s (inkl.)', $label); + } + if ($this->price < 0) { return sprintf('%s (-%s€ Rabatt)', $label, number_format(abs($this->price), 2, ',', '.')); } diff --git a/src/Form/BookingEditParticipantType.php b/src/Form/BookingEditParticipantType.php index e147be3..39fb931 100644 --- a/src/Form/BookingEditParticipantType.php +++ b/src/Form/BookingEditParticipantType.php @@ -102,10 +102,14 @@ class BookingEditParticipantType extends AbstractType 'choice_value' => 'id', 'choice_label' => function (?Service $service) use ($participantIndex) { $price = $service->individualPrice[$participantIndex] ?? $service->price; - if (null === $price || 0.0 === $price) { + if (null === $price) { return $service->label; } + if (0.0 === $price) { + return sprintf('%s (inkl.)', $service->label); + } + return sprintf( '%s (%s €)', $service->label, @@ -256,10 +260,14 @@ class BookingEditParticipantType extends AbstractType $price = $pickup->price; - if (null === $price || 0.0 === $price) { + if (null === $price) { return $pickupLabel; } + if (0.0 === $price) { + return sprintf('%s (inkl.)', $pickupLabel); + } + return sprintf( '%s %s€', $pickupLabel, diff --git a/src/Form/Service/ParticipantFieldOptionsProvider.php b/src/Form/Service/ParticipantFieldOptionsProvider.php index 4a9bcdc..969867f 100644 --- a/src/Form/Service/ParticipantFieldOptionsProvider.php +++ b/src/Form/Service/ParticipantFieldOptionsProvider.php @@ -605,10 +605,14 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider return ''; } - if (null === $service->price || 0.0 === $service->price) { + 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, ',', '.')); diff --git a/src/Service/ParticipantCardDataService.php b/src/Service/ParticipantCardDataService.php index 9066fc8..1165870 100644 --- a/src/Service/ParticipantCardDataService.php +++ b/src/Service/ParticipantCardDataService.php @@ -86,7 +86,7 @@ class ParticipantCardDataService $name = trim($firstName.' '.$lastName); if ('' === $name) { - return 0 === $index ? 'Anmelder:in' : sprintf('Teilnehmer:in %d', $index); + return 0 === $index ? 'Anmelder:in' : 'Teilnehmer:in'; } return $name; diff --git a/src/Service/ServicePricingCalculator.php b/src/Service/ServicePricingCalculator.php index eed8e89..be19dfa 100644 --- a/src/Service/ServicePricingCalculator.php +++ b/src/Service/ServicePricingCalculator.php @@ -217,10 +217,6 @@ class ServicePricingCalculator $servicesBySubtypeAndSign = []; foreach ($serviceAggregation as $serviceData) { - if (0.0 === $serviceData['totalPrice']) { - continue; // Skip zero-price services - } - $subType = $serviceData['subType'] ?? 'other'; // Normalize rental subtypes to avoid duplicate sections diff --git a/src/Twig/AppExtension.php b/src/Twig/AppExtension.php index 86ee014..a5fe3f1 100644 --- a/src/Twig/AppExtension.php +++ b/src/Twig/AppExtension.php @@ -14,6 +14,7 @@ class AppExtension extends AbstractExtension new TwigFilter('file_size', [AppRuntime::class, 'formatBytes']), new TwigFilter('file_icon', [AppRuntime::class, 'fileIconFilter'], ['is_safe' => ['html']]), new TwigFilter('format_money', [AppRuntime::class, 'formatMoney']), + new TwigFilter('format_service_price', [AppRuntime::class, 'formatServicePrice']), new TwigFilter('map_gender', [AppRuntime::class, 'mapGender']), new TwigFilter('map_status', [AppRuntime::class, 'mapStatus']), new TwigFilter('map_country', [AppRuntime::class, 'mapCountry']), diff --git a/src/Twig/AppRuntime.php b/src/Twig/AppRuntime.php index 5081871..9c4a001 100644 --- a/src/Twig/AppRuntime.php +++ b/src/Twig/AppRuntime.php @@ -52,6 +52,26 @@ class AppRuntime implements RuntimeExtensionInterface return $this->intlExtension->formatCurrency($amount, 'EUR'); } + /** + * Formats a service price, showing "inkl." for zero-priced (included) services. + * + * @param float|int|null $price The price to format + * + * @return string The formatted price or "inkl." for zero/null prices + */ + public function formatServicePrice(float|int|null $price): string + { + if (null === $price) { + return ''; + } + + if (0 === $price || 0.0 === $price) { + return 'inkl.'; + } + + return $this->intlExtension->formatCurrency((float) $price, 'EUR'); + } + public function mapStatus(string $status): string { $status = strtoupper($status); diff --git a/templates/booking/_form_theme.html.twig b/templates/booking/_form_theme.html.twig index 578b69d..2509064 100644 --- a/templates/booking/_form_theme.html.twig +++ b/templates/booking/_form_theme.html.twig @@ -67,8 +67,8 @@ {% if choiceData %} - {% if choiceData.price is defined and choiceData.price is not null %} - {{ choiceData.price | format_currency('EUR') }} + {% if choiceData.price is defined %} + {{ choiceData.price | format_service_price }} {% endif %} {% endif %} diff --git a/templates/booking/_participant_form.html.twig b/templates/booking/_participant_form.html.twig index 070e35a..5bd70e6 100644 --- a/templates/booking/_participant_form.html.twig +++ b/templates/booking/_participant_form.html.twig @@ -51,9 +51,7 @@ {%- endif -%} - {% if price is not null %} - {{ price | format_currency('EUR') }} - {% endif %} + {{ price | format_service_price }} {% set childTooltip = child.vars.attr['data-tooltip']|default(null) %} @@ -84,9 +82,7 @@ {%- endif -%} - {% if price is not null %} - {{ price | format_currency('EUR') }} - {% endif %} + {{ price | format_service_price }} {% set fieldTooltip = checkboxField.vars.attr['data-tooltip']|default(null) %} @@ -124,9 +120,7 @@ {%- endif -%} - {% if price is not null %} - {{ price | format_currency('EUR') }} - {% endif %} + {{ price | format_service_price }} {% set childTooltip = child.vars.attr['data-tooltip']|default(null) %} @@ -364,11 +358,11 @@ ) }} {% endif %} - {% elseif form.skiPass is defined %} - {# Skipass field exists but no rentals yet - user needs to select skipass first #} + {% elseif form.skiPass is defined and not form.vars.data.participant.skiPass %} + {# Skipass field exists but no skipass selected yet #}
Bitte zuerst den Skipass auswählen
{% else %} - {# No skipass field (e.g., baby participant) - rentals not available #} + {# No skipass field or skipass selected but no matching rentals #}
Nicht wählbar
{% endif %} diff --git a/templates/booking/_summary.html.twig b/templates/booking/_summary.html.twig index 6a8b3b3..3347b0c 100644 --- a/templates/booking/_summary.html.twig +++ b/templates/booking/_summary.html.twig @@ -146,7 +146,7 @@ {{ servicePricing.participantCount }}x {{ servicePricing.label }} - {{ servicePricing.totalPrice | format_currency('EUR') }} + {{ servicePricing.totalPrice | format_service_price }} {% endfor %} diff --git a/tests/Service/ParticipantCardDataServiceTest.php b/tests/Service/ParticipantCardDataServiceTest.php index 6e72d67..f3ce443 100644 --- a/tests/Service/ParticipantCardDataServiceTest.php +++ b/tests/Service/ParticipantCardDataServiceTest.php @@ -289,7 +289,7 @@ class ParticipantCardDataServiceTest extends TestCase $this->assertEquals('500,00 €', $result[1]['price']); // Third participant (no name) - $this->assertEquals('Teilnehmer:in 2', $result[2]['name']); + $this->assertEquals('Teilnehmer:in', $result[2]['name']); $this->assertEquals('Doppelzimmer', $result[2]['roomName']); $this->assertEquals('480,00 €', $result[2]['price']); } @@ -347,8 +347,8 @@ class ParticipantCardDataServiceTest extends TestCase $result3 = $this->service->getCardData($bookingDto, 2); $this->assertEquals('Anmelder:in', $result1['name']); - $this->assertEquals('Teilnehmer:in 1', $result2['name']); - $this->assertEquals('Teilnehmer:in 2', $result3['name']); + $this->assertEquals('Teilnehmer:in', $result2['name']); + $this->assertEquals('Teilnehmer:in', $result3['name']); } public function testGetCardDataWithValidationReturnsValidCard(): void