Files
dashboard/.claude/plans/2026-03-28-buchhaltung-design.md

13 KiB

Buchhaltung System — Design

Date: 2026-03-28 Status: Approved


Overview

A budget and transaction tracking system for the Feuerwehr. Tracks money flows against budget pots and real bank accounts, scoped by fiscal year. Integrates with the existing Bestellungen system and permission matrix.


Full Cycle

Budget Plan → Fiscal Year → Freigaben → Bestellungen → Pending Transactions → Booked Transactions

1. Data Model

buchhaltung_konto_typen

Dynamic list of bank account types (Girokonto, Tagesgeldkonto, Handkasse, PayPal, …). Managed in settings by users with manage_settings.

Column Type Notes
id serial PK
name varchar
beschreibung text nullable
created_at timestamptz

buchhaltung_bankkonten

Real bank accounts (where money physically is).

Column Type Notes
id serial PK
name varchar
typ_id FK konto_typen
iban varchar nullable
opening_balance decimal(12,2) starting balance when first added
aktiv bool default true
created_at timestamptz

Balance = opening_balance + SUM(booked transactions).


buchhaltung_haushaltsjahre

Fiscal years. Bank accounts are not year-scoped; budget accounts are.

Column Type Notes
id serial PK
jahr int unique (e.g. 2026)
start_datum date
end_datum date
abgeschlossen bool default false; manually closed by manage_settings user

Closing a year locks all its transactions (no edits, no new bookings).


buchhaltung_konten

Budget accounts / pots. Scoped per fiscal year.

Column Type Notes
id serial PK
haushaltsjahr_id FK haushaltsjahre
name varchar
beschreibung text nullable
budget decimal(12,2)
alert_threshold int % (0-100); falls back to global setting if null
aktiv bool default true
created_at timestamptz

buchhaltung_transaktionen

Core transaction table.

Column Type Notes
id serial PK internal DB key
haushaltsjahr_id FK haushaltsjahre
laufende_nummer int sequential per fiscal year; display as 2026/0001
datum date
beschreibung text
betrag decimal(12,2) nullable for variable recurring transactions
typ enum einnahme, ausgabe, transfer
konto_id FK konten nullable (null for transfers)
bankkonto_id FK bankkonten source bank account
transfer_ziel_bankkonto_id FK bankkonten nullable; only for typ=transfer
bestellung_id FK bestellungen nullable; set when auto-created from order
split_gruppe_id uuid nullable; groups split transactions from same Bestellung
gebucht bool false = pending, true = booked
erstellt_von FK users
gebucht_von FK users nullable
gebucht_am timestamptz nullable
created_at timestamptz

Display ID: ${jahr}/${laufende_nummer.toString().padStart(4, '0')} e.g. 2026/0001. laufende_nummer is unique per haushaltsjahr_id, incremented per new transaction.


buchhaltung_belege

Receipt/invoice attachments per transaction.

Column Type Notes
id serial PK
transaktion_id FK transaktionen
dateiname varchar
pfad varchar disk path
mime_type varchar
erstellt_von FK users
created_at timestamptz

Uses same multer disk storage pattern as Bestellungen (uploads/buchhaltung/).


buchhaltung_wiederkehrend

Recurring transaction templates.

Column Type Notes
id serial PK
beschreibung varchar
betrag decimal(12,2) nullable (e.g. variable energy bills)
typ enum einnahme, ausgabe
konto_id FK konten nullable
bankkonto_id FK bankkonten nullable
frequenz enum monatlich, vierteljaehrlich, jaehrlich
naechste_faelligkeit date advanced by job after each creation
aktiv bool
erstellt_von FK users

Job runs daily → creates pending transaction → advances naechste_faelligkeit. Betrag null → pending transaction has null betrag → Kassenwart fills it in before booking.


buchhaltung_freigaben

Budget delegation — authorizes a user to spend against a budget account.

Column Type Notes
id serial PK
konto_id FK konten budget account
user_id FK users authorized user
beschreibung text what this covers
betrag_limit decimal(12,2) nullable; max spending allowed
planposition_id FK planpositionen nullable; source plan item
aktiv bool
erstellt_von FK users
created_at timestamptz

Amount is editable after creation (plan may change mid-year). Users with a Freigabe can create Bestellungen against that account even without buchhaltung:create.


buchhaltung_audit

Append-only audit log.

Column Type Notes
id serial PK
transaktion_id FK transaktionen
aktion enum erstellt, gebucht, bearbeitet, geloescht
benutzer_id FK users
details jsonb before/after snapshot
created_at timestamptz

buchhaltung_einstellungen

Key/value store for global settings.

Key Default Notes
default_alert_threshold 80 % budget used before notification
pdf_footer '' reuses PDF settings pattern

Budget Planning Tables

buchhaltung_planung

A plan for a future fiscal year.

Column Type Notes
id serial PK
ziel_jahr int the year being planned
name varchar e.g. "Haushaltsplan 2027"
status enum entwurf, genehmigt
erstellt_von FK users
created_at timestamptz

Starts pre-populated from current open budget accounts. New accounts can be added. Approving locks the plan. Only approved plans can be used for fiscal year rollover.

buchhaltung_plankonten

Planned budget pots within a plan.

Column Type Notes
id serial PK
planung_id FK planung
konto_referenz_id FK konten nullable; links to existing account if carried over
name varchar
beschreibung text nullable
sort_order int

buchhaltung_planpositionen

Line items within a planned pot. Sum = pot total.

Column Type Notes
id serial PK
plankonto_id FK plankonten
beschreibung text what it's for
betrag decimal(12,2)
sort_order int

From approved plan items → manual Freigaben creation flow (UI, not automatic). Amount editable on Freigabe creation and after.


2. Permissions

New buchhaltung feature group in the existing permission matrix:

Permission Description
buchhaltung:view See transactions, accounts, bank accounts
buchhaltung:create Add transactions manually
buchhaltung:edit Edit unbooked transactions
buchhaltung:delete Delete unbooked transactions
buchhaltung:manage_accounts Create/edit/archive budget accounts, bank accounts, Freigaben
buchhaltung:manage_settings Global settings, account types, fiscal year management, recurring templates
buchhaltung:export PDF and CSV export
buchhaltung:widget See the dashboard widget

manage_settingsdashboard_admin — a dedicated Kassenwart can have this without being a global admin.


3. Backend Architecture

Follows the existing project pattern (service → controller → routes → app.ts).

Routes mounted at /api/buchhaltung/:

Route Description
bankkonten CRUD bank accounts
konten CRUD budget accounts (scoped by fiscal year)
transaktionen list, create, book, edit, delete
transfers create bank-to-bank transfers
freigaben CRUD budget delegations
wiederkehrend CRUD recurring templates
haushaltsjahre create new year, close year, list
planung CRUD budget plans and plan items
einstellungen key/value settings
export/pdf annual report PDF
export/csv transaction CSV export
audit/:transaktionId audit trail for one transaction

Jobs:

  • buchhaltung-recurring.job.ts — runs daily; creates pending transactions from due recurring templates; advances naechste_faelligkeit
  • Budget alert fires inline after each booking: recalculates account total → if crosses threshold → notificationService.createNotification() for manage_accounts users (deduped by DB constraint)

4. Bestellungen Integration

Costs on positions

  • bestellpositionen gets an einzelpreis / gesamtpreis field (if not already present)
  • Status change to vollstaendig/abgeschlossen is blocked until all positions have costs entered
  • Attempting the status change without costs → showWarning snackbar: "Bestellung kann erst abgeschlossen werden, wenn Kosten für alle Positionen eingetragen wurden."
  • Backend enforces the same rule (400 response)
  • The order creator/manager enters costs — not the Kassenwart

Pending transaction creation

When a Bestellung reaches abgeschlossen:

  1. buchhaltungService.createPendingFromBestellung(bestellung) is called
  2. Creates a pending ausgabe transaction: betrag = sum of position costs, beschreibung = order title, bestellung_id = FK
  3. Notifies users with buchhaltung:create or buchhaltung:manage_accounts

Split booking

When booking a pending transaction from a Bestellung, the Kassenwart can split it into N parts:

  • Each part: betrag, budget konto, bankkonto, Beleg (receipt)
  • Parts sum to ≤ total (partial booking allowed; remainder stays pending)
  • Each part becomes its own booked transaction with its own <year>/<id>
  • All parts share the same bestellung_id and split_gruppe_id (visually grouped in audit trail)

5. Frontend Pages

Navigation

New "Buchhaltung" sidebar group:

  • Übersicht
  • Transaktionen
  • Bankkonten
  • Konten & Haushaltsjahre
  • Freigaben (only with manage_accounts)
  • Haushaltsplan (only with manage_settings)

Pages

Buchhaltung.tsx — overview. Year selector at top. Budget account cards: name, budget, spent, remaining, % bar, alert indicator. Pending transactions count with link.

Transaktionen.tsx — full list. Filter by year / account / bank account / typ. Columns: ID (2026/0001), Datum, Beschreibung, Konto, Bankkonto, Betrag, Status. Click → booking dialog.

BankkontoDetail.tsx — statement view. Running balance column per row. Date range filter. CSV/PDF export.

BuchhaltungKonten.tsx — tabs: Konten (current year) | Bankkonten | Haushaltsjahre. Year-end actions: "Jahr abschließen" (manual, locks year) + "Neues Haushaltsjahr" (from approved plan or copy previous).

BuchhaltungEinstellungen.tsx (or AdminDashboard tab) — Kontentypen list, default alert threshold, recurring templates.

Haushaltsplan.tsx — plan management. Pre-populated from current accounts. Add/edit/remove plankonten and planpositionen. Approve plan. From approved items → create Freigaben (manual flow, amounts editable).

Booking Dialog

Used from pending transaction list:

  • Pre-filled from Bestellung (amount, description, reference)
  • "Split" button adds a row
  • Each row: betrag, budget konto, bankkonto, Beleg upload
  • Sum validation
  • Confirm → books all rows at once

Widget

BuchhaltungWidget.tsx — compact card in Status group. Shows pending transaction count. Tap → filtered pending list. Only shown with buchhaltung:widget.


6. PDF Export

Uses existing addPdfHeader / addPdfFooter from frontend/src/utils/pdfExport (same red header, logo, org name as Bestellungen and Kalender).

Content of annual report TBD (separate discussion).


7. Fiscal Year Rollover

  1. Close current year — manual action by manage_settings user → sets abgeschlossen = true → all transactions locked
  2. Create new year — choose source:
    • From approved plan — plankonten become new konten, plankonto totals (sum of planpositionen) become budgets
    • Copy previous year — all active konten copied with same budget amounts (editable before confirming)
  3. Bank accounts carry over automatically (not year-scoped)
  4. Old year remains readable/exportable forever

8. Future: Sub-Budget Flow from Plan Items

(Planned feature — not in initial implementation)

From an approved plan's items, a manual UI flow to create Freigaben:

  • User selects plan items
  • Sets/adjusts amount per Freigabe (editable at creation and later — plan may change mid-year)
  • Assigns a user
  • Creates the Freigabe linked to planposition_id

This closes the full loop: Plan item → Freigabe → Bestellung → Pending transaction → Booked transaction.