Rechnungsmodul: Entwurf/Ausstellen/Bezahlt/Storno, PDF, Firmenstammdaten

This commit is contained in:
Kundencenter 2026-09-27 09:05:13 +02:00
parent 012f0aa0db
commit 43dc5bc827
18 changed files with 783 additions and 3 deletions

49
docs/rechnungen.md Normal file
View file

@ -0,0 +1,49 @@
# Rechnungen
Eigenes Rechnungsmodul (`apps/api/src/modules/invoices`, Migration 016). Kein Invoice Ninja, keine
externe Buchhaltungssoftware – bewusst so gewählt.
## Lebenszyklus
`draft` → `open` (ausgestellt) → `paid` **oder** `cancelled` (per Storno).
- **Entwurf (`draft`):** frei bearbeitbar (Positionen ersetzen, löschen). Kunden sehen Entwürfe nie.
- **Ausstellen (`POST /invoices/:id/issue`):** vergibt die Rechnungsnummer (fortlaufend, `<Präfix>-<Zähler>`,
Standard `RE-1000, RE-1001, …`, **ohne Lücken und ohne Rücksetzung pro Jahr** – das ist bewusst so,
weil eine lückenlose Nummerierung einfacher rechtssicher zu halten ist als eine jährliche mit
Neustart). Ab hier ist die Rechnung **unveränderlich**: Positionen, Beträge und Nummer werden nie
wieder geändert. Setzt Ausstellungs- und Fälligkeitsdatum (Zahlungsziel aus den Firmeneinstellungen
oder explizit übergeben).
- **Bezahlt (`POST /invoices/:id/mark-paid`):** rein manuell gepflegt. Es gibt keine Anbindung an ein
Zahlungssystem – niemand hat bisher festgelegt, welcher Zahlungsanbieter (falls überhaupt einer)
genutzt werden soll.
- **Storno (`POST /invoices/:id/cancel`):** Eine ausgestellte Rechnung wird **nie** gelöscht oder
nachträglich verändert (§ 14 UStG, GoBD). Stattdessen entsteht eine neue, ausgestellte
Stornorechnung mit umgekehrten Vorzeichen (eigene Nummer aus derselben Zählerreihe), die auf die
Originalrechnung verweist (`cancelsInvoiceId`); die Originalrechnung bekommt den Status `cancelled`
und einen Rückverweis (`cancelledByInvoiceId`). Beide bleiben für immer einsehbar.
## Firmenstammdaten (`company_settings`, `/einstellungen/firma`)
Name, Anschrift, Steuernummer/USt-IdNr., Bankverbindung, Rechnungspräfix, Zahlungsziel, Zahlungsarten
(Freitextliste), Fußzeile. **Beim ersten Einrichten absichtlich leer** – nichts davon wurde geraten.
Ohne vollständige Angaben (Name, Anschrift, Steuernummer oder USt-IdNr.) lässt sich keine Rechnung
ausstellen (`COMPANY_SETTINGS_INCOMPLETE`), Entwürfe lassen sich aber schon vorbereiten. Ändern kann
das nur ein Superadministrator (`settings.write`).
## Rechte
`invoices.read`: Personal (alle Rollen) sowie Kunden für die eigene Organisation (nie Entwürfe).
`invoices.write`: Buchhaltung, Administratoren, Superadministratoren – nicht Support (nur lesend).
## PDF
Wird bei jedem Aufruf aus den gespeicherten Daten neu gerendert (`pdfkit`, kein Zwischenspeichern),
`GET /invoices/:id/pdf`, nur für ausgestellte Rechnungen. Enthält die Pflichtangaben, soweit aus den
vorhandenen Daten ableitbar (Rechnungsnummer, Datum, Leistungsbeschreibung, Entgelt/Steuersatz je
Position, Steuernummer/USt-IdNr., Empfängeranschrift). **Keine rechtliche Prüfung durch einen
Steuerberater/Anwalt hat stattgefunden** – vor dem ersten Versand an echte Kunden empfohlen.
## Offen / bewusst nicht gebaut
- Kein Zahlungsanbieter (SEPA-Lastschrift, Kartenzahlung, …) – Entscheidung steht aus.
- Keine automatische Rechnungsstellung aus Verträgen (wiederkehrend); es gibt nur
`GET /invoices/suggest-from-contract/:id` als Vorschlag für eine einzelne Position, die man von
Hand übernimmt.
- Kein E-Mail-Versand der Rechnung (liegt nur zum Abruf bereit, wie beim Lizenzschlüssel-Muster).
- Keine E-Rechnung (XRechnung/ZUGFeRD) – falls das für B2B mit Behörden nötig wird, gesondert klären.