7.8 KiB
Buchhaltung — Remaining Features Implementation Plan
Context
The Buchhaltung system has core CRUD, transfers, bank statements, Haushaltsplan, and PDF export implemented. Three features from the original design doc remain:
- Budget Delegation Freigaben — authorizes a user to spend against a budget account (distinct from the existing transaction-approval freigaben)
- Split Booking Dialog — book a pending transaction into N parts across different budget accounts
- Sub-Budget Flow — create budget delegation Freigaben from approved plan positions
Task Dependency Graph
T1: Migration 083 (budget_freigaben) T2: Migration 084 (split_gruppe_id)
| |
v v
T3: Backend budget freigaben CRUD T4: Backend split booking endpoint
| |
+------------+ v
| | T7: Frontend BookingDialog
v v
T5: Backend plan→ T6: Frontend budget
freigabe endpoint freigaben tab + sidebar
| |
+-----+------+
v
T8: Frontend plan position → freigabe action
Tasks
T1 — Migration 083: buchhaltung_budget_freigaben table
Teammate: database | blockedBy: none
Create backend/src/database/migrations/083_buchhaltung_budget_freigaben.sql:
CREATE TABLE IF NOT EXISTS buchhaltung_budget_freigaben (
id SERIAL PRIMARY KEY,
konto_id INT NOT NULL REFERENCES buchhaltung_konten(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
beschreibung TEXT,
betrag_limit NUMERIC(12,2),
planposition_id INT REFERENCES buchhaltung_planpositionen(id) ON DELETE SET NULL,
aktiv BOOLEAN NOT NULL DEFAULT TRUE,
erstellt_von UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_budget_freigaben_konto ON buchhaltung_budget_freigaben(konto_id);
CREATE INDEX idx_budget_freigaben_user ON buchhaltung_budget_freigaben(user_id);
T2 — Migration 084: Add split_gruppe_id to transaktionen
Teammate: database | blockedBy: none
Create backend/src/database/migrations/084_buchhaltung_split_gruppe.sql:
ALTER TABLE buchhaltung_transaktionen
ADD COLUMN IF NOT EXISTS split_gruppe_id UUID;
CREATE INDEX IF NOT EXISTS idx_buch_trans_split_gruppe
ON buchhaltung_transaktionen(split_gruppe_id) WHERE split_gruppe_id IS NOT NULL;
T3 — Backend: Budget Freigaben CRUD
Teammate: backend | blockedBy: T1
Files: buchhaltung.service.ts, buchhaltung.controller.ts, buchhaltung.routes.ts
Service functions (add before export block ~line 1937):
listBudgetFreigaben(filters: { konto_id?, user_id? })— JOIN users + konten for display namesgetMyBudgetFreigaben(userId)— active freigaben for current usercreateBudgetFreigabe(data, erstelltVon)— INSERTupdateBudgetFreigabe(id, data, userId)— UPDATE beschreibung/betrag_limitdeactivateBudgetFreigabe(id, userId)— SET aktiv=false
Routes (add BEFORE /:id catch-all routes):
GET /budget-freigaben manage_accounts listBudgetFreigaben
GET /budget-freigaben/my buchhaltung:view getMyBudgetFreigaben
POST /budget-freigaben manage_accounts createBudgetFreigabe
PATCH /budget-freigaben/:id manage_accounts updateBudgetFreigabe
DELETE /budget-freigaben/:id manage_accounts deactivateBudgetFreigabe
T4 — Backend: Split Booking Endpoint
Teammate: backend | blockedBy: T2
Files: buchhaltung.service.ts, buchhaltung.controller.ts
Modify bookTransaktion(id, userId, splits?):
- No splits → existing behavior (unchanged)
- With splits
Array<{ betrag, konto_id, bankkonto_id? }>:- Validate sum equals original betrag
- BEGIN transaction
- Generate UUID
split_gruppe_id - Mark original as
status='gebucht', setsplit_gruppe_id - INSERT N new booked transactions copying fields from original but with split-specific betrag/konto_id/bankkonto_id, same
split_gruppe_id - Budget alert check per split konto
- Audit:
logAudit(id, 'split_gebucht', { splits, split_gruppe_id }, userId)
Controller: read optional req.body.splits, pass to service.
T5 — Backend: Plan Position → Freigabe Endpoint
Teammate: backend | blockedBy: T3
Files: buchhaltung.service.ts, buchhaltung.controller.ts, buchhaltung.routes.ts
New function createBudgetFreigabeFromPlanposition(posId, { user_id, betrag_limit?, beschreibung? }, erstelltVon):
- Fetch planposition, verify konto_id exists
- Default betrag_limit to sum of budget_gwg + budget_anlagen + budget_instandhaltung
- Call
createBudgetFreigabewithplanposition_idset
Route: POST /planung/positionen/:posId/freigabe — manage_accounts
T6 — Frontend: Budget Freigaben Tab + Sidebar
Teammate: frontend | blockedBy: T3
Files: buchhaltung.types.ts, buchhaltung.ts, Buchhaltung.tsx, Sidebar.tsx
Types:
BudgetFreigabe— id, konto_id, user_id, beschreibung, betrag_limit, planposition_id, aktiv, konto_bezeichnung, user_nameBudgetFreigabeFormData— konto_id, user_id, beschreibung?, betrag_limit?
Service: getBudgetFreigaben, getMyBudgetFreigaben, createBudgetFreigabe, updateBudgetFreigabe, deleteBudgetFreigabe
Buchhaltung.tsx:
- Add tab 3 "Freigaben" (visible only with
manage_accounts) - Table: Konto, Benutzer, Beschreibung, Betrag-Limit, Status, Aktionen
- Create/Edit dialog with Konto select, User select, Betrag-Limit, Beschreibung
- Pattern: follow TransaktionenTab Table+Dialog structure
Sidebar.tsx (~line 155): add { text: 'Freigaben', path: '/buchhaltung?tab=3' } to buchhaltung subItems
T7 — Frontend: Split Booking Dialog
Teammate: frontend | blockedBy: T4
Files: buchhaltung.types.ts, buchhaltung.ts, Buchhaltung.tsx
Types: add SplitRow { betrag: number; konto_id: number; bankkonto_id?: number }
Service: update buchenTransaktion(id, splits?) to POST body with optional splits array
Buchhaltung.tsx:
- Add
BookingDialogcomponent (inline):- Shows transaction details (betrag, beschreibung)
- "Einfach buchen" button for simple booking (no splits)
- "Aufteilen" toggle reveals split rows
- Each row: betrag input, konto select, bankkonto select, remove button
- "Zeile hinzufuegen" button
- Sum validation with remaining amount display
- "Buchen" button
- Replace buchen IconButton click: open BookingDialog instead of direct mutation
- Update
buchenMutto accept{ id, splits? }
T8 — Frontend: Plan Position → Freigabe Action
Teammate: frontend | blockedBy: T5, T6
Files: buchhaltung.ts, HaushaltsplanDetail.tsx
Service: add createBudgetFreigabeFromPosition(posId, { user_id, betrag_limit?, beschreibung? })
HaushaltsplanDetail.tsx:
- Add "Freigabe erstellen" IconButton per position row (only when canManage && pos.konto_id set)
- Dialog: User select (fetch members), Betrag-Limit (pre-filled from position total), Beschreibung (pre-filled from position name)
- Pattern: follow existing position edit dialog
Teammate Assignments
| Teammate | Tasks | Mode |
|---|---|---|
| database | T1, T2 | bypassPermissions |
| backend | T3, T4, T5 | bypassPermissions |
| frontend | T6, T7, T8 | bypassPermissions |
Verification
- Migration: check tables exist via backend startup logs
- Backend: test budget-freigaben CRUD via curl/frontend
- Frontend: verify Freigaben tab renders, BookingDialog opens on buchen click
- Split booking: create pending transaction, open dialog, add split rows, book
- Plan→Freigabe: open HaushaltsplanDetail, click freigabe button on position with konto_id