Buchungskalender: Mails an Gäste, Vermieter und Team
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
118be44847
commit
e15486263f
18 files changed
+1095
-6
No files matched your search
@@ -0,0 +1,52 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!--
|
||||||
|
G16 · Buchungsanfrage eingegangen (Buchungskalender)
|
||||||
|
Betreff: Ihre Buchungsanfrage bei {{firma}} ist eingegangen
|
||||||
|
Absender: „<Firma>“ <Portal-Postfach> · Antworten an die E-Mail-Adresse des Vermieters
|
||||||
|
Empfänger: Gast (E-Mail aus der Buchungsanfrage) · ohne BCC
|
||||||
|
Platzhalter: {{gast_name}}, {{unterkunft}}, {{zeitraum}}, {{naechte}}, {{personen}}, {{nummer}}, {{firma}}, {{farbe}}, {{anbieter_kontakt}}
|
||||||
|
Sie-Form, keine Anrede-Blöcke, neutrales Layout in der Akzentfarbe des Vermieters, ohne Logo. Footer: Anbieter-Kontakt des Vermieters.
|
||||||
|
naechte und personen sind fertige Texte (z. B. „2 Nächte“, „2 Personen“); zeitraum z. B. „12.11.2026 – 30.11.2026“; posten_text = eine Zeile je Posten; gesamt z. B. „1.234,00 €“; anbieter_kontakt = Name, Anschrift, E-Mail und Telefon des Vermieters.
|
||||||
|
Keine Buchung und kein Vertrag: der Vermieter prüft die Anfrage und meldet sich mit Preis und Buchungslink.
|
||||||
|
Texte sind Entwurf für die Mail-Überarbeitung des Inhabers; verbindlich sind nur Platzhalter und Blöcke.
|
||||||
|
-->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
|
<title>Ihre Buchungsanfrage bei {{firma}} ist eingegangen</title>
|
||||||
|
<!--[if mso]><style>table,td{font-family:Arial,Helvetica,sans-serif!important;}</style><![endif]-->
|
||||||
|
<style>
|
||||||
|
a{color:#111111;}
|
||||||
|
@media (max-width:620px){.w{width:100%!important;}.px{padding-left:24px!important;padding-right:24px!important;}.lbl{display:block!important;width:100%!important;padding-bottom:2px!important;border-bottom:0!important;}.val{display:block!important;width:100%!important;padding-top:0!important;}}
|
||||||
|
@media (prefers-color-scheme:dark){.bg{background:#161615!important;}.card{background:#1f1f1e!important;}.ink{color:#f2f1ee!important;}.mut{color:#a8a6a0!important;}.box{background:#2a2a28!important;border-color:#3a3a37!important;}.btn{background:#f2f1ee!important;}.btn a{color:#161615!important;}.btn2{border-color:#f2f1ee!important;}.btn2 a{color:#f2f1ee!important;}.rowline{border-color:#3a3a37!important;}.pill{background:#1f1f1e!important;}.pill span{color:#f2f1ee!important;}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;padding:0;background:#f1f0ec;">
|
||||||
|
<table role="presentation" class="bg" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f1f0ec;">
|
||||||
|
<tr><td align="center" style="padding:32px 12px;">
|
||||||
|
<table role="presentation" class="w" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:600px;">
|
||||||
|
<tr><td class="px" bgcolor="{{farbe}}" style="background:{{farbe}};border-radius:16px 16px 0 0;padding:18px 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td class="pill" bgcolor="#ffffff" style="background:#ffffff;border-radius:8px;padding:8px 14px;font-family:Arial,Helvetica,sans-serif;font-size:17px;line-height:22px;font-weight:bold;color:#111111;"><span style="color:#111111;">{{firma}}</span></td></tr></table></td></tr>
|
||||||
|
<tr><td class="card" style="background:#ffffff;border-radius:0 0 16px 16px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||||
|
<tr><td class="px ink" style="padding:36px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:26px;line-height:32px;mso-line-height-rule:exactly;font-weight:bold;letter-spacing:-0.5px;color:#111111;">Ihre Anfrage ist eingegangen</td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Guten Tag {{gast_name}},<br>vielen Dank für Ihre Buchungsanfrage. {{firma}} prüft sie und meldet sich bei Ihnen mit Preis und einem Link zur verbindlichen Buchung.</td></tr>
|
||||||
|
<tr><td class="px" style="padding:24px 40px 0 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;mso-line-height-rule:exactly;">
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Unterkunft</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;"><strong>{{unterkunft}}</strong></td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Zeitraum</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{zeitraum}} ({{naechte}})</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Personen</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{personen}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Buchungsnummer</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{nummer}}</td></tr>
|
||||||
|
</table></td></tr>
|
||||||
|
<tr><td class="px" style="padding:20px 40px 0 40px;"><table role="presentation" class="box" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f7f6f3;border:1px solid #e8e6e0;border-radius:12px;"><tr><td class="ink" style="padding:16px 20px;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;color:#111111;"><strong>Noch keine Buchung:</strong> Mit dieser Anfrage ist noch nichts verbindlich gebucht und es entstehen Ihnen keine Kosten. Der Zeitraum ist erst reserviert, wenn Sie das Angebot von {{firma}} bestätigt haben.</td></tr></table></td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:28px 40px 40px 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:24px;color:#111111;">Freundliche Grüße<br><strong>{{firma}}</strong></td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:24px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;"><div><strong>{{firma}}</strong></div><div>{{anbieter_kontakt}}</div></td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:12px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;">Diese E-Mail wurde im Auftrag von {{firma}} über den Buchungskalender von WEBklar versendet. Antworten gehen direkt an {{firma}}.</td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!--
|
||||||
|
G17 · Buchungsanfrage freigegeben (Buchungskalender)
|
||||||
|
Betreff: Ihre Buchungsanfrage {{nummer}} bei {{firma}}: jetzt buchen
|
||||||
|
Absender: „<Firma>“ <Portal-Postfach> · Antworten an die E-Mail-Adresse des Vermieters
|
||||||
|
Empfänger: Gast (E-Mail aus der Buchungsanfrage) · ohne BCC
|
||||||
|
Platzhalter: {{gast_name}}, {{unterkunft}}, {{zeitraum}}, {{naechte}}, {{personen}}, {{nummer}}, {{posten_text}}, {{gesamt}}, {{frist}}, {{buchungs_link}}, {{stornobedingungen}}, {{nachricht_vermieter}}, {{firma}}, {{farbe}}, {{anbieter_kontakt}}
|
||||||
|
Optionale Blöcke (zwischen WENN-/ENDE-Kommentaren; bei Nichtzutreffen samt Inhalt entfernen): nachricht
|
||||||
|
Sie-Form, keine Anrede-Blöcke, neutrales Layout in der Akzentfarbe des Vermieters, ohne Logo. Footer: Anbieter-Kontakt des Vermieters.
|
||||||
|
naechte und personen sind fertige Texte (z. B. „2 Nächte“, „2 Personen“); zeitraum z. B. „12.11.2026 – 30.11.2026“; posten_text = eine Zeile je Posten; gesamt z. B. „1.234,00 €“; anbieter_kontakt = Name, Anschrift, E-Mail und Telefon des Vermieters.
|
||||||
|
frist = Ende der Reservierung, z. B. „15.11.2026, 18:00 Uhr“; buchungs_link führt zur Buchungsseite mit dem persönlichen Token. Block nachricht: Mitteilung des Vermieters (nachricht_vermieter).
|
||||||
|
Texte sind Entwurf für die Mail-Überarbeitung des Inhabers; verbindlich sind nur Platzhalter und Blöcke.
|
||||||
|
-->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
|
<title>Ihre Buchungsanfrage {{nummer}} bei {{firma}}: jetzt buchen</title>
|
||||||
|
<!--[if mso]><style>table,td{font-family:Arial,Helvetica,sans-serif!important;}</style><![endif]-->
|
||||||
|
<style>
|
||||||
|
a{color:#111111;}
|
||||||
|
@media (max-width:620px){.w{width:100%!important;}.px{padding-left:24px!important;padding-right:24px!important;}.lbl{display:block!important;width:100%!important;padding-bottom:2px!important;border-bottom:0!important;}.val{display:block!important;width:100%!important;padding-top:0!important;}}
|
||||||
|
@media (prefers-color-scheme:dark){.bg{background:#161615!important;}.card{background:#1f1f1e!important;}.ink{color:#f2f1ee!important;}.mut{color:#a8a6a0!important;}.box{background:#2a2a28!important;border-color:#3a3a37!important;}.btn{background:#f2f1ee!important;}.btn a{color:#161615!important;}.btn2{border-color:#f2f1ee!important;}.btn2 a{color:#f2f1ee!important;}.rowline{border-color:#3a3a37!important;}.pill{background:#1f1f1e!important;}.pill span{color:#f2f1ee!important;}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;padding:0;background:#f1f0ec;">
|
||||||
|
<table role="presentation" class="bg" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f1f0ec;">
|
||||||
|
<tr><td align="center" style="padding:32px 12px;">
|
||||||
|
<table role="presentation" class="w" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:600px;">
|
||||||
|
<tr><td class="px" bgcolor="{{farbe}}" style="background:{{farbe}};border-radius:16px 16px 0 0;padding:18px 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td class="pill" bgcolor="#ffffff" style="background:#ffffff;border-radius:8px;padding:8px 14px;font-family:Arial,Helvetica,sans-serif;font-size:17px;line-height:22px;font-weight:bold;color:#111111;"><span style="color:#111111;">{{firma}}</span></td></tr></table></td></tr>
|
||||||
|
<tr><td class="card" style="background:#ffffff;border-radius:0 0 16px 16px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||||
|
<tr><td class="px ink" style="padding:36px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:26px;line-height:32px;mso-line-height-rule:exactly;font-weight:bold;letter-spacing:-0.5px;color:#111111;">Ihre Anfrage wurde freigegeben</td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Guten Tag {{gast_name}},<br>{{firma}} hat Ihre Anfrage geprüft und den Zeitraum für Sie reserviert. Bitte buchen Sie bis {{frist}} verbindlich, danach läuft die Reservierung ab.</td></tr>
|
||||||
|
<!-- WENN nachricht -->
|
||||||
|
<tr><td class="px" style="padding:20px 40px 0 40px;"><table role="presentation" class="box" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f7f6f3;border:1px solid #e8e6e0;border-radius:12px;"><tr><td class="ink" style="padding:16px 20px;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;color:#111111;">{{nachricht_vermieter}}</td></tr></table></td></tr>
|
||||||
|
<!-- ENDE nachricht -->
|
||||||
|
<tr><td class="px" style="padding:24px 40px 0 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;mso-line-height-rule:exactly;">
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Unterkunft</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;"><strong>{{unterkunft}}</strong></td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Zeitraum</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{zeitraum}} ({{naechte}})</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Personen</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{personen}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Buchungsnummer</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{nummer}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Leistungen</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{posten_text}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Gesamtpreis</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;"><strong>{{gesamt}}</strong></td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Reserviert bis</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{frist}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Stornobedingungen</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{stornobedingungen}}</td></tr>
|
||||||
|
</table></td></tr>
|
||||||
|
<tr><td class="px" style="padding:28px 40px 0 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td class="btn" bgcolor="#111111" style="background:#111111;border-radius:10px;"><a href="{{buchungs_link}}" style="display:block;padding:14px 26px;font-family:Arial,Helvetica,sans-serif;font-size:15px;font-weight:bold;color:#ffffff;text-decoration:none;">Jetzt verbindlich buchen →</a></td></tr></table></td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:20px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Erst mit dem Klick auf „Zahlungspflichtig buchen“ auf der Buchungsseite ist Ihre Buchung verbindlich.</td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:28px 40px 40px 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:24px;color:#111111;">Freundliche Grüße<br><strong>{{firma}}</strong></td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:24px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;"><div><strong>{{firma}}</strong></div><div>{{anbieter_kontakt}}</div></td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:12px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;">Diese E-Mail wurde im Auftrag von {{firma}} über den Buchungskalender von WEBklar versendet. Antworten gehen direkt an {{firma}}.</td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!--
|
||||||
|
G18 · Buchungsanfrage abgelehnt (Buchungskalender)
|
||||||
|
Betreff: Zu Ihrer Buchungsanfrage {{nummer}} bei {{firma}}
|
||||||
|
Absender: „<Firma>“ <Portal-Postfach> · Antworten an die E-Mail-Adresse des Vermieters
|
||||||
|
Empfänger: Gast (E-Mail aus der Buchungsanfrage) · ohne BCC
|
||||||
|
Platzhalter: {{gast_name}}, {{unterkunft}}, {{zeitraum}}, {{naechte}}, {{personen}}, {{nummer}}, {{nachricht_vermieter}}, {{firma}}, {{farbe}}, {{anbieter_kontakt}}
|
||||||
|
Optionale Blöcke (zwischen WENN-/ENDE-Kommentaren; bei Nichtzutreffen samt Inhalt entfernen): nachricht
|
||||||
|
Sie-Form, keine Anrede-Blöcke, neutrales Layout in der Akzentfarbe des Vermieters, ohne Logo. Footer: Anbieter-Kontakt des Vermieters.
|
||||||
|
naechte und personen sind fertige Texte (z. B. „2 Nächte“, „2 Personen“); zeitraum z. B. „12.11.2026 – 30.11.2026“; posten_text = eine Zeile je Posten; gesamt z. B. „1.234,00 €“; anbieter_kontakt = Name, Anschrift, E-Mail und Telefon des Vermieters.
|
||||||
|
Block nachricht: Begründung oder Mitteilung des Vermieters (nachricht_vermieter).
|
||||||
|
Texte sind Entwurf für die Mail-Überarbeitung des Inhabers; verbindlich sind nur Platzhalter und Blöcke.
|
||||||
|
-->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
|
<title>Zu Ihrer Buchungsanfrage {{nummer}} bei {{firma}}</title>
|
||||||
|
<!--[if mso]><style>table,td{font-family:Arial,Helvetica,sans-serif!important;}</style><![endif]-->
|
||||||
|
<style>
|
||||||
|
a{color:#111111;}
|
||||||
|
@media (max-width:620px){.w{width:100%!important;}.px{padding-left:24px!important;padding-right:24px!important;}.lbl{display:block!important;width:100%!important;padding-bottom:2px!important;border-bottom:0!important;}.val{display:block!important;width:100%!important;padding-top:0!important;}}
|
||||||
|
@media (prefers-color-scheme:dark){.bg{background:#161615!important;}.card{background:#1f1f1e!important;}.ink{color:#f2f1ee!important;}.mut{color:#a8a6a0!important;}.box{background:#2a2a28!important;border-color:#3a3a37!important;}.btn{background:#f2f1ee!important;}.btn a{color:#161615!important;}.btn2{border-color:#f2f1ee!important;}.btn2 a{color:#f2f1ee!important;}.rowline{border-color:#3a3a37!important;}.pill{background:#1f1f1e!important;}.pill span{color:#f2f1ee!important;}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;padding:0;background:#f1f0ec;">
|
||||||
|
<table role="presentation" class="bg" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f1f0ec;">
|
||||||
|
<tr><td align="center" style="padding:32px 12px;">
|
||||||
|
<table role="presentation" class="w" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:600px;">
|
||||||
|
<tr><td class="px" bgcolor="{{farbe}}" style="background:{{farbe}};border-radius:16px 16px 0 0;padding:18px 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td class="pill" bgcolor="#ffffff" style="background:#ffffff;border-radius:8px;padding:8px 14px;font-family:Arial,Helvetica,sans-serif;font-size:17px;line-height:22px;font-weight:bold;color:#111111;"><span style="color:#111111;">{{firma}}</span></td></tr></table></td></tr>
|
||||||
|
<tr><td class="card" style="background:#ffffff;border-radius:0 0 16px 16px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||||
|
<tr><td class="px ink" style="padding:36px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:26px;line-height:32px;mso-line-height-rule:exactly;font-weight:bold;letter-spacing:-0.5px;color:#111111;">Zu Ihrer Anfrage</td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Guten Tag {{gast_name}},<br>leider kann {{firma}} Ihre Buchungsanfrage für den gewünschten Zeitraum nicht annehmen.</td></tr>
|
||||||
|
<!-- WENN nachricht -->
|
||||||
|
<tr><td class="px" style="padding:20px 40px 0 40px;"><table role="presentation" class="box" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f7f6f3;border:1px solid #e8e6e0;border-radius:12px;"><tr><td class="ink" style="padding:16px 20px;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;color:#111111;">{{nachricht_vermieter}}</td></tr></table></td></tr>
|
||||||
|
<!-- ENDE nachricht -->
|
||||||
|
<tr><td class="px" style="padding:24px 40px 0 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;mso-line-height-rule:exactly;">
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Unterkunft</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;"><strong>{{unterkunft}}</strong></td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Zeitraum</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{zeitraum}} ({{naechte}})</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Personen</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{personen}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Buchungsnummer</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{nummer}}</td></tr>
|
||||||
|
</table></td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:20px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Sie können gern einen anderen Zeitraum anfragen oder {{firma}} direkt kontaktieren, indem Sie auf diese E-Mail antworten.</td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:28px 40px 40px 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:24px;color:#111111;">Freundliche Grüße<br><strong>{{firma}}</strong></td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:24px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;"><div><strong>{{firma}}</strong></div><div>{{anbieter_kontakt}}</div></td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:12px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;">Diese E-Mail wurde im Auftrag von {{firma}} über den Buchungskalender von WEBklar versendet. Antworten gehen direkt an {{firma}}.</td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!--
|
||||||
|
G19 · Buchungsbestätigung (Buchungskalender, dauerhafter Datenträger)
|
||||||
|
Betreff: Ihre Buchung {{nummer}} bei {{firma}} ist bestätigt
|
||||||
|
Absender: „<Firma>“ <Portal-Postfach> · Antworten an die E-Mail-Adresse des Vermieters
|
||||||
|
Empfänger: Gast (E-Mail aus der Buchungsanfrage) · ohne BCC
|
||||||
|
Platzhalter: {{gast_name}}, {{unterkunft}}, {{zeitraum}}, {{naechte}}, {{personen}}, {{nummer}}, {{posten_text}}, {{gesamt}}, {{zahlart_text}}, {{rechnung_nummer}}, {{stornobedingungen}}, {{firma}}, {{farbe}}, {{anbieter_kontakt}}
|
||||||
|
Optionale Blöcke (zwischen WENN-/ENDE-Kommentaren; bei Nichtzutreffen samt Inhalt entfernen): online_unbezahlt
|
||||||
|
Sie-Form, keine Anrede-Blöcke, neutrales Layout in der Akzentfarbe des Vermieters, ohne Logo. Footer: Anbieter-Kontakt des Vermieters.
|
||||||
|
naechte und personen sind fertige Texte (z. B. „2 Nächte“, „2 Personen“); zeitraum z. B. „12.11.2026 – 30.11.2026“; posten_text = eine Zeile je Posten; gesamt z. B. „1.234,00 €“; anbieter_kontakt = Name, Anschrift, E-Mail und Telefon des Vermieters.
|
||||||
|
Dauerhafter Datenträger: alle Angaben der Buchungsseite stehen in der Mail, die Rechnung hängt als PDF an. Block online_unbezahlt: Zahlart online, noch nicht bezahlt. Kein Widerrufsrecht (§ 312g Abs. 2 Satz 1 Nr. 9 BGB), der Hinweis steht fest im Text.
|
||||||
|
Texte sind Entwurf für die Mail-Überarbeitung des Inhabers; verbindlich sind nur Platzhalter und Blöcke.
|
||||||
|
-->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
|
<title>Ihre Buchung {{nummer}} bei {{firma}} ist bestätigt</title>
|
||||||
|
<!--[if mso]><style>table,td{font-family:Arial,Helvetica,sans-serif!important;}</style><![endif]-->
|
||||||
|
<style>
|
||||||
|
a{color:#111111;}
|
||||||
|
@media (max-width:620px){.w{width:100%!important;}.px{padding-left:24px!important;padding-right:24px!important;}.lbl{display:block!important;width:100%!important;padding-bottom:2px!important;border-bottom:0!important;}.val{display:block!important;width:100%!important;padding-top:0!important;}}
|
||||||
|
@media (prefers-color-scheme:dark){.bg{background:#161615!important;}.card{background:#1f1f1e!important;}.ink{color:#f2f1ee!important;}.mut{color:#a8a6a0!important;}.box{background:#2a2a28!important;border-color:#3a3a37!important;}.btn{background:#f2f1ee!important;}.btn a{color:#161615!important;}.btn2{border-color:#f2f1ee!important;}.btn2 a{color:#f2f1ee!important;}.rowline{border-color:#3a3a37!important;}.pill{background:#1f1f1e!important;}.pill span{color:#f2f1ee!important;}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;padding:0;background:#f1f0ec;">
|
||||||
|
<table role="presentation" class="bg" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f1f0ec;">
|
||||||
|
<tr><td align="center" style="padding:32px 12px;">
|
||||||
|
<table role="presentation" class="w" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:600px;">
|
||||||
|
<tr><td class="px" bgcolor="{{farbe}}" style="background:{{farbe}};border-radius:16px 16px 0 0;padding:18px 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td class="pill" bgcolor="#ffffff" style="background:#ffffff;border-radius:8px;padding:8px 14px;font-family:Arial,Helvetica,sans-serif;font-size:17px;line-height:22px;font-weight:bold;color:#111111;"><span style="color:#111111;">{{firma}}</span></td></tr></table></td></tr>
|
||||||
|
<tr><td class="card" style="background:#ffffff;border-radius:0 0 16px 16px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||||
|
<tr><td class="px ink" style="padding:36px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:26px;line-height:32px;mso-line-height-rule:exactly;font-weight:bold;letter-spacing:-0.5px;color:#111111;">Ihre Buchung ist bestätigt</td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Guten Tag {{gast_name}},<br>vielen Dank, Ihre Buchung bei {{firma}} ist verbindlich. Alle Angaben finden Sie unten; die Rechnung {{rechnung_nummer}} hängt als PDF an.</td></tr>
|
||||||
|
<tr><td class="px" style="padding:24px 40px 0 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;mso-line-height-rule:exactly;">
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Unterkunft</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;"><strong>{{unterkunft}}</strong></td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Zeitraum</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{zeitraum}} ({{naechte}})</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Personen</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{personen}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Buchungsnummer</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{nummer}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Leistungen</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{posten_text}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Gesamtpreis</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;"><strong>{{gesamt}}</strong></td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Zahlung</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{zahlart_text}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Stornobedingungen</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{stornobedingungen}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Anbieter</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{anbieter_kontakt}}</td></tr>
|
||||||
|
</table></td></tr>
|
||||||
|
<!-- WENN online_unbezahlt -->
|
||||||
|
<tr><td class="px" style="padding:20px 40px 0 40px;"><table role="presentation" class="box" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f7f6f3;border:1px solid #e8e6e0;border-radius:12px;"><tr><td class="ink" style="padding:16px 20px;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;color:#111111;">Den Bezahl-Link finden Sie auf der Buchungsseite aus unserer vorigen E-Mail.</td></tr></table></td></tr>
|
||||||
|
<!-- ENDE online_unbezahlt -->
|
||||||
|
<tr><td class="px" style="padding:20px 40px 0 40px;"><table role="presentation" class="box" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f7f6f3;border:1px solid #e8e6e0;border-radius:12px;"><tr><td class="ink" style="padding:16px 20px;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;color:#111111;"><strong>Kein Widerrufsrecht:</strong> Für Verträge über die Beherbergung zu einem bestimmten Zeitraum besteht nach § 312g Abs. 2 Satz 1 Nr. 9 BGB kein gesetzliches Widerrufsrecht. Es gelten die oben genannten Stornobedingungen.</td></tr></table></td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:28px 40px 40px 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:24px;color:#111111;">Freundliche Grüße<br><strong>{{firma}}</strong></td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:24px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;"><div><strong>{{firma}}</strong></div><div>{{anbieter_kontakt}}</div></td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:12px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;">Diese E-Mail wurde im Auftrag von {{firma}} über den Buchungskalender von WEBklar versendet. Antworten gehen direkt an {{firma}}.</td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!--
|
||||||
|
G20 · Zahlung eingegangen (Buchungskalender)
|
||||||
|
Betreff: Zahlung zu Ihrer Buchung {{nummer}} eingegangen
|
||||||
|
Absender: „<Firma>“ <Portal-Postfach> · Antworten an die E-Mail-Adresse des Vermieters
|
||||||
|
Empfänger: Gast (E-Mail aus der Buchungsanfrage) · ohne BCC
|
||||||
|
Platzhalter: {{gast_name}}, {{unterkunft}}, {{zeitraum}}, {{naechte}}, {{personen}}, {{nummer}}, {{gesamt}}, {{rechnung_nummer}}, {{firma}}, {{farbe}}, {{anbieter_kontakt}}
|
||||||
|
Sie-Form, keine Anrede-Blöcke, neutrales Layout in der Akzentfarbe des Vermieters, ohne Logo. Footer: Anbieter-Kontakt des Vermieters.
|
||||||
|
naechte und personen sind fertige Texte (z. B. „2 Nächte“, „2 Personen“); zeitraum z. B. „12.11.2026 – 30.11.2026“; posten_text = eine Zeile je Posten; gesamt z. B. „1.234,00 €“; anbieter_kontakt = Name, Anschrift, E-Mail und Telefon des Vermieters.
|
||||||
|
Nach erfolgreicher Online-Zahlung; rechnung_nummer = Nummer der schon versendeten Rechnung.
|
||||||
|
Texte sind Entwurf für die Mail-Überarbeitung des Inhabers; verbindlich sind nur Platzhalter und Blöcke.
|
||||||
|
-->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
|
<title>Zahlung zu Ihrer Buchung {{nummer}} eingegangen</title>
|
||||||
|
<!--[if mso]><style>table,td{font-family:Arial,Helvetica,sans-serif!important;}</style><![endif]-->
|
||||||
|
<style>
|
||||||
|
a{color:#111111;}
|
||||||
|
@media (max-width:620px){.w{width:100%!important;}.px{padding-left:24px!important;padding-right:24px!important;}.lbl{display:block!important;width:100%!important;padding-bottom:2px!important;border-bottom:0!important;}.val{display:block!important;width:100%!important;padding-top:0!important;}}
|
||||||
|
@media (prefers-color-scheme:dark){.bg{background:#161615!important;}.card{background:#1f1f1e!important;}.ink{color:#f2f1ee!important;}.mut{color:#a8a6a0!important;}.box{background:#2a2a28!important;border-color:#3a3a37!important;}.btn{background:#f2f1ee!important;}.btn a{color:#161615!important;}.btn2{border-color:#f2f1ee!important;}.btn2 a{color:#f2f1ee!important;}.rowline{border-color:#3a3a37!important;}.pill{background:#1f1f1e!important;}.pill span{color:#f2f1ee!important;}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;padding:0;background:#f1f0ec;">
|
||||||
|
<table role="presentation" class="bg" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f1f0ec;">
|
||||||
|
<tr><td align="center" style="padding:32px 12px;">
|
||||||
|
<table role="presentation" class="w" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:600px;">
|
||||||
|
<tr><td class="px" bgcolor="{{farbe}}" style="background:{{farbe}};border-radius:16px 16px 0 0;padding:18px 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td class="pill" bgcolor="#ffffff" style="background:#ffffff;border-radius:8px;padding:8px 14px;font-family:Arial,Helvetica,sans-serif;font-size:17px;line-height:22px;font-weight:bold;color:#111111;"><span style="color:#111111;">{{firma}}</span></td></tr></table></td></tr>
|
||||||
|
<tr><td class="card" style="background:#ffffff;border-radius:0 0 16px 16px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||||
|
<tr><td class="px ink" style="padding:36px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:26px;line-height:32px;mso-line-height-rule:exactly;font-weight:bold;letter-spacing:-0.5px;color:#111111;">Zahlung eingegangen</td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Guten Tag {{gast_name}},<br>wir haben Ihre Zahlung erhalten. Vielen Dank!</td></tr>
|
||||||
|
<tr><td class="px" style="padding:24px 40px 0 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;mso-line-height-rule:exactly;">
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Unterkunft</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;"><strong>{{unterkunft}}</strong></td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Zeitraum</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{zeitraum}} ({{naechte}})</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Personen</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{personen}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Buchungsnummer</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{nummer}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Bezahlter Betrag</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;"><strong>{{gesamt}}</strong></td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Rechnung</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{rechnung_nummer}}</td></tr>
|
||||||
|
</table></td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:20px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Ihre Buchung bleibt wie bestätigt bestehen. Die Rechnung haben Sie mit der Buchungsbestätigung erhalten.</td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:28px 40px 40px 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:24px;color:#111111;">Freundliche Grüße<br><strong>{{firma}}</strong></td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:24px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;"><div><strong>{{firma}}</strong></div><div>{{anbieter_kontakt}}</div></td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:12px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;">Diese E-Mail wurde im Auftrag von {{firma}} über den Buchungskalender von WEBklar versendet. Antworten gehen direkt an {{firma}}.</td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!--
|
||||||
|
G21 · Reservierung abgelaufen (Buchungskalender)
|
||||||
|
Betreff: Ihre Reservierung {{nummer}} bei {{firma}} ist abgelaufen
|
||||||
|
Absender: „<Firma>“ <Portal-Postfach> · Antworten an die E-Mail-Adresse des Vermieters
|
||||||
|
Empfänger: Gast (E-Mail aus der Buchungsanfrage) · ohne BCC
|
||||||
|
Platzhalter: {{gast_name}}, {{unterkunft}}, {{zeitraum}}, {{naechte}}, {{personen}}, {{nummer}}, {{frist}}, {{firma}}, {{farbe}}, {{anbieter_kontakt}}
|
||||||
|
Sie-Form, keine Anrede-Blöcke, neutrales Layout in der Akzentfarbe des Vermieters, ohne Logo. Footer: Anbieter-Kontakt des Vermieters.
|
||||||
|
naechte und personen sind fertige Texte (z. B. „2 Nächte“, „2 Personen“); zeitraum z. B. „12.11.2026 – 30.11.2026“; posten_text = eine Zeile je Posten; gesamt z. B. „1.234,00 €“; anbieter_kontakt = Name, Anschrift, E-Mail und Telefon des Vermieters.
|
||||||
|
Die Frist zur verbindlichen Buchung ist verstrichen; der Zeitraum ist wieder frei. Kein Link (der Server kennt das Token nicht mehr).
|
||||||
|
Texte sind Entwurf für die Mail-Überarbeitung des Inhabers; verbindlich sind nur Platzhalter und Blöcke.
|
||||||
|
-->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
|
<title>Ihre Reservierung {{nummer}} bei {{firma}} ist abgelaufen</title>
|
||||||
|
<!--[if mso]><style>table,td{font-family:Arial,Helvetica,sans-serif!important;}</style><![endif]-->
|
||||||
|
<style>
|
||||||
|
a{color:#111111;}
|
||||||
|
@media (max-width:620px){.w{width:100%!important;}.px{padding-left:24px!important;padding-right:24px!important;}.lbl{display:block!important;width:100%!important;padding-bottom:2px!important;border-bottom:0!important;}.val{display:block!important;width:100%!important;padding-top:0!important;}}
|
||||||
|
@media (prefers-color-scheme:dark){.bg{background:#161615!important;}.card{background:#1f1f1e!important;}.ink{color:#f2f1ee!important;}.mut{color:#a8a6a0!important;}.box{background:#2a2a28!important;border-color:#3a3a37!important;}.btn{background:#f2f1ee!important;}.btn a{color:#161615!important;}.btn2{border-color:#f2f1ee!important;}.btn2 a{color:#f2f1ee!important;}.rowline{border-color:#3a3a37!important;}.pill{background:#1f1f1e!important;}.pill span{color:#f2f1ee!important;}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;padding:0;background:#f1f0ec;">
|
||||||
|
<table role="presentation" class="bg" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f1f0ec;">
|
||||||
|
<tr><td align="center" style="padding:32px 12px;">
|
||||||
|
<table role="presentation" class="w" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:600px;">
|
||||||
|
<tr><td class="px" bgcolor="{{farbe}}" style="background:{{farbe}};border-radius:16px 16px 0 0;padding:18px 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td class="pill" bgcolor="#ffffff" style="background:#ffffff;border-radius:8px;padding:8px 14px;font-family:Arial,Helvetica,sans-serif;font-size:17px;line-height:22px;font-weight:bold;color:#111111;"><span style="color:#111111;">{{firma}}</span></td></tr></table></td></tr>
|
||||||
|
<tr><td class="card" style="background:#ffffff;border-radius:0 0 16px 16px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||||
|
<tr><td class="px ink" style="padding:36px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:26px;line-height:32px;mso-line-height-rule:exactly;font-weight:bold;letter-spacing:-0.5px;color:#111111;">Reservierung abgelaufen</td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Guten Tag {{gast_name}},<br>die Frist für Ihre verbindliche Buchung ist am {{frist}} abgelaufen. Der Zeitraum ist deshalb nicht mehr für Sie reserviert.</td></tr>
|
||||||
|
<tr><td class="px" style="padding:24px 40px 0 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;mso-line-height-rule:exactly;">
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Unterkunft</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;"><strong>{{unterkunft}}</strong></td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Zeitraum</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{zeitraum}} ({{naechte}})</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Personen</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{personen}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Buchungsnummer</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{nummer}}</td></tr>
|
||||||
|
</table></td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:20px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Wenn Sie weiterhin Interesse haben, stellen Sie gern eine neue Anfrage oder antworten Sie auf diese E-Mail.</td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:28px 40px 40px 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:24px;color:#111111;">Freundliche Grüße<br><strong>{{firma}}</strong></td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:24px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;"><div><strong>{{firma}}</strong></div><div>{{anbieter_kontakt}}</div></td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:12px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;">Diese E-Mail wurde im Auftrag von {{firma}} über den Buchungskalender von WEBklar versendet. Antworten gehen direkt an {{firma}}.</td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!--
|
||||||
|
G22 · Buchung storniert (Buchungskalender)
|
||||||
|
Betreff: Ihre Buchung {{nummer}} bei {{firma}} wurde storniert
|
||||||
|
Absender: „<Firma>“ <Portal-Postfach> · Antworten an die E-Mail-Adresse des Vermieters
|
||||||
|
Empfänger: Gast (E-Mail aus der Buchungsanfrage) · ohne BCC
|
||||||
|
Platzhalter: {{gast_name}}, {{unterkunft}}, {{zeitraum}}, {{naechte}}, {{personen}}, {{nummer}}, {{rechnung_nummer}}, {{erstattung}}, {{nachricht_vermieter}}, {{firma}}, {{farbe}}, {{anbieter_kontakt}}
|
||||||
|
Optionale Blöcke (zwischen WENN-/ENDE-Kommentaren; bei Nichtzutreffen samt Inhalt entfernen): erstattung, nachricht
|
||||||
|
Sie-Form, keine Anrede-Blöcke, neutrales Layout in der Akzentfarbe des Vermieters, ohne Logo. Footer: Anbieter-Kontakt des Vermieters.
|
||||||
|
naechte und personen sind fertige Texte (z. B. „2 Nächte“, „2 Personen“); zeitraum z. B. „12.11.2026 – 30.11.2026“; posten_text = eine Zeile je Posten; gesamt z. B. „1.234,00 €“; anbieter_kontakt = Name, Anschrift, E-Mail und Telefon des Vermieters.
|
||||||
|
rechnung_nummer = Nummer der Stornorechnung (hängt als PDF an). Block erstattung: Erstattung wird nach dem Versand der Mail veranlasst („wird veranlasst“, nicht „wurde erstattet“). Block nachricht: Grund des Vermieters.
|
||||||
|
Texte sind Entwurf für die Mail-Überarbeitung des Inhabers; verbindlich sind nur Platzhalter und Blöcke.
|
||||||
|
-->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
|
<title>Ihre Buchung {{nummer}} bei {{firma}} wurde storniert</title>
|
||||||
|
<!--[if mso]><style>table,td{font-family:Arial,Helvetica,sans-serif!important;}</style><![endif]-->
|
||||||
|
<style>
|
||||||
|
a{color:#111111;}
|
||||||
|
@media (max-width:620px){.w{width:100%!important;}.px{padding-left:24px!important;padding-right:24px!important;}.lbl{display:block!important;width:100%!important;padding-bottom:2px!important;border-bottom:0!important;}.val{display:block!important;width:100%!important;padding-top:0!important;}}
|
||||||
|
@media (prefers-color-scheme:dark){.bg{background:#161615!important;}.card{background:#1f1f1e!important;}.ink{color:#f2f1ee!important;}.mut{color:#a8a6a0!important;}.box{background:#2a2a28!important;border-color:#3a3a37!important;}.btn{background:#f2f1ee!important;}.btn a{color:#161615!important;}.btn2{border-color:#f2f1ee!important;}.btn2 a{color:#f2f1ee!important;}.rowline{border-color:#3a3a37!important;}.pill{background:#1f1f1e!important;}.pill span{color:#f2f1ee!important;}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;padding:0;background:#f1f0ec;">
|
||||||
|
<table role="presentation" class="bg" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f1f0ec;">
|
||||||
|
<tr><td align="center" style="padding:32px 12px;">
|
||||||
|
<table role="presentation" class="w" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:600px;">
|
||||||
|
<tr><td class="px" bgcolor="{{farbe}}" style="background:{{farbe}};border-radius:16px 16px 0 0;padding:18px 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td class="pill" bgcolor="#ffffff" style="background:#ffffff;border-radius:8px;padding:8px 14px;font-family:Arial,Helvetica,sans-serif;font-size:17px;line-height:22px;font-weight:bold;color:#111111;"><span style="color:#111111;">{{firma}}</span></td></tr></table></td></tr>
|
||||||
|
<tr><td class="card" style="background:#ffffff;border-radius:0 0 16px 16px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||||
|
<tr><td class="px ink" style="padding:36px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:26px;line-height:32px;mso-line-height-rule:exactly;font-weight:bold;letter-spacing:-0.5px;color:#111111;">Ihre Buchung wurde storniert</td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Guten Tag {{gast_name}},<br>{{firma}} hat Ihre Buchung storniert. Die Stornorechnung {{rechnung_nummer}} hängt als PDF an.</td></tr>
|
||||||
|
<!-- WENN nachricht -->
|
||||||
|
<tr><td class="px" style="padding:20px 40px 0 40px;"><table role="presentation" class="box" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f7f6f3;border:1px solid #e8e6e0;border-radius:12px;"><tr><td class="ink" style="padding:16px 20px;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;color:#111111;">{{nachricht_vermieter}}</td></tr></table></td></tr>
|
||||||
|
<!-- ENDE nachricht -->
|
||||||
|
<tr><td class="px" style="padding:24px 40px 0 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;mso-line-height-rule:exactly;">
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Unterkunft</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;"><strong>{{unterkunft}}</strong></td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Zeitraum</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{zeitraum}} ({{naechte}})</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Personen</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{personen}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="150" valign="top" style="width:150px;padding:11px 12px 11px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Buchungsnummer</td><td class="val ink rowline" valign="top" style="padding:11px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{nummer}}</td></tr>
|
||||||
|
</table></td></tr>
|
||||||
|
<!-- WENN erstattung -->
|
||||||
|
<tr><td class="px" style="padding:20px 40px 0 40px;"><table role="presentation" class="box" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f7f6f3;border:1px solid #e8e6e0;border-radius:12px;"><tr><td class="ink" style="padding:16px 20px;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;color:#111111;">Eine Erstattung von {{erstattung}} wird veranlasst. Je nach Zahlart kann es einige Tage dauern, bis der Betrag bei Ihnen sichtbar ist.</td></tr></table></td></tr>
|
||||||
|
<!-- ENDE erstattung -->
|
||||||
|
<tr><td class="px ink" style="padding:28px 40px 40px 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:24px;color:#111111;">Freundliche Grüße<br><strong>{{firma}}</strong></td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:24px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;"><div><strong>{{firma}}</strong></div><div>{{anbieter_kontakt}}</div></td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:12px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;">Diese E-Mail wurde im Auftrag von {{firma}} über den Buchungskalender von WEBklar versendet. Antworten gehen direkt an {{firma}}.</td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!--
|
||||||
|
K31 · Neue Buchungsanfrage (an den Vermieter)
|
||||||
|
Betreff: Neue Buchungsanfrage {{nummer}} für {{unterkunft}}
|
||||||
|
Absender: „WEBklar“ <Portal-Postfach>
|
||||||
|
Empfänger: Kunde (E-Mail des Kundenkontos) · OHNE BCC (enthält Angaben des Gastes)
|
||||||
|
Platzhalter: {{kunde_name}}, {{gast_name}}, {{gast_email}}, {{unterkunft}}, {{zeitraum}}, {{naechte}}, {{personen}}, {{nummer}}, {{nachricht_gast}}, {{portal_url}}
|
||||||
|
Ein Gast hat über die Website eine Anfrage gestellt; der Vermieter prüft und gibt sie im Portal frei oder lehnt sie ab. nachricht_gast steht „–“, wenn der Gast keine geschrieben hat. portal_url = Buchungen des Projekts im Kundenportal.
|
||||||
|
Texte sind Entwurf für die Mail-Überarbeitung des Inhabers; verbindlich sind nur Platzhalter und Blöcke.
|
||||||
|
-->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
|
<title>Neue Buchungsanfrage {{nummer}} für {{unterkunft}}</title>
|
||||||
|
<!--[if mso]><style>table,td{font-family:Arial,Helvetica,sans-serif!important;}</style><![endif]-->
|
||||||
|
<style>
|
||||||
|
a{color:#111111;}
|
||||||
|
@media (max-width:620px){.w{width:100%!important;}.px{padding-left:24px!important;padding-right:24px!important;}.lbl{display:block!important;width:100%!important;padding-bottom:2px!important;border-bottom:0!important;}.val{display:block!important;width:100%!important;padding-top:0!important;}}
|
||||||
|
@media (prefers-color-scheme:dark){.bg{background:#161615!important;}.card{background:#1f1f1e!important;}.ink{color:#f2f1ee!important;}.mut{color:#a8a6a0!important;}.box{background:#2a2a28!important;border-color:#3a3a37!important;}.btn{background:#f2f1ee!important;}.btn a{color:#161615!important;}.rowline{border-color:#3a3a37!important;}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;padding:0;background:#f1f0ec;">
|
||||||
|
<table role="presentation" class="bg" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f1f0ec;">
|
||||||
|
<tr><td align="center" style="padding:32px 12px;">
|
||||||
|
<table role="presentation" class="w" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:600px;">
|
||||||
|
<tr><td class="px" style="padding:0 40px 20px 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0"><tr>
|
||||||
|
<td><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td valign="middle" style="padding-right:10px;"><img src="https://project.webklar.com/webklar-logo.png" width="40" height="40" alt="WEBklar" style="display:block;width:40px;height:40px;border:0;outline:none;text-decoration:none;border-radius:10px;"></td><td valign="middle" class="ink" style="font-family:Arial,Helvetica,sans-serif;font-size:26px;font-weight:bold;letter-spacing:-1px;color:#111111;">WEBklar</td></tr></table></td>
|
||||||
|
<td align="right" class="mut" style="font-family:Arial,Helvetica,sans-serif;font-size:12px;color:#6b6964;">Buchungskalender</td>
|
||||||
|
</tr></table></td></tr>
|
||||||
|
<tr><td class="card" style="background:#ffffff;border-radius:16px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||||
|
<tr><td class="px" style="padding:44px 40px 8px 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td style="background:#e4f2e6;border-radius:100px;padding:6px 12px;font-family:Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;color:#1d5b2c;">Neue Anfrage</td></tr></table></td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:16px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:28px;line-height:34px;mso-line-height-rule:exactly;font-weight:bold;letter-spacing:-0.5px;color:#111111;">Neue Buchungsanfrage {{nummer}}</td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Hallo {{kunde_name}},<br>über Deine Website ist eine neue Buchungsanfrage für {{unterkunft}} eingegangen. Bitte prüfe sie und gib sie frei oder lehne sie ab.</td></tr>
|
||||||
|
<tr><td class="px" style="padding:28px 40px 0 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;mso-line-height-rule:exactly;">
|
||||||
|
<tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Gast</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;"><strong>{{gast_name}}</strong></td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">E-Mail</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{gast_email}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Zeitraum</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{zeitraum}} ({{naechte}})</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Personen</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{personen}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Nachricht</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{nachricht_gast}}</td></tr>
|
||||||
|
</table></td></tr>
|
||||||
|
<tr><td class="px" style="padding:28px 40px 0 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td class="btn" bgcolor="#111111" style="background:#111111;border-radius:10px;"><a href="{{portal_url}}" style="display:block;padding:14px 26px;font-family:Arial,Helvetica,sans-serif;font-size:15px;font-weight:bold;color:#ffffff;text-decoration:none;">Anfrage ansehen →</a></td></tr></table></td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:28px 40px 44px 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:24px;color:#111111;">Viele Grüße<br><strong>Dein WEBklar-Team</strong></td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td class="px" align="center" style="padding:24px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;"><a class="mut" href="https://project.webklar.com/dashboard.html" style="color:#4a4843;text-decoration:underline;">Kundenportal</a></td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;">WEBklar GbR · Am Schwimmbad 10 · 67722 Winnweiler<br>Telefon <a class="mut" href="tel:+4917623726355" style="color:#6b6964;text-decoration:none;">0176 23726355</a> · <a class="mut" href="mailto:support@webklar.com" style="color:#6b6964;text-decoration:none;">support@webklar.com</a><br>Vertreten durch die Gesellschafter Kenso Grimm und Justin Klein</td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!--
|
||||||
|
K32 · Neue Buchung (an den Vermieter)
|
||||||
|
Betreff: Neue Buchung {{nummer}} für {{unterkunft}}
|
||||||
|
Absender: „WEBklar“ <Portal-Postfach>
|
||||||
|
Empfänger: Kunde (E-Mail des Kundenkontos) · OHNE BCC (enthält Angaben des Gastes)
|
||||||
|
Platzhalter: {{kunde_name}}, {{gast_name}}, {{unterkunft}}, {{zeitraum}}, {{naechte}}, {{personen}}, {{nummer}}, {{gesamt}}, {{zahlart_text}}, {{portal_url}}
|
||||||
|
Der Gast hat verbindlich gebucht; Rechnung und Bestätigung sind schon an ihn raus. zahlart_text = „Online-Zahlung“ oder „Zahlung vor Ort“.
|
||||||
|
Texte sind Entwurf für die Mail-Überarbeitung des Inhabers; verbindlich sind nur Platzhalter und Blöcke.
|
||||||
|
-->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
|
<title>Neue Buchung {{nummer}} für {{unterkunft}}</title>
|
||||||
|
<!--[if mso]><style>table,td{font-family:Arial,Helvetica,sans-serif!important;}</style><![endif]-->
|
||||||
|
<style>
|
||||||
|
a{color:#111111;}
|
||||||
|
@media (max-width:620px){.w{width:100%!important;}.px{padding-left:24px!important;padding-right:24px!important;}.lbl{display:block!important;width:100%!important;padding-bottom:2px!important;border-bottom:0!important;}.val{display:block!important;width:100%!important;padding-top:0!important;}}
|
||||||
|
@media (prefers-color-scheme:dark){.bg{background:#161615!important;}.card{background:#1f1f1e!important;}.ink{color:#f2f1ee!important;}.mut{color:#a8a6a0!important;}.box{background:#2a2a28!important;border-color:#3a3a37!important;}.btn{background:#f2f1ee!important;}.btn a{color:#161615!important;}.rowline{border-color:#3a3a37!important;}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;padding:0;background:#f1f0ec;">
|
||||||
|
<table role="presentation" class="bg" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f1f0ec;">
|
||||||
|
<tr><td align="center" style="padding:32px 12px;">
|
||||||
|
<table role="presentation" class="w" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:600px;">
|
||||||
|
<tr><td class="px" style="padding:0 40px 20px 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0"><tr>
|
||||||
|
<td><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td valign="middle" style="padding-right:10px;"><img src="https://project.webklar.com/webklar-logo.png" width="40" height="40" alt="WEBklar" style="display:block;width:40px;height:40px;border:0;outline:none;text-decoration:none;border-radius:10px;"></td><td valign="middle" class="ink" style="font-family:Arial,Helvetica,sans-serif;font-size:26px;font-weight:bold;letter-spacing:-1px;color:#111111;">WEBklar</td></tr></table></td>
|
||||||
|
<td align="right" class="mut" style="font-family:Arial,Helvetica,sans-serif;font-size:12px;color:#6b6964;">Buchungskalender</td>
|
||||||
|
</tr></table></td></tr>
|
||||||
|
<tr><td class="card" style="background:#ffffff;border-radius:16px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||||
|
<tr><td class="px" style="padding:44px 40px 8px 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td style="background:#e4f2e6;border-radius:100px;padding:6px 12px;font-family:Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;color:#1d5b2c;">Neue Buchung</td></tr></table></td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:16px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:28px;line-height:34px;mso-line-height-rule:exactly;font-weight:bold;letter-spacing:-0.5px;color:#111111;">Neue Buchung {{nummer}}</td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Hallo {{kunde_name}},<br>{{gast_name}} hat {{unterkunft}} verbindlich gebucht. Die Bestätigung mit der Rechnung ist schon beim Gast.</td></tr>
|
||||||
|
<tr><td class="px" style="padding:28px 40px 0 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;mso-line-height-rule:exactly;">
|
||||||
|
<tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Zeitraum</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{zeitraum}} ({{naechte}})</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Personen</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{personen}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Gesamt</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;"><strong>{{gesamt}}</strong></td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Zahlung</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{zahlart_text}}</td></tr>
|
||||||
|
</table></td></tr>
|
||||||
|
<tr><td class="px" style="padding:28px 40px 0 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td class="btn" bgcolor="#111111" style="background:#111111;border-radius:10px;"><a href="{{portal_url}}" style="display:block;padding:14px 26px;font-family:Arial,Helvetica,sans-serif;font-size:15px;font-weight:bold;color:#ffffff;text-decoration:none;">Buchung ansehen →</a></td></tr></table></td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:28px 40px 44px 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:24px;color:#111111;">Viele Grüße<br><strong>Dein WEBklar-Team</strong></td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td class="px" align="center" style="padding:24px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;"><a class="mut" href="https://project.webklar.com/dashboard.html" style="color:#4a4843;text-decoration:underline;">Kundenportal</a></td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;">WEBklar GbR · Am Schwimmbad 10 · 67722 Winnweiler<br>Telefon <a class="mut" href="tel:+4917623726355" style="color:#6b6964;text-decoration:none;">0176 23726355</a> · <a class="mut" href="mailto:support@webklar.com" style="color:#6b6964;text-decoration:none;">support@webklar.com</a><br>Vertreten durch die Gesellschafter Kenso Grimm und Justin Klein</td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!--
|
||||||
|
K33 · Kalenderabgleich fehlgeschlagen (an den Vermieter)
|
||||||
|
Betreff: Der Kalenderabgleich für {{unterkunft}} funktioniert nicht
|
||||||
|
Absender: „WEBklar“ <Portal-Postfach>
|
||||||
|
Empfänger: Kunde (E-Mail des Kundenkontos) · BCC intern
|
||||||
|
Platzhalter: {{kunde_name}}, {{unterkunft}}, {{portal_url}}
|
||||||
|
Der Abgleich mit einem externen Kalender (iCal-Link, z. B. Airbnb oder Booking.com) schlägt wiederholt fehl; belegte Tage von dort fehlen sonst im Buchungskalender. Höchstens einmal je Tag.
|
||||||
|
Texte sind Entwurf für die Mail-Überarbeitung des Inhabers; verbindlich sind nur Platzhalter und Blöcke.
|
||||||
|
-->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
|
<title>Der Kalenderabgleich für {{unterkunft}} funktioniert nicht</title>
|
||||||
|
<!--[if mso]><style>table,td{font-family:Arial,Helvetica,sans-serif!important;}</style><![endif]-->
|
||||||
|
<style>
|
||||||
|
a{color:#111111;}
|
||||||
|
@media (max-width:620px){.w{width:100%!important;}.px{padding-left:24px!important;padding-right:24px!important;}.lbl{display:block!important;width:100%!important;padding-bottom:2px!important;border-bottom:0!important;}.val{display:block!important;width:100%!important;padding-top:0!important;}}
|
||||||
|
@media (prefers-color-scheme:dark){.bg{background:#161615!important;}.card{background:#1f1f1e!important;}.ink{color:#f2f1ee!important;}.mut{color:#a8a6a0!important;}.box{background:#2a2a28!important;border-color:#3a3a37!important;}.btn{background:#f2f1ee!important;}.btn a{color:#161615!important;}.rowline{border-color:#3a3a37!important;}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;padding:0;background:#f1f0ec;">
|
||||||
|
<table role="presentation" class="bg" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f1f0ec;">
|
||||||
|
<tr><td align="center" style="padding:32px 12px;">
|
||||||
|
<table role="presentation" class="w" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:600px;">
|
||||||
|
<tr><td class="px" style="padding:0 40px 20px 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0"><tr>
|
||||||
|
<td><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td valign="middle" style="padding-right:10px;"><img src="https://project.webklar.com/webklar-logo.png" width="40" height="40" alt="WEBklar" style="display:block;width:40px;height:40px;border:0;outline:none;text-decoration:none;border-radius:10px;"></td><td valign="middle" class="ink" style="font-family:Arial,Helvetica,sans-serif;font-size:26px;font-weight:bold;letter-spacing:-1px;color:#111111;">WEBklar</td></tr></table></td>
|
||||||
|
<td align="right" class="mut" style="font-family:Arial,Helvetica,sans-serif;font-size:12px;color:#6b6964;">Buchungskalender</td>
|
||||||
|
</tr></table></td></tr>
|
||||||
|
<tr><td class="card" style="background:#ffffff;border-radius:16px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||||
|
<tr><td class="px" style="padding:44px 40px 8px 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td style="background:#e4f2e6;border-radius:100px;padding:6px 12px;font-family:Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;color:#1d5b2c;">Hinweis</td></tr></table></td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:16px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:28px;line-height:34px;mso-line-height-rule:exactly;font-weight:bold;letter-spacing:-0.5px;color:#111111;">Kalenderabgleich fehlgeschlagen</td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Hallo {{kunde_name}},<br>wir konnten den externen Kalender von {{unterkunft}} nicht abrufen. Solange das nicht klappt, fehlen die dort belegten Tage im Buchungskalender, und Gäste könnten Zeiträume anfragen, die schon vergeben sind. Bitte prüfe den Kalender-Link in den Einstellungen.</td></tr>
|
||||||
|
<tr><td class="px" style="padding:28px 40px 0 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td class="btn" bgcolor="#111111" style="background:#111111;border-radius:10px;"><a href="{{portal_url}}" style="display:block;padding:14px 26px;font-family:Arial,Helvetica,sans-serif;font-size:15px;font-weight:bold;color:#ffffff;text-decoration:none;">Kalender prüfen →</a></td></tr></table></td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:28px 40px 44px 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:24px;color:#111111;">Viele Grüße<br><strong>Dein WEBklar-Team</strong></td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td class="px" align="center" style="padding:24px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;"><a class="mut" href="https://project.webklar.com/dashboard.html" style="color:#4a4843;text-decoration:underline;">Kundenportal</a></td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;">WEBklar GbR · Am Schwimmbad 10 · 67722 Winnweiler<br>Telefon <a class="mut" href="tel:+4917623726355" style="color:#6b6964;text-decoration:none;">0176 23726355</a> · <a class="mut" href="mailto:support@webklar.com" style="color:#6b6964;text-decoration:none;">support@webklar.com</a><br>Vertreten durch die Gesellschafter Kenso Grimm und Justin Klein</td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!--
|
||||||
|
K34 · Anfrage Buchungskalender entschieden (Annahme oder Ablehnung)
|
||||||
|
Betreff: Deine Anfrage zum Buchungskalender für {{projekt}}
|
||||||
|
Absender: „WEBklar“ <Portal-Postfach>
|
||||||
|
Empfänger: Kunde (E-Mail des Kundenkontos) · BCC intern
|
||||||
|
Platzhalter: {{kunde_name}}, {{projekt}}, {{preis}}, {{ust_hinweis}}, {{begruendung}}, {{portal_url}}
|
||||||
|
Optionale Blöcke (zwischen WENN-/ENDE-Kommentaren; bei Nichtzutreffen samt Inhalt entfernen): angenommen, abgelehnt
|
||||||
|
Genau einer der Blöcke ist an. angenommen = Anfrage angenommen, der Kunde kann den Buchungskalender jetzt bestellen; abgelehnt = mit Begründung des Teams.
|
||||||
|
preis = Preis der Einrichtung, z. B. „299 €“; ust_hinweis = Umsatzsteuerhinweis; portal_url = Seite des Projekts im Kundenportal.
|
||||||
|
Texte sind Entwurf für die Mail-Überarbeitung des Inhabers; verbindlich sind nur Platzhalter und Blöcke.
|
||||||
|
-->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
|
<title>Deine Anfrage zum Buchungskalender für {{projekt}}</title>
|
||||||
|
<!--[if mso]><style>table,td{font-family:Arial,Helvetica,sans-serif!important;}</style><![endif]-->
|
||||||
|
<style>
|
||||||
|
a{color:#111111;}
|
||||||
|
@media (max-width:620px){.w{width:100%!important;}.px{padding-left:24px!important;padding-right:24px!important;}.lbl{display:block!important;width:100%!important;padding-bottom:2px!important;border-bottom:0!important;}.val{display:block!important;width:100%!important;padding-top:0!important;}}
|
||||||
|
@media (prefers-color-scheme:dark){.bg{background:#161615!important;}.card{background:#1f1f1e!important;}.ink{color:#f2f1ee!important;}.mut{color:#a8a6a0!important;}.box{background:#2a2a28!important;border-color:#3a3a37!important;}.btn{background:#f2f1ee!important;}.btn a{color:#161615!important;}.rowline{border-color:#3a3a37!important;}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;padding:0;background:#f1f0ec;">
|
||||||
|
<table role="presentation" class="bg" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f1f0ec;">
|
||||||
|
<tr><td align="center" style="padding:32px 12px;">
|
||||||
|
<table role="presentation" class="w" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:600px;">
|
||||||
|
<tr><td class="px" style="padding:0 40px 20px 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0"><tr>
|
||||||
|
<td><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td valign="middle" style="padding-right:10px;"><img src="https://project.webklar.com/webklar-logo.png" width="40" height="40" alt="WEBklar" style="display:block;width:40px;height:40px;border:0;outline:none;text-decoration:none;border-radius:10px;"></td><td valign="middle" class="ink" style="font-family:Arial,Helvetica,sans-serif;font-size:26px;font-weight:bold;letter-spacing:-1px;color:#111111;">WEBklar</td></tr></table></td>
|
||||||
|
<td align="right" class="mut" style="font-family:Arial,Helvetica,sans-serif;font-size:12px;color:#6b6964;">Buchungskalender</td>
|
||||||
|
</tr></table></td></tr>
|
||||||
|
<tr><td class="card" style="background:#ffffff;border-radius:16px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||||
|
<tr><td class="px" style="padding:44px 40px 8px 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td style="background:#e4f2e6;border-radius:100px;padding:6px 12px;font-family:Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;color:#1d5b2c;">Buchungskalender</td></tr></table></td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:16px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:28px;line-height:34px;mso-line-height-rule:exactly;font-weight:bold;letter-spacing:-0.5px;color:#111111;"><!-- WENN angenommen -->Deine Anfrage wurde angenommen<!-- ENDE angenommen --><!-- WENN abgelehnt -->Zu Deiner Anfrage<!-- ENDE abgelehnt --></td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">Hallo {{kunde_name}},<br><!-- WENN angenommen -->wir haben Deine Anfrage zum Buchungskalender für {{projekt}} angenommen. Du kannst ihn jetzt im Kundenportal bestellen; danach richten wir ihn ein.<!-- ENDE angenommen --><!-- WENN abgelehnt -->leider können wir Deine Anfrage zum Buchungskalender für {{projekt}} nicht annehmen. Den Grund findest Du unten. Wenn Du Fragen hast, antworte einfach auf diese E-Mail.<!-- ENDE abgelehnt --></td></tr>
|
||||||
|
<tr><td class="px" style="padding:28px 40px 0 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;mso-line-height-rule:exactly;">
|
||||||
|
<tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Projekt</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;"><strong>{{projekt}}</strong></td></tr>
|
||||||
|
<!-- WENN angenommen --><tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Preis</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;">einmalig {{preis}} ({{ust_hinweis}})</td></tr>
|
||||||
|
<!-- ENDE angenommen --><!-- WENN abgelehnt --><tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Begründung</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{begruendung}}</td></tr>
|
||||||
|
<!-- ENDE abgelehnt --></table></td></tr>
|
||||||
|
<tr><td class="px" style="padding:28px 40px 0 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td class="btn" bgcolor="#111111" style="background:#111111;border-radius:10px;"><a href="{{portal_url}}" style="display:block;padding:14px 26px;font-family:Arial,Helvetica,sans-serif;font-size:15px;font-weight:bold;color:#ffffff;text-decoration:none;">Zum Kundenportal →</a></td></tr></table></td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:28px 40px 44px 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:24px;color:#111111;">Viele Grüße<br><strong>Dein WEBklar-Team</strong></td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td class="px" align="center" style="padding:24px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;"><a class="mut" href="https://project.webklar.com/dashboard.html" style="color:#4a4843;text-decoration:underline;">Kundenportal</a></td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;">WEBklar GbR · Am Schwimmbad 10 · 67722 Winnweiler<br>Telefon <a class="mut" href="tel:+4917623726355" style="color:#6b6964;text-decoration:none;">0176 23726355</a> · <a class="mut" href="mailto:support@webklar.com" style="color:#6b6964;text-decoration:none;">support@webklar.com</a><br>Vertreten durch die Gesellschafter Kenso Grimm und Justin Klein</td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!--
|
||||||
|
T22 · Neue Anfrage Buchungskalender
|
||||||
|
Betreff: [Buchungskalender] Neue Anfrage: {{kunde_name}}
|
||||||
|
Absender: „WEBklar Kundenportal“ <Portal-Postfach>
|
||||||
|
Empfänger: kenso@webklar.com, andrej@webklar.com
|
||||||
|
Platzhalter: {{kunde_name}}, {{email}}, {{projekt}}, {{nachricht}}, {{woms_url}}
|
||||||
|
Die Vorlage hat keine optionalen Blöcke. Ein Kunde hat im Portal eine Anfrage für den Buchungskalender gestellt („auf Anfrage“); das Team entscheidet im Ticketsystem (Kunde, Reiter Funktionen): annehmen oder ablehnen.
|
||||||
|
nachricht steht „–“, wenn der Kunde keine geschrieben hat. woms_url führt zum Kunden im Ticketsystem (Reiter Funktionen).
|
||||||
|
Texte sind Entwurf für die Mail-Überarbeitung des Inhabers; verbindlich sind nur Platzhalter und Blöcke.
|
||||||
|
-->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
|
<title>[Buchungskalender] Neue Anfrage: {{kunde_name}}</title>
|
||||||
|
<!--[if mso]><style>table,td{font-family:Arial,Helvetica,sans-serif!important;}</style><![endif]-->
|
||||||
|
<style>
|
||||||
|
a{color:#111111;}
|
||||||
|
@media (max-width:620px){.w{width:100%!important;}.px{padding-left:24px!important;padding-right:24px!important;}.lbl{display:block!important;width:100%!important;padding-bottom:2px!important;border-bottom:0!important;}.val{display:block!important;width:100%!important;padding-top:0!important;}}
|
||||||
|
@media (prefers-color-scheme:dark){.bg{background:#161615!important;}.card{background:#1f1f1e!important;}.ink{color:#f2f1ee!important;}.mut{color:#a8a6a0!important;}.box{background:#2a2a28!important;border-color:#3a3a37!important;}.btn{background:#f2f1ee!important;}.btn a{color:#161615!important;}.rowline{border-color:#3a3a37!important;}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;padding:0;background:#f1f0ec;">
|
||||||
|
<table role="presentation" class="bg" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f1f0ec;">
|
||||||
|
<tr><td align="center" style="padding:32px 12px;">
|
||||||
|
<table role="presentation" class="w" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:600px;">
|
||||||
|
<tr><td class="px" style="padding:0 40px 20px 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0"><tr>
|
||||||
|
<td><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td valign="middle" style="padding-right:10px;"><img src="https://project.webklar.com/webklar-logo.png" width="40" height="40" alt="WEBklar" style="display:block;width:40px;height:40px;border:0;outline:none;text-decoration:none;border-radius:10px;"></td><td valign="middle" class="ink" style="font-family:Arial,Helvetica,sans-serif;font-size:26px;font-weight:bold;letter-spacing:-1px;color:#111111;">WEBklar</td></tr></table></td>
|
||||||
|
<td align="right" class="mut" style="font-family:Arial,Helvetica,sans-serif;font-size:12px;color:#6b6964;">Buchungskalender</td>
|
||||||
|
</tr></table></td></tr>
|
||||||
|
<tr><td class="card" style="background:#ffffff;border-radius:16px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||||
|
<tr><td class="px" style="padding:44px 40px 8px 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td style="background:#e4f2e6;border-radius:100px;padding:6px 12px;font-family:Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;color:#1d5b2c;">Neue Anfrage</td></tr></table></td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:16px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:28px;line-height:34px;mso-line-height-rule:exactly;font-weight:bold;letter-spacing:-0.5px;color:#111111;">Neue Anfrage: Buchungskalender</td></tr>
|
||||||
|
<tr><td class="px mut" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:25px;mso-line-height-rule:exactly;color:#4a4843;">{{kunde_name}} möchte den Buchungskalender für {{projekt}} und hat eine Anfrage gestellt.</td></tr>
|
||||||
|
<tr><td class="px" style="padding:28px 40px 0 40px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;mso-line-height-rule:exactly;">
|
||||||
|
<tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Kunde</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;"><strong>{{kunde_name}}</strong></td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">E-Mail</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{email}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Projekt</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{projekt}}</td></tr>
|
||||||
|
<tr><td class="lbl mut rowline" width="170" valign="top" style="width:170px;padding:12px 12px 12px 0;border-bottom:1px solid #ecebe6;color:#6b6964;">Nachricht</td><td class="val ink rowline" valign="top" style="padding:12px 0;border-bottom:1px solid #ecebe6;color:#111111;">{{nachricht}}</td></tr>
|
||||||
|
</table></td></tr>
|
||||||
|
<tr><td class="px" style="padding:28px 40px 0 40px;"><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr><td class="btn" bgcolor="#111111" style="background:#111111;border-radius:10px;"><a href="{{woms_url}}" style="display:block;padding:14px 26px;font-family:Arial,Helvetica,sans-serif;font-size:15px;font-weight:bold;color:#ffffff;text-decoration:none;">Im Ticketsystem öffnen →</a></td></tr></table></td></tr>
|
||||||
|
<tr><td class="px ink" style="padding:28px 40px 44px 40px;font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:24px;color:#111111;">Viele Grüße<br><strong>Dein WEBklar-Team</strong></td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td class="px" align="center" style="padding:24px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;"><a class="mut" href="https://project.webklar.com/dashboard.html" style="color:#4a4843;text-decoration:underline;">Kundenportal</a></td></tr>
|
||||||
|
<tr><td class="px mut" align="center" style="padding:14px 40px 0 40px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:19px;color:#6b6964;">WEBklar GbR · Am Schwimmbad 10 · 67722 Winnweiler<br>Telefon <a class="mut" href="tel:+4917623726355" style="color:#6b6964;text-decoration:none;">0176 23726355</a> · <a class="mut" href="mailto:support@webklar.com" style="color:#6b6964;text-decoration:none;">support@webklar.com</a><br>Vertreten durch die Gesellschafter Kenso Grimm und Justin Klein</td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -191,9 +191,9 @@ const oneLine = (value) => String(value ?? '').replace(/\s*[\r\n]+\s*/g, ' ').tr
|
|||||||
* (Betriebs-E-Mail), nie BCC. Mit `ics` haengt die Kalenderdatei als icalEvent (nodemailer-Kalenderteil, kein Anhang) an.
|
* (Betriebs-E-Mail), nie BCC. Mit `ics` haengt die Kalenderdatei als icalEvent (nodemailer-Kalenderteil, kein Anhang) an.
|
||||||
* Wirft bei fehlendem SMTP, ungueltiger Adresse oder Methode - der Aufrufer (Outbox) entscheidet ueber Wiederholung.
|
* Wirft bei fehlendem SMTP, ungueltiger Adresse oder Methode - der Aufrufer (Outbox) entscheidet ueber Wiederholung.
|
||||||
* @param {{ to: string, fromName: string, replyTo?: string|null, subject: string, html: string, text: string,
|
* @param {{ to: string, fromName: string, replyTo?: string|null, subject: string, html: string, text: string,
|
||||||
* ics?: string|null, icsMethod?: 'REQUEST'|'CANCEL'|'PUBLISH' }} message
|
* ics?: string|null, icsMethod?: 'REQUEST'|'CANCEL'|'PUBLISH', attachments?: object[]|null (nodemailer, unveraendert, z. B. Rechnungs-PDF) }} message
|
||||||
*/
|
*/
|
||||||
export async function sendGuestMail({ to, fromName, replyTo = null, subject, html, text, ics = null, icsMethod = 'REQUEST' }) {
|
export async function sendGuestMail({ to, fromName, replyTo = null, subject, html, text, ics = null, icsMethod = 'REQUEST', attachments = null }) {
|
||||||
const mailer = getTransporter()
|
const mailer = getTransporter()
|
||||||
if (!mailer) throw new Error('SMTP nicht konfiguriert')
|
if (!mailer) throw new Error('SMTP nicht konfiguriert')
|
||||||
checkMailbox(to, 'to')
|
checkMailbox(to, 'to')
|
||||||
@@ -211,6 +211,7 @@ export async function sendGuestMail({ to, fromName, replyTo = null, subject, htm
|
|||||||
text,
|
text,
|
||||||
html,
|
html,
|
||||||
icalEvent: ics ? { method: icsMethod, filename: 'termin.ics', content: ics } : undefined,
|
icalEvent: ics ? { method: icsMethod, filename: 'termin.ics', content: ics } : undefined,
|
||||||
|
attachments: Array.isArray(attachments) && attachments.length ? attachments : undefined,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { Query, countDocuments, createDocument, deleteDocument, getDocument, kei
|
|||||||
import { withLocks } from '../keyedLock.js'
|
import { withLocks } from '../keyedLock.js'
|
||||||
import { sendGuestMail } from '../mailer.js'
|
import { sendGuestMail } from '../mailer.js'
|
||||||
import { renderMail } from '../mailTemplates.js'
|
import { renderMail } from '../mailTemplates.js'
|
||||||
|
import { ladeRechnung } from '../unterkunft/speicher.js'
|
||||||
|
import { rechnungPdf } from '../unterkunft/rechnungPdf.js'
|
||||||
import { pruefeNow, pruefeText, zeitMs } from './util.js'
|
import { pruefeNow, pruefeText, zeitMs } from './util.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -286,6 +288,7 @@ async function sende(id, now) {
|
|||||||
if (!platz) return 'wartet'
|
if (!platz) return 'wartet'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const attachments = await baueAnhaenge(doc)
|
||||||
await sendGuestMail({
|
await sendGuestMail({
|
||||||
to: doc.an,
|
to: doc.an,
|
||||||
fromName: doc.fromName,
|
fromName: doc.fromName,
|
||||||
@@ -295,6 +298,7 @@ async function sende(id, now) {
|
|||||||
text: doc.text,
|
text: doc.text,
|
||||||
ics: doc.ics || null,
|
ics: doc.ics || null,
|
||||||
icsMethod: doc.icsMethod || 'REQUEST',
|
icsMethod: doc.icsMethod || 'REQUEST',
|
||||||
|
...(attachments.length ? { attachments } : {}),
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
gibPlatzFrei(platz)
|
gibPlatzFrei(platz)
|
||||||
@@ -305,6 +309,39 @@ async function sende(id, now) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Nach so vielen Fehlschlaegen beim Erzeugen des Anhangs geht die Mail ohne ihn raus (Buchungskalender). */
|
||||||
|
const ANHANG_VERSUCHE = 3
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Anhaenge des Eintrags. Das Feld `anhang` ist JSON {art:'ukRechnung', rechnungId}: die Rechnung (ukRechnungen) wird als PDF
|
||||||
|
* gerendert (Rechnung-<nr>.pdf bzw. Stornorechnung-<nr>.pdf). Ohne `anhang` oder mit anderem Inhalt: keine Anhaenge. Scheitert das
|
||||||
|
* Laden oder Rendern, wirft die Funktion einen gewoehnlichen Fehler (der Eintrag bleibt `wartet` und wird wie jeder vorlaeufige
|
||||||
|
* Fehler wiederholt); ab dem vierten Versuch geht die Mail ohne Anhang raus. Der Fehlertext enthaelt keine Personendaten.
|
||||||
|
*/
|
||||||
|
async function baueAnhaenge(doc) {
|
||||||
|
if (!doc.anhang) return []
|
||||||
|
let verweis
|
||||||
|
try {
|
||||||
|
verweis = JSON.parse(doc.anhang)
|
||||||
|
} catch {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
if (verweis?.art !== 'ukRechnung' || typeof verweis.rechnungId !== 'string' || !verweis.rechnungId) return []
|
||||||
|
try {
|
||||||
|
const rechnung = await ladeRechnung(verweis.rechnungId)
|
||||||
|
if (!rechnung?.inhalt) throw new Error('Rechnung nicht lesbar')
|
||||||
|
const storno = rechnung.art === 'storno'
|
||||||
|
const pdf = rechnungPdf(rechnung.inhalt, { art: rechnung.art, nummer: rechnung.nummer, datum: rechnung.datum })
|
||||||
|
return [{ filename: `${storno ? 'Stornorechnung' : 'Rechnung'}-${rechnung.nummer}.pdf`, content: Buffer.from(pdf), contentType: 'application/pdf' }]
|
||||||
|
} catch (err) {
|
||||||
|
if ((Number(doc.versuche) || 0) >= ANHANG_VERSUCHE) {
|
||||||
|
console.error(`[termine-outbox] Eintrag ${doc.$id}: Rechnungs-PDF nicht erzeugt, Mail geht ohne Anhang raus (${String(err?.message).slice(0, 80)})`)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
throw new Error(`Anhang nicht erzeugt: ${String(err?.message).slice(0, 80)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ist der Bestaetigungslink dieser G01 schon verfallen? Das Halte-Fenster (config.termine.holdMinutes, bei jedem Aufruf
|
* Ist der Bestaetigungslink dieser G01 schon verfallen? Das Halte-Fenster (config.termine.holdMinutes, bei jedem Aufruf
|
||||||
* gelesen) plus eine Minute Toleranz ab Einreihen; genau so alt ist noch nicht veraltet. G01 ohne Buchung (zu_viele) hat
|
* gelesen) plus eine Minute Toleranz ab Einreihen; genau so alt ist noch nicht veraltet. G01 ohne Buchung (zu_viele) hat
|
||||||
|
|||||||
@@ -1,10 +1,179 @@
|
|||||||
// Stub: Task 20 ersetzt den Inhalt, die Signaturen bleiben. Protokolliert die Aufrufe fuer Tests.
|
import { config } from '../../config.js'
|
||||||
|
import { loadTemplate } from '../mailTemplates.js'
|
||||||
|
import { sendAdminNotice, sendCustomerMail } from '../mailer.js'
|
||||||
|
import { einreihen } from '../termine/outbox.js'
|
||||||
|
import { formatTag } from './datum.js'
|
||||||
|
import { formatEuro } from './preis.js'
|
||||||
|
import { ladeEinstellungen, ladePreise, ladeUnterkunft } from './speicher.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mails des Buchungskalenders: G16-G22 an Gaeste (ueber die Mail-Warteschlange termineOutbox, Absendername = Firma des Vermieters,
|
||||||
|
* Antworten an dessen E-Mail), K31-K34 an den Vermieter (Kunde des Portals, ohne BCC bei Gastdaten) und T22 ans Team.
|
||||||
|
* `_aufrufe` protokolliert jeden Aufruf fuer Tests (die Mail geht trotzdem raus). Fehler werden geworfen; der Aufrufer
|
||||||
|
* (ablauf.js) faengt sie ab, weil der gespeicherte Schritt nicht rueckgaengig gemacht wird.
|
||||||
|
*
|
||||||
|
* extra je Gastmail: G17 token (Roh-Token, nur hier und nur einmal bekannt; ergibt den Buchungslink), text (G18 Begruendung),
|
||||||
|
* rechnung (G19: ukRechnungen-Dokument), stornoRechnung + erstattetCent + grund (G22), G21 braucht nichts.
|
||||||
|
*/
|
||||||
export const _aufrufe = []
|
export const _aufrufe = []
|
||||||
|
|
||||||
|
const LEER = '–'
|
||||||
|
const text = (v) => (typeof v === 'string' ? v.trim() : '')
|
||||||
|
const GAST_ARTEN = ['G16', 'G17', 'G18', 'G19', 'G20', 'G21', 'G22']
|
||||||
|
|
||||||
|
const mehrzahl = (n, eins, viele) => `${n} ${n === 1 ? eins : viele}`
|
||||||
|
const FRIST_FORMAT = new Intl.DateTimeFormat('de-DE', { timeZone: 'Europe/Berlin', day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', hourCycle: 'h23' })
|
||||||
|
|
||||||
|
function fristText(wert) {
|
||||||
|
const t = Date.parse(wert)
|
||||||
|
if (!Number.isFinite(t)) return LEER
|
||||||
|
const teile = Object.fromEntries(FRIST_FORMAT.formatToParts(new Date(t)).map((p) => [p.type, p.value]))
|
||||||
|
return `${teile.day}.${teile.month}.${teile.year}, ${teile.hour}:${teile.minute} Uhr`
|
||||||
|
}
|
||||||
|
|
||||||
|
function anbieterKontakt(e) {
|
||||||
|
return [text(e.firma), text(e.strasse), [text(e.plz), text(e.ort)].filter(Boolean).join(' '), text(e.email), text(e.telefon)].filter(Boolean).join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
function postenText(posten) {
|
||||||
|
if (!Array.isArray(posten) || posten.length === 0) return LEER
|
||||||
|
return posten.map((p) => `${p.text}: ${formatEuro(p.menge * p.einzelCent)}`).join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
const ZAHLART = { online: 'Online-Zahlung', vorort: 'Zahlung vor Ort' }
|
||||||
|
|
||||||
|
/** Einstellungen des Vermieters: aus dem Kontext, sonst aus der Datenbank. */
|
||||||
|
async function einstellungenVon(ctx, featureId) {
|
||||||
|
return ctx?.einstellungen ?? (featureId ? await ladeEinstellungen(featureId) : null) ?? {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gemeinsame Werte aller Mails zu einer Anfrage. */
|
||||||
|
async function werteZuAnfrage(ctx, anfrage) {
|
||||||
|
const e = await einstellungenVon(ctx, anfrage.featureId ?? ctx?.featureId)
|
||||||
|
const u = anfrage.unterkunftId ? await ladeUnterkunft(anfrage.unterkunftId) : null
|
||||||
|
const n = Number(anfrage.naechte) || naechteVon(anfrage)
|
||||||
|
return {
|
||||||
|
einstellungen: e,
|
||||||
|
unterkunft: u,
|
||||||
|
werte: {
|
||||||
|
gast_name: text(anfrage.gast?.name) || 'Gast',
|
||||||
|
firma: text(e.firma) || text(u?.name) || 'Ihr Gastgeber',
|
||||||
|
farbe: /^#[0-9a-fA-F]{6}$/.test(text(e.akzentfarbe)) ? e.akzentfarbe : '#111111',
|
||||||
|
anbieter_kontakt: anbieterKontakt(e) || LEER,
|
||||||
|
unterkunft: text(u?.name) || LEER,
|
||||||
|
zeitraum: `${formatTag(anfrage.von)} – ${formatTag(anfrage.bis)}`,
|
||||||
|
naechte: mehrzahl(n, 'Nacht', 'Nächte'),
|
||||||
|
personen: mehrzahl(Number(anfrage.personen) || 0, 'Person', 'Personen'),
|
||||||
|
nummer: text(anfrage.nummer) || LEER,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function naechteVon(a) {
|
||||||
|
return Math.max(0, Math.round((Date.parse(a.bis) - Date.parse(a.von)) / 86400000))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stornoText(anfrage) {
|
||||||
|
// Schnappschuss bei der Freigabe gilt; nur ohne ihn die aktuellen Bedingungen
|
||||||
|
if (text(anfrage.stornobedingungenText)) return text(anfrage.stornobedingungenText)
|
||||||
|
const preise = anfrage.unterkunftId ? await ladePreise(anfrage.unterkunftId) : null
|
||||||
|
return text(preise?.stornobedingungen) || 'Es wurden keine besonderen Stornobedingungen vereinbart.'
|
||||||
|
}
|
||||||
|
|
||||||
export async function anGast(art, ctx, anfrage, extra = {}) {
|
export async function anGast(art, ctx, anfrage, extra = {}) {
|
||||||
_aufrufe.push({ an: 'gast', art, anfrageId: anfrage?.$id, extra })
|
_aufrufe.push({ an: 'gast', art, anfrageId: anfrage?.$id, extra })
|
||||||
|
if (!GAST_ARTEN.includes(art)) throw new TypeError(`${String(art).slice(0, 20)} ist keine Gastmail`)
|
||||||
|
extra = extra ?? {}
|
||||||
|
const gast = anfrage?.gast ?? {}
|
||||||
|
if (!text(gast.email)) throw new TypeError('Empfänger fehlt')
|
||||||
|
const { einstellungen: e, werte } = await werteZuAnfrage(ctx, anfrage)
|
||||||
|
const values = { ...werte }
|
||||||
|
const blocks = {}
|
||||||
|
let anhang = null
|
||||||
|
|
||||||
|
if (art !== 'G16' && art !== 'G18' && art !== 'G21' && art !== 'G20') values.stornobedingungen = await stornoText(anfrage)
|
||||||
|
if (art === 'G17' || art === 'G19' || art === 'G20') values.gesamt = formatEuro(anfrage.summen?.bruttoCent ?? 0)
|
||||||
|
if (art === 'G17' || art === 'G19') values.posten_text = postenText(anfrage.posten)
|
||||||
|
if (art === 'G17') {
|
||||||
|
values.frist = fristText(anfrage.fristAt)
|
||||||
|
const token = text(extra.token)
|
||||||
|
values.buchungs_link = `${config.publicBaseUrl}/unterkunft/${ctx?.siteKey ?? ''}/buchung/${anfrage.$id}?t=${token}`
|
||||||
|
values.nachricht_vermieter = text(anfrage.nachrichtVermieter)
|
||||||
|
blocks.nachricht = values.nachricht_vermieter !== ''
|
||||||
|
if (!token) throw new TypeError('Token für den Buchungslink fehlt')
|
||||||
|
}
|
||||||
|
if (art === 'G18') {
|
||||||
|
values.nachricht_vermieter = text(extra.text)
|
||||||
|
blocks.nachricht = values.nachricht_vermieter !== ''
|
||||||
|
}
|
||||||
|
if (art === 'G19') {
|
||||||
|
const zahlart = anfrage.zahlart
|
||||||
|
values.zahlart_text = ZAHLART[zahlart] ?? LEER
|
||||||
|
values.rechnung_nummer = text(extra.rechnung?.nummer) || LEER
|
||||||
|
blocks.online_unbezahlt = zahlart === 'online' && anfrage.status !== 'bezahlt'
|
||||||
|
if (extra.rechnung?.$id) anhang = { art: 'ukRechnung', rechnungId: extra.rechnung.$id }
|
||||||
|
}
|
||||||
|
if (art === 'G20') {
|
||||||
|
values.rechnung_nummer = text(extra.rechnung?.nummer) || text(anfrage.rechnungNummer) || LEER
|
||||||
|
}
|
||||||
|
if (art === 'G21') values.frist = fristText(anfrage.fristAt)
|
||||||
|
if (art === 'G22') {
|
||||||
|
values.rechnung_nummer = text(extra.stornoRechnung?.nummer) || LEER
|
||||||
|
values.nachricht_vermieter = text(extra.grund)
|
||||||
|
blocks.nachricht = values.nachricht_vermieter !== ''
|
||||||
|
const erstattung = Number(extra.erstattetCent) || 0
|
||||||
|
values.erstattung = erstattung > 0 ? formatEuro(erstattung) : ''
|
||||||
|
blocks.erstattung = erstattung > 0
|
||||||
|
if (extra.stornoRechnung?.$id) anhang = { art: 'ukRechnung', rechnungId: extra.stornoRechnung.$id }
|
||||||
|
}
|
||||||
|
|
||||||
|
return einreihen({
|
||||||
|
featureId: anfrage.featureId ?? ctx?.featureId,
|
||||||
|
bookingId: anfrage.$id,
|
||||||
|
art: `uk-${art}`,
|
||||||
|
an: text(gast.email),
|
||||||
|
fromName: values.firma.slice(0, 120),
|
||||||
|
replyTo: text(e.email) || null,
|
||||||
|
template: art,
|
||||||
|
values,
|
||||||
|
blocks,
|
||||||
|
...(anhang ? { anhang: JSON.stringify(anhang) } : {}),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const portalUrl = (ctx) => `${config.publicBaseUrl}/dashboard.html#buchungen?projekt=${encodeURIComponent(ctx?.projectId ?? '')}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mail an den Vermieter (K31-K34, Kunde des Portals) bzw. ans Team (T22). K31/K32: values.anfrage; die Werte lassen sich ueberschreiben,
|
||||||
|
* values.an ersetzt die Adresse (Standard ctx.besitzerEmail). T22 geht an die Team-Adressen und wirft nie.
|
||||||
|
*/
|
||||||
export async function anVermieter(template, ctx, values = {}) {
|
export async function anVermieter(template, ctx, values = {}) {
|
||||||
_aufrufe.push({ an: 'vermieter', template, values })
|
_aufrufe.push({ an: 'vermieter', template, values })
|
||||||
|
const { anfrage, an, ...eigene } = values ?? {}
|
||||||
|
const vorgabe = {}
|
||||||
|
if (anfrage) {
|
||||||
|
const { werte } = await werteZuAnfrage(ctx, anfrage)
|
||||||
|
Object.assign(vorgabe, werte, {
|
||||||
|
kunde_name: text(ctx?.kundeName) || werte.firma,
|
||||||
|
gast_email: text(anfrage.gast?.email) || LEER,
|
||||||
|
nachricht_gast: text(anfrage.gast?.nachricht) || LEER,
|
||||||
|
gesamt: formatEuro(anfrage.summen?.bruttoCent ?? 0),
|
||||||
|
zahlart_text: ZAHLART[anfrage.zahlart] ?? LEER,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const alle = { portal_url: portalUrl(ctx), ...vorgabe, ...eigene }
|
||||||
|
if (template === 'T22') {
|
||||||
|
const r = await sendAdminNotice({ template, values: nurPlatzhalter(template, alle) })
|
||||||
|
if (!r?.sent) console.warn(`[unterkunft] Team-Mail ${template} nicht gesendet: ${r?.reason || 'unbekannt'}`)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
const empfaenger = text(an) || text(ctx?.besitzerEmail)
|
||||||
|
if (!empfaenger) throw new TypeError('Empfänger fehlt')
|
||||||
|
return sendCustomerMail(empfaenger, { template, values: nurPlatzhalter(template, alle), blocks: eigene.blocks ?? {}, bccTeam: ['K33', 'K34'].includes(template) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Nur die Werte, die die Vorlage kennt (keine Warnung wegen Zusatzwerten wie farbe oder anbieter_kontakt). */
|
||||||
|
function nurPlatzhalter(template, werte) {
|
||||||
|
const kennt = loadTemplate(template).placeholders
|
||||||
|
return Object.fromEntries(Object.entries(werte).filter(([k]) => kennt.includes(k) && k !== 'blocks'))
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ const DIR = new URL('../server/mail-templates/', import.meta.url)
|
|||||||
// G01–G08 Gästemails des Terminplaners (Task 9, neutrales Layout im Auftrag eines Betriebs), K20 und K21 Hinweise an den Betrieb;
|
// G01–G08 Gästemails des Terminplaners (Task 9, neutrales Layout im Auftrag eines Betriebs), K20 und K21 Hinweise an den Betrieb;
|
||||||
// K12, K14 und T12 (Wechsel ins Wartungs-Abo) aus Task 9, K13 und T13 (Zusatzspeicher) aus Task 8 (Fix 1); Endfassung der Texte in Task 12; T14 „Löschfristen“ aus Task 21 (F39); T15 Push-Zeitmail; K15, K16, T16 Funktionen (News-Pinnwand, Terminplaner; K15, K16 und T16 gelten je Funktion, Terminplaner TP2 B9a und B9b); T17 Repo zuordnen; K17 Chat-Hinweis an Kunden; K18, K19 Website-Analyse; K22, K23 Terminplaner beendet/läuft wieder (Terminplaner TP2 E3); T18 Bankzugang erneuern (Finanzen); T19 Erstattung fällig und T20 Terminplaner Betriebsmeldung (Terminplaner TP2, Team-Hinweise; T16 gilt je Funktion, T19 aus B9b); T21 Neue Webshop-Anfrage (Webshop B1, Team-Hinweis); K24 Webshop-Anfrage entschieden (Webshop B2, Kunde); G09–G14 Käufermails des Webshops (Bestellbestätigung, versendet, abholbereit, Eingang Widerruf, Erstattung, Zahlung fehlgeschlagen; Webshop E4, eigener Fuß statt Terminplaner-Fuß, Sie-Form) und K25, K26 an den Händler (neue Bestellung, Widerruf; Webshop E4); K27–K30 Öffnungszeiten live (eingeschaltet, Feiertage prüfen, Abo beendet, abgeschaltet; Öffnungszeiten live Task 13); K04 und T09 gelten beim Widerruf einer Funktion je Funktion (produkt, T09 zusätzlich preis; Terminplaner TP2 F9)
|
// K12, K14 und T12 (Wechsel ins Wartungs-Abo) aus Task 9, K13 und T13 (Zusatzspeicher) aus Task 8 (Fix 1); Endfassung der Texte in Task 12; T14 „Löschfristen“ aus Task 21 (F39); T15 Push-Zeitmail; K15, K16, T16 Funktionen (News-Pinnwand, Terminplaner; K15, K16 und T16 gelten je Funktion, Terminplaner TP2 B9a und B9b); T17 Repo zuordnen; K17 Chat-Hinweis an Kunden; K18, K19 Website-Analyse; K22, K23 Terminplaner beendet/läuft wieder (Terminplaner TP2 E3); T18 Bankzugang erneuern (Finanzen); T19 Erstattung fällig und T20 Terminplaner Betriebsmeldung (Terminplaner TP2, Team-Hinweise; T16 gilt je Funktion, T19 aus B9b); T21 Neue Webshop-Anfrage (Webshop B1, Team-Hinweis); K24 Webshop-Anfrage entschieden (Webshop B2, Kunde); G09–G14 Käufermails des Webshops (Bestellbestätigung, versendet, abholbereit, Eingang Widerruf, Erstattung, Zahlung fehlgeschlagen; Webshop E4, eigener Fuß statt Terminplaner-Fuß, Sie-Form) und K25, K26 an den Händler (neue Bestellung, Widerruf; Webshop E4); K27–K30 Öffnungszeiten live (eingeschaltet, Feiertage prüfen, Abo beendet, abgeschaltet; Öffnungszeiten live Task 13); K04 und T09 gelten beim Widerruf einer Funktion je Funktion (produkt, T09 zusätzlich preis; Terminplaner TP2 F9)
|
||||||
const ids = (prefix, count) => Array.from({ length: count }, (_, i) => `${prefix}${String(i + 1).padStart(2, '0')}`)
|
const ids = (prefix, count) => Array.from({ length: count }, (_, i) => `${prefix}${String(i + 1).padStart(2, '0')}`)
|
||||||
const EXPECTED = [...ids('G', 15), ...ids('K', 30), ...ids('T', 21)]
|
const EXPECTED = [...ids('G', 22), ...ids('K', 34), ...ids('T', 22)]
|
||||||
// GUEST = Gästemails des Terminplaners (gemeinsamer Fuß, Anrede-Blöcke); SHOP_GUEST = Käufermails des Webshops (G09–G14, siehe unten)
|
// GUEST = Gästemails des Terminplaners (gemeinsamer Fuß, Anrede-Blöcke); SHOP_GUEST = Käufermails des Webshops (G09–G14, siehe unten)
|
||||||
const GUEST = ids('G', 8)
|
const GUEST = ids('G', 8)
|
||||||
const SHOP_GUEST = ['G09', 'G10', 'G11', 'G12', 'G13', 'G14', 'G15']
|
const SHOP_GUEST = ['G09', 'G10', 'G11', 'G12', 'G13', 'G14', 'G15']
|
||||||
@@ -27,7 +27,7 @@ for (const id of EXPECTED) {
|
|||||||
const title = source.match(/<title>([\s\S]*?)<\/title>/)[1]
|
const title = source.match(/<title>([\s\S]*?)<\/title>/)[1]
|
||||||
assert.equal(title, tpl.subject, 'Betreff im Kopf und <title> sind gleich')
|
assert.equal(title, tpl.subject, 'Betreff im Kopf und <title> sind gleich')
|
||||||
// F34: Team-Hinweise tragen [Abo], [Speicher] (Zusatzspeicher-Aufgaben), [Datenschutz] (Loeschfristen), [Zeit] (Push-Zeitmail), [Finanzen] (Bankzugang), [Terminplaner] (Betriebsmeldung) oder [Webshop] (neue Anfrage)
|
// F34: Team-Hinweise tragen [Abo], [Speicher] (Zusatzspeicher-Aufgaben), [Datenschutz] (Loeschfristen), [Zeit] (Push-Zeitmail), [Finanzen] (Bankzugang), [Terminplaner] (Betriebsmeldung) oder [Webshop] (neue Anfrage)
|
||||||
assert.equal(id.startsWith('T'), /^\[(Abo|Speicher|Datenschutz|Zeit|Finanzen|Terminplaner|Webshop)\]/.test(tpl.subject) || id === 'T01' || id === 'T16' || id === 'T17', 'Team-Hinweise ab T02 mit [Abo], [Speicher], [Datenschutz], [Zeit], [Finanzen], [Terminplaner] oder [Webshop] (T16 Funktion bestellt und T17 Repo zuordnen ohne Kennung)')
|
assert.equal(id.startsWith('T'), /^\[(Abo|Speicher|Datenschutz|Zeit|Finanzen|Terminplaner|Webshop|Buchungskalender)\]/.test(tpl.subject) || id === 'T01' || id === 'T16' || id === 'T17', 'Team-Hinweise ab T02 mit [Abo], [Speicher], [Datenschutz], [Zeit], [Finanzen], [Terminplaner], [Webshop] oder [Buchungskalender] (T16 Funktion bestellt und T17 Repo zuordnen ohne Kennung)')
|
||||||
|
|
||||||
const used = new Set([...tpl.body.matchAll(/\{\{(\w+)\}\}/g), ...tpl.subject.matchAll(/\{\{(\w+)\}\}/g)].map((m) => m[1]))
|
const used = new Set([...tpl.body.matchAll(/\{\{(\w+)\}\}/g), ...tpl.subject.matchAll(/\{\{(\w+)\}\}/g)].map((m) => m[1]))
|
||||||
assert.deepEqual([...used].sort(), [...tpl.placeholders].sort(), 'deklarierte = verwendete Platzhalter')
|
assert.deepEqual([...used].sort(), [...tpl.placeholders].sort(), 'deklarierte = verwendete Platzhalter')
|
||||||
@@ -1280,3 +1280,75 @@ test('K26: Betreff, Platzhalter, Erklärung nur im Block, Hinweis auf die 14 Tag
|
|||||||
assert.match(mit.text, /innerhalb von 14 Tagen/)
|
assert.match(mit.text, /innerhalb von 14 Tagen/)
|
||||||
assert.doesNotMatch(renderMail('K26', K26_WERTE, {}).text, /Ich widerrufe|Erklärung:/)
|
assert.doesNotMatch(renderMail('K26', K26_WERTE, {}).text, /Ich widerrufe|Erklärung:/)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Buchungskalender (Task 20): G16–G22 an Gäste (Sie-Form, neutral), K31–K34 und T22 (Du-Form, WEBklar)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const UK_GAST = ['G16', 'G17', 'G18', 'G19', 'G20', 'G21', 'G22']
|
||||||
|
const UK_BASIS = ['gast_name', 'unterkunft', 'zeitraum', 'naechte', 'personen', 'nummer', 'firma', 'farbe', 'anbieter_kontakt']
|
||||||
|
const UK_ERWARTET = {
|
||||||
|
G16: { p: [], b: [] },
|
||||||
|
G17: { p: ['posten_text', 'gesamt', 'frist', 'buchungs_link', 'stornobedingungen', 'nachricht_vermieter'], b: ['nachricht'] },
|
||||||
|
G18: { p: ['nachricht_vermieter'], b: ['nachricht'] },
|
||||||
|
G19: { p: ['posten_text', 'gesamt', 'zahlart_text', 'rechnung_nummer', 'stornobedingungen'], b: ['online_unbezahlt'] },
|
||||||
|
G20: { p: ['gesamt', 'rechnung_nummer'], b: [] },
|
||||||
|
G21: { p: ['frist'], b: [] },
|
||||||
|
G22: { p: ['rechnung_nummer', 'erstattung', 'nachricht_vermieter'], b: ['erstattung', 'nachricht'] },
|
||||||
|
}
|
||||||
|
|
||||||
|
test('G16–G22: Platzhalter und Blöcke, Sie-Form, kein WEBklar-Logo, keine festen Adressen', () => {
|
||||||
|
for (const id of UK_GAST) {
|
||||||
|
const tpl = loadTemplate(id)
|
||||||
|
assert.deepEqual([...tpl.placeholders].sort(), [...UK_BASIS, ...UK_ERWARTET[id].p].sort(), `${id}: Platzhalter`)
|
||||||
|
assert.deepEqual([...tpl.blocks].sort(), [...UK_ERWARTET[id].b].sort(), `${id}: Blöcke`)
|
||||||
|
assert.doesNotMatch(sourceOf(id), /<img|webklar-logo|project\.webklar\.com/i, `${id}: neutral`)
|
||||||
|
for (const on of [true, false]) {
|
||||||
|
const mail = renderMail(id, sampleValues(tpl), allBlocks(tpl, on))
|
||||||
|
assert.deepEqual(mail.problems, [], id)
|
||||||
|
assert.doesNotMatch(mail.text, DU_FORM, `${id}: Sie-Form\n${mail.text}`)
|
||||||
|
assert.match(mail.html, /background:Wert-farbe/, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('G17: Preis, Frist, Stornobedingungen, Knopf und der Satz zur verbindlichen Buchung', () => {
|
||||||
|
const mail = renderMail('G17', sampleValues(loadTemplate('G17')), { nachricht: true })
|
||||||
|
for (const w of ['Wert-posten_text', 'Wert-gesamt', 'Wert-frist', 'Wert-stornobedingungen']) assert.ok(mail.text.includes(w), w)
|
||||||
|
assert.match(mail.html, /href="Wert-buchungs_link"[^>]*>Jetzt verbindlich buchen/)
|
||||||
|
assert.ok(mail.text.includes('Erst mit dem Klick auf „Zahlungspflichtig buchen“ auf der Buchungsseite ist Ihre Buchung verbindlich.'))
|
||||||
|
})
|
||||||
|
|
||||||
|
test('G19: dauerhafter Datenträger mit allen Angaben, Hinweis kein Widerrufsrecht, Bezahl-Hinweis nur im Block', () => {
|
||||||
|
const tpl = loadTemplate('G19')
|
||||||
|
const aus = renderMail('G19', sampleValues(tpl), { online_unbezahlt: false })
|
||||||
|
const an = renderMail('G19', sampleValues(tpl), { online_unbezahlt: true })
|
||||||
|
for (const w of ['Wert-posten_text', 'Wert-gesamt', 'Wert-zahlart_text', 'Wert-stornobedingungen', 'Wert-anbieter_kontakt', 'Wert-rechnung_nummer']) assert.ok(aus.text.includes(w), w)
|
||||||
|
assert.match(aus.text, /kein gesetzliches Widerrufsrecht/)
|
||||||
|
assert.ok(!aus.text.includes('Bezahl-Link'))
|
||||||
|
assert.ok(an.text.includes('Den Bezahl-Link finden Sie auf der Buchungsseite aus unserer vorigen E-Mail.'))
|
||||||
|
})
|
||||||
|
|
||||||
|
test('G22: Erstattung „wird veranlasst“ nur im Block', () => {
|
||||||
|
const tpl = loadTemplate('G22')
|
||||||
|
assert.ok(renderMail('G22', { ...sampleValues(tpl), erstattung: '50,00 €' }, { erstattung: true }).text.includes('Eine Erstattung von 50,00 € wird veranlasst.'))
|
||||||
|
assert.ok(!renderMail('G22', sampleValues(tpl), { erstattung: false }).text.includes('Erstattung von'))
|
||||||
|
})
|
||||||
|
|
||||||
|
test('K31–K34, T22: Dateien, Platzhalter, Du-Form', () => {
|
||||||
|
const erwartet = {
|
||||||
|
K31: ['kunde_name', 'gast_name', 'gast_email', 'unterkunft', 'zeitraum', 'naechte', 'personen', 'nummer', 'nachricht_gast', 'portal_url'],
|
||||||
|
K32: ['kunde_name', 'gast_name', 'unterkunft', 'zeitraum', 'naechte', 'personen', 'nummer', 'gesamt', 'zahlart_text', 'portal_url'],
|
||||||
|
K33: ['kunde_name', 'unterkunft', 'portal_url'],
|
||||||
|
K34: ['kunde_name', 'projekt', 'preis', 'ust_hinweis', 'begruendung', 'portal_url'],
|
||||||
|
T22: ['kunde_name', 'email', 'projekt', 'nachricht', 'woms_url'],
|
||||||
|
}
|
||||||
|
for (const [id, p] of Object.entries(erwartet)) {
|
||||||
|
const tpl = loadTemplate(id)
|
||||||
|
assert.deepEqual([...tpl.placeholders].sort(), [...p].sort(), id)
|
||||||
|
const mail = renderMail(id, sampleValues(tpl), allBlocks(tpl, true))
|
||||||
|
assert.deepEqual(mail.problems, [], id)
|
||||||
|
}
|
||||||
|
assert.equal(loadTemplate('K32').file, 'K32 Neue Buchung.html')
|
||||||
|
assert.equal(loadTemplate('T22').subject, '[Buchungskalender] Neue Anfrage: {{kunde_name}}')
|
||||||
|
})
|
||||||
@@ -10,7 +10,7 @@ import { TERMINE_DATETIMES } from '../server/services/termine/schema.js'
|
|||||||
// Produktion. Der Mailtransport ist ein Fake, der Sendeversuche zaehlt und gezielt Fehler wirft (nodemailer-Fehler
|
// Produktion. Der Mailtransport ist ein Fake, der Sendeversuche zaehlt und gezielt Fehler wirft (nodemailer-Fehler
|
||||||
// tragen `code` und SMTP-`responseCode`). Alle Zeiten sind feste Parameter.
|
// tragen `code` und SMTP-`responseCode`). Alle Zeiten sind feste Parameter.
|
||||||
const aw = await startAppwriteMock({
|
const aw = await startAppwriteMock({
|
||||||
schema: { termineOutbox: schemaKeys('termineOutbox') },
|
schema: { termineOutbox: schemaKeys('termineOutbox'), ukRechnungen: schemaKeys('ukRechnungen') },
|
||||||
datetimes: TERMINE_DATETIMES,
|
datetimes: TERMINE_DATETIMES,
|
||||||
defaultLimit: 25,
|
defaultLimit: 25,
|
||||||
})
|
})
|
||||||
@@ -1290,3 +1290,39 @@ test('einreihen: optionaler Anhang-Verweis wird gespeichert (Senden unverändert
|
|||||||
const attribut = (await import('../server/services/termine/schema.js')).TERMINE_COLLECTIONS.termineOutbox.attributes.find((a) => a.key === 'anhang')
|
const attribut = (await import('../server/services/termine/schema.js')).TERMINE_COLLECTIONS.termineOutbox.attributes.find((a) => a.key === 'anhang')
|
||||||
assert.deepEqual(attribut, { key: 'anhang', type: 'string', size: 500, required: false })
|
assert.deepEqual(attribut, { key: 'anhang', type: 'string', size: 500, required: false })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Buchungskalender (Task 20): anhang {art:'ukRechnung', rechnungId} -> Rechnungs-PDF als Anhang
|
||||||
|
const RE_INHALT = { aussteller: { firma: 'Haus' }, empfaenger: { name: 'Eva' }, unterkunft: { name: 'Wohnung' }, von: '2026-11-12', bis: '2026-11-14', naechte: 2, personen: 2, posten: [{ text: 'Übernachtung', menge: 2, einzelCent: 10000, ustProzent: 0 }], summen: { bruttoCent: 20000, nettoCent: 20000, ust: [] }, steuerart: 'kleinunternehmer' }
|
||||||
|
const seedRechnung = (extra = {}) => aw.seed('ukRechnungen', { $id: 'r1', featureId: 'f_feature01', anfrageId: 'a1', art: 'rechnung', nummer: 'RE-2026-0001', datum: '2026-11-01', bruttoCent: 20000, inhalt: JSON.stringify(RE_INHALT), ...extra })
|
||||||
|
|
||||||
|
test('Anhang ukRechnung: gesendete Mail hat Rechnung-<nr>.pdf (beginnt mit %PDF); Stornorechnung heißt Stornorechnung-<nr>.pdf', async () => {
|
||||||
|
seedRechnung()
|
||||||
|
seedRechnung({ $id: 'r2', art: 'storno', nummer: 'RE-2026-0002' })
|
||||||
|
await einreihen(eintrag({ bookingId: 'b_re', anhang: JSON.stringify({ art: 'ukRechnung', rechnungId: 'r1' }) }), JETZT)
|
||||||
|
await einreihen(eintrag({ bookingId: 'b_st', anhang: JSON.stringify({ art: 'ukRechnung', rechnungId: 'r2' }) }), JETZT)
|
||||||
|
await einreihen(eintrag({ bookingId: 'b_ohne' }), JETZT)
|
||||||
|
await warteAufVersand()
|
||||||
|
const namen = tr.senke.sent.map((m) => (m.attachments ?? []).map((a) => a.filename))
|
||||||
|
assert.deepEqual(namen.map((n) => n.join()).sort(), ['', 'Rechnung-RE-2026-0001.pdf', 'Stornorechnung-RE-2026-0002.pdf'])
|
||||||
|
const mit = tr.senke.sent.find((m) => m.attachments?.length)
|
||||||
|
assert.equal(Buffer.from(mit.attachments[0].content, 'base64').subarray(0, 4).toString(), '%PDF')
|
||||||
|
assert.equal(mit.attachments[0].contentType, 'application/pdf')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Anhang nicht erzeugbar: Eintrag bleibt wartet (Wiederholung), nach 3 Fehlversuchen geht die Mail ohne Anhang raus', async () => {
|
||||||
|
const doc = await einreihen(eintrag({ bookingId: 'b_fehlt', art: 'uk-G19', anhang: JSON.stringify({ art: 'ukRechnung', rechnungId: 'gibtsnicht' }) }), JETZT)
|
||||||
|
await warteAufVersand()
|
||||||
|
let d = await lies(doc.$id)
|
||||||
|
assert.equal(d.status, 'wartet')
|
||||||
|
assert.equal(d.versuche, 1)
|
||||||
|
assert.equal(tr.aufrufe, 0)
|
||||||
|
let jetzt = JETZT
|
||||||
|
for (const warte of [2 * MIN, 6 * MIN, 31 * MIN]) {
|
||||||
|
jetzt = plus(warte, jetzt)
|
||||||
|
await sendeEintrag(doc.$id, jetzt)
|
||||||
|
}
|
||||||
|
d = await lies(doc.$id)
|
||||||
|
assert.equal(d.status, 'gesendet')
|
||||||
|
assert.equal(tr.senke.sent.length, 1)
|
||||||
|
assert.equal(tr.senke.sent[0].attachments?.length ?? 0, 0)
|
||||||
|
})
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import { test, after, beforeEach } from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { startAppwriteMock } from './support/appwriteMock.js'
|
||||||
|
import { mailSink } from './support/mailSink.js'
|
||||||
|
import { useTestEnv } from './support/testEnv.js'
|
||||||
|
import { schemaKeys } from './support/schema.js'
|
||||||
|
import { TERMINE_DATETIMES } from '../server/services/termine/schema.js'
|
||||||
|
|
||||||
|
// Buchungskalender Task 20: Mails an Gaeste (ueber die Warteschlange), Vermieter und Team
|
||||||
|
const NAMEN = ['ukUnterkuenfte', 'ukPreise', 'ukAnfragen', 'ukRechnungen', 'ukEinstellungen', 'termineOutbox']
|
||||||
|
const aw = await startAppwriteMock({
|
||||||
|
schema: Object.fromEntries(NAMEN.map((n) => [n, schemaKeys(n)])),
|
||||||
|
datetimes: { ...TERMINE_DATETIMES, ukAnfragen: ['fristAt', 'freigegebenAt', 'gebuchtAt', 'abgelehntAt', 'abgelaufenAt', 'storniertAt', 'bezahltAt'] },
|
||||||
|
defaultLimit: 25,
|
||||||
|
})
|
||||||
|
after(() => aw.close())
|
||||||
|
useTestEnv({ appwriteUrl: aw.url, stripeUrl: 'http://127.0.0.1:1/v1' })
|
||||||
|
const { config } = await import('../server/config.js')
|
||||||
|
const { deleteDocument } = await import('../server/services/appwriteAdmin.js')
|
||||||
|
const { setMailTransportForTests } = await import('../server/services/mailer.js')
|
||||||
|
const { warteAufVersand, resetOutboxForTests } = await import('../server/services/termine/outbox.js')
|
||||||
|
const mails = await import('../server/services/unterkunft/mails.js')
|
||||||
|
const sp = await import('../server/services/unterkunft/speicher.js')
|
||||||
|
after(() => setMailTransportForTests(null))
|
||||||
|
|
||||||
|
const EIN = { firma: 'Haus am See', strasse: 'Weg 1', plz: '10115', ort: 'Berlin', email: 'vermieter@example.com', telefon: '0123', akzentfarbe: '#336699' }
|
||||||
|
const ctx = { featureId: 'f1', customerId: 'c1', projectId: 'p1', siteKey: 'abcdefghijklmnop', besitzerEmail: 'besitzer@example.com', einstellungen: EIN }
|
||||||
|
const POSTEN = [{ text: 'Übernachtung, 2 Nächte', menge: 2, einzelCent: 10000, ustProzent: 7 }, { text: 'Endreinigung', menge: 1, einzelCent: 5000, ustProzent: 19 }]
|
||||||
|
const anfrage = (extra = {}) => ({
|
||||||
|
$id: 'a1', featureId: 'f1', unterkunftId: 'u1', nummer: 'A-K7QM', von: '2026-11-12', bis: '2026-11-14', personen: 2, status: 'freigegeben',
|
||||||
|
gast: { name: 'Eva Gast', email: 'eva@example.com', nachricht: 'Hallo' }, posten: POSTEN, summen: { bruttoCent: 25000 },
|
||||||
|
fristAt: '2026-11-05T16:00:00.000Z', stornobedingungenText: 'Kostenlos bis 7 Tage vorher.', nachrichtVermieter: '', ...extra,
|
||||||
|
})
|
||||||
|
let sink
|
||||||
|
beforeEach(async () => {
|
||||||
|
for (const c of ['termineOutbox', 'ukRechnungen', 'ukUnterkuenfte', 'ukEinstellungen']) for (const id of aw.docs(c).keys()) await deleteDocument(c, id)
|
||||||
|
config.smtp.from = 'noreply@webklar.com'
|
||||||
|
resetOutboxForTests()
|
||||||
|
mails._aufrufe.length = 0
|
||||||
|
sink = mailSink()
|
||||||
|
setMailTransportForTests(sink)
|
||||||
|
aw.seed('ukUnterkuenfte', { $id: 'u1', featureId: 'f1', name: 'Wohnung See', maxPersonen: 4, mindestNaechte: 1, aktiv: true })
|
||||||
|
})
|
||||||
|
const gesendet = async () => {
|
||||||
|
await warteAufVersand()
|
||||||
|
return sink.sent
|
||||||
|
}
|
||||||
|
|
||||||
|
test('G17: Absender = Firma, Antworten an den Vermieter, Buchungslink mit Token, Preis und Frist', async () => {
|
||||||
|
await mails.anGast('G17', ctx, anfrage(), { token: 'tok123' })
|
||||||
|
const [m] = await gesendet()
|
||||||
|
assert.equal(m.from.name, 'Haus am See')
|
||||||
|
assert.equal(m.replyTo[0].address, 'vermieter@example.com')
|
||||||
|
assert.equal(m.to[0].address, 'eva@example.com')
|
||||||
|
assert.ok(m.text.includes(`${config.publicBaseUrl}/unterkunft/abcdefghijklmnop/buchung/a1?t=tok123`))
|
||||||
|
assert.ok(m.text.includes('250,00 €'))
|
||||||
|
assert.ok(m.text.includes('12.11.2026 – 14.11.2026'))
|
||||||
|
assert.ok(m.text.includes('Kostenlos bis 7 Tage vorher.'))
|
||||||
|
assert.ok(m.text.includes('05.11.2026, 17:00 Uhr'))
|
||||||
|
assert.ok(m.text.includes('Erst mit dem Klick auf „Zahlungspflichtig buchen“'))
|
||||||
|
assert.equal(mails._aufrufe[0].art, 'G17')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Minimaler ctx: Einstellungen und Unterkunft werden selbst geladen', async () => {
|
||||||
|
aw.seed('ukEinstellungen', { $id: 'f1', featureId: 'f1', ...EIN, firma: 'Geladene Firma' })
|
||||||
|
await mails.anGast('G21', { featureId: 'f1' }, anfrage())
|
||||||
|
const [m] = await gesendet()
|
||||||
|
assert.equal(m.from.name, 'Geladene Firma')
|
||||||
|
assert.ok(m.text.includes('Wohnung See'))
|
||||||
|
assert.doesNotMatch(m.text, /buchung\/a1/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('G17 ohne Token wirft, G16 hat keinen Link', async () => {
|
||||||
|
await assert.rejects(() => mails.anGast('G17', ctx, anfrage(), {}), TypeError)
|
||||||
|
await mails.anGast('G16', ctx, anfrage({ status: 'neu', posten: null, summen: null }))
|
||||||
|
const [m] = await gesendet()
|
||||||
|
assert.doesNotMatch(m.text, /\?t=/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('G19 hängt die Rechnung als PDF an (online, unbezahlt: Bezahl-Hinweis), G22 die Stornorechnung mit Erstattung', async () => {
|
||||||
|
const inhalt = { aussteller: { firma: 'Haus am See' }, empfaenger: { name: 'Eva Gast' }, unterkunft: { name: 'Wohnung See' }, von: '2026-11-12', bis: '2026-11-14', naechte: 2, personen: 2, posten: POSTEN, summen: { bruttoCent: 25000, nettoCent: 23000, ust: [] }, steuerart: 'kleinunternehmer', zahlart: 'online' }
|
||||||
|
const re = await sp.legeRechnungAn({ featureId: 'f1', anfrageId: 'a1', art: 'rechnung', nummer: 'RE-2026-0001', datum: '2026-11-01', bruttoCent: 25000, inhalt })
|
||||||
|
const st = await sp.legeRechnungAn({ featureId: 'f1', anfrageId: 'a1', art: 'storno', nummer: 'RE-2026-0002', datum: '2026-11-02', bruttoCent: -25000, inhalt: { ...inhalt, bezugNummer: 'RE-2026-0001' } })
|
||||||
|
await mails.anGast('G19', ctx, anfrage({ status: 'gebucht', zahlart: 'online' }), { rechnung: re })
|
||||||
|
await mails.anGast('G22', ctx, anfrage({ status: 'storniert' }), { stornoRechnung: st, erstattetCent: 25000, grund: 'Wasserschaden' })
|
||||||
|
const sent = await gesendet()
|
||||||
|
const g19 = sent.find((m) => m.subject.includes('bestätigt'))
|
||||||
|
const g22 = sent.find((m) => m.subject.includes('storniert'))
|
||||||
|
assert.equal(g19.attachments.length, 1)
|
||||||
|
assert.equal(g19.attachments[0].filename, 'Rechnung-RE-2026-0001.pdf')
|
||||||
|
assert.ok(Buffer.from(g19.attachments[0].content, 'base64').subarray(0, 4).toString() === '%PDF')
|
||||||
|
assert.ok(g19.text.includes('Den Bezahl-Link finden Sie auf der Buchungsseite'))
|
||||||
|
assert.equal(g22.attachments[0].filename, 'Stornorechnung-RE-2026-0002.pdf')
|
||||||
|
assert.ok(g22.text.includes('Eine Erstattung von 250,00 € wird veranlasst.'))
|
||||||
|
assert.ok(g22.text.includes('Wasserschaden'))
|
||||||
|
})
|
||||||
|
|
||||||
|
test('G22 ohne Erstattung: kein Erstattungssatz; G19 vor Ort bezahlt: kein Bezahl-Hinweis', async () => {
|
||||||
|
await mails.anGast('G22', ctx, anfrage({ status: 'storniert' }), { erstattetCent: 0 })
|
||||||
|
await mails.anGast('G19', ctx, anfrage({ status: 'gebucht', zahlart: 'vorort' }), {})
|
||||||
|
const sent = await gesendet()
|
||||||
|
assert.ok(sent.every((m) => !m.text.includes('Erstattung von') && !m.text.includes('Bezahl-Link')))
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Vermieter: K31 geht an den Besitzer ohne BCC, mit Portal-Link; T22 ans Team; unbekannte Gastart wirft', async () => {
|
||||||
|
await mails.anVermieter('K31', ctx, { anfrage: anfrage({ status: 'neu' }) })
|
||||||
|
const [k] = await gesendet()
|
||||||
|
assert.equal(k.to[0].address, 'besitzer@example.com')
|
||||||
|
assert.ok(!k.bcc || k.bcc.length === 0)
|
||||||
|
assert.ok(k.text.includes(`${config.publicBaseUrl}/dashboard.html#buchungen?projekt=p1`))
|
||||||
|
assert.ok(k.text.includes('Eva Gast'))
|
||||||
|
await assert.rejects(() => mails.anGast('K31', ctx, anfrage()), TypeError)
|
||||||
|
})
|
||||||
Reference in new issue
Block a user