Finanzen: Spec und Plaene 1-3 (Bank, Stripe/Auswertung, WOMS/Livegang)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
30412cb0ea
commit
53b50011e8
4 files changed
+870
No files matched your search
@@ -0,0 +1,208 @@
|
||||
# Finanzen Plan 3: WOMS-Oberfläche und Livegang Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Die WOMS-Seite „Finanzen“ zeigt Stripe-Rechnungen und das Finom-Konto nach dem Seitenaufbau der Spec, Projektrechnungen werden in WOMS geschrieben, InvoiceNinja verschwindet aus der Oberfläche, und das Ganze geht kontrolliert live.
|
||||
|
||||
**Architecture:** WOMS spricht ausschließlich die Portal-Routen `/api/admin/finanzen/*` aus Plan 1 und 2 an (Appwrite-JWT, wiederverwendeter JWT-Cache wie Serverpuls). Reine Formatierungs- und Parse-Logik in `src/lib/finance*.js` (mit `node --test` geprüft), Darstellung in `src/components/finance/*` und drei Seiten.
|
||||
|
||||
**Tech Stack:** React 18 + Vite (WOMS), vorhandene UI-Bausteine `src/components/ui` (PageHeader, Card, Button, Badge, Tabs, EmptyState, Field, Kpi, AutocompleteInput), `styles/global.css`, `node --test` mit `tests/unit/load.mjs`.
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-10-01-finanzen-design.md` (im Portal-Repo, Worktree `kundenbereich.webklar.com/dev-finanzen`)
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- WOMS-Worktree: `/home/webklar/apps/ticket.webklar.com/dev-finanzen`, Branch `feature/finanzen` von `origin/test`, `node_modules` als Symlink auf `../build/node_modules`. `node_modules` ist in Git getrackt: **nur einzelne Dateien adden, nie `git add -A`**. **Nie pushen** ohne OK des Nutzers (der Webhook stellt jede gepushte Branch live).
|
||||
- Tests: `npm test` (`node --test tests/*.test.mjs tests/unit/*.test.mjs`) grün vor jedem Commit. Reine Logik gehört nach `src/lib/` (ohne Appwrite-Import), damit `loadModule` sie bündeln kann.
|
||||
- Build-Prüfung unter Speichergrenze: `systemd-run --scope -p MemoryMax=1500M -p MemorySwapMax=0 npm run build` im Worktree.
|
||||
- Portal-Basis-URL: `import.meta.env.VITE_PROJECT_ADMIN_URL || 'https://project.webklar.com'`; Auth `Authorization: Bearer <JWT>` über `createJwtCache` + `fetchWithRetry` aus `src/lib/serverpulsAuth.js`. Kein `PUT` (Portal-CORS erlaubt GET, POST, PATCH, DELETE).
|
||||
- Nur Admins (`isAdmin` aus `useAuth()`) sehen Menüpunkt und Seiten; Nicht-Admins werden auf `/tickets` umgeleitet.
|
||||
- Darstellung: Beträge de-DE mit `€`, rechtsbündig, Klasse mit `font-variant-numeric: tabular-nums`; Daten `TT.MM.JJJJ`; nie Farbe allein (immer Text oder Symbol dazu); keine Prozent-Veränderungen, keine Diagramme außer optionaler Treppenlinie (nicht in diesem Plan).
|
||||
- Texte für Nutzer auf Deutsch mit echten Umlauten.
|
||||
- InvoiceNinja-Server und `ticket-integrations` bleiben unangetastet.
|
||||
- Seitenblöcke, Aufgabenarten und Zuordnungsregeln exakt wie in der Spec.
|
||||
|
||||
## Review Focus
|
||||
|
||||
- **Rückkehrseite doppelt geladen** (React StrictMode, Neuladen, Zurück-Taste): der Code darf nur einmal an `/bank/rueckkehr` gehen; zweiter Versuch zeigt die Serverantwort verständlich statt „Fehler“. Test in Task 2 (`parseCallback` + Einmal-Schutz in der Seite).
|
||||
- **Betragseingabe** „1.234,50“, „50“, „50,5“, „ 12 “, „-5“, „abc“ → 123450, 5000, 5050, 1200, Fehler, Fehler. Test in Task 1.
|
||||
- **Portal nicht erreichbar oder 403** (Admin ohne 2FA-Sitzung): Seite zeigt eine Fehlerkarte mit Text und „Erneut versuchen“, kein weißer Bildschirm. Test in Task 7 (Smoke).
|
||||
- **Unumkehrbare Zuordnung:** Bestätigen einer Rechnungszuordnung verlangt einen zweiten Klick mit dem Hinweis „wird in Stripe als bezahlt markiert, nicht umkehrbar“. Test in Task 7 (Smoke).
|
||||
- **Kunde ohne E-Mail:** „Rechnung schreiben“ für diesen Kunden ist gesperrt mit Hinweis „E-Mail fehlt“ (Stripe verschickt per Mail). Test in Task 6 (`canInvoice`).
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Formatierung, Parser und API-Client
|
||||
|
||||
**Files:**
|
||||
- Create: `src/lib/financeFormat.js`
|
||||
- Create: `src/lib/financeApi.js`
|
||||
- Test: `tests/unit/financeFormat.test.mjs`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces (`financeFormat.js`, rein):
|
||||
- `euro(cents) → string` (`euro(123456) === '1.234,56 €'`, `euro(null) === '–'`)
|
||||
- `euroWhole(cents) → string` (`euroWhole(10893) === '109 €'`)
|
||||
- `signedEuro(cents) → string` (`'+168,79 €'`, `'−4,49 €'` mit U+2212)
|
||||
- `dateDe('2027-03-29') === '29.03.2027'`; `sinceLabel(iso, now) → 'gerade eben' | 'vor 12 Min.' | 'vor 3 Std.' | 'vor 2 Tagen'`
|
||||
- `parseEuro(text) → number` (Cent) oder wirft `Error('Ungültiger Betrag')`; erlaubt Tausenderpunkte und Dezimalkomma, keine negativen Werte
|
||||
- `INVOICE_STATUS = { entwurf: { label: 'Entwurf', tone: 'muted' }, offen: { label: 'Offen', tone: 'info' }, ueberfaellig: { label: 'Überfällig', tone: 'warn' }, bezahlt: { label: 'Bezahlt', tone: 'ok' }, storniert: { label: 'Storniert', tone: 'muted' }, uneinbringlich: { label: 'Uneinbringlich', tone: 'danger' } }`
|
||||
- `CATEGORY_LABEL = { rechnung: 'Rechnung', 'rechnung-extern': 'Einnahme ohne Stripe-Rechnung', 'stripe-auszahlung': 'Stripe-Auszahlung', fixkosten: 'Fixkosten', privat: 'Privat/Umbuchung', sonstiges: 'Sonstiges', '': 'Offen' }`
|
||||
- `URGENCY = { hoch: { label: 'Dringend', tone: 'danger' }, mittel: { label: 'Bald', tone: 'warn' }, info: { label: 'Hinweis', tone: 'muted' } }`
|
||||
- `AGE_GROUP_LABEL = { 'nicht-faellig': 'Nicht fällig', '1-30': '1–30 Tage', '31-60': '31–60 Tage', 'ueber-60': 'Über 60 Tage' }`
|
||||
- `pageOf(list, page, size = 50) → { items, page, pages }`
|
||||
- `canInvoice(customer) → { ok: boolean, reason?: string }` – `customerStatus` `lead`/`lost` → „Noch kein fester Kunde“; ohne `email` → „E-Mail fehlt“.
|
||||
- Produces (`financeApi.js`): `getOverview()`, `getInvoices({ status, kunde })`, `createInvoice(body)`, `sendInvoice(id)`, `voidInvoice(id)`, `deleteInvoice(id)`, `getTransactions()`, `assignTransaction(id, target)`, `unassignTransaction(id)`, `getBank()`, `refreshBank()`, `startBankAuth()`, `completeBankAuth({ code, state, error })`, `getSettings()`, `patchSettings(patch)`, `getFixedCosts()`, `createFixedCost(body)`, `updateFixedCost(id, patch)`, `deleteFixedCost(id)` – Pfade und Methoden wie in Plan 1/2; Fehler werfen `Error(data.error || 'API-Fehler <status>')` mit `.status`.
|
||||
|
||||
- [ ] **Step 1: Worktree anlegen**
|
||||
|
||||
```bash
|
||||
cd /home/webklar/apps/ticket.webklar.com && git -C build fetch -q origin && git -C build worktree add ../dev-finanzen -b feature/finanzen origin/test && ln -s ../build/node_modules dev-finanzen/node_modules
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Write the failing tests** in `tests/unit/financeFormat.test.mjs` mit allen Beispielwerten oben (inkl. Review-Focus-Fälle für `parseEuro` und `canInvoice`).
|
||||
- [ ] **Step 3: Run** `npm test` – Expected: FAIL (Modul fehlt).
|
||||
- [ ] **Step 4: Implement** beide Dateien (`financeApi.js` ohne Test, folgt dem Muster von `serverpulsApi.js`).
|
||||
- [ ] **Step 5: Run** `npm test` – Expected: PASS.
|
||||
- [ ] **Step 6: Commit** `git add src/lib/financeFormat.js src/lib/financeApi.js tests/unit/financeFormat.test.mjs && git commit -m "Finanzen: Formatierung, Betragsparser und Portal-API-Client"`
|
||||
|
||||
### Task 2: Zugriff nur für Admins, Routen, Rückkehrseite
|
||||
|
||||
**Files:**
|
||||
- Create: `src/components/AdminRoute.jsx`
|
||||
- Create: `src/lib/bankCallback.js`
|
||||
- Create: `src/pages/BankCallbackPage.jsx`
|
||||
- Modify: `src/App.jsx` (Routen `/finance`, `/finance/einstellungen`, `/finanzen/bank-callback`, alle in `ProtectedRoute` + `AdminRoute`)
|
||||
- Modify: `src/components/Navbar.jsx:64` (Eintrag „Finanzen“ nur bei `isAdmin`)
|
||||
- Test: `tests/unit/bankCallback.test.mjs`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces:
|
||||
- `AdminRoute({ children })` – wartet auf geladenen Nutzer, sonst `<Navigate to="/tickets" replace />` für Nicht-Admins.
|
||||
- `parseCallback(search) → { code, state, error } | null` (`null`, wenn weder `code` noch `error`); `error_description` wird an `error` angehängt („access_denied: …“).
|
||||
- `BankCallbackPage`: liest `location.search`, ruft `completeBankAuth` genau einmal (`useRef`-Schutz), zeigt „Bankzugang wird eingerichtet …“, dann Erfolg „Bankzugang erneuert, gültig bis TT.MM.JJJJ“ oder die Fehlermeldung des Servers, jeweils mit Knopf „Zu den Finanzen“ (`/finance`). Entfernt danach `code`/`state` aus der Adresszeile (`navigate('/finanzen/bank-callback', { replace: true, state: result })`).
|
||||
|
||||
- [ ] **Step 1: Write the failing tests:** `parseCallback('?state=a&code=b') → { code: 'b', state: 'a', error: '' }`; `'?state=a&error=access_denied&error_description=abgebrochen' → error 'access_denied: abgebrochen'`; `'' → null`.
|
||||
- [ ] **Step 2: Run** `npm test` – Expected: FAIL.
|
||||
- [ ] **Step 3: Implement** Dateien und Routen.
|
||||
- [ ] **Step 4: Run** `npm test` und den Build-Befehl aus Global Constraints – Expected: PASS, Build ohne Fehler.
|
||||
- [ ] **Step 5: Commit** `git commit -m "Finanzen: nur fuer Admins, Rueckkehrseite fuer die Bankfreigabe"`
|
||||
|
||||
### Task 3: Seitenkopf – Statuszeile, Liquidität, Zu erledigen
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/pages/FinancePage.jsx` (komplett neu, InvoiceNinja-Inhalt entfällt)
|
||||
- Create: `src/components/finance/StatusLine.jsx`, `LiquidityHead.jsx`, `TodoList.jsx`
|
||||
- Create: `src/hooks/useFinanceOverview.js` (`{ data, loading, error, reload }` über `getOverview`)
|
||||
- Modify: `src/styles/global.css` (Klassen `.fin-amount` mit `tabular-nums` und rechtsbündig, `.fin-todo`, `.fin-status`; Farben nur über vorhandene Variablen)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: Task 1, Übersicht-Format aus Plan 2 Task 8.
|
||||
- Produces:
|
||||
- `StatusLine({ stand, onRefresh, refreshing })` – Text nach Spec Block 0; „Aktualisieren“ ruft `refreshBank`, zeigt 429-Meldung des Servers als Hinweis.
|
||||
- `LiquidityHead({ kopf })` – drei `Kpi`: „Kontostand“ (`euroWhole`), „Verfügbar nach Rücklagen“ (`euroWhole`, Unterzeile „Aufschlüsselung“ klappt `kontostand − Steuer-Rücklage − Fixkosten 30 Tage − Mindestpuffer` als Liste auf), „Tiefster Stand in 30 Tagen“ (`sicher: … am TT.MM.` / `erwartet: … am TT.MM.`).
|
||||
- `TodoList({ aufgaben, onAction })` – höchstens 7 Zeilen, danach „Alle N anzeigen“; je Zeile Badge (`URGENCY`), Titel, Text, ein Knopf. `onAction(aktion)` wird in `FinancePage` behandelt: `bank-verbinden` → `startBankAuth()` und `window.location.assign(url)`; `bank-aktualisieren` → Refresh; `rechnung-oeffnen` → `hostedUrl` in neuem Tab bzw. Sprung zum Rechnungsblock; `zuordnen` → öffnet Zuordnen-Dialog (Task 5); `kunde-oeffnen` → `/customers/:id`; `fixkosten-uebernehmen` → Bestätigung, dann `createFixedCost({ name, amountCents, interval, nextDate: <lastDate + Rhythmus>, counterpartyMatch: key })`; `einstellungen` → `/finance/einstellungen`. Leerzustand: `EmptyState` „Nichts offen“.
|
||||
- `FinancePage`: Ladezustand, Fehlerkarte mit Meldung und „Erneut versuchen“, danach Blöcke 0–2.
|
||||
|
||||
- [ ] **Step 1: Implement** Komponenten, Hook, Seite (Blöcke 0–2).
|
||||
- [ ] **Step 2: Run** `npm test` und Build – Expected: PASS.
|
||||
- [ ] **Step 3: Commit** `git commit -m "Finanzen: Statuszeile, Liquiditaetskopf und Aufgabenliste"`
|
||||
|
||||
### Task 4: Vorschau, Steuer, Abos
|
||||
|
||||
**Files:**
|
||||
- Create: `src/components/finance/ForecastTable.jsx`, `TaxCard.jsx`, `AboCard.jsx`
|
||||
- Modify: `src/pages/FinancePage.jsx` (Blöcke 3–5; Desktop: Vorschau links, Steuer rechts; Abos halbe Breite)
|
||||
|
||||
**Interfaces:**
|
||||
- Produces:
|
||||
- `ForecastTable({ vorschau })` – Spalten Datum, Posten, „sicher“/„erwartet“ (Text, nicht nur Farbe), Betrag (`signedEuro`), Saldo sicher, Saldo erwartet (laufend ab Kontostand); darunter `vorschau.annahme`; leer → „Keine Zahlungen in den nächsten 30 Tagen bekannt.“
|
||||
- `TaxCard({ steuer })` – Rücklage (`euro`, „30 % von <Gewinn> Gewinn 2026“), Kleinunternehmer-Zeile „<Betrag> von 25.000 € (2026)“ und „Grenze 100.000 €“, Vorjahr nur wenn gesetzt, Gewerbesteuer-Hinweis nur wenn `gewerbesteuerHinweis`, nächste ESt-Vorauszahlung nur wenn gesetzt.
|
||||
- `AboCard({ abos })` – „Abo-Einnahmen pro Monat“, „zahlende Kunden“, „gefährdet“ (nur > 0), Liste Kunde · Produkt · Status · Betrag · nächste Abbuchung; leer → „Noch keine Abos.“
|
||||
|
||||
- [ ] **Step 1: Implement.**
|
||||
- [ ] **Step 2: Run** `npm test` und Build – Expected: PASS.
|
||||
- [ ] **Step 3: Commit** `git commit -m "Finanzen: Vorschau, Steuer und Abos"`
|
||||
|
||||
### Task 5: Rechnungen, Rechnung schreiben, Buchungen, Zuordnen
|
||||
|
||||
**Files:**
|
||||
- Create: `src/components/finance/InvoiceTable.jsx`, `InvoiceCreateModal.jsx`, `TransactionTable.jsx`, `AssignDialog.jsx`
|
||||
- Modify: `src/pages/FinancePage.jsx` (Blöcke 6–7, Dialoge)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: Task 1 (`financeApi`, `financeFormat`), `useCustomers` (vorhanden) für die Kundenauswahl.
|
||||
- Produces:
|
||||
- `InvoiceTable({ altersgruppen, customerId = null, onChanged })` – lädt `getInvoices({ status, kunde: customerId })`; Filter `.seg` Offen/Überfällig/Bezahlt/Entwürfe/Alle (Standard Offen); Altersgruppen-Zeile mit Anzahl und Summe; Spalten nach Spec Block 6; Aktionen je Status: Entwurf → „Senden“, „Löschen“ (mit Rückfrage); offen/überfällig → „Zahlungsseite“, „PDF“, „Stornieren“ (Rückfrage, nur `source 'projekt'`); bezahlt → „PDF“, bei `outOfBand` Hinweis „per Überweisung“. Knopf „Rechnung schreiben“.
|
||||
- `InvoiceCreateModal({ open, onClose, onCreated, fixedCustomer = null })` – Kunde (AutocompleteInput über Kunden mit `canInvoice(c).ok`, gesperrte mit Grund), Positionen (Beschreibung, Menge, Einzelpreis in Euro → `parseEuro`), „+ Position“, Zahlungsziel (Standard 14 Tage), Notiz; Summe; Hinweis „Gemäß § 19 UStG ohne Umsatzsteuer.“; Knöpfe „Als Entwurf speichern“ und „Senden“ (`senden: true`); Serverfehler im Dialog anzeigen.
|
||||
- `TransactionTable({ onAssign })` – `getTransactions()`, 50 je Seite (`pageOf`), neueste zuerst; Spalten Datum, Gegenpartei + Verwendungszweck (zweizeilig), Betrag (`signedEuro`), Zuordnung (`CATEGORY_LABEL`, bei `rechnung` mit Nummer); offene Eingänge mit stärkstem Vorschlag als Text und Knopf „Zuordnen“.
|
||||
- `AssignDialog({ transaction, invoices, onClose, onDone })` – Auswahl: Vorschläge, andere offene Rechnung, Stripe-Auszahlung (aus Vorschlägen), Fixkosten (nur Ausgänge, aus `getFixedCosts`), Kategorie (Einnahme ohne Stripe-Rechnung nur bei Eingang, Privat/Umbuchung, Sonstiges), Notiz; bei Rechnung zweistufig: erst „Zuordnen“, dann Bestätigungstext „Die Rechnung <Nr.> wird in Stripe als bezahlt markiert. Das lässt sich nicht rückgängig machen.“ mit „Ja, als bezahlt markieren“; bei bestehender Zuordnung (außer `rechnung`) Knopf „Zuordnung lösen“. Serverfehler (409 Betrag weicht ab usw.) im Dialog.
|
||||
|
||||
- [ ] **Step 1: Implement.**
|
||||
- [ ] **Step 2: Run** `npm test` und Build – Expected: PASS.
|
||||
- [ ] **Step 3: Commit** `git commit -m "Finanzen: Rechnungen schreiben und verwalten, Buchungen zuordnen"`
|
||||
|
||||
### Task 6: Einstellungen, Kunden-Tab, InvoiceNinja aus der Oberfläche
|
||||
|
||||
**Files:**
|
||||
- Create: `src/pages/FinanceSettingsPage.jsx`
|
||||
- Create: `src/components/finance/CustomerInvoices.jsx`
|
||||
- Modify: `src/pages/CustomerDetailPage.jsx` (Tab „Rechnungen“ → `CustomerInvoices`; Ninja-Sync beim Speichern `:133-156` entfernen; Karte „Rechnungsdaten (für InvoiceNinja)“ → „Rechnungsdaten“; Badge „verknüpft“ entfernen; `syntheticTicket` entfernen)
|
||||
- Modify: `src/components/TicketCard.jsx` (Tab `invoices` und `InvoicePanel` entfernen)
|
||||
- Modify: `src/pages/CustomersPage.jsx` (Untertitel „Sync mit InvoiceNinja“ und Badge „Ninja“ entfernen)
|
||||
- Modify: `src/lib/leads.js` (`ninjaPayload`, `syncCustomerToNinja` und den Ninja-Teil von `upgradeToCustomer` entfernen; `isInvoiceReady` entfernen, falls danach unbenutzt)
|
||||
- Delete: `src/components/InvoicePanel.jsx`, `src/hooks/useInvoices.js`, `src/hooks/useFinanceSummary.js`, `src/lib/integrationsApi.js` (nur wenn `grep -rn integrationsApi src` danach leer ist)
|
||||
|
||||
**Interfaces:**
|
||||
- Produces:
|
||||
- `FinanceSettingsPage` (`/finance/einstellungen`): Formular Einstellungen (Mindestpuffer und Vorjahresumsatz/ESt-Vorauszahlung in Euro über `parseEuro`, Rücklage in %, Bankverbindungstext) mit „Speichern“; Fixkostenliste (anlegen, bearbeiten, aktiv/inaktiv, löschen mit Rückfrage); Karte „Bankzugang“ (Status, IBAN maskiert, gültig bis, letzter Abruf, Fehler) mit „Jetzt abrufen“ und „Neu verbinden“; Link zurück zu `/finance`.
|
||||
- `CustomerInvoices({ customer })` – `canInvoice(customer)`: nicht ok → `EmptyState` mit Grund und „Daten erfassen“ (wechselt zum Übersicht-Tab); ok → `InvoiceTable({ customerId })` + `InvoiceCreateModal({ fixedCustomer: customer })`.
|
||||
|
||||
- [ ] **Step 1: Implement** Seite und Komponente.
|
||||
- [ ] **Step 2: Remove** InvoiceNinja-Stellen wie oben; danach `grep -rni "ninja\|invoicePanel\|useInvoices\|useFinanceSummary" src` – Expected: keine Treffer.
|
||||
- [ ] **Step 3: Run** `npm test` und Build – Expected: PASS.
|
||||
- [ ] **Step 4: Commit** (geänderte und gelöschte Dateien einzeln: `git add <dateien>` und `git rm <dateien>`) `git commit -m "Finanzen: Einstellungen, Rechnungen am Kunden, InvoiceNinja aus der Oberflaeche entfernt"`
|
||||
|
||||
### Task 7: Oberflächentest (Smoke) gegen Nachbau
|
||||
|
||||
**Files:**
|
||||
- Create (nicht committen): Scratchpad `finanzen-ui/` mit `fixtures.json` (Übersicht, Rechnungen, Buchungen aus Plan-2-Testdaten) und `smoke.mjs`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: gebautes `dist/` aus dem Worktree.
|
||||
|
||||
- [ ] **Step 1: Write** `smoke.mjs`: statischer Server für `dist/` (SPA-Fallback auf `index.html`) unter einer festen Herkunft; Playwright (`headless shell`, unter `systemd-run --scope -p MemoryMax=700M`) mit `page.route` für `**/v1/account*` (Admin-Nutzer mit Label `admin`), `**/v1/account/jwt*` und `https://project.webklar.com/api/admin/finanzen/**` aus `fixtures.json`. Prüfungen:
|
||||
1. `/finance` zeigt „Kontostand“ mit „109 €“, „Zu erledigen“ mit der Aufgabe „Zahlungseingang zuordnen“, Vorschau-Annahmezeile, Rechnungsfilter „Offen“.
|
||||
2. Zuordnen eines Eingangs zu einer Rechnung: nach erstem Klick erscheint der Satz „lässt sich nicht rückgängig machen“, erst der zweite Klick sendet `POST …/zuordnen` (Anfrage mitzählen).
|
||||
3. Übersicht antwortet 403 → Fehlerkarte mit „Erneut versuchen“, kein leerer Bildschirm.
|
||||
4. Nutzer ohne Label `admin` → `/finance` landet auf `/tickets`, Menüpunkt „Finanzen“ fehlt.
|
||||
5. `/finanzen/bank-callback?state=s&code=c` → genau ein `POST …/bank/rueckkehr`, Text „Bankzugang erneuert, gültig bis 29.03.2027“.
|
||||
6. Breite 390 px: keine horizontale Scrollleiste auf `/finance`.
|
||||
- [ ] **Step 2: Run** `node smoke.mjs` – Expected: alle 6 Prüfungen „ok“.
|
||||
- [ ] **Step 3:** Gefundene Fehler in den Komponenten beheben, `npm test` + Build, Commit `git commit -m "Finanzen: Korrekturen aus dem Oberflaechentest"` (nur falls Änderungen).
|
||||
|
||||
### Task 8: Livegang (jeder Schritt mit OK des Nutzers)
|
||||
|
||||
**Files:**
|
||||
- Modify: `/home/webklar/apps/docker-compose.yml` (Sicherung vorher: `docker-compose.yml.bak-2026-10-XX-finanzen`)
|
||||
- Move: `/home/webklar/apps/ticket-integrations/enablebanking.pem` und `.pub` → `/home/webklar/apps/secrets/enablebanking/`
|
||||
|
||||
- [ ] **Step 1: Schlüssel verschieben** (Ordner 700, Schlüssel 600; Inhalt nie ausgeben):
|
||||
|
||||
```bash
|
||||
install -d -m 700 /home/webklar/apps/secrets/enablebanking && mv /home/webklar/apps/ticket-integrations/enablebanking.pem /home/webklar/apps/ticket-integrations/enablebanking.pub /home/webklar/apps/secrets/enablebanking/ && chmod 600 /home/webklar/apps/secrets/enablebanking/enablebanking.pem
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Compose** beim Dienst `kundenbereich-server` ergänzen: Volume `- ./secrets/enablebanking:/run/secrets/enablebanking:ro`; Umgebung `- ENABLE_BANKING_APP_ID=b2582270-11bf-4489-affb-1a151f076c07` und `- ENABLE_BANKING_KEY_PATH=/run/secrets/enablebanking/enablebanking.pem`. Prüfen: `docker compose config -q` ohne Fehler.
|
||||
- [ ] **Step 3: Portal live** (OK des Nutzers): `git -C /home/webklar/apps/kundenbereich.webklar.com/dev-finanzen push origin feature/finanzen:main`; warten, bis `build/` auf dem neuen Stand ist (`git -C …/build log -1`); dann `cd /home/webklar/apps && docker compose up -d kundenbereich-server` (übernimmt Volume und Umgebung). Erwartet: `docker logs webklar-kundenbereich-server --since 2m` zeigt den Start ohne Fehler; `curl -s https://project.webklar.com/api/health` → `ok: true`.
|
||||
- [ ] **Step 4: Collections und Sitzung übernehmen** (Werte aus `ticket-integrations/enablebanking-session.txt`, ohne sie auszugeben):
|
||||
|
||||
```bash
|
||||
F=/home/webklar/apps/ticket-integrations/enablebanking-session.txt; docker exec -w /app webklar-kundenbereich-server node scripts/setup-finanzen.mjs --session "$(sed -n 's/^session_id=//p' $F)" --account "$(sed -n 's/^account_uid=//p' $F)" --valid-until "$(sed -n 's/^valid_until=//p' $F)"
|
||||
```
|
||||
|
||||
Erwartet: „Sitzung übernommen, gültig bis 29.03.2027“. Danach `enablebanking-session.txt` und `enablebanking-app-id.txt` löschen. Nach höchstens 2 Minuten (Job) oder per `POST /bank/aktualisieren`: 9 Buchungen, Kontostand 108,93 €.
|
||||
- [ ] **Step 5: Stripe-Dashboard** (Nutzer): Rechnungsnummern fortlaufend auf Kontoebene; Zahlarten für Rechnungen Karte und SEPA-Lastschrift; in WOMS-Einstellungen den Bankverbindungstext (IBAN, BIC) eintragen.
|
||||
- [ ] **Step 6: WOMS live** (OK des Nutzers): `git -C /home/webklar/apps/ticket.webklar.com/dev-finanzen push origin feature/finanzen:test`. Erwartet: neues Bundle in `ticket.webklar.com/out` enthält „Verfügbar nach Rücklagen“; Seite lädt für Kenso.
|
||||
- [ ] **Step 7: Daten nachziehen:** Buchung „Rechnung 0005 vom 28.07.2026“ als „Einnahme ohne Stripe-Rechnung“ zuordnen; prüfen, dass Justin das Label `admin` hat (sonst Nutzer fragen).
|
||||
- [ ] **Step 8: Merken:** Memory-Datei `webklar-finanzen.md` mit Livestand, Commits und offenen Punkten aktualisieren (Enable-Banking-Vertrag, Kundenportal-Tab „Rechnungen“ noch InvoiceNinja).
|
||||
Reference in new issue
Block a user