295 lines
24 KiB
Markdown
295 lines
24 KiB
Markdown
# 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.
|
||
|
||
## 17. Nachträge aus der Code-Prüfung (2026-10-07, vor dem Plan)
|
||
|
||
- **N1 (ersetzt U16):** Widget nach dem Muster `shop.js`/`termine.js` statt Shadow DOM: eigenes `<style id="wku-style">` mit
|
||
`@layer wku`, Klassenpräfix `.wku-`, Variablen `--wku-akzent`, `--wku-akzent-text`, `--wku-hintergrund`, `--wku-text`,
|
||
`--wku-rand`, `--wku-radius`, `--wku-schrift`, `--wku-schrift-titel`. Öffentliche API unter `/api/public/unterkunft/v1/`.
|
||
- **N2 (ersetzt den Bucket `uk-rechnungen`):** Keine Datei-Ablage. `ukRechnungen.inhalt` speichert den vollständigen,
|
||
unveränderlichen Schnappschuss (Aussteller, Empfänger, Unterkunft, Zeitraum, Posten, Summen, Steuerart, Bezug); das PDF
|
||
wird bei jedem Abruf daraus deterministisch erzeugt. Eigener minimaler PDF-Writer (Helvetica, WinAnsi inkl. Umlaute und
|
||
€), keine neue Abhängigkeit.
|
||
- **N3:** Die Mail-Warteschlange (`termine/outbox.js`) bekommt ein optionales Feld `anhang` (JSON-Verweis
|
||
`{art:'ukRechnung', rechnungId}`), `sendGuestMail` einen Parameter `attachments`; das PDF entsteht beim Versand.
|
||
- **N4 (konkretisiert U15):** `ukEinstellungen.stripeAccountId` ist eigen; hat das Projekt ein Webshop-Konto
|
||
(`shopConfig.stripeAccountId`), bietet das Portal „vorhandenes Stripe-Konto verwenden“ an. Metadaten `wkUk:'1'`;
|
||
`account.updated` aktualisiert auch `ukEinstellungen`; `erstatten` bekommt einen Parameter für das Metadatum `webklar`.
|
||
- **N5:** Datenschutz-Abschnitt bleibt § 6f (6e ist für Öffnungszeiten reserviert, noch nicht live).
|
||
- **N6:** Kein Textform-Nachweis (nicht in `TEXTFORM_KEYS`); Mimi wird im WOMS über „Freischalten“ (`action:'activate'`)
|
||
ohne Zahlung freigeschaltet.
|
||
- **N7:** Kein Jahres-ZIP der Rechnungen (YAGNI); Einzel-PDF-Download je Rechnung. Bei Abschaltung Hinweis-Mail mit der Bitte, die PDFs herunterzuladen.
|