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 %}
@@ -26,4 +26,6 @@
<td>{{ booking.dateTo|date('d.m.Y') }}</td>
</tr>
</table>
{% include 'email/_price_breakdown.html.twig' %}
{% endblock %}
@@ -1,37 +1,25 @@
{% extends 'email/layout.html.twig' %}
{% block body %}
{% set info_url = 'https://www.ep-reisen.de/reisen-fuer-gruppen/infos-zusatzleistungen/reiseinfos/ablauf-und-fristen-eurer-buchung/ablauf-und-fristen-eurer-buchung-fuer-sommeraufenthalte/' %}
{# Two states only: the office has published an offer (access link present), or the
inquiry has just arrived and the offer still has to be prepared. #}
{% if accessLink %}
<h1>
{{ booking.accepted ? 'Deine Buchung ist bestätigt' : 'Dein Angebot ist bereit' }}
Dein Angebot ist bereit
</h1>
<p>
Hallo {{ booking.firstName }},
</p>
{% if not booking.accepted %}
<p>
vielen Dank! Hiermit bestätigen wir den Eingang deiner Buchung.
</p>
<p>
Wir übergeben die Buchung nun für die Detailabstimmung an die Kollegen und Kolleginnen in der
Gruppenabwicklung. Sie werden sich rechtzeitig bei Euch melden und alle weiteren Details mit euch
besprechen. Keine Sorge, sollte es sich ein wenig verzögern, der Termin ist fest für euch reserviert und
geblockt! Wie es danach für euch weitergeht, könnt ihr euch <a href="{{ info_url }}" class="underline">hier schon einmal anschauen</a>.
</p>
{% else %}
<p>
vielen Dank für deine Anfrage! Wir melden uns so schnell wie möglich bei dir mit deinem persönlichen
Angebot.
</p>
{% endif %}
<p>
vielen Dank für deine Anfrage! Dein persönliches Angebot ist fertig. Schau es dir in Ruhe an und buche
verbindlich, wenn alles passt. Wir prüfen die Buchung anschließend und bestätigen sie dir per E-Mail.
</p>
<p>
<a href="{{ accessLink }}" class="button">
{{ booking.accepted ? 'Buchung ansehen' : 'Angebot ansehen & bestätigen' }}
Angebot ansehen &amp; buchen
</a>
</p>
{% else %}
@@ -64,6 +52,8 @@
</tr>
</table>
{% include 'email/_price_breakdown.html.twig' %}
<p>
Bei Fragen antworte einfach auf diese E-Mail.
</p>
@@ -12,7 +12,7 @@
</p>
<p>
vielen Dank! Hiermit bestätigen wir den Eingang deiner Buchung.
wir haben deine Buchung geprüft und bestätigen sie dir hiermit verbindlich.
</p>
<p>
Wir übergeben die Buchung nun für die Detailabstimmung an die Kollegen und Kolleginnen in der
@@ -46,6 +46,8 @@
</tr>
</table>
{% include 'email/_price_breakdown.html.twig' %}
{% if booking.remarks %}
<h2>
Anmerkungen
+2
View File
@@ -25,6 +25,8 @@
</tr>
</table>
{% include 'email/_price_breakdown.html.twig' %}
{% if booking.remarks %}
<h2>Anmerkungen</h2>
<p>{{ booking.remarks|nl2br }}</p>