feat: additional confirmation cycle and status for groups inquiries

This commit is contained in:
Björn Fromme
2026-08-18 12:48:17 +02:00
parent 5045846eed
commit 8dee65d0dc
28 changed files with 677 additions and 95 deletions
+137
View File
@@ -0,0 +1,137 @@
{#- Price breakdown for booking emails. Mirrors groups/booking/_offer_summary.html.twig, but
with inline styles: the email layout ships no table styling and no Tailwind.
Context: booking, priceBreakdown, currency (set in AccommodationBookingService). -#}
{% if priceBreakdown is not null and currency is not null %}
{% set cell = 'padding: 4px 0; text-align: left; vertical-align: top;' %}
{% set amount = 'padding: 4px 0; text-align: right; vertical-align: top; white-space: nowrap;' %}
{% set note = 'display: block; font-size: 12px; color: #6b7280; margin: 0;' %}
<h2>Preise</h2>
<table style="width: 100%; border-collapse: collapse; font-size: 14px;">
<tr>
<td style="{{ cell }}">Zeitraum</td>
<td style="{{ amount }}">
{{ booking.dateFrom|date('d.m.Y') }} {{ booking.dateTo|date('d.m.Y') }},
{{ booking.nights }} {{ booking.nights != 1 ? 'Nächte' : 'Nacht' }}
</td>
</tr>
<tr>
<td style="{{ cell }}">Anzahl Personen</td>
<td style="{{ amount }}">{{ booking.paxCount }}</td>
</tr>
{% if booking.minorsCount > 0 %}
<tr>
<td style="{{ cell }}">davon Kinder (03 J.)</td>
<td style="{{ amount }}">{{ booking.minorsCount }}</td>
</tr>
{% endif %}
{% if booking.childrenCount > 0 %}
<tr>
<td style="{{ cell }}">davon Kinder (4{{ booking.accommodation.maxAdolescentAge }} J.)</td>
<td style="{{ amount }}">{{ booking.childrenCount }}</td>
</tr>
{% endif %}
{% if priceBreakdown.basePrice > 0 %}
<tr>
<td style="{{ cell }}">
Basispreis
<span style="{{ note }}">für {{ priceBreakdown.includedPax }} Pers.</span>
</td>
<td style="{{ amount }}">{{ (priceBreakdown.basePrice / 100)|format_currency(currency) }}</td>
</tr>
{% endif %}
{% if priceBreakdown.additionalPersonsPrice > 0 %}
<tr>
<td style="{{ cell }}">
Aufpreis Personenzahl
<span style="{{ note }}">für {{ priceBreakdown.effectivePax - priceBreakdown.includedPax }} Pers.</span>
</td>
<td style="{{ amount }}">{{ (priceBreakdown.additionalPersonsPrice / 100)|format_currency(currency) }}</td>
</tr>
{% endif %}
{% if priceBreakdown.shortTermSurcharge > 0 %}
<tr>
<td style="{{ cell }}">Aufpreis Kurzzeit</td>
<td style="{{ amount }}">{{ (priceBreakdown.shortTermSurcharge / 100)|format_currency(currency) }}</td>
</tr>
{% endif %}
{% if priceBreakdown.boardPrice > 0 %}
<tr>
<td style="{{ cell }}">Verpflegung{% if booking.boardServiceLabel %} ({{ booking.boardServiceLabel }}){% endif %}</td>
<td style="{{ amount }}">{{ (priceBreakdown.boardPrice / 100)|format_currency(currency) }}</td>
</tr>
{% endif %}
{% if priceBreakdown.undersubscriptionSurcharge > 0 %}
<tr>
<td style="{{ cell }}">
Verpflegungs-Aufschlag
<span style="{{ note }}">für Gruppen unter {{ priceBreakdown.undersubscriptionThreshold }} Pers.</span>
</td>
<td style="{{ amount }}">{{ (priceBreakdown.undersubscriptionSurcharge / 100)|format_currency(currency) }}</td>
</tr>
{% endif %}
{% for service in priceBreakdown.serviceDetails %}
{% if service.price > 0 %}
<tr>
<td style="{{ cell }}">{{ service.label }}</td>
<td style="{{ amount }}">{{ (service.price / 100)|format_currency(currency) }}</td>
</tr>
{% endif %}
{% endfor %}
{% if priceBreakdown.runningCosts > 0 %}
<tr>
<td style="{{ cell }}">Strom- und Abfallgebühren</td>
<td style="{{ amount }}">{{ (priceBreakdown.runningCosts / 100)|format_currency(currency) }}</td>
</tr>
{% endif %}
<tr>
<td style="{{ cell }} border-top: 1px solid #d1d5db; font-weight: bold;">
Gesamtpreis
<span style="{{ note }} font-weight: normal;">zzgl. ortsabhängiger Gebühren</span>
</td>
<td style="{{ amount }} border-top: 1px solid #d1d5db; font-weight: bold;">
{{ (priceBreakdown.total / 100)|format_currency(currency) }}
</td>
</tr>
{% set accommodationDiscountAmount = booking.accommodationDiscount is not null
? ((priceBreakdown.basePrice + priceBreakdown.additionalPersonsPrice) * booking.accommodationDiscount / 100) | round
: 0 %}
{% set boardDiscountAmount = booking.boardServiceDiscount is not null
? (priceBreakdown.boardPrice * booking.boardServiceDiscount / 100) | round
: 0 %}
{% set servicesDiscountAmount = booking.additionalServicesDiscount is not null
? (priceBreakdown.servicesPrice * booking.additionalServicesDiscount / 100) | round
: 0 %}
{% set discountSum = accommodationDiscountAmount + boardDiscountAmount + servicesDiscountAmount %}
{% if booking.accommodationDiscount is not null %}
<tr>
<td style="{{ cell }}">Rabatt Unterkunft {{ booking.accommodationDiscount }}&nbsp;%</td>
<td style="{{ amount }}"> {{ (accommodationDiscountAmount / 100)|format_currency(currency) }}</td>
</tr>
{% endif %}
{% if booking.boardServiceDiscount is not null %}
<tr>
<td style="{{ cell }}">Rabatt Verpflegung {{ booking.boardServiceDiscount }}&nbsp;%</td>
<td style="{{ amount }}"> {{ (boardDiscountAmount / 100)|format_currency(currency) }}</td>
</tr>
{% endif %}
{% if booking.additionalServicesDiscount is not null %}
<tr>
<td style="{{ cell }}">Rabatt Zusatzleistungen {{ booking.additionalServicesDiscount }}&nbsp;%</td>
<td style="{{ amount }}"> {{ (servicesDiscountAmount / 100)|format_currency(currency) }}</td>
</tr>
{% endif %}
{% if discountSum > 0 %}
{#- The frozen snapshot wins where it exists; without one the discounted total has to
be derived from the very rows shown above, so the mail can never contradict itself. -#}
<tr>
<td style="{{ cell }} border-top: 1px solid #d1d5db; font-weight: bold;">Gesamtpreis nach Rabatt</td>
<td style="{{ amount }} border-top: 1px solid #d1d5db; font-weight: bold;">
{{ ((booking.totalPrice ?? (priceBreakdown.total - discountSum)) / 100)|format_currency(currency) }}
</td>
</tr>
{% endif %}
</table>
{% endif %}