feat: consistent display of included services and prices
This commit is contained in:
@@ -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, ',', '.'));
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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, ',', '.'));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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']),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -67,8 +67,8 @@
|
||||
</td>
|
||||
{% if choiceData %}
|
||||
<td class="border border-primary-bg p-2 align-top w-24 text-right whitespace-nowrap">
|
||||
{% 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 %}
|
||||
</td>
|
||||
{% endif %}
|
||||
|
||||
@@ -51,9 +51,7 @@
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="border border-primary-bg p-2 align-top w-24 text-right whitespace-nowrap">
|
||||
{% if price is not null %}
|
||||
{{ price | format_currency('EUR') }}
|
||||
{% endif %}
|
||||
{{ price | format_service_price }}
|
||||
</td>
|
||||
<td class="border border-primary-bg p-2 align-top w-12 text-center">
|
||||
{% set childTooltip = child.vars.attr['data-tooltip']|default(null) %}
|
||||
@@ -84,9 +82,7 @@
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="border border-primary-bg p-2 align-top w-24 text-right whitespace-nowrap">
|
||||
{% if price is not null %}
|
||||
{{ price | format_currency('EUR') }}
|
||||
{% endif %}
|
||||
{{ price | format_service_price }}
|
||||
</td>
|
||||
<td class="border border-primary-bg p-2 align-top w-12 text-center">
|
||||
{% set fieldTooltip = checkboxField.vars.attr['data-tooltip']|default(null) %}
|
||||
@@ -124,9 +120,7 @@
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="border border-primary-bg p-2 align-top w-24 text-right whitespace-nowrap">
|
||||
{% if price is not null %}
|
||||
{{ price | format_currency('EUR') }}
|
||||
{% endif %}
|
||||
{{ price | format_service_price }}
|
||||
</td>
|
||||
<td class="border border-primary-bg p-2 align-top w-12 text-center">
|
||||
{% set childTooltip = child.vars.attr['data-tooltip']|default(null) %}
|
||||
@@ -364,11 +358,11 @@
|
||||
) }}
|
||||
{% endif %}
|
||||
</table>
|
||||
{% 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 #}
|
||||
<div class="p-2 text-sm text-gray-500">Bitte zuerst den Skipass auswählen</div>
|
||||
{% else %}
|
||||
{# No skipass field (e.g., baby participant) - rentals not available #}
|
||||
{# No skipass field or skipass selected but no matching rentals #}
|
||||
<div class="p-2 text-sm text-gray-500">Nicht wählbar</div>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
{{ servicePricing.participantCount }}x {{ servicePricing.label }}
|
||||
</td>
|
||||
<td class="px-2 pb-2 align-top w-24 text-right whitespace-nowrap">
|
||||
{{ servicePricing.totalPrice | format_currency('EUR') }}
|
||||
{{ servicePricing.totalPrice | format_service_price }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user