44 KiB
Terminplaner – TP2 Verkauf: Design
Stand 2026-10-03. Basis: Portal main 3a0b008, WOMS test 65e9931, TP1-Spec
docs/superpowers/specs/2026-10-02-terminplaner-design.md (E1–E8, Abschnitt „TP2 Verkauf“), TP1-Ledger R1–R55
(/root/reports/terminplaner-tp1/progress.md). Erkundung: /root/reports/terminplaner-tp2/erkundung-2026-10-03.json.
Pfade: P = Portal-Repo (kundenbereich.webklar.com), W = WOMS-Repo (ticket.webklar.com).
Alles unten ist aus dem Code gelesen (nichts gegen Produktion oder Stripe gelaufen), außer Aussagen mit „(Annahme)“.
„Festlegung“ = technische Entscheidung. Die geschäftlichen und rechtlichen Entscheidungen D1–D8 hat der Inhaber am
2026-10-03 getroffen (Abschnitt 10); sie sind in alle Abschnitte eingearbeitet.
1. Ziel und Abgrenzung
TP2 liefert: Ein Kunde mit laufendem Wartungs-Abo bestellt den Terminplaner selbst: Kachel → Fragebogen (Entwurf) →
Bestellung mit Zustimmungen → Stripe (149 € einmalig) → automatisches Ticket mit Einbauanleitung → Einbau durch das Team →
Freischaltung → Abo-Kopplung (endet das Abo, endet der Terminplaner, Löschung nach 30 Tagen). Dazu der Rechtstexte-Satz
(Leistungsbeschreibung, AVV, Datenschutz 6c, Gästetexte final) und danach TERMINE_ENABLED=on.
Bleibt für TP3: Online-Zahlung der Gäste (Stripe Connect, Erstattungen, Connect-Webhook), Abo-Bedingungen v6 bzw. Rahmen-AVV mit Anlage je Funktion, Stripe-Absatz in 6c, Nachweisdatensätze über 90 Tage.
Pilotkunde „test“ (kenso.gri@gmail.com): bleibt per WOMS „Aktivieren (ohne Zahlung)“ aktiv (unlockedByPurchase=false).
Festlegung: Der tägliche Abo-Abgleich (Abschnitt 6) nimmt solche Dokumente aus, sonst würde der Pilot beim ersten Lauf
abgeschaltet. Seine Daten und Einstellungen bleiben unverändert. Er dient als Prüfkunde für den Livegang-Test (Abschnitt 9).
Festlegungen zum Umfang:
- Ein Terminplaner gilt je Website-Projekt (Feature-Dokument je Projekt, wie News). 149 € fällt je Projekt an und umfasst ein Widget an einer Stelle mit Gestaltung nach Fragebogen und den Einbau; Mehrwünsche (zweites Widget, Knöpfe je Leistung, Sonderdesign) nach Stunden (D4).
- Nur der Modus „vor Ort bezahlen“ (E7). Keine Gruppen, kein Gesundheitsbereich (Spec „Nicht Teil“).
- News bleibt unverändert nutzbar; es wird nur auf das Register umgestellt (Abschnitt 4), Verhalten und Ticket-Text von News bleiben identisch.
2. Ablauf aus Kundensicht
| Zustand | Kachel (P/public/funktionen.js) |
Ansicht #termine (P/public/termine.js) |
|
|---|---|---|---|
| ohne Wartungs-Abo | „Im Wartungs-Abo enthalten. Zu Verträge“ (wie analyseTile), kein Fragebogen |
Hinweis | – |
off (Entwurf, auch ohne Doc) |
„Terminplaner einrichten · 149 €“ bzw. „Fragebogen fortsetzen“ | Fragebogen, 8 Schritte + „Prüfen und bestellen“ | – |
ordered |
„Zahlung offen“ | Statuskarte, Knopf „Zahlung fortsetzen“, Einstellungen nur lesbar | – |
setup |
„In Einrichtung“ | Statuskarte (Ticketnummer, Rechnung), Einstellungen nur lesbar | K15, T16 |
active |
„Termine öffnen“ | wie TP1 | K16 |
disabled |
„Termine ansehen“ | lesen + CSV-Export, Banner nach Grund (Abo beendet + Löschdatum) | K22 |
Schritte:
- Kunde mit laufendem Wartungs-Abo (Prüfung Abschnitt 6) sieht die Kachel. Er startet den Fragebogen; jeder Schritt speichert sofort als Entwurf (Abschnitt 3).
- „Prüfen und bestellen“: Zusammenfassung, offene Punkte (Server rechnet
bestellpruefung), Zustimmungen (Abschnitt 4.3), Knopf „Zahlungspflichtig bestellen“. POST /api/funktionen/termine/checkoutlegtorderedan, Stripe Checkout (Karte + SEPA wie News). Rücksprung#termine?bestellt=1&projekt=<id>.- Webhook (bestehender Pfad,
abo.js:520-525) →markPaid:setup, Ticket, K15 an Kunde, T16 an Team. - Team baut ein (Anleitung im WOMS), Abnahme, Freischaltung durch Ticket „Erledigt“ oder Staff-Knopf (Abschnitt 5) →
active, K16. - Ab dann Betrieb wie TP1. Endet das Abo: täglicher Abgleich →
disabled, K22, nach 30 Tagen Löschung (Abschnitt 6).
Festlegung Sperre ab Bestellung: Ab ordered ist die Einstellung für den Kunden nur lesbar, weil das Team gegen den bezahlten
Stand baut (sonst driftet Ticket-Kurzfassung gegen Einstellung). Änderungen laufen über den Chat; Staff in Kundenansicht darf
weiter per PATCH ändern. Scheitert die Zahlung (markCheckoutFailed, ordered → off), ist der Entwurf wieder bearbeitbar.
Mails (alle Du-Form, Layouts der Inhaber-Vorlagen, nur Platzhalter ergänzen; Annahme: Inhaber akzeptiert das):
- K15 Bestellbestätigung:
{{funktion}},{{preis}}aus Register, Block „kein Abo“ nur für News. Neu: Anhänge Leistungsbeschreibung + AVV (PDF, Fassung) und Widerrufsbelehrung mit Muster-Formular auf dauerhaftem Datenträger (Muster K18/legalPdfAttachments). - K16 „bereit“:
{{funktion}},{{portal_url}}=#${returnHash}, Knopftext aus Register. - T16 Team:
{{funktion}}, Link zur Anleitung, neue Blöckekein_aboundbereits_aktiv(Abschnitt 4.5). - K22 „Terminplaner beendet“ (Varianten Abo beendet, auf Hosting gewechselt, vor Einrichtung beendet mit Erstattung) und K23 „läuft wieder“; T19 an das Team „Erstattung fällig“ (4.6). IDs K22, K23, T19 sind frei (
test/mailTemplates.test.jsführt die Liste und den Kopfkommentar). Layouts nach den Vorlagen des Inhabers, Texte gehen in seine Mail-Überarbeitung. - K04/T09 (Widerruf):
{{produkt}},{{preis}}statt fester „News-Pinnwand, 30 €“-Texte.
3. Fragebogen
3.1 Speicherung (Festlegung: ein Dokument, kein Kopieren)
- Der Entwurf ist die spätere Live-Einstellung:
termineConfig.einstellung, Doc-ID = Feature-ID (kontext.js:12). Kein Compile- oder Kopierschritt bei Bestellung oder Freischaltung. - Neues Attribut
termineConfig.fragebogen(JSON, 16 000 Zeichen, Schema 1):bestaetigt[](Schrittschlüssel),bundeslandGewaehlt,preiseGeprueft,einbau{modus inline|knopf, knopfText ≤40, stelle ≤300, gestaltung ≤1000, bemerkung ≤1000}. Grund:normalisiereEinstellungverwirft unbekannte Schlüssel (einstellungen.js:505-541), und Einbauwünsche dürfen nie in die öffentliche Konfiguration. Setup überscripts/termine-setup.mjs(idempotent),test/termineSetup.test.js:78undtest/support/schema.jsmitziehen. - Schritt „erledigt“ lässt sich nicht aus den Daten ableiten (Vorlagen machen jeden Stand gültig), deshalb die gespeicherte Bestätigung je Schritt.
PUT /api/termine/fragebogenschreibt nurfragebogen,updatedAt,updatedByunterwithLocks('termine-cfg:'+id), ohneversionzu erhöhen (offene Editoren bleiben gültig). Nicht auf der Kundenansicht-Allow-Liste: Schritte bestätigt nur der Kunde selbst.
3.2 Zugriff in Entwurfszuständen
withTermine({ entwurf: true })(P/server/routes/termine.js:137-157) nur fürGET /einstellungen,POST /einstellungen/vorlage,PATCH /einstellungen,PUT /fragebogen. Erlaubtoff,active;ordered/setupnur lesend (GET), Schreiben dort nur mitreq.session.viewAs.disabledbleibt wie TP1. Alle anderen Routen behalten die Schrankeactive|disabled(Gäste- und Job-Pfade unberührt:kontextPerFeaturebleibt, neuer HelferkontextFuerPortal).- Ohne Feature-Dokument antwortet GET 200
{state:'off', einstellung:null, vorlagen, branchen}und legt nichts an; der erste Schreibzugriff ruftensureFeatureDoc(Docoff, dauerhafter siteKey). - Entwürfe sind nie öffentlich:
kontextPerSiteKey→findActiveBySiteKeyverlangtactive(siteFeatures.js:390-400). - PATCH ruft
normalisiereEinstellung(..., {entwurf: state!=='active'})(überspringt nur R13/R13a), Antwort enthältfehlt: fehlendePflichtangaben(...); der Client (termine-speichern.js:116) übernimmt es statt[]. TeststermineRoutes.test.js:247-264ändern sich entsprechend.
3.3 Schritte und Wiederverwendung der TP1-Editoren
| # | Schritt | Umsetzung |
|---|---|---|
| 1 | Branche, Vorlage, Gesundheits-Ausschluss | BRANCHEN-Liste in vorlagen.js; Eintrag „Gesundheitsbereich“ ohne Vorlage blockt; POST vorlage mit ersetzen:true im Entwurf, optionalen betrieb-Feldern (Name, E-Mail, Telefon, Anschrift, Bundesland, Website) und Vorbelegung der Website aus dem Projekt |
| 2 | Wer wird gebucht | neue kleine Form: Einzelperson / Team / Plätze, Namen; setzt neue Ressourcen im selben PATCH allen Leistungen zu (Validator verlangt aktive Ressource) |
| 3 | Leistungen | termine-tab-leistungen.js render unverändert, plus Haken „Preise geprüft“ (Vorlagenpreise sind Platzhalter) |
| 4 | Zeiten, Bundesland, Urlaub | Wochenplan-Helfer aus termine-tab-team.js:69 exportiert, abwesenheiten/feiertage/sonderoeffnungen (bereits exportiert); Bundesland ohne Vorauswahl (Server-Default RP bei fehlendem Wert, einstellungen.js:512) |
| 5 | Regeln, sofort/Anfrage | regeln, storno, rechtliches (gaesteSind, freizeit), zahlung aus termine-tab-einstellungen.js exportiert; Sammelknopf „sofort/Anfrage für alle“ |
| 6 | Gastfelder | gastFelder exportiert |
| 7 | Benachrichtigungen, Anrede, Erinnerung | darstellung, erinnerung, betrieb.email (einzige Adresse; sie ist auch Reply-To und Ziel von K20/K21) |
| 8 | Website | betriebLinks (Impressum, Datenschutz, AGB, Website), telefon (= Ausweichtelefon), Einbauwünsche-Form |
| 9 | Prüfen und bestellen | siehe Abschnitt 4 |
Festlegungen UI: neue Datei P/public/termine-fragebogen.js (Name in die Liste test/termineView.test.js:277-280; Du-Form, kein innerHTML, kein href); die Bestellseite wegen der Link-Regel (termineView.test.js ~290: nur ein exportUrl-href) in P/public/funktion-bestellen.js (heißt nicht termine*.js, wird relativ importiert, R4). Jeder Schritt = ein <form> mit Weiter = ein PATCH über neuerSpeicher (einziger PATCH-Pfad), danach PUT /fragebogen. Pro Betrieb nur ein Ressourcentyp (Annahme: Person+Platz gemischt ist im Validator nicht verboten, das Verhalten ist ungeprüft).
3.4 Bestell-Gate (serverseitig, server/services/termine/fragebogen.js, reine Funktion bestellpruefung)
Offen = Liste {schritt, text}; Checkout lehnt bei nicht-leerer Liste ab, GET zeigt sie. Festlegung strikt: bestellbar nur wenn
(a) fehlendePflichtangaben leer, (b) mind. eine sichtbare Leistung, (c) alle Schritte bestätigt, (d) Bundesland gewählt, (e) einbau.modus gesetzt, (f) Branche nicht Gesundheit, (g) impressumUrl und datenschutzUrl vorhanden (der Gästehinweis verweist auf die Datenschutzerklärung; das Widget lässt den Link sonst still weg, embed/termine.js:519), (h) Wartungs-Abo sauber laufend (Abschnitt 6). Offene Punkte klärt der Kunde vor der Bestellung, nicht per Chat danach.
3.5 Gesundheits-Ausschluss (D6)
Heute nur Hinweistexte (rechtstexte.js:169), keine Sperre. Umsetzung nach D6: Branchenfrage mit hartem Ausschluss (kein Entwurf),
Haken „Mein Angebot liegt nicht im Gesundheitsbereich“ im Bestellschritt (mit Zeit im Zustimmungsnachweis), und eine weiche
Stichwortprüfung über Leistungsnamen, Beschreibungen und Zusatzfeld-Beschriftungen (Liste in fragebogen.js, z. B. Physio,
Therapie, Rezept, Beschwerden, Diagnose, Behandlung, Arzt, Heilpraktiker): nur ein Warnblock in T16 und im Ticket, kein Fehler
für den Kunden. Das Team kann die Einrichtung ablehnen (Erstattung nach 4.6, Variante Widerruf/Kulanz). „Sonstiges“ bleibt wählbar.
3.6 Einbauwünsche
Modus inline oder knopf (Snippet erweitert um data-modus, data-knopf; Widget kann das, embed/termine.js:232-234), Knopftext, Stelle auf der Website in Worten, Gestaltungswunsch, Bemerkung. Festlegung V1: keine Knöpfe je Leistung (data-leistung nur auf Nachfrage über Bemerkung, Mehraufwand nach Stunden). Texte des Kunden werden im Ticket nur als Text und in einem Block „Kundenwunsch (Text des Kunden)“ ausgegeben.
3.7 Entwürfe aufbewahren und löschen
Heute löscht niemand einen Entwurf (Job loescheAbgeschaltete nur disabled; disableCustomerFeatures überspringt off, siteFeatures.js:321-326). Festlegung: Tagesjob-Schritt löscht termineConfig + Feature-Doc von Dokumenten in off (nie ordered/setup), die 90 Tage nicht geändert wurden (Annahme: 90 Tage passt zur Datenschutz-Aussage, Rechtsrunde bestätigt); Kundenlöschung (customerAdmin.js:289) entfernt Entwürfe ebenfalls. Die Datenschutzerklärung (6c, §7) nennt Entwurfsdaten und die Frist.
4. Bestellung und Zahlung
4.1 Register config.features (ersetzt feste News-Werte)
P/server/config.js: neben newsSetup neuer Katalogeintrag termineSetup = {key, name:'Terminplaner – Einrichtung', amountCents:14900, lookupKey:'webklar_termine_einrichtung_14900', oneTime:true, priceNickname}, Stripe-Metadaten-Schlüssel termine-einrichtung. Neu config.features = { news:{…}, termine:{…} } mit: product, label{name,kurz,keine}, priceLabel, setupEstimate, consent{version, items[]}, leistungsbeschreibungVersion, returnHash, ticket{topic(project), details(ctx)}, mails{funktion, knopf, danach, preisHinweis}, requiresWartungsAbo, disabledMessage, orderedConflictMessage. config.funktionen.{setupEstimate,consentText} bleiben als News-Alias, damit funktionenRoutes.test.js:63-64 nur gezielt geändert wird.
Zu ersetzende News-Festverdrahtungen (alle verifiziert): stripeBilling.js:54-61 (PRODUCT_METADATA, catalogFor), :649 (ensurePrice('newsSetup')), :661, :665-666 (URLs), routes/funktionen.js:122,137,158-171, siteFeatures.js:17-18 (DISABLED_MESSAGE), :360 (activateClosedTickets nur news), :406 (portalUrl = #news), :537-538, :584-587, ticketAdmin.js:80,85,95-102, public/funktionen.js:9,261-317.
4.2 Stripe
createFeatureCheckout(customer, {projectId, key, consentAt}) liest Preis, Rechnungsbeschreibung, Rücksprung und Abbruch-URL aus dem Register; invoice_data.metadata mit featureKey, projectId. SEPA bleibt wie bei News (Festlegung; Einbau startet erst bei setup, also nach Zahlungseingang). Der Preis termineSetup wird vor dem ersten Live-Kauf per scripts/stripe-products.mjs --pruefen/--anlegen angelegt (KEYS :20 und catalogEntry :30-34 ergänzen; Test nach Vorbild stripeBilling.test.js:246-260), sonst legt ihn der erste Kunden-Checkout lazy an (und ensurePrice sucht nur in den ersten 100 aktiven Produkten, :81-82).
4.3 Route und Zustimmungen
POST /api/funktionen/:key/checkout(Schlüssel gegen Register;/news/checkoutbleibt Alias). Nicht unter/api/termine/: dort erlaubtkundenansichtGuard.js:19jedes POST für Staff in Kundenansicht. Zusätzlichreq.session.viewAs→ 403 (wiefunktionen.js:139).- Reihenfolge: viewAs, Zustimmungen vollständig und Fassung = aktuelle Serverfassung (sonst 409 mit neuer Fassung), Projekt gehört dem Kunden,
termineFuerKunde(email)(Pilot-/Modusschranke),bestellpruefungleer, Wartungs-Abo (Abschnitt 6), Lockfeature-order:<customer>:<project>, Zustandsprüfung (Meldungen aus dem Register),markOrdered. - Zustimmungen für Termine (Wortlaut ist Rechtsrunde, hier nur Struktur; nach D1 werden alle Besteller wie Verbraucher behandelt): (1) Leistungsbeschreibung + AVV Terminplaner, Fassung X, mit versionierten Links; (2) „kein Gesundheitsbereich“; (3) ausdrückliches Verlangen des Beginns vor Ablauf der Widerrufsfrist mit Hinweis auf Erlöschen (§ 356 Abs. 5 Nr. 2 BGB) und Wertersatz (§ 357a Abs. 2 BGB; die News-Zustimmung hat nur die erste Hälfte); (4) Pflichtinformationen (Leistung, Gesamtpreis, Ende mit dem Wartungs-Abo, Einrichtungsdauer) und volle Widerrufsbelehrung + Muster-Formular vor dem Knopf; (5) Knopf „Zahlungspflichtig bestellen“; ob „· 149 €“ am Knopf steht, entscheidet die Rechtsrunde (Recherche rät zu nichts außer den gesetzlichen Worten; die Spec schreibt „· 149 €“).
- Nachweis: neue append-only Collection
featureConsents(MusteranalyseConsents; Felder customerId, projectId, featureKey, stripeSessionId, Fassungen, vollständige gezeigte Texte, Hash, Zeit, IP, UA, Konto). Die 1000-Zeichen-SpalteportalFeatures.consentTextreicht für den Satz nicht (setup-abo-collections.mjs:277). IP/UA kommen in Datenschutz §7. - Widerrufs-Anker: Zeit des Klicks (Vertragsschluss) aus
featureConsents, nichtorderedAt(der bei Neubestellung überschrieben wird). Annahme: so ist die Frist korrekt gedeutet, Rechtsrunde.
4.4 Rechnung
Stripe erzeugt sie nach Zahlung (invoice_creation), backfillInvoiceLinks ergänzt den Link später (K15 geht deshalb oft ohne Link, Block rechnung; unverändert). Preistext „149 € (ohne Umsatzsteuer gemäß § 19 UStG)“ statt „brutto/inklusive Mehrwertsteuer“ (Spec E2 sagt „brutto“; Stripe-Footer und Abo sagen § 19, Annahme: WEBklar bleibt Kleinunternehmer, Rechtsrunde bestätigt; gilt gleichzeitig für News).
4.5 Fehlerfälle
- Verschluckte Zahlung (heute real): Aktiviert Staff ein
ordered-Dokument per „Aktivieren (ohne Zahlung)“, sieht der Webhookcreated:falseund tut nichts (siteFeatures.js:249,521). Festlegung:onFeaturePaidschickt in dem Fall T16-Blockbereits_aktiv(Zahlung ohne Ticket), und WOMS blendet die Aktion inordered/setupfür Termine aus (Abschnitt 5). - Zahlung ohne Abo (Abo endete während der 24-h-Sitzung): Dokument geht trotzdem auf
setupsamt Ticket (Geld ist da), T16 mit Blockkein_abo; Aktivierung bleibt gesperrt, bis ein Abo läuft. Nach D3 wird voll erstattet, wenn das Abo vor der Fertigstellung endet (siehe 4.6). - Ticket scheitert nach Zahlung: bestehende Wiederaufnahme (
setupohneticketId, Stripe-Retry) bleibt; neu zusätzlich Fallback auf Minimal-Details (Abschnitt 5). - Zahlung offen: Knopf „Zahlung fortsetzen“ (derselbe Checkout liefert die offene Sitzung,
openOrderSession); SEPA kann Tage inorderedbleiben.
4.6 Erstattung (D3)
- Gesetzlicher Widerruf: Erstattung abzüglich Wertersatz für einen schon begonnenen Einbau (nach Stundenaufwand, § 357a Abs. 2 BGB).
- Zusätzlich volle Erstattung, wenn das Wartungs-Abo endet oder widerrufen wird, bevor der Terminplaner
activeist (Zustandordered/setup). Festlegung: Der tägliche Abgleich (Abschnitt 6) erkennt den Fall, setzt das Dokument aufdisabledmitdisabledReason='abo_ende_vor_einrichtung', schließt keine Tickets selbst, und meldet ihn dem Team per Mail T19 „Erstattung fällig: Terminplaner-Einrichtung“ (T17/T18 sind vergeben) (Kunde, Projekt, Stripe-Sitzung, Rechnung). Die Erstattung selbst löst der Inhaber in Stripe aus (keine automatische Rückzahlung, keine Live-Stripe-Aufrufe durch Code ohne Freigabe). Der Kunde bekommt K22 in der Variante „vor Einrichtung beendet, Betrag wird erstattet“. - Das Ticket bleibt offen, bis das Team es mit Vermerk schließt; die Freischaltung beim Schließen prüft Abo und Zustand und schaltet dann nicht frei.
4.7 Widerrufsformular
- Widerruf:
widerrufen.htmlbekommt eine eigene Auswahl „Terminplaner (Einrichtung)“;withdrawFeature/paidFeatureOrdersrechnen die Frist je Funktion (heute Maximum über alle Bestellungen,abo.js:1091-1093), T09 nennt die Funktion. Abwicklung bleibt Handarbeit des Teams (Rückerstattung, Abschalten, Ticket).
5. Ticket, Einbauanleitung, WOMS
Ticket: markPaid übergibt topic/details aus dem Register an createFeatureTicket (kann es schon, analyse.js nutzt es). Festlegungen: topic auf 128 Zeichen kürzen (heute ungekürzt, ticketAdmin.js:85), Details-Deckel 7000 Zeichen (Schema nennt 8000, ungeprüft, das Setup-Skript ist für portalFeatures nachweislich veraltet); scheitert createDocument mit langen Details, einmal mit Minimal-Details (Kopfzeilen + Verweis) wiederholen. Zuweisung: keine, wie News; T16 meldet das Ticket. Ticket-Hinweis „Nicht auf das Stundenkontingent anrechnen“ (nach analyseTexts.js).
Generator: P/server/services/termine/einbauanleitung.js, rein und deterministisch (now Parameter), liest ladeEinstellung(featureId) (kontext.js:75, kein Modus-/Zustandscheck), nie feedToken (Feldweißliste + Test). Ausgabe {stand{version,updatedAt}, warnungen[], abschnitte[{id,titel,text,kopierbar}], text} mit: Modell, Leistungen, Ressourcen + Zeiten, Regeln, Gastfelder, Benachrichtigungen, Einbau (Snippet mit siteKey; einbauSnippet aus termine.js:185-196 hierher verschoben und um knopf erweitert; CSS-Variablen --wkt-akzent aus farbe, Kontrastfarbe per WCAG-Helfer; Ausweichtext; Impressum/Datenschutz-Links), Datenschutz-Absatz (Sie/Du nach anrede), Abnahme-Checkliste (bedingt: Verbraucher → Widerruf, Knopf → dauerhafter Widerrufsknopf), Abrechnung („Einrichtung bezahlt (149 €, Rechnung …), Zeit im Arbeitsblatt, nicht auf Abo-Stunden“). Die Quelle des Datenschutz-Absatzes wandert aus dem Markdown als datenschutzAbsatz(einstellung, anrede) nach rechtstexte.js (Test pinnt beide, wie bisher).
- Kurzfassung in
ticket.details(Klartext, WOMS zeigtpre-wrap,TicketCard.jsx:124-127): Kopf (Funktion, Projekt, Domain, Kunde, siteKey, „Konfigurationsstand: Version N, Datum“), Modus/Stelle/Gestaltungswunsch, Zählungen + erste Namen mit Kürzung, Snippet, 5–6 Schritte, VerweiswomsBaseUrl/customers/<id>?tab=funktionen. Ohne Datenschutz-Absatz (4,8 k Zeichen je Variante). - Route:
GET /api/team/funktionen/:customerId/termine/anleitung?projectId=infunktionenAdmin.js(requireStaffJwt,assertOwnProject,getFeatureDoc; kein TERMINE-Modus-Gate, kein viewLog, nie aus Kundenseite erreichbar). Vergleich mit dem Ticket-Stand liefert Warnung „Einstellungen seit der Bestellung geändert (Ticket Version N, jetzt M)“. Fehlen Einstellungen: 200 mit Warnung und nur Snippet. - Datenschutz-Absatz-Sperre: Solange die Gästetexte als ENTWURF gekennzeichnet sind, druckt die Anleitung das Banner mit; mit der Freigabe (Abschnitt 7) fällt es weg.
WOMS (W/src/components/customer/CustomerFunktionen.jsx, W/src/lib/funktionenView.js, teamApi.js):
- Je Termine-Zeile Knopf „Anleitung“, klappt ein Panel unter der Zeile auf (Stand, Ticketlink, Warnungen, „Alles kopieren“, Teilkopien für Snippet/CSS/Datenschutz Sie/Du); Zeilen werden zu Fragmenten mit Key. Neu
teamApi.getTermineAnleitung. aktionen(state, key): für Termine kein „Aktivieren (ohne Zahlung)“ inordered/setup; inoff/disabledbleibt es. Neu insetup: „Freischalten (Abnahme)“ (aktiviert wie Ticket-Schließen, schickt K16, setzt Ticket nicht zurück) und bei „Ticket fehlt“ „Ticket nachlegen“ (idempotent). TestsfunktionenView.test.mjs:48-55anpassen.
Freischaltung (Festlegung): activateClosedTickets filtert Query.equal('featureKey', [Schlüssel mit Ticket-Freischaltung]) (heute fest news, siteFeatures.js:360). Für Termine verlangt sie vorhandene Einstellungen und ein laufendes Abo, sonst bleibt setup (Log + T16-Hinweis). Der Öffentlich-API-Zugriff gibt es nur bei active (kontext.js:106); eine echte Probebuchung vor dem Livegang ist damit nicht möglich. Deshalb der Ablauf: Einbau auf der Kundenseite (Widget bleibt unsichtbar, da aktiv:false) → „Freischalten (Abnahme)“ → Probebuchung nach Checkliste und Stornierung → Ticket „Erledigt“. Reines Schließen des Tickets aktiviert weiterhin automatisch (Fallback, Idempotenz via Lock feature-activate:<id>). Ein Kundenfreigabe-Schritt entfällt (Festlegung, wie News).
6. Abo-Kopplung
Gemeinsame Prüfung (Festlegung): P/server/services/aboStatus.js zieht hasWartungsAbo aus analyse.js:43-46 heraus; liest nur den Appwrite-Spiegel subscriptions (getCurrentSubscription, stripeBilling.js:178), ruft Stripe nicht auf. Zwei Prädikate:
laeuft(customerId)=productKey||'wartung'(wieabo.js:68; die Analyse vergleicht heute strikt=== 'wartung', Annahme: Altbestand ohneproductKeyexistiert, Produktion nicht geprüft) und Status ∈ACTIVE_STATUSES(active, trialing, past_due). Dient dem Weiterlaufen und dem Abgleich. Lesefehler werfen (nie abschalten bei Fehler).bestellbar(customerId)= Statusactiveund keine Kündigung vorgemerkt (cancelAtPeriodEnd/cancelAt, Muster Zusatzspeicherabo.js~92-95). Dient Kachel, Checkout undmarkPaid. Verhindert 149 € in der letzten Woche eines gekündigten Abos oder in der Widerrufs-Probezeit. Meldungstexte je Funktion (kein_abo(funktion)statt „Website-Analyse“);analyseEligibilityändert sein Verhalten nicht. Pro Lauf Memo jecustomerId(Feature-Docs sind je Projekt, das Abo je Kunde).
Schema (vor dem Deploy ausführen, sonst Unknown attribute): portalFeatures.disabledReason (abo_ende|team|kunde_geloescht|frist), aboEndeMailAt (datetime), in scripts/setup-abo-collections.mjs (test/support/schema.js liest sie). disable(reason) wird idempotent: ein bereits disabled Dokument behält disabledAt (heute überschreibt es siteFeatures.js:304-312 und würde die 30-Tage-Löschung endlos verschieben). activate() löscht zusätzlich disabledReason/aboEndeMailAt (R36 löscht disabledAt schon).
Täglicher Abgleich gleicheAboAb in P/server/services/termine/aboAbgleich.js, im taeglicherJob (jobs.js:420-429) vor loescheAbgeschaltete, Ergebnis nur als Zählung im Tagesbericht (aboAbgleich{geprueft, abgeschaltet, wiederEin, mails, fehler}), Listen mit listAllDocuments.
active-Termine gruppiert je Kunde; Kunde ohnelaeuft→ unterwithLocks('feature-admin:<projectId>')neu lesen, noch aktiv und noch kein Abo →disable('abo_ende', 'Abgeschaltet: Wartungs-Abo beendet'). Ausgenommen:unlockedByPurchase !== true(Pilot/Kulanz, siehe Abschnitt 1). Lesefehler → Kunde überspringen. 1b.ordered/setup-Termine (bezahlt oder in Zahlung) eines Kunden ohnelaeuft→ unter derselben Sperre neu lesen →disable('abo_ende_vor_einrichtung'), T19 an das Team (Erstattung nach D3, 4.6), K22 Variante „vor Einrichtung beendet“. Eine offene Stripe-Sitzung (ordered) wird nicht storniert; kommt die Zahlung danach noch an, meldetonFeaturePaidden Fall ebenfalls mit T19 statt ein Ticket anzulegen.- Mail-Nachlauf:
disabled+abo_ende+ keinaboEndeMailAt→ K22 (Enddatum, Löschdatum =disabledAt+ 30 Tage, was bleibt möglich: ansehen, CSV-Export, Gäste dürfen absagen; Betrieb soll seine Gäste informieren) und erst nach erfolgreichem VersandaboEndeMailAtsetzen (Analyse sendet K19 nur einmal ohne Nachlauf, das wäre hier der Verlust der Löschmitteilung). - Wiederkehr:
disabled+abo_ende+laeuft+termineConfignoch vorhanden →activate()(Daten und Einstellungen bleiben), K23. Nach der Löschung nie automatisch (Konfig fehlt).
Sofortpfade: Widerruf eines Wechsels in der Frist (abo.js:1051-1058, beendet heute schon die Analyse) → Termine ebenfalls sofort, abo_ende, K22. Kundenlöschung deaktiviert Termine bereits (customerAdmin.js:285-292).
Löschung nach 30 Tagen: unverändert (loescheAbgeschaltete, nochAbgeschaltet). Neu: vor jedem unumkehrbaren Schritt bei abo_ende frische Abo-Prüfung (bricht ab, wenn das Abo wieder läuft). Nach der Löschung setzt der Job das Feature-Doc auf off (disabledAt leer, unlockedByPurchase=false, ticketId leer), damit es nicht täglich neu gescannt wird (final-deferred.md:25) und neu bestellt werden kann (heute blockt routes/funktionen.js:160 jedes disabled); der siteKey bleibt, das eingebettete Snippet bleibt gültig. Test termineJobs.test.js ~771 („Funktionsdokument bleibt“) ändert sich. Die Bestellung nach Löschung kostet 149 € erneut (D2).
Modus: Der Abgleich läuft im taeglicherJob und steht bei TERMINE_ENABLED=off still (jobs.js:93); im Pilot läuft er für alle Termine-Docs, nicht nur Pilot-Adressen. Festlegung: Abgleich und Löschung laufen auch bei off, solange es Dokumente mit unlockedByPurchase=true gibt (Aufteilung des Guards in fuehreAus), denn die Datenschutz-Zusage „30 Tage danach“ darf nicht an einem Schalter hängen. Der Abgleich muss vor on live sein (der Satz in 6c ist sonst falsch).
Rückkehr (D2): Innerhalb der 30 Tage schaltet der Abgleich automatisch und ohne Gebühr wieder ein (Daten und Einstellungen bleiben). Nach der Löschung braucht es eine neue Einrichtung: neuer Fragebogen und neue 149 €. Dieser Satz steht in LB, AVV, 6c und K22.
Mehr: K02/K03 (Kündigungsbestätigungen) bekommen einen Block „Analyse und Terminplaner enden mit dem Abo“. Banner/Meldung in public/termine.js:65-67 und routes/termine.js:59 unterscheiden nach disabledReason und zeigen das Löschdatum; featureView liefert disabledReason/disabledAt, WOMS zeigt „Abgeschaltet (Abo beendet), Löschung am …“. Erinnerungen an Gäste (G05) laufen weiter, wie R34 entschieden (unverändert; K22 weist darauf hin).
7. Rechtstexte und Rechtsrunde
7.1 Zu erstellen (Quellen Markdown in P/docs/rechtliches/, HTML + PDF in P/public/rechtliches/)
- Leistungsbeschreibung Terminplaner (Geschwister der LB Website-Analyse): Geltung/Rangfolge (Zusatz zum Wartungs-Abo, Hosting reicht nicht), Leistung, Nicht enthalten (SMS, Online-Zahlung, Kalender-Sync, Gesundheitsbereich), Voraussetzungen und Pflichten des Betriebs (Impressum/Datenschutz/AGB, Telefon-Ausweichlösung, Texte unverändert lassen, Beschäftigte informieren), Verfügbarkeit, Haftung für verpasste Termine, Einrichtung 149 € (Leistung, Ablauf per Ticket, Dauer, Preis nach § 19 UStG, nicht auf Abo-Stunden), Ende mit dem Abo (Abschalten spätestens am Folgetag, Mail, Export, Löschung nach 30 Tagen, auch bei Wechsel auf Hosting und Widerruf), Einstellungsrecht, keine Rechtsberatung/Mustertexte, BFSG-Satz.
- AVV Terminplaner (Geschwister des AVV Analyse, kein Zusatz dazu, damit Analyse-Kunden nicht neu zustimmen müssen): eigene Nr. 1–3 (Gast-Name, E-Mail, optional Telefon/Nachricht/Zusatzfelder, Termin, Hashes, Mailstatus; Betroffene Gäste und Beschäftigte; Art. 9 ausgeschlossen), Nr. 7 Hetzner Falkenstein und Nürnberg (Buchungsmails über Webhosting-SMTP), Nr. 8 Betroffenenrechte (Anonymisieren, CSV), Nr. 9 Löschfristen genau wie gebaut (Abgeschaltet 30 Tage, 48 h/30 Tage/
aufbewahrungTage, Outbox 7 Tage/48 h, Backups bis 7 Tage), TOM-Anlage; Abschluss per Haken + Staff nur mit Textform. - Datenschutz 6c aus
termine-datenschutz-6c-entwurf.mdals<h2 id="termine">inP/public/datenschutz.html, die 8 offenen Punkte klären; §6 (Kundenansicht: Staff darf Termine lesen und ändern, Protokoll) und §7 (Nachweise bei Funktionsbestellung, Entwurfsaufbewahrung) ergänzen;docs/WEBklar-Datenschutzerklaerung.pdfneu bauen; WächtertesttermineRechtstexte.test.js:267-274entfernen,legal_datenschutz_kundenansichtanpassen. - Gästetexte final: ENTWURF-Köpfe in
termine-hinweise.md,-widerrufsbelehrung-muster.md,-datenschutz-absatz.mdentfernen,rechtstexte.jsund Markdown gemeinsam ändern (Tests pinnen wörtlich),RECHTSTEXTE_VERSIONanheben, Archiv je Fassung (Nachweis speichert nur Hashes,ablauf.js:279-290). - Bestelltexte (Abschnitt 4.3) und Zustimmungsfassungen im Register; Widerrufsformular-Beschriftung; K15-Anhänge.
- WOMS „Rechtliches“: Karten
leistungsbeschreibung-terminplaner,avv-terminplaner(und ggf. Datenschutz) inW/scripts/setup-legal.mjsDOCUMENTS; Upload der Versionen macht der Inhaber (Löschen nicht möglich). - Technik dazu:
analyseLegal.jszu kleiner Dokument-Registry verallgemeinern (bzw. paralleles Modul), eigene Fassungskonstanteconfig.termine.avvVersionunabhängig vonconfig.analyse.avvVersion,build-legal-pdfs.shum Modustermine; Mails und Zustimmungstexte verlinken nur versionierte Adressen.
7.2 Prüfung durch eine Kanzlei (D5)
Das Kernpaket prüft eine Kanzlei, bevor der erste fremde Betrieb startet: Widerrufsbelehrung und Muster-Formular der
149-€-Bestellung, Zustimmungssatz (vorzeitiger Beginn, Erlöschen, Wertersatz), AVV Terminplaner, Haftungs- und
Ende-mit-dem-Abo-Klauseln der Leistungsbeschreibung, sowie die Gästetexte mit Gesetzlichkeitsfiktion (Widerrufsbelehrung
Gäste, Hinweise im Widget). Alles andere (6c, Portal-Hinweise, Mailtexte) prüft die Hausrunde wie bei der Analyse.
Lieferung von uns: Kanzlei-Paket P/docs/rechtliches/termine-kanzlei-paket.md mit den Texten in Endfassung, dem
Produktablauf in Kurzform, den konkreten Fragen (Liste 7.3) und den Fundstellen im Code; der Inhaber beauftragt die Kanzlei.
Befund und Änderungen fließen vor Livegang-Schritt 9 ein.
7.3 Rechtliche Punkte, vor on zu entscheiden oder zu erledigen (Fragen ans Kanzlei-Paket markiert mit ⚖)
- ⚖ Stand der Widerrufsbelehrung für Funktionen: LB News §6 hat keine gesetzliche Musterbelehrung, K15 keine Belehrung/Anhänge. Meine Lesart (kein Rechtsurteil): die Frist läuft für Verbraucher womöglich nicht an (§ 356 Abs. 3 BGB). Für News-Altkunden entscheidet der Befund (D7): Nur wenn die Frist danach nicht lief, wird die Belehrung per Mail nachgeholt; sonst gelten die neuen Texte nur künftig.
- Zitat „§ 356 Abs. 4“ → „Abs. 5 Nr. 2“ in
config.js:192,196,funktionen-leistungsbeschreibung.html:51,73,funktionen-news-design.md:73und TestlegalTexts.test.js:485; fehlende Wertersatz-Hälfte in der News-Zustimmung; „inklusive Mehrwertsteuer“ (funktionen.js:272, LB:49,:60) widerspricht § 19 UStG. Gleiche Runde (die Spec schiebt die Korrektur ausdrücklich nach TP2). - Wer fügt den Datenschutz-Absatz auf der Kundenseite ein und ist das im Preis (Analyse LB Nr. 4 Abs. 4 sagt: WEBklar, nicht auf Stunden). Eine Regel für beide LB.
- Portalseitige Rechtsaussagen:
WIDERRUF_HINWEIS,FREIZEIT_HINWEIS(Massage/Weiterbildung „in der Regel keine Freizeitleistung“ trägt die Recherche nicht,termine-tab-einstellungen.js:16-23),stornoMuster, Gesundheitshinweise; Storno-Vorgabe 50 %/24 h gilt auch für kostenlose Leistungen (Festlegung: bei Leistungen ohne Preis keine Storno-Vorgabe und kein Pflichthaken, Text als Muster kennzeichnen). - Anfrage mit Festpreis und der Bestätigungslink als „Bestellung“ (G01-Knopf, Seitenknopf „Zahlungspflichtig bestätigen“), Einstufung Massage/Fotograf/Coaching (Spec Offene Punkte 2).
- Pflicht von Datenschutz-/Impressum-Link (hier als Bestell-Gate gesetzt) und der „jederzeit widerrufen“-Satz unter dem Erinnerungshaken.
- Abo-Ende-Texte (LB, AVV, 6c, K22) und Aussagen „Drittland“ (Kalender-Abos bei Google/Apple), „Falkenstein und Nürnberg“, Anker für „30 Tage“; BFSG-Satz nur so weit getestet (kein axe-/Screenreader-Lauf in TP1).
- Staff-Aktivierung (
adminActivate,siteFeatures.js:351-356) prüft heute weder Einwilligung noch AVV. Festlegung: ab ModusonverlangtadminActivatefür Termine einen Textform-Nachweis wie die Analyse (analyse.js~262-270, WOMS-Chatvorlage nachanalyseView.js:55-90), ausgenommen Pilot-Adressen. - Owner-Prüfung bei Hetzner (nur im Konto prüfbar): AV-Vertrag für Cloud und Webhosting, Anlage 1 mit „Terminbucher/Beschäftigte/Buchungsdaten“, Kontoadresse = Impressum, Backups an.
7.4 Später
Abo-Bedingungen v6 bzw. Rahmen-AVV mit Anlage je Funktion; TP3-Themen (Stripe-Absatz, ZAG/Connect, Vorauszahlung in AGB, PAngV); Geschäftsbrief-Fußzeilen § 37a HGB; Rechtsmissbrauch bei spätem Widerruf; Minderjährige; Mitgliedschaften; wiederholtes Versenden von G08/K21 nach Fristende; Widerruf nur vor Terminbeginn im Verwaltungslink.
8. Technische Voraussetzungen aus TP1
Skalen-Schätzungen (Annahmen: 6 Buchungen je Betrieb und Tag, 12 Zellen je Buchung, 20 Tage Vorlauf, Spitzenfaktor 6; echte Werte können um Faktor 2–3 abweichen). Heute reicht alles für 1 Betrieb; die Listen laufen bei 40–60 Betrieben (bei Aufbewahrung 365 Tage schon bei ~10) in list_limit_exceeded, der nur geloggt wird.
MUSS vor TERMINE_ENABLED=on (ein fremder Betrieb im Pilot braucht #1, #3, #5, #6):
| # | Maßnahme | Ort |
|---|---|---|
| 1 | Aufbewahrungs-Löschung begrenzt und seitenweise: je Feature Query.limit(100) + Index (status, endsAt), Deckel je Lauf (z. B. 2000 je Feature), Rest läuft an Folgetagen; heute listet loeschKandidaten alle (Deckel 20 000, buchungen.js:191), Überlauf = nie Löschung von Gastdaten, nur Fehlerlog. Gleiches Muster für abgesagt/abgelehnt/verfallen, loescheAbgeschaltete und export.csv |
jobs.js:296-313,350-353, routes/termine.js:450-453 |
| 2 | Zellen-Sweep seitenweise über idx_bis (lessThan('bis', jetzt-1d), Limit 100, Deckel 5000/Lauf) statt alleZellen() (Deckel 100 000); Map.get().push() statt quadratischem [...old, zelle] (zellen.js:247); Zeitfenster-Prüfung in ungueltigeZellen |
zellen.js:46,224-270,92-99 |
| 3 | Kundenansicht-Wildcard POST /api/termine/.+ (kundenansichtGuard.js:19) durch die vier heutigen Routen ersetzen (vorlage, feed, buchungen, aktion) + Test „unbekanntes POST = 403“; neue TP2-Routen bleiben unter /api/funktionen |
kundenansichtGuard.js |
| 4 | Mail-Fairness: Kontingent je Feature (z. B. 40/h unter globalen 200/h), Vorrang G01/G02/G08 vor G05/K20/K21, veraltete G01 (älter als Haltezeit + 1 min) verwerfen statt toter Link, Empfänger-Kappe über Betriebe (z. B. 3/h, 10/Tag je Adresse für G01 und Freitext-G04/G06), Frühausstieg in verarbeite bei Pause/leerem Budget und begrenzte Liste (orderAsc('naechsterVersuch'), limit, select; Query-Helfer und appwriteMock.js:145 um select/isNull/notEqual erweitern); HALTUNGEN_JE_SITE 60 → ~25/h |
outbox.js, limits.js, appwriteAdmin.js |
| 5 | Sichtbarkeit: Warnlog stündlich ab 70 % Budget, bei ältestem wartet > 10 min, bei SMTP-Pause (pausiertBis() ist ungenutzt), bei Tagesjob-Fehlern; GET /api/team/termine/status (Budget, Queue, Alter, Pause, letzter Tagesjob) und ein Staff-Mail je Vorfall |
jobs.js, neuer Team-Route |
| 6 | disable() idempotent (Abschnitt 6) und activateClosedTickets verallgemeinert |
siteFeatures.js |
| 7 | Messprüfungen mit Freigabe: Seiten-Listing > 500 Dokumente auf der Live-Appwrite 1.8.1 (bisher nur Mock/Spike), tatsächliche Hetzner-Antwort bei > 500 Mails/h, tatsächliche Größe workorders.details |
siehe Abschnitt 9 |
SOLLTE (TP2 oder kurz danach): minutenJob in zwei unabhängig geschützte Jobs (Verfall, Mailversand) teilen; Transport pool:true, Parallelität der einreihen-Sendungen begrenzen (mailer.js:25); Budget nach Neustart aus der Outbox rekonstruieren; /tage und /zeiten außerhalb des Horizonts vor imZeitraum kurzschließen (publicTermine.js:450,471); Index termineOutbox(bookingId); Hetzner-Überlimit-Antworten als Pause statt dauerhaft fehler einstufen (outbox.js:82-88); mailSicher schreibt bei Fehlschlag einen fehler-Marker, damit das Portal „nicht zugestellt“ zeigt; Tagesjob zu fester Nachtzeit (heute 180 s nach jedem Start, index.js:220); optional TERMINE_TOKEN_SECRET mit Fallback SESSION_SECRET; engines >=20; axe-/Screenreader-Lauf von Widget und Gästeseite; CSV-Export und loescheAbgeschaltete seitenweise für sehr große Einzelbetriebe. Kann warten: Lock-Wartezeiten, halte ohne termine-cfg-Lock, Fetch-Timeouts, Widget-Monolith.
Mailweg (D8): Terminplaner-Mails gehen weiter über den Hetzner-SMTP, mit Kontingent je Betrieb und Alarm (Punkt #4/#5). Ab 25 aktiven Terminplaner-Betrieben (Zählung im Tagesbericht; Warnlog und Team-Mail ab 20) wird der Umstieg auf einen EU-Versanddienst geplant (eigenes kleines Vorhaben: Anbieterwahl, AVV/6c-Nachtrag, DKIM/SPF).
Betriebsannahme: Ein Portal-Prozess (Locks, Limiter, Mailbudget liegen im Speicher; Zellen bleiben durch atomares Bulk-Create sicher). Jeder zweite Prozess bricht das; im Livegang-Plan festhalten.
9. Livegang TP2 (je Schritt Freigabe des Inhabers)
- Rechtsrunde: Texte (Abschnitt 7.1) und Kanzlei-Paket (7.2) fertigstellen; der Inhaber beauftragt die Kanzlei; Hausrunde für den Rest. Kein Push auf
maindurch Claude, das macht der Inhaber. - Code in Worktrees (Portal, WOMS), TDD je Block: Register + Checkout, Fragebogen, Anleitung/WOMS, Abo-Kopplung, TP1-Schuld (Abschnitt 8). Test-Pins aus den Berichten mit ändern (
funktionenRoutes,termineRoutes,termineSetup,termineView,kundenansichtGuard,legalTexts,mailTemplates,termineJobs). - Schema in Produktions-Appwrite (Schreibzugriff, Freigabe):
scripts/termine-setup.mjs --check, dann anwenden (termineConfig.fragebogen, IndextermineBookings(status,endsAt),termineOutbox(bookingId));setup-abo-collections.mjs(portalFeatures.disabledReason,aboEndeMailAt, neue CollectionfeatureConsents). Vor dem Code-Deploy (Indexaufbau ist asynchron, Abfragen funktionieren auch ohne). - Stripe (Aufruf, Freigabe):
stripe-products.mjs --pruefen, dann--anlegenfürtermineSetup; Inhaber prüft im Dashboard, dass der Live-Webhookasync_payment_succeeded/failedundexpiredabonniert hat (Annahme: ja, News läuft seit 2026-09-30). - Messprüfungen (Freigabe):
details-Größe, Seitenlisting > 500, Hetzner-Mailantwort (Abschnitt 8 #7); Hetzner-AV-Prüfung durch den Inhaber (7.2). - Deploy Portal + WOMS (Inhaber pusht): Modus bleibt
pilotmit leeremTERMINE_PILOT_EMAILSplus Pilotkunde „test“; Abo-Abgleich läuft zunächst nur beobachtend (Zählung im Tagesbericht, Pilotkunde ausgenommen). - Rechtstexte live: PDFs bauen, HTML/PDF unter
/rechtliches, 6c + PDF-Datenschutz, WOMS-Karten (Skript mit API-Key, Freigabe) und Uploads durch den Inhaber, Gästetexte final (RECHTSTEXTE_VERSION). - Ende-zu-Ende-Test mit einem Testkunden und echtem Wartungs-Abo: Fragebogen → Bestellung (echte 149 €, Rückerstattung durch den Inhaber danach; Annahme, es gibt kein Test-Konto für diesen Preis) → Ticket + Anleitung → „Freischalten (Abnahme)“ → Probebuchung → Abo-Ende-Simulation (Testkunde) → K22 → Rückkehr K23 → Löschlauf auf Wegwerfdaten.
- Erster fremder Kunde im Pilot (erst nach dem Kanzleibefund, D5): nur seine Adresse in
TERMINE_PILOT_EMAILS; Beobachtung Tagesbericht, Mailbudget, Status-Route (Freigabe der Adresse). TERMINE_ENABLED=on(Compose-Änderung, nurkundenbereich-serverneu erstellen) als letzter Schritt; danach 1 Woche Beobachtung, News-Altfälle (D7) und Entwurfs-Löschjob prüfen.
9b. Umsetzung in einem Plan, sechs Blöcke
A Technik-Voraussetzungen aus TP1 (Abschnitt 8, MUSS-Liste) · B Register + Checkout + Zustimmungsnachweis (4) · C Fragebogen (3) · D Ticket, Einbauanleitung, WOMS (5) · E Abo-Kopplung (6) · F Rechtstexte + Kanzlei-Paket + News-Korrektur (7). Reihenfolge A → B → E → C → D → F (F kann parallel zu C/D laufen, da überwiegend Text). Jeder Block endet mit grünen Tests; der Livegang (Abschnitt 9) beginnt erst nach F.
10. Entscheidungen des Inhabers (2026-10-03)
| # | Frage | Entscheidung |
|---|---|---|
| D1 | Verbraucher oder Unternehmer bei der 149-€-Bestellung | Alle wie Verbraucher: Haken „Beginn vor Fristablauf, Erlöschen, Wertersatz“, volle Belehrung + Muster-Formular für alle (4.3) |
| D2 | Abo endet und kommt wieder | Innerhalb von 30 Tagen automatisch und gratis wieder an; nach der Löschung neuer Fragebogen und neue 149 € (Abschnitt 6) |
| D3 | Erstattung der 149 € | Gesetzlicher Widerruf (mit Wertersatz für begonnenen Einbau) und volle Erstattung, wenn das Abo vor der Fertigstellung endet (4.6) |
| D4 | Umfang der 149 € | Je Website-Projekt ein Widget an einer Stelle, Gestaltung nach Fragebogen, Einbau; Mehrwünsche nach Stunden (Abschnitt 1) |
| D5 | Rechtsprüfung | Kanzlei für das Kernpaket, Rest Hausrunde; erster fremder Betrieb erst nach dem Befund (7.2) |
| D6 | Gesundheits-Ausschluss | Branchenfrage + Haken + Stichwort-Warnung ans Team; Team kann ablehnen (3.5) |
| D7 | News-Altkunden nach Textkorrektur | Nach dem Kanzleibefund entscheiden; Belehrung nur nachholen, wenn die Frist nicht lief (7.3) |
| D8 | Mailweg bei mehr Betrieben | Hetzner-SMTP mit Kontingent und Alarm, Umstieg auf EU-Versanddienst ab 25 aktiven Betrieben (Abschnitt 8) |
11. TP3 (Ausblick, eigener Plan nach TP2)
Online-Zahlung der Gäste über Stripe Connect (Standard-ähnliches Konto, direkte Zahlungen, Hosted Checkout, eigener Connect-Webhook, Erstattungen nach Regel des Betriebs; PayPal nicht möglich, SEPA ausgeschlossen; Haltezeit ≥ 32 min). Voraussetzung: Connect-Plattform im Stripe-Live-Modus eingerichtet (bisher nur Sandbox, Konto acct_1UM7AVBONHfkVuDM).