Pläne: Hinweise aus dem Review von Plan 1 (Livegang-Prüfung, Rechnungszeilen, lookup_keys)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-27 15:47:02 +00:00
1 parent 47c1af7015
commit f016ad0326
2 files changed
+7

No files matched your search

@@ -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'`.
@@ -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