feat: additional confirmation cycle and status for groups inquiries
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
{#- Offen covers two situations that need different office action, told apart by the access
|
||||
link: no link yet means the offer still has to be prepared, a link means the customer is
|
||||
the one who has to act. Display only — both are the same status. -#}
|
||||
{{- booking.status.label -}}
|
||||
{%- if booking.open %} · {{ booking.accessLinkIssuedAt is not null ? 'Angebot versendet' : 'Angebot ausstehend' }}{% endif -%}
|
||||
@@ -47,6 +47,11 @@
|
||||
Angenommen am {{ booking.acceptedAt | date('d.m.Y, H:i') }}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if booking.confirmedAt is not null %}
|
||||
<p class="lg:col-span-2 text-xs text-gray-500">
|
||||
Bestätigt am {{ booking.confirmedAt | date('d.m.Y, H:i') }}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if form.managedBy is defined %}
|
||||
<div class="lg:col-span-2">
|
||||
{{ form_row(form.managedBy) }}
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ booking.type.label }} · {{ booking.status.label }}
|
||||
{{ booking.type.label }} · {% include 'admin/accommodation_booking/_status_label.html.twig' %}
|
||||
</td>
|
||||
<td>
|
||||
{% set discounts = [] %}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{% extends 'htmx_confirmation_modal.html.twig' %}
|
||||
|
||||
{% block title %}Buchung bestätigen{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
Möchtest du die Buchung für <em>{{ booking.groupName }}</em> verbindlich bestätigen?
|
||||
<em>{{ booking.email }}</em> erhält daraufhin die Buchungsbestätigung inklusive
|
||||
Preisübersicht per E-Mail.
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block button_confirm %}Bestätigen{% endblock %}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
{% block content %}
|
||||
<twig:page:heading>
|
||||
{{ booking.type.label }} · {{ booking.status.label }}
|
||||
{{ booking.type.label }} · {% include 'admin/accommodation_booking/_status_label.html.twig' %}
|
||||
{{ booking.groupName }}
|
||||
</twig:page:heading>
|
||||
|
||||
@@ -56,11 +56,15 @@
|
||||
<dt class="font-medium">Art</dt>
|
||||
<dd>{{ booking.type.label }}</dd>
|
||||
<dt class="font-medium">Status</dt>
|
||||
<dd>{{ booking.status.label }}</dd>
|
||||
<dd>{% include 'admin/accommodation_booking/_status_label.html.twig' %}</dd>
|
||||
{% if booking.acceptedAt is not null %}
|
||||
<dt class="font-medium">Angenommen am</dt>
|
||||
<dd>{{ booking.acceptedAt | date('d.m.Y, H:i') }}</dd>
|
||||
{% endif %}
|
||||
{% if booking.confirmedAt is not null %}
|
||||
<dt class="font-medium">Bestätigt am</dt>
|
||||
<dd>{{ booking.confirmedAt | date('d.m.Y, H:i') }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
@@ -188,6 +192,23 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if booking.received %}
|
||||
<div class="mt-6 border-t border-gray-200 pt-6">
|
||||
<h2 class="text-lg font-bold mb-2">Bestätigung</h2>
|
||||
<p class="text-sm text-gray-500 mb-2">
|
||||
Der Kunde hat die Buchung verbindlich abgeschickt, aber noch keine Bestätigung
|
||||
erhalten. Prüfe Leistungen und Kapazitäten und bestätige die Buchung erst dann.
|
||||
</p>
|
||||
<button type="button"
|
||||
class="button button--primary button--small"
|
||||
hx-get="{{ path('app_admin_accommodationbooking_confirm', { id: booking.id }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
Buchung bestätigen
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-6 border-t border-gray-200 pt-6">
|
||||
<h2 class="text-lg font-bold mb-2">Zugangslink</h2>
|
||||
{% if accessLink %}
|
||||
|
||||
@@ -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 (0–3 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 }} %</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 }} %</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 }} %</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 & 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>
|
||||
|
||||
+3
-1
@@ -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
|
||||
@@ -25,6 +25,8 @@
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{% include 'email/_price_breakdown.html.twig' %}
|
||||
|
||||
{% if booking.remarks %}
|
||||
<h2>Anmerkungen</h2>
|
||||
<p>{{ booking.remarks|nl2br }}</p>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
{% import _self as macros %}
|
||||
|
||||
{% block title %}{{ booking.accepted ? 'Deine Buchung' : 'Dein Angebot' }}{% endblock %}
|
||||
{% block title %}{{ booking.acceptedAt is not null ? 'Deine Buchung' : 'Dein Angebot' }}{% endblock %}
|
||||
|
||||
{% block background %}bg-outer bg-outer--summer{% endblock %}
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
<hgroup>
|
||||
<h1 class="text-2xl lg:text-4xl font-bold mb-2">
|
||||
{{ booking.accepted ? 'Deine Buchung' : 'Dein Angebot' }}
|
||||
{{ booking.acceptedAt is not null ? 'Deine Buchung' : 'Dein Angebot' }}
|
||||
</h1>
|
||||
<p class="text-2xl">
|
||||
{{ ctx.accommodation.name }}
|
||||
@@ -63,7 +63,7 @@
|
||||
{% if booking.boardServiceLabel or booking.additionalServices | length > 0 %}
|
||||
<div>
|
||||
<h2 class="text-xl font-bold mb-2">
|
||||
{{ booking.accepted ? 'Gebuchte' : 'Angefragte' }} Leistungen
|
||||
{{ booking.acceptedAt is not null ? 'Gebuchte' : 'Angefragte' }} Leistungen
|
||||
</h2>
|
||||
<ul class="list-disc pl-4">
|
||||
{% if booking.boardServiceLabel %}
|
||||
@@ -86,11 +86,19 @@
|
||||
Angebot verbindlich buchen
|
||||
</button>
|
||||
</div>
|
||||
{% elseif booking.confirmedAt is not null %}
|
||||
<div class="flex items-center space-x-2">
|
||||
{{ icon('info', 'w-5 h-5') }}
|
||||
<div class="text-gray-500">
|
||||
Buchung bestätigt am {{ booking.confirmedAt | date('d.m.Y') }}
|
||||
</div>
|
||||
</div>
|
||||
{% elseif booking.acceptedAt is not null %}
|
||||
<div class="flex items-center space-x-2">
|
||||
{{ icon('info', 'w-5 h-5') }}
|
||||
<div class="text-gray-500">
|
||||
Angebot angenommen am {{ booking.acceptedAt | date('d.m.Y') }}
|
||||
Am {{ booking.acceptedAt | date('d.m.Y') }} bei uns eingegangen — wir prüfen deine
|
||||
Buchung und melden uns mit der Bestätigung bei dir.
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
title: 'Vielen Dank!',
|
||||
messages: [
|
||||
resultType == 'booking'
|
||||
? 'Deine Buchung ist bei uns eingegangen. Du erhältst in Kürze eine Bestätigung.'
|
||||
? 'Deine Buchung ist bei uns eingegangen. Sobald sie geprüft ist, erhälst du eine Bestätigung per E-Mail.'
|
||||
: 'Deine Anfrage ist bei uns eingegangen. Wir melden uns so schnell wie möglich bei dir.'
|
||||
]
|
||||
} %}
|
||||
|
||||
Reference in New Issue
Block a user