From f016ad0326b8124b314bf33b30529dd0b44c00d3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 27 Sep 2026 15:47:02 +0000 Subject: [PATCH] =?UTF-8?q?Pl=C3=A4ne:=20Hinweise=20aus=20dem=20Review=20v?= =?UTF-8?q?on=20Plan=201=20(Livegang-Pr=C3=BCfung,=20Rechnungszeilen,=20lo?= =?UTF-8?q?okup=5Fkeys)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../plans/2026-09-27-vertraege-plan-1-vorarbeiten.md | 3 +++ .../superpowers/plans/2026-09-27-vertraege-plan-2-sichtbar.md | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/docs/superpowers/plans/2026-09-27-vertraege-plan-1-vorarbeiten.md b/docs/superpowers/plans/2026-09-27-vertraege-plan-1-vorarbeiten.md index 2f24117..16ebcde 100644 --- a/docs/superpowers/plans/2026-09-27-vertraege-plan-1-vorarbeiten.md +++ b/docs/superpowers/plans/2026-09-27-vertraege-plan-1-vorarbeiten.md @@ -562,6 +562,8 @@ docker exec -i -w /app webklar-kundenbereich-server node --input-type=module - < Expected: Die letzte Zeile lautet `Fertig.`, und für `productKey` gibt es eine neue Zeile. Alles andere ist mit „existiert“ markiert. +Dieser Schritt bleibt Pflicht, auch mit dem Schutz aus dem Review. Ohne das Feld spiegelt der Server nur ohne `productKey` und schreibt einen Fehler ins Log. Das betrifft auch den öffentlichen Kündigungs- und Widerrufsknopf. + - [ ] **Step 4: Push nach main, nur mit Freigabe des Nutzers.** Der Push löst den Deploy aus. ```bash @@ -574,6 +576,7 @@ Mit dabei sind die Konzept- und Plan-Dokumente. Sie sind harmlos, weil sie in `d - `git -C /home/webklar/apps/kundenbereich.webklar.com/build log --oneline -1` zeigt den gepushten Commit. - Der Container ist neu gestartet, und in `docker logs --since 5m webklar-kundenbereich-server` steht kein `[abo]`- und kein `[stripe]`-Fehler. - `curl -s -o /dev/null -w '%{http_code}' https://project.webklar.com/api/abo` liefert `401`. + - `docker logs --since 1h webklar-kundenbereich-server 2>&1 | grep -E 'kein bekannter Abo-Preis|Appwrite kennt das Feld'` bleibt leer. Jeder Treffer ist ein Abo ohne bekannten Preis oder ein fehlendes Feld und wird vor der nächsten Abbuchung geklärt. - [ ] **Step 6: Spiegel live bestätigen** (Lesen in Produktion nur mit Freigabe des Nutzers) - Der Nutzer sendet im Stripe-Dashboard ein `customer.subscription.updated` seines Testkonto-Abos erneut oder wartet die nächste Abbuchung ab. - Danach hat das Dokument in `subscriptions` `productKey: 'wartung'`. diff --git a/docs/superpowers/plans/2026-09-27-vertraege-plan-2-sichtbar.md b/docs/superpowers/plans/2026-09-27-vertraege-plan-2-sichtbar.md index 47790f0..2322239 100644 --- a/docs/superpowers/plans/2026-09-27-vertraege-plan-2-sichtbar.md +++ b/docs/superpowers/plans/2026-09-27-vertraege-plan-2-sichtbar.md @@ -62,6 +62,7 @@ - `contractEvents.type` ist `abschluss`, `wechsel`, `speicher`, `kuendigung` oder `widerruf`. `status` ist `offen`, `erledigt` oder `widerrufen`. - Stripe-Metadaten am Abo: `webklarCustomerId`, `email`, `productKey`, `termStartAt`, `termsVersion`, `termsAcceptedAt`, `earlyStartConsentAt`, `consentIp`, `consentUserAgent`, `switchedFrom`, `switchedAt`. - Testrabatte: `WEBKLAR-TEST-050` (Wartung, amount_off 4950, besteht schon) und `WEBKLAR-TEST-050-HOSTING` (Hosting, amount_off 1450). +- lookup_keys werden in Stripe nie übertragen (`transfer_lookup_key`). Ein neuer Preis bekommt einen neuen lookup_key im Katalog, denn Zuordnung und Stundenregel hängen am lookup_key (Plan 1). **Deploy und Produktion** @@ -629,6 +630,9 @@ test('Widerrufsfristen aus den Ereignissen', async () => { - Es entsteht das Ereignis `speicher`: Status `offen`, `dueAt` = jetzt (Plus) bzw. `currentPeriodEnd` (Minus), `details: { fromUnits, toUnits }`. - Danach gehen K13 an den Kunden (beim Plus mit PDF) und T13 ans Team. - **`recordPaidStorage(freshInvoice)`:** Hat die Rechnung eine reguläre Zusatzspeicher-Zeile (nicht anteilig), setzt die Funktion `storagePaidUnits` auf deren `quantity` und `storagePaidPeriodEnd` auf `line.period.end`. + - **Alle Rechnungszeilen laden** (Hinweis aus dem Review von Plan 1): `invoices.retrieve` bettet höchstens 10 Zeilen ein. Anteilige Zeilen stehen vor den Abo-Zeilen, und mehrere Speicheränderungen in einem Monat können die Wartungs-Zeile hinausschieben. + - `grantCreditForInvoice` und `recordPaidStorage` laden deshalb bei `lines.has_more` alle Zeilen per `invoices.listLineItems(id, { limit: 100 })`. + - Test: Rechnung mit 11 Zeilen, die Wartungs-Zeile ist die elfte. Ergebnis: 60 Minuten. - **Routen:** - `POST /api/abo/storage/preview` mit dem Body `{ units }` - `POST /api/abo/storage` mit dem Body `{ units, acceptTerms, termsVersion }`, dabei ist `acceptTerms` beim Plus Pflicht