Buchungskalender: Strang mail zusammenführen

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5.5 committed 2026-10-07 14:49:33 +00:00
commit 97dd84fbb8
45 files changed
+3007 -21

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 &rarr;</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 &rarr;</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 &rarr;</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 &rarr;</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 &rarr;</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 &rarr;</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>
+3 -2
View File
@@ -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.
* 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,
* 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()
if (!mailer) throw new Error('SMTP nicht konfiguriert')
checkMailbox(to, 'to')
@@ -211,6 +211,7 @@ export async function sendGuestMail({ to, fromName, replyTo = null, subject, htm
text,
html,
icalEvent: ics ? { method: icsMethod, filename: 'termin.ics', content: ics } : undefined,
attachments: Array.isArray(attachments) && attachments.length ? attachments : undefined,
})
}
+37
View File
@@ -3,6 +3,8 @@ import { Query, countDocuments, createDocument, deleteDocument, getDocument, kei
import { withLocks } from '../keyedLock.js'
import { sendGuestMail } from '../mailer.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'
/**
@@ -286,6 +288,7 @@ async function sende(id, now) {
if (!platz) return 'wartet'
try {
const attachments = await baueAnhaenge(doc)
await sendGuestMail({
to: doc.an,
fromName: doc.fromName,
@@ -295,6 +298,7 @@ async function sende(id, now) {
text: doc.text,
ics: doc.ics || null,
icsMethod: doc.icsMethod || 'REQUEST',
...(attachments.length ? { attachments } : {}),
})
} catch (err) {
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
* gelesen) plus eine Minute Toleranz ab Einreihen; genau so alt ist noch nicht veraltet. G01 ohne Buchung (zu_viele) hat
+184 -3
View File
@@ -1,10 +1,191 @@
// 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 { customerOf } from '../siteFeatures.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 = []
/** Protokoll nur im Test (UK_TESTPROTOKOLL, setzt useTestEnv); nie Token, Werte oder Gastdaten. */
const testProtokoll = () => process.env.NODE_ENV === 'test' || process.env.UK_TESTPROTOKOLL === '1'
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 = {}) {
_aufrufe.push({ an: 'gast', art, anfrageId: anfrage?.$id, extra })
if (testProtokoll()) _aufrufe.push({ an: 'gast', art, anfrageId: anfrage?.$id, hatToken: Boolean(extra?.token), grund: extra?.grund })
if (!GAST_ARTEN.includes(art)) throw new TypeError(`${String(art).slice(0, 20)} ist keine Gastmail`)
extra = extra ?? {}
const gast = anfrage?.gast ?? {}
if (art === 'G17' && (!text(extra.token) || !text(ctx?.siteKey))) throw new TypeError('Token oder siteKey für den Buchungslink fehlt')
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 (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 = {}) {
_aufrufe.push({ an: 'vermieter', template, values })
if (testProtokoll()) _aufrufe.push({ an: 'vermieter', template, anfrageId: values?.anfrage?.$id })
const { anfrage, an, ...eigene } = values ?? {}
const vorgabe = {}
if (anfrage) {
const { werte } = await werteZuAnfrage(ctx, anfrage)
Object.assign(vorgabe, werte, {
kunde_name: text(ctx?.kundeName) || (await kundenName(ctx)) || 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'))
}
/** Name des Kunden (Vermieters) aus dem Kundendokument; Fehler beim Laden machen die Mail nicht kaputt. */
async function kundenName(ctx) {
try {
return text((await customerOf(ctx?.customerId))?.name)
} catch {
return ''
}
}
+169
View File
@@ -0,0 +1,169 @@
// Minimaler PDF-Writer (Buchungskalender N2): Helvetica und Helvetica-Bold, WinAnsiEncoding, keine Abhaengigkeit.
// Koordinaten: Punkt, Ursprung oben links (y waechst nach unten). Ausgabe ist deterministisch.
// Helvetica-AFM-Breiten (1/1000 em) fuer die WinAnsi-Codes 32..255 (0 = nicht definiert, wird als '?' geschrieben).
const W_NORMAL = [
278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278,
556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556,
1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778,
667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556,
333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556,
556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 0,
556, 0, 222, 556, 333, 1000, 556, 556, 333, 1000, 667, 333, 1000, 0, 611, 0,
0, 222, 222, 333, 333, 350, 556, 1000, 333, 1000, 500, 333, 944, 0, 500, 667,
278, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 333, 737, 333,
400, 584, 333, 333, 333, 556, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611,
667, 667, 667, 667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278,
722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611,
556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278,
556, 556, 556, 556, 556, 556, 556, 584, 611, 556, 556, 556, 556, 500, 556, 500,
]
const W_FETT = [
278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278,
556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611,
975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778,
667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556,
333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611,
611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 0,
556, 0, 278, 556, 500, 1000, 556, 556, 333, 1000, 667, 333, 1000, 0, 611, 0,
0, 278, 278, 500, 500, 350, 556, 1000, 333, 1000, 556, 333, 944, 0, 500, 667,
278, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 333, 737, 333,
400, 584, 333, 333, 333, 611, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611,
722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278,
722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611,
556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278,
611, 611, 611, 611, 611, 611, 611, 584, 611, 611, 611, 611, 611, 556, 611, 556,
]
// CP1252-Zeichen oberhalb von U+007F im Bereich 0x80..0x9F.
const CP1252 = new Map(Object.entries({
'€': 0x80, '‚': 0x82, 'ƒ': 0x83, '„': 0x84, '…': 0x85, '†': 0x86, '‡': 0x87, 'ˆ': 0x88, '‰': 0x89, 'Š': 0x8a,
'‹': 0x8b, 'Œ': 0x8c, 'Ž': 0x8e, '‘': 0x91, '’': 0x92, '“': 0x93, '”': 0x94, '•': 0x95, '–': 0x96, '—': 0x97,
'˜': 0x98, '™': 0x99, 'š': 0x9a, '›': 0x9b, 'œ': 0x9c, 'ž': 0x9e, 'Ÿ': 0x9f,
}))
/** Ein Zeichen als WinAnsi-Code; Unbekanntes wird '?'. */
function code(ch) {
if (ch === '−') return 0x2d
const c = ch.codePointAt(0)
if (c === 0x0a || c === 0x0d || c === 0x09) return 0x20
if (c >= 32 && c < 127) return c
if (c >= 160 && c <= 255) return c
return CP1252.get(ch) ?? 0x3f
}
const bytes = (str) => Array.from(String(str ?? ''), code)
const zahl = (n) => String(Math.round(n * 100) / 100)
export class PdfDoc {
constructor({ breite = 595.28, hoehe = 841.89 } = {}) {
this.b = breite
this.h = hoehe
this.seiten = []
this.aktuell = null
this.seite()
}
/** Neue Seite beginnen (und zur aktuellen machen). */
seite() {
this.seiten.push([])
this.aktuell = this.seiten.length - 1
return this.aktuell
}
/** Auf eine bestehende Seite zurueckschalten (z. B. fuer Fusszeilen). */
aufSeite(n) {
if (!(n >= 0 && n < this.seiten.length)) throw new RangeError('Seite existiert nicht')
this.aktuell = n
}
get seitenzahl() {
return this.seiten.length
}
breite(str, groesse = 10, fett = false) {
const w = fett ? W_FETT : W_NORMAL
let sum = 0
for (const c of bytes(str)) sum += w[c - 32] || w[0x3f - 32]
return (sum * groesse) / 1000
}
/** Text; y = Grundlinie von oben, x = linker Rand (bei rechts: rechter Rand). */
text(x, y, str, { groesse = 10, fett = false, rechts = false } = {}) {
const bs = bytes(str)
if (bs.length === 0) return
const px = rechts ? x - this.breite(str, groesse, fett) : x
let s = ''
for (const c of bs) s += c === 0x5c || c === 0x28 || c === 0x29 ? `\\${String.fromCharCode(c)}` : String.fromCharCode(c)
this.seiten[this.aktuell].push(`BT /${fett ? 'F2' : 'F1'} ${zahl(groesse)} Tf ${zahl(px)} ${zahl(this.h - y)} Td (${s}) Tj ET`)
}
linie(x1, y1, x2, y2, { dicke = 0.5 } = {}) {
this.seiten[this.aktuell].push(`${zahl(dicke)} w ${zahl(x1)} ${zahl(this.h - y1)} m ${zahl(x2)} ${zahl(this.h - y2)} l S`)
}
/** Bricht an Leerzeichen um; ueberlange Woerter werden hart getrennt. */
umbrechen(str, maxBreite, groesse = 10, fett = false) {
const zeilen = []
for (const absatz of String(str ?? '').split(/\r?\n/)) {
let zeile = ''
for (let wort of absatz.split(/ +/).filter(Boolean)) {
const probe = zeile ? `${zeile} ${wort}` : wort
if (this.breite(probe, groesse, fett) <= maxBreite) {
zeile = probe
continue
}
if (zeile) zeilen.push(zeile)
zeile = ''
while (this.breite(wort, groesse, fett) > maxBreite && wort.length > 1) {
let n = wort.length - 1
while (n > 1 && this.breite(wort.slice(0, n), groesse, fett) > maxBreite) n--
zeilen.push(wort.slice(0, n))
wort = wort.slice(n)
}
zeile = wort
}
zeilen.push(zeile)
}
return zeilen
}
toBuffer() {
const teile = []
const offs = []
let len = 0
const add = (buf) => {
const b = Buffer.isBuffer(buf) ? buf : Buffer.from(buf, 'latin1')
teile.push(b)
len += b.length
}
const obj = (nr, inhalt) => {
offs[nr] = len
add(`${nr} 0 obj\n`)
add(inhalt)
add('\nendobj\n')
}
const n = this.seiten.length
add('%PDF-1.4\n%\xe2\xe3\xcf\xd3\n')
obj(1, '<< /Type /Catalog /Pages 2 0 R >>')
const kids = this.seiten.map((_, i) => `${5 + i * 2} 0 R`).join(' ')
obj(2, `<< /Type /Pages /Kids [${kids}] /Count ${n} >>`)
obj(3, '<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>')
obj(4, '<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>')
this.seiten.forEach((ops, i) => {
const pn = 5 + i * 2
obj(pn, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${zahl(this.b)} ${zahl(this.h)}] /Resources << /Font << /F1 3 0 R /F2 4 0 R >> >> /Contents ${pn + 1} 0 R >>`)
const strom = Buffer.from(ops.join('\n') + '\n', 'latin1')
offs[pn + 1] = len
add(`${pn + 1} 0 obj\n<< /Length ${strom.length} >>\nstream\n`)
add(strom)
add('endstream\nendobj\n')
})
const total = 5 + n * 2
const xref = len
add(`xref\n0 ${total}\n0000000000 65535 f \n`)
for (let i = 1; i < total; i++) add(`${String(offs[i]).padStart(10, '0')} 00000 n \n`)
add(`trailer\n<< /Size ${total} /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`)
return Buffer.concat(teile)
}
}
+134
View File
@@ -0,0 +1,134 @@
// Rechnungs-PDF aus dem unveraenderlichen Schnappschuss ukRechnungen.inhalt (N2). Deterministisch, A4.
import { PdfDoc } from './pdf.js'
import { formatEuro } from './preis.js'
import { formatTag } from './datum.js'
const L = 56
const R = 595.28 - 56
const UNTEN = 745 // Inhalt endet hier, darunter Fusszeile
const tag = (t) => (/^\d{4}-\d{2}-\d{2}$/.test(String(t ?? '')) ? formatTag(t) : String(t ?? ''))
const nz = (s) => String(s ?? '').trim()
const ortZeile = (o) => [nz(o?.plz), nz(o?.ort)].filter(Boolean).join(' ')
export function rechnungPdf(inhalt, { art = 'rechnung', nummer = '', datum = '' } = {}) {
const i = inhalt || {}
const a = i.aussteller || {}
const e = i.empfaenger || {}
const storno = art === 'storno'
const klein = (i.steuerart || i.summen?.steuerart) === 'kleinunternehmer'
const doc = new PdfDoc()
let y
// Absenderzeile (klein)
const absender = [nz(a.firma), nz(a.strasse), ortZeile(a)].filter(Boolean).join(' · ')
doc.text(L, 60, absender, { groesse: 7.5 })
doc.linie(L, 64, L + 250, 64, { dicke: 0.3 })
// Empfaenger
const adr = [nz(e.firma), nz(e.name), nz(e.strasse), ortZeile(e), nz(e.land)].filter(Boolean)
adr.forEach((z, k) => doc.text(L, 82 + k * 13, z, { groesse: 10 }))
// Rechts: Nummer, Datum, Leistungszeitraum
const kopf = [
[storno ? 'Stornonummer' : 'Rechnungsnummer', nz(nummer)],
['Datum', tag(datum)],
['Leistungszeitraum', `${tag(i.von)} – ${tag(i.bis)}`],
]
if (storno && i.bezugNummer) kopf.splice(1, 0, ['Zu Rechnung', nz(i.bezugNummer)])
kopf.forEach(([k, v], n) => {
doc.text(350, 82 + n * 13, k, { groesse: 9 })
doc.text(R, 82 + n * 13, v, { groesse: 9, fett: true, rechts: true })
})
// Titel
y = 190
doc.text(L, y, storno ? `Stornorechnung zu ${nz(i.bezugNummer)}` : 'Rechnung', { groesse: 16, fett: true })
y += 22
const leistung = `Beherbergung ${nz(i.unterkunft?.name)}, ${tag(i.von)}–${tag(i.bis)} (${i.naechte} ${i.naechte === 1 ? 'Nacht' : 'Nächte'}, ${i.personen} ${i.personen === 1 ? 'Person' : 'Personen'})`
for (const z of doc.umbrechen(leistung, R - L, 10)) {
doc.text(L, y, z, { groesse: 10 })
y += 13
}
y += 10
// Posten-Tabelle
const cMenge = 318
const cUst = 362
const cEinzel = 450
const cBetrag = R
const textBreite = (klein ? cMenge - 30 : cUst - 40) - L - 20
const kopfzeile = () => {
doc.text(L, y, 'Leistung', { groesse: 9, fett: true })
doc.text(cMenge, y, 'Menge', { groesse: 9, fett: true, rechts: true })
if (!klein) doc.text(cUst, y, 'USt', { groesse: 9, fett: true, rechts: true })
doc.text(cEinzel, y, 'Einzelpreis', { groesse: 9, fett: true, rechts: true })
doc.text(cBetrag, y, 'Betrag', { groesse: 9, fett: true, rechts: true })
doc.linie(L, y + 4, R, y + 4)
y += 18
}
kopfzeile()
for (const p of i.posten || []) {
const zeilen = doc.umbrechen(nz(p.text), textBreite, 10)
if (y + zeilen.length * 12 > UNTEN) {
doc.seite()
y = 70
kopfzeile()
}
const betrag = Math.round(p.menge * p.einzelCent)
zeilen.forEach((z, k) => doc.text(L, y + k * 12, z, { groesse: 10 }))
doc.text(cMenge, y, String(p.menge), { groesse: 10, rechts: true })
if (!klein) doc.text(cUst, y, `${p.ustProzent} %`, { groesse: 10, rechts: true })
doc.text(cEinzel, y, formatEuro(p.einzelCent), { groesse: 10, rechts: true })
doc.text(cBetrag, y, formatEuro(betrag), { groesse: 10, rechts: true })
y += zeilen.length * 12 + 4
}
doc.linie(L, y - 2, R, y - 2)
y += 12
// Summen (zusammenhaengend auf einer Seite)
const s = i.summen || {}
const zeilenSumme = []
if (klein) {
zeilenSumme.push(['Gesamtbetrag', formatEuro(s.bruttoCent ?? 0), true])
} else {
for (const u of s.ust || []) {
zeilenSumme.push([`Nettobetrag ${u.satz} %`, formatEuro(u.nettoCent), false])
zeilenSumme.push([`Enthaltene USt ${u.satz} %`, formatEuro(u.steuerCent), false])
}
zeilenSumme.push(['Gesamtbetrag (brutto)', formatEuro(s.bruttoCent ?? 0), true])
}
const hinweis = klein ? doc.umbrechen('Gemäß § 19 UStG wird keine Umsatzsteuer berechnet.', R - L, 9) : []
const zahlText = i.zahlart === 'online' ? 'Zahlart: Online-Zahlung' : i.zahlart === 'vorort' ? 'Zahlart: Zahlung vor Ort' : ''
const noetig = zeilenSumme.length * 14 + hinweis.length * 12 + 40
if (y + noetig > UNTEN) {
doc.seite()
y = 70
}
for (const [k, v, fett] of zeilenSumme) {
doc.text(cEinzel, y, k, { groesse: 10, fett, rechts: true })
doc.text(cBetrag, y, v, { groesse: 10, fett, rechts: true })
y += 14
}
y += 8
for (const z of hinweis) {
doc.text(L, y, z, { groesse: 9 })
y += 12
}
if (zahlText) {
y += 4
doc.text(L, y, zahlText, { groesse: 9 })
}
// Fusszeile auf jeder Seite
const fuss1 = [nz(a.firma), nz(a.inhaber) && `Inh. ${nz(a.inhaber)}`, nz(a.strasse), ortZeile(a)].filter(Boolean).join(' · ')
const fuss2 = [nz(a.steuernummer) && `Steuernummer ${nz(a.steuernummer)}`, nz(a.ustId) && `USt-IdNr. ${nz(a.ustId)}`, nz(a.email), nz(a.telefon)].filter(Boolean).join(' · ')
const fuss3 = [nz(a.bank), nz(a.iban) && `IBAN ${nz(a.iban)}`, nz(a.bic) && `BIC ${nz(a.bic)}`].filter(Boolean).join(' · ')
const n = doc.seitenzahl
for (let p = 0; p < n; p++) {
doc.aufSeite(p)
doc.linie(L, 790, R, 790, { dicke: 0.3 })
;[fuss1, fuss2, fuss3].filter(Boolean).forEach((z, k) => doc.text(L, 802 + k * 10, z, { groesse: 7.5 }))
if (n > 1) doc.text(R, 826, `Seite ${p + 1} von ${n}`, { groesse: 7.5, rechts: true })
}
return doc.toBuffer()
}
+3 -1
View File
@@ -13,6 +13,8 @@ import { markiereBezahlt } from './ablauf.js'
*/
export const _aufrufe = []
/** Aufrufprotokoll nur im Test (UK_TESTPROTOKOLL, setzt useTestEnv); in Produktion waechst nichts. */
const testProtokoll = () => process.env.NODE_ENV === 'test' || process.env.UK_TESTPROTOKOLL === '1'
const SITZUNG_MINUTEN = 31 // Stripe verlangt mindestens 30 Minuten Restlaufzeit beim Anlegen
const KOERPER_MAX = 250
@@ -176,6 +178,6 @@ export function setzeErstattungFuerTests(fn) {
}
export async function erstatte(ctx, anfrage, betragCent, { now, idempotencyKey } = {}) {
_aufrufe.push({ anfrageId: anfrage?.$id, betragCent, idempotencyKey })
if (testProtokoll()) _aufrufe.push({ anfrageId: anfrage?.$id, betragCent, idempotencyKey })
return erstatter(ctx, anfrage, betragCent, { now, idempotencyKey })
}