Spec: Buchungskalender für Unterkünfte (Funktion unterkunft)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5.5 committed 2026-10-07 13:22:02 +00:00
1 parent 8814c4aef1
commit 5a05bd27e9
1 file changed
+275
@@ -0,0 +1,275 @@
# Buchungskalender für Unterkünfte (Funktion `unterkunft`): Design
Stand 2026-10-07. Basis: Portal `origin/main` 8814c4a (TP2, Webshop, Öffnungszeiten im Pilot). Arbeitszweig
`feature/unterkunft`, Worktree `dev-unterkunft`. Pfade: **P** = Portal-Repo, **W** = WOMS-Repo, **D** = Website Donau Home
(`WEBklar/donau-home`, lokaler Klon `/root/reports/donau-home-website`).
Auftrag des Inhabers (2026-10-07): Auf der Donau-Home-Seite (MIMI FEWO, erste Kundin) ersetzt ein Kalender die Felder
Anreise/Abreise; der Gast wählt den Zeitraum, gibt Personen, Name, E-Mail, Telefon, Nachricht an. Die Anfrage geht an
die Vermieterin; gibt sie frei, bekommt der Gast eine Mail mit Rechnung und zahlt direkt oder vor Ort. Als Funktion im
Kundenportal, für alle Kunden. Entscheidungen des Inhabers U1–U7 (Abschnitt 13), alle übrigen von Claude (U8 ff.).
---
## 1. Ziel und Abgrenzung
Eine Funktion für Ferienwohnungen, Monteurzimmer und kleine Pensionen: Belegungskalender auf der Website, Anfrage,
Freigabe durch den Vermieter, verbindliche Buchung durch den Gast, Rechnung im Namen des Vermieters, Zahlung online
(Stripe, direkt an den Vermieter) oder vor Ort, Abgleich mit Booking.com/Airbnb per iCal.
Nicht im Umfang: Meldeschein (Bundesmeldegesetz), Kurtaxe/Gästebeitrag, Anzahlungen und Teilzahlungen, Sofortbuchung
ohne Freigabe, saisonale Preise (nur Staffel nach Aufenthaltsdauer), Gutscheine, Channel-Manager-APIs außer iCal,
Mehrsprachigkeit (nur Deutsch).
Begriffe: **Vermieter** = unser Kunde (Mimi). **Gast** = wer auf der Website anfragt. **Unterkunft** = buchbare Einheit
(Wohnung, Zimmer).
## 2. Ablauf
1. **Website (Widget):** Kalender zeigt belegte Nächte der gewählten Unterkunft (bei nur einer Unterkunft keine Auswahl).
Erster Klick Anreise, zweiter Abreise, Spanne markiert, Zusammenfassung „12.–30. Nov. · 18 Nächte“. Belegte Nächte
und Vergangenheit nicht wählbar; ein Zeitraum über eine belegte Nacht hinweg ebenso nicht; Mindestnächte werden
geprüft. Formular: Personen (1 … Höchstzahl), Firma (optional), Name, E-Mail, Telefon (optional), Nachricht
(optional), Datenschutzhinweis. Knopf **„Anfrage senden“** (unverbindlich, U6).
→ Anfrage `neu`, **G16** Eingangsbestätigung an den Gast, **K31** Neue Anfrage an den Vermieter.
2. **Portal, Reiter „Buchungen“ → Anfragen:** Detail mit vorgerechnetem Preis (Posten editierbar: Betrag, Text, Posten
hinzufügen/entfernen), Reservierungsfrist (Standard aus Einstellungen), optionale Nachricht an den Gast.
**Freigeben** → Prüfung unter Sperre, dass der Zeitraum frei ist; Status `freigegeben`, Nächte als Reservierung
gesperrt bis `fristAt`, **G17** an den Gast mit Preis, Stornobedingungen und Buchungslink.
**Ablehnen** (optional mit Text) → `abgelehnt`, **G18**.
3. **Buchungsseite** `https://project.webklar.com/unterkunft/<siteKey>/buchung/<id>?t=<token>` (Abschnitt 6):
Rechnungsadresse, Zahlart „online“/„vor Ort“, alle Pflichtangaben, Knopf **„Zahlungspflichtig buchen“** (U6).
→ Prüfung unter Sperre (Status `freigegeben`, Frist nicht abgelaufen, Zeitraum frei); Status `gebucht`,
Rechnungsnummer vergeben, PDF erzeugen, **G19** Buchungsbestätigung mit PDF an den Gast, **K32** an den Vermieter.
Bei „online“ direkt weiter zu Stripe Checkout; Zahlung → `bezahlt`, **G20**. Bricht der Gast die Zahlung ab, bleibt
die Buchung `gebucht` (unbezahlt); G19 und die Buchungsseite enthalten „Jetzt online bezahlen“.
4. **Frist abgelaufen** (Minuten-Job): `freigegeben` → `abgelaufen`, Nächte frei, **G21**.
5. **Vor Ort bezahlt:** Vermieter setzt „bezahlt (vor Ort)“ mit Datum; keine Mail.
6. **Stornierung** durch den Vermieter (nur `freigegeben`, `gebucht`, `bezahlt`): Grund, bei Online-Zahlung Erstattung
0 … Gesamtbetrag über Stripe; bei vorhandener Rechnung Stornorechnung (Rechnungskorrektur mit eigener Nummer, voller
Betrag negativ); **G22** an den Gast mit Stornorechnung. Stornierungen auf Wunsch des Gastes laufen ebenso über den
Vermieter (der Gast schreibt ihm; keine Selbststornierung im Widget).
Statusmaschine (nur vorwärts, Übergänge idempotent unter Sperre `unterkunft:<unterkunftId>`):
`neu → freigegeben → gebucht → bezahlt`; `neu → abgelehnt`; `freigegeben → abgelaufen`;
`freigegeben|gebucht|bezahlt → storniert`. `zahlart` ∈ {`online`, `vorort`} wird bei `gebucht` gesetzt;
`bezahltVia` ∈ {`stripe`, `vorort`}.
## 3. Datenmodell (Appwrite, `scripts/unterkunft-setup.mjs`, nur Server-Key, Muster `shop-setup.mjs`)
Alle Dokumente tragen `customerId`, `projectId`, `featureId` (portalFeatures-Dokument der Funktion).
- **`ukUnterkuenfte`**: `name`, `beschreibung` (kurz, optional), `maxPersonen` (1–20), `mindestNaechte` (1–90),
`checkIn`/`checkOut` (HH:MM), `sort`, `aktiv`, `icalImporte` (JSON-Array {id, name, url, letzterAbrufAt,
letzterErfolgAt, fehler}), `icalExportToken` (32 Byte, zufällig, erneuerbar).
- **`ukPreise`** (1:1 zur Unterkunft): `staffeln` (JSON [{abNaechten, preisProNachtCent}], aufsteigend, erste ab 1),
`reinigungCent`, `personenInklusive`, `aufpreisPersonNachtCent`, `direktrabattProzent` (0–50, ganze Zahl),
`steuerart` (`kleinunternehmer` | `regel`), `ustUebernachtung` (Standard 7), `ustReinigung` (Standard 7),
`fristTage` (1–14, Standard 3), `stornobedingungen` (Text, Pflicht vor dem Freischalten).
- **`ukSperren`**: `unterkunftId`, `von`, `bis` (Datum, `bis` = Abreisetag exklusiv, also belegte Nächte von..bis-1),
`quelle` (`manuell` | `ical:<importId>` | `anfrage:<anfrageId>`), `notiz` (nur manuell, nur Portal).
Reservierungen und Buchungen werden nicht als Sperre kopiert, sondern direkt aus `ukAnfragen` gelesen (Status
`freigegeben` mit `fristAt` > jetzt, `gebucht`, `bezahlt`); `ukSperren` enthält nur `manuell` und `ical:*`.
- **`ukAnfragen`**: `nummer` (kurz, z. B. `A-7K2Q`), `unterkunftId`, `von`, `bis`, `personen`, `status`, `gast`
(JSON {name, firma, email, telefon, nachricht}), `rechnungsadresse` (JSON {name, firma, strasse, plz, ort, land}),
`posten` (JSON [{text, menge, einzelCent, ustProzent}], Schnappschuss bei Freigabe), `summen` (JSON {netto, ust je
Satz, brutto}), `nachrichtVermieter`, `fristAt`, `tokenHash` (sha256 des Buchungslink-Tokens), `zahlart`,
`bezahltVia`, `bezahltAt`, `stripeSessionId`, `stripePaymentIntentId`, `erstattetCent`, `stornoGrund`,
`stornobedingungenText` (Schnappschuss bei Freigabe), `rechtstexteFassung`, `ipHash` (nur bis Freigabe/Ablehnung,
danach entfernt), Zeitstempel je Status.
- **`ukRechnungen`**: `anfrageId`, `art` (`rechnung` | `storno`), `nummer`, `datum`, `fileId` (Bucket
`uk-rechnungen`), `aussteller` (JSON-Schnappschuss der Firmendaten), `bruttoCent`.
- **`ukEinstellungen`** (1 je Funktion): `firma`, `inhaber`, `strasse`, `plz`, `ort`, `email`, `telefon`,
`steuernummer`/`ustId` (eins Pflicht), `iban`/`bic`/`bank` (optional, für „vor Ort/Überweisung“-Hinweis), `rechnungsPraefix`
(2–8 Zeichen A–Z0–9, Standard `RE`), `rechnungZaehler` (JSON {jahr, n}), `akzentfarbe`, `stripeAccountId`,
`stripeStatus` (Muster Webshop), `onlineZahlung` (an/aus; aus, solange Stripe nicht aktiv).
Rechnungsnummern: `<Präfix>-<Jahr>-<n vierstellig>`, lückenlos je Funktion und Jahr, vergeben unter Sperre
`uk-rechnungsnr:<featureId>` (keyedLock) mit Lesen–Erhöhen–Schreiben des Zählers vor dem PDF; schlägt das PDF fehl, wird
es neu erzeugt (Nummer bleibt). Storno-Rechnungen ziehen aus demselben Zähler.
## 4. Preisrechnung (`server/services/unterkunft/preis.js`, rein, ohne I/O)
`berechnePreis({ preise, von, bis, personen })` → `{ naechte, posten, summen }`:
- `naechte` = Tage zwischen `von` und `bis`; Staffel = höchste `abNaechten` ≤ `naechte` für **alle** Nächte.
- Posten: „Übernachtung, N Nächte à X €“ (ust Übernachtung); falls `personen` > `personenInklusive`: „Zusatzperson(en),
k × N Nächte à Y €“; „Endreinigung“ (falls > 0, ust Reinigung); „Direktbuchungsrabatt P %“ als negativer Posten auf
Übernachtung + Zusatzpersonen (nicht auf Reinigung), mit ust Übernachtung.
- Beträge sind Bruttobeträge in Cent. Bei `regel` wird die enthaltene USt je Satz aus der Summe der Posten dieses Satzes
herausgerechnet und kaufmännisch auf Cent gerundet; bei `kleinunternehmer` keine USt.
- Mimis Änderungen ersetzen die Posten vollständig; `summen` werden immer serverseitig aus den Posten neu berechnet
(`summiere(posten, steuerart)`), nie aus dem Browser übernommen.
## 5. Verfügbarkeit und iCal (`server/services/unterkunft/belegung.js`, `ical.js`)
- `belegteNaechte(unterkunftId, von, bis)`: Vereinigung aus `ukSperren` und aktiven Anfragen (Abschnitt 3).
`istFrei(...)` wird bei Freigabe und Buchung **unter der Sperre** erneut geprüft (U9). Eine eigene Anfrage zählt bei
ihrer eigenen Buchung nicht als belegt.
- **Import** (Job alle 15 Minuten, je Import höchstens 1 MB, Zeitlimit 10 s, nur `https:`, keine privaten/lokalen
Ziele — Prüfung der aufgelösten IP gegen private Netze): VEVENT mit `DTSTART`/`DTEND` (DATE oder DATE-TIME →
Datum), ohne `DTEND` = 1 Nacht, `STATUS:CANCELLED` ignorieren, nur Zeitraum heute−1 Tag bis +18 Monate. Erfolgreicher
Abruf ersetzt alle Sperren `ical:<importId>` (erst neue anlegen, dann alte löschen, damit nie eine Lücke entsteht). Fehler: alter Stand
bleibt, `fehler` gesetzt; ab 6 Stunden ohne Erfolg **K33** (höchstens einmal je 24 h und Import). Parser selbst
geschrieben (Zeilenfaltung, Escapes), keine Abhängigkeit.
- **Export** `GET /unterkunft/ical/<icalExportToken>.ics`: VCALENDAR mit einem VEVENT „Belegt“ je gebuchter/bezahlter
Buchung, Reservierung und manueller Sperre (nicht die importierten, sonst Echo), `UID` stabil, keine Gastdaten.
Bei der Booking.com-Anleitung im Portal: Link kopieren, bei Booking.com „Kalender synchronisieren“ eintragen.
## 6. Website-Widget und öffentliche Schnittstellen
Einbau (Muster Termine/Shop): `<div data-webklar-unterkunft="<siteKey>"></div>` +
`<script src="https://project.webklar.com/embed/unterkunft.js" defer></script>`. Shadow DOM, Stile über CSS-Variablen
(`--wk-uk-akzent`, `--wk-uk-text`, `--wk-uk-flaeche`, `--wk-uk-linie`, `--wk-uk-radius`, `--wk-uk-schrift`), damit jede
Seite das Widget anpassen kann. Layout: zwei Spalten (Kalender links, Formular rechts), unter 760 px untereinander.
Kalender: ein Monat sichtbar (zwei ab 1100 px Breite), Pfeile, Tastatur (Pfeiltasten, Enter), `aria-label` je Tag mit
Datum und Status, belegte Nächte durchgestrichen/grau, Anreise an einem Tag, an dem nur eine Abreise liegt, ist möglich
(Nächte-Logik).
Öffentliche Routen (`server/routes/publicUnterkunft.js`, Antworten nie mit IDs außer Unterkunft-IDs, keine Gastdaten):
- `GET /api/public/unterkunft/:siteKey` → Unterkünfte (id, name, maxPersonen, mindestNaechte, checkIn/Out),
Akzentfarbe, Datenschutz-Link-Ziel, Anbietername.
- `GET /api/public/unterkunft/:siteKey/belegung?unterkunft=<id>&von=&bis=` (höchstens 18 Monate) → belegte Nächte als
Bereiche. Cache 60 s.
- `POST /api/public/unterkunft/:siteKey/anfrage` → Prüfung (Pflichtfelder, Längen, E-Mail, Zeitraum frei und gültig,
Personen), Honeypot-Feld + Mindest-Ausfüllzeit, Rate-Limit je IP-Hash und Site (Muster Termine). Antwort: Anfragenummer.
- `GET /api/public/unterkunft/:siteKey/buchung/:id?t=` und `POST …/buchung/:id` (Token prüfen gegen `tokenHash`,
zeitkonstanter Vergleich; nach `abgelaufen`/`abgelehnt`/`storniert` nur Statusanzeige) → Buchungsseite.
- `POST …/buchung/:id/zahlung?t=` → Stripe Checkout Session auf dem verbundenen Konto (Direct Charge, Muster Webshop:
`mode: payment`, ein Posten „Aufenthalt <Unterkunft> <von>–<bis>, Rechnung <Nr>“, Betrag = brutto, `expires_at` 30 min,
`metadata` {ukAnfrageId}, `success_url` zurück zur Buchungsseite). Abschluss über den bestehenden Connect-Webhook
(Weiterleitung nach `metadata`) **und** beim Laden der Buchungsseite (Muster Webshop „Danke-Seite schließt selbst ab“).
**Buchungsseite** (Standalone-Seite im Portal-Design mit Akzentfarbe des Vermieters, § 312j BGB und Art. 246a EGBGB,
unmittelbar über dem Knopf): Anbieter (Firma, Anschrift, Kontakt), Unterkunft, An-/Abreise mit Check-in/-out-Zeiten,
Personen, alle Posten, Gesamtpreis inkl. USt bzw. Kleinunternehmer-Hinweis, Zahlarten, Stornobedingungen (Schnappschuss),
Hinweis „Ein Widerrufsrecht besteht nicht (§ 312g Abs. 2 Nr. 9 BGB).“, Datenschutzhinweis des Vermieters, Frist.
Pflichtfelder Rechnungsadresse (Name/Firma, Straße, PLZ, Ort, Land; Vorbelegung aus der Anfrage). Korrektur bis zum Knopf
möglich. Knopf **„Zahlungspflichtig buchen“**; „online“ nur wählbar, wenn `onlineZahlung` an.
## 7. Portal für den Vermieter (Funktionen → „Buchungen“, Hash `#buchungen`)
Nach dem Muster des Webshop-Bereichs; nur sichtbar, wenn die Funktion für das Projekt aktiv ist (Einrichtung: schon in
`setup` nutzbar zum Vorbereiten, Widget erst nach Freischaltung öffentlich).
- **Anfragen:** Filter offen (neu, freigegeben) / gebucht / alle; Liste mit Nummer, Zeitraum, Nächten, Gast, Status,
Betrag. Detail: Gastangaben, Kalenderausschnitt, Posten-Editor mit Live-Summe, Frist, Nachricht, Freigeben/Ablehnen;
bei Buchungen Rechnung herunterladen, „bezahlt (vor Ort)“, Stornieren (mit Erstattungsbetrag bei Online-Zahlung).
- **Kalender:** Monatsansicht je Unterkunft, Farben nach Quelle (Direkt gebucht, reserviert, Booking/iCal, manuell);
Klick-Klick auf freie Nächte → manuelle Sperre mit Notiz; Klick auf manuelle Sperre → löschen.
- **Unterkünfte & Preise:** Anlegen/Bearbeiten/Sortieren/Deaktivieren, Preis-Editor mit Beispielrechnung („7 Nächte, 2
Personen = …“), iCal-Importe (Name + URL, Status letzter Abruf), Export-Link mit Kopieren und Anleitung für Booking.com
und Airbnb, „Link erneuern“.
- **Rechnungen:** Liste (Nummer, Datum, Gast, Betrag, Art), PDF-Download, Jahres-ZIP-Download.
- **Einstellungen:** Firmendaten, Steuer, Rechnungspräfix (änderbar nur vor der ersten Rechnung), Bankverbindung,
Akzentfarbe, Stripe verbinden (Wiederverwendung `shop/connect.js`, ein Stripe-Konto je Kunde: hat der Kunde bereits ein
Shop-Konto, wird es angeboten), Online-Zahlung an/aus, Mustertext Stornobedingungen zum Übernehmen.
- Pflicht vor dem Freischalten (`freischaltbar`): mindestens eine aktive Unterkunft mit Preisen und Stornobedingungen,
Firmendaten vollständig, Steuernummer oder USt-ID.
## 8. Rechnung (PDF, `server/services/unterkunft/rechnungPdf.js`)
Mit der vorhandenen PDF-Bibliothek des Portals (Plan prüft, welche; keine neue Abhängigkeit, wenn vermeidbar).
Pflichtangaben § 14 Abs. 4 UStG: Name/Anschrift Aussteller und Empfänger, Steuernummer oder USt-ID, Rechnungsdatum,
Rechnungsnummer, Leistung („Beherbergung <Unterkunft>, <Anschrift der Unterkunft = Firmenanschrift, sofern nicht
abweichend>“), Leistungszeitraum (An–Abreise), Posten, Entgelt netto je Steuersatz, Steuersatz und Steuerbetrag bzw.
„Gemäß § 19 UStG wird keine Umsatzsteuer berechnet.“, Gesamtbetrag. Zusätzlich Zahlart und Status („bezahlt am …“ wird
nicht nachträglich ins PDF geschrieben; die Rechnung bleibt unverändert). Storno-Rechnung verweist auf die
Ursprungsrechnung. Kein WEBklar-Hinweis auf der Rechnung (U10), sie ist die des Vermieters.
## 9. Bestellung der Funktion bei WEBklar (Register-Eintrag `unterkunft`)
Muster Webshop: `product: 'unterkunftSetup'` (19900 Cent, Lookup-Key `webklar_unterkunft_einrichtung_19900`),
`priceLabel '199 €'`, `setupEstimate 'meist innerhalb von 1–2 Wochen'`, `requiresWartungsAbo: false`,
`ticketFreischaltung: true`, `returnHash 'buchungen'`, `returnProjekt: true`. Haken: `verfuegbar` (Schalter
`UNTERKUNFT_ENABLED` aus/pilot/an + `UNTERKUNFT_PILOT_EMAILS`), `bestellpruefung` (Anfrage angenommen, Muster Webshop
`anfrage.js`), `unterlagenBereit` (Leistungsbeschreibung + AVV Buchungskalender), `belehrung` (gemeinsame),
`freischaltbar` (Abschnitt 7). Anfrageformular der Funktion: Art der Unterkunft, Zahl der Einheiten, Booking.com/Airbnb
genutzt ja/nein, Nachricht → **K34** an den Kunden, **T22** an das Team. Zustimmungen wie Webshop: unterlagen,
verantwortung („Ich vermiete in eigenem Namen; für Preise, Stornobedingungen, Rechnungen und Meldepflichten bin ich
verantwortlich.“), stripe (nur wenn Online-Zahlung genutzt werden soll; sonst entfällt), beginn, information.
Ticket „Buchungskalender einbauen – <Projekt>“ mit siteKey, Snippet, Schritten (Kalender-Bereich anlegen, Snippet,
CSS-Variablen an die Seite anpassen, Datenschutz-Absatz einfügen, Testanfrage), Einbauanleitung
`docs/funktionen/unterkunft-einbau.md`. Für Kunden, die die Funktion als Teil ihres Projekts bekommen (Mimi), schaltet
das Team sie wie bei den anderen Funktionen manuell frei (vorhandener Weg „ohne Zahlung freischalten“ im WOMS; der Plan
prüft, ob es ihn gibt, sonst Skript `scripts/unterkunft-freischalten.mjs`).
## 10. WOMS
Wie Webshop: Anfragen der Funktion in der Kunden-Funktionsansicht annehmen/ablehnen; Anzeige des Funktionsstatus;
Rechtliches-Karten `leistungsbeschreibung-unterkunft` und `avv-unterkunft` (Sortnummern nach den belegten, der Plan
prüft `setup-legal.mjs`). Keine Verwaltung der Buchungen im WOMS (Datenhoheit beim Kunden).
## 11. Mails (Vorlagen im Stil der bestehenden, `server/mail-templates`)
Gast (Absender WEBklar-Versandadresse, Anzeigename = Firma des Vermieters, Reply-To = E-Mail des Vermieters):
G16 Anfrage eingegangen · G17 Ihre Anfrage ist frei – jetzt buchen (Preis, Frist, Stornobedingungen, Link) ·
G18 Leider nicht möglich · G19 Buchungsbestätigung (alle Angaben, Stornobedingungen, Rechnungs-PDF, ggf. Bezahl-Link;
dauerhafter Datenträger § 312f BGB) · G20 Zahlung eingegangen · G21 Reservierung abgelaufen · G22 Stornierung
(Stornorechnung-PDF, Erstattungsbetrag).
Vermieter: K31 Neue Buchungsanfrage · K32 Neue Buchung · K33 Abgleich fehlgeschlagen · K34 Ihre Anfrage zum
Buchungskalender. Team: T22 Neue Anfrage Buchungskalender. Nummern vor dem Bau gegen `main` prüfen (Stand: K30, G15,
T21 belegt).
## 12. Datenschutz, Aufbewahrung, Rechtstexte
- Rollen: Vermieter ist Verantwortlicher für Gastdaten, WEBklar Auftragsverarbeiter → **AVV Buchungskalender** (Muster
AVV Webshop; Unterauftragsverarbeiter Hetzner, Stripe nur als eigener Vertragspartner des Vermieters).
- Fristen: `ipHash` weg bei Freigabe/Ablehnung bzw. nach 30 Tagen; Anfragen ohne Buchung (abgelehnt, abgelaufen, neu
älter als 60 Tage) nach 6 Monaten anonymisiert; Gastdaten gebuchter Aufenthalte 3 Jahre nach Jahresende der Abreise
anonymisiert, Rechnungs-PDFs und Rechnungsadresse bleiben 8 Jahre (§ 147 AO, Buchungsbelege) solange die Funktion
läuft; bei Abschaltung: Jahres-ZIP-Download angeboten, Hinweis-Mail, Löschung 90 Tage danach (Muster Webshop).
Täglicher Job im bestehenden Aufräum-Muster (`retention.js`).
- Datenschutz WEBklar: neuer Abschnitt **§ 6f** (6d Webshop, 6e Öffnungszeiten; Plan prüft). Muster-Absatz für die
Website des Vermieters `docs/funktionen/unterkunft-datenschutz-absatz.md`. Mustertext Stornobedingungen (mit Hinweis
„Vorlage, keine Rechtsberatung“).
- WEBklar-Texte (Fassung `2026-10-07`): Leistungsbeschreibung Buchungskalender, AVV Buchungskalender, Kanzlei-Fragen
`docs/rechtliches/unterkunft-kanzlei-fragen.md` (u. a. Button-Lösung über Mail-Link, USt-Satz Endreinigung, Rolle bei
Rechnungserstellung im Namen des Vermieters § 14 Abs. 2 S. 4 UStG — Rechnung durch Dritten im Auftrag, Vermieter muss
zustimmen → Zustimmung beim Bestellen und Einstellungen-Haken).
- Upload der Texte in WOMS Rechtliches macht Claude selbst (Feedback des Inhabers), sobald der Inhaber die Texte
freigibt.
## 13. Entscheidungen
Inhaber (2026-10-07): **U1** allgemeine Funktion für alle Kunden, Mimi zuerst. **U2** Stripe Connect wie Webshop, Geld
direkt an den Vermieter, PDF-Rechnung in dessen Namen, Zahlung online oder vor Ort. **U3** Preise im Portal hinterlegt,
vorgerechnet, vor Freigabe änderbar. **U4** iCal-Abgleich in beide Richtungen + manuelle Sperren. **U5** mehrere
Unterkünfte je Website. **U6** Anfrage unverbindlich, Buchung erst per „Zahlungspflichtig buchen“ nach Freigabe,
Reservierung bis Frist (Standard 3 Tage). **U7** 199 € einmalig auf Anfrage, keine laufenden Kosten. Bau in parallelen
Strängen.
Claude: **U8** eigenes Modul statt Terminplaner-Erweiterung oder Fremdsystem. **U9** Freigabe und Buchung prüfen
Verfügbarkeit unter Sperre erneut. **U10** keine WEBklar-Werbung auf der Rechnung. **U11** Staffelpreis gilt für alle
Nächte. **U12** Rabatt nicht auf Reinigung. **U13** Stornierung nur durch den Vermieter. **U14** Zahlungsabbruch lässt
die Buchung bestehen (unbezahlt). **U15** ein Stripe-Konto je Kunde für Shop und Buchungen. **U16** Widget ohne
Fremdbibliothek, Shadow DOM, CSS-Variablen.
## 14. Website Donau Home (D)
Abschnitt `#anfrage`: das bisherige Formular wird durch das Widget ersetzt (Kalender links unter Überschrift und
Einleitung, Formular rechts; Telefon/E-Mail unter dem Kalender). CSS-Variablen in Palette A (Eiche & Leinen) und
Schrift Figtree; Widget-Kalender optisch an den Zeichenstil angelehnt (Rahmen, Akzent Eiche). Bis zur Freischaltung
zeigt die Vorschau das Widget über `UNTERKUNFT_ENABLED=pilot` mit Mimis siteKey. Einleitungstext anpassen („Wählen Sie
Ihren Zeitraum im Kalender …“). Kein Push durch Claude.
## 15. Tests und Livegang
- Unit: `preis.js` (Staffeln, Zusatzpersonen, Rabatt, USt-Rundung, Kleinunternehmer), `ical.js` (Parser inkl. Faltung,
DATE/DATE-TIME, CANCELLED, Export), Statusmaschine, Rechnungsnummern unter Parallelität, Belegung/Überschneidung,
öffentliche Routen (Validierung, Rate-Limit, Token), Aufbewahrung. Muster der bestehenden Tests (Appwrite-Mock),
Node-Läufe mit `--max-old-space-size=1024` und Timeout.
- UI: Playwright-Durchlauf (Muster `test/ui/shopTestServer.mjs`): Anfrage im Widget → Freigabe im Portal → Buchungsseite
→ „vor Ort“ → Rechnung; Online-Zahlung gegen Stripe-Attrappe.
- Livegang (Notiz `docs/funktionen/unterkunft-livegang.md`): Setup-Skript, Stripe-Preis anlegen, Connect-Webhook-Events
prüfen (vorhandener Endpunkt), compose/.env `UNTERKUNFT_ENABLED=pilot`, `UNTERKUNFT_PILOT_EMAILS=kenso.gri@gmail.com,
<Mimi>`, Push durch den Inhaber (`feature/unterkunft:main`, nie unter eigenem Namen), Rechtstexte hochladen, Pilot mit
Mimi, danach `on`.
## 16. Parallele Stränge (für den Plan)
0. **Kern zuerst** (seriell, klein): Schema + Setup-Skript, Schalter, Register-Eintrag, `preis.js`, `belegung.js`,
Statusmaschine mit festen Funktionsschnittstellen und Attrappen.
Danach gleichzeitig:
1. **Ablauf + Server:** öffentliche Routen, Portal-API, Stripe, Jobs (Frist, iCal, Aufbewahrung), Rechnungsnummern.
2. **Portal-Oberfläche + WOMS.**
3. **Widget + Buchungsseite + Donau-Home-Einbau.**
4. **Mails, Rechnungs-PDF, Rechtstexte, Einbau- und Livegang-Notiz.**
Abschluss: Zusammenführen, Gesamttests, Review, UI-Durchlauf.