Spezifikation: Auftragsbestätigung und projektbezogene Rechnungen

Entwurf aus dem Brainstorming vom 06.10.2026 (Abschnitte im Chat freigegeben):
Projektkonto, Rechnungsarten Anzahlung/Schluss/Aufwand/Festbetrag über Stripe,
Auftragsbestätigung als eigenes PDF mit Bestätigungsklick und Verbraucher-Variante,
zwei Pläne.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-06 10:03:25 +00:00
1 parent 8814c4aef1
commit e00b4ae572
1 file changed
+402
@@ -0,0 +1,402 @@
# Aufträge und Rechnungen: Auftragsbestätigung und projektbezogene Rechnungen
Stand: 2026-10-06 · Status: Entwurf zur Freigabe (Abschnitte im Chat einzeln freigegeben)
Grundlage: Wunsch des Nutzers vom 06.10.2026 („Auftragsbestätigung und Rechnungen bei uns, wenn wir auf Kunden gehen;
Projekt auswählen, Zusammenfassung sehen, Festbeträge möglich“), Recherche im selben Gespräch (Quellen am Ende),
Bestand: Finanzen-Spezifikation `2026-10-01-finanzen-design.md` (Stripe-Rechnungen aus WOMS, live seit 01.10.2026).
## Ziel
Im Ticketsystem (WOMS) legt ein Admin beim Kunden **Auftragsbestätigungen (AB)** und **Rechnungen zu einem Projekt** an.
Nach der Projektwahl zeigt eine **Zusammenfassung**, was vereinbart ist, was schon berechnet und bezahlt ist, was noch
offen ist und wie viel Zeit im Projekt steckt. Die Rechnung entsteht als Anzahlung, Schlussrechnung, nach Aufwand oder
als frei gewählter **Festbetrag** – auch wenn die erfasste Zeit mehr wert wäre (Beispiel: Frau Wolf, 500 € laut
mündlicher Absprache).
Erfolg:
- Eine Projektrechnung (Festbetrag oder Aufwand) ist in unter zwei Minuten geschrieben und versendet.
- Eine AB geht mit PDF an den Kunden, der sie per Klick bestätigt; bei Privatkunden ist sie widerrufsrechtlich korrekt.
- Kein Arbeitsblatt wird doppelt berechnet, keine Anzahlung wird in der Schlussrechnung vergessen.
- Jede Rechnung erfüllt § 34a UStDV, jede gesendete AB liegt unveränderlich mit Prüfsumme vor.
## Feststehende Rahmenbedingungen (geprüft)
- **WEBklar GbR, Kleinunternehmer nach § 19 UStG.** Rechnungen müssen nach **§ 34a UStDV** (seit 01.01.2025) enthalten:
Name und Anschrift beider Seiten, **Steuernummer** (oder USt-IdNr./Kleinunternehmer-IdNr.), Ausstellungsdatum, Art
und Umfang der Leistung, Entgelt in einer Summe mit Hinweis auf die Kleinunternehmerbefreiung. Leistungszeitpunkt und
Rechnungsnummer sind für Kleinunternehmer keine Pflicht; § 33 UStDV (Kleinbetrag bis 250 €) bleibt unberührt.
**Befund:** Der heutige Rechnungsfuß (`config.stripe.invoiceFooter`) enthält keine Steuernummer.
- **Aufbewahrung:** Rechnungsdoppel 8 Jahre (§ 14b UStG seit 2025), Auftragsbestätigungen als Beleg 8 bzw. als
Geschäftsbrief 6 Jahre. E-Rechnungen muss ein Kleinunternehmer nicht ausstellen.
- **Verbraucher:** Bei Fernabsatzverträgen (Telefon, E-Mail, Chat) muss der Unternehmer den Vertrag auf einem
dauerhaften Datenträger bestätigen, spätestens bevor er mit der Leistung beginnt (§ 312f Abs. 2 BGB), samt der Angaben
nach Art. 246a EGBGB und Widerrufsbelehrung. Ohne Belehrung läuft die Widerrufsfrist bis zu 12 Monate und 14 Tage; ohne
ausdrückliches Verlangen des Verbrauchers vor Fristende schuldet er bei Widerruf keinen Wertersatz.
- **Kostenschätzung:** Zeichnet sich eine wesentliche Überschreitung eines Kostenanschlags ab, muss der Unternehmer den
Besteller unverzüglich informieren (§ 649 Abs. 2 BGB).
- **AGB (webklar.com/agb):** Vergütung „ergibt sich aus dem Angebot“, Aufwand in 15-Minuten-Schritten, Unterstützung
zur Hälfte (Nr. 9), Mitwirkung (Nr. 7), Abnahme (Nr. 8), „Projektleistungen sind nach Vereinbarung fällig“.
Stundensatz in den Abo-Bedingungen: 100 € pro Stunde (Endpreis, § 19 UStG).
- **Stripe** (SDK `stripe@19`, API 2025-10-29.clover): Rechnungen mit bis zu **4 Zusatzfeldern** im Kopf (Name ≤ 40,
Wert ≤ 140 Zeichen), Seitengröße per `rendering.pdf.page_size` (Standard außerhalb Japans: US-Letter), Rechnungsposten
mit **negativem `amount`** (mindert den Betrag), Steuer-IDs des Kontos im Dashboard (Einstellungen → Billing →
Rechnungen → „Steuerinformationen für Rechnungen“). Stripe-„Quotes“ taugen nicht als AB (Aufpreis, PDF-Titel
„Angebot“, keine Verbraucher-Variante).
- **Bestand im Portal-Server:** `createProjectInvoice` in `server/services/finance/stripeFinance.js` (Entwurf/Senden,
Adress-Sperre über 250 €, Doppel-Schutz), Finanzen-API `/api/admin/finanzen/*` mit `requireAppwriteStaff` (Admin,
zweiter Faktor), Mailer `sendCustomerMail` mit Anhängen, Fristenlogik `deadlines.js` (`withdrawalDeadline`),
Frühbeginn-Erklärungen nach Muster Terminplaner/Webshop, öffentliche Widerrufsseite `widerrufen.html`
(`/api/abo/public/withdraw`), Kundenansicht-Sperre (fail-closed), Arbeitszeit-Regeln `worksheetMinutes.js`.
- **Bestand im WOMS:** Reiter „Rechnungen“ am Kunden (nur Admins), Dialog „Rechnung schreiben“ (freie Positionen),
Finanzen-Seite. Projekte (`websiteProjects`) hängen an genau einem Ticket (`ticketId`), Arbeitsblätter
(`worksheets.totalTime`, `supporters`, `countsToPlan`) an Tickets.
- **WOMS-Redesign** (weißes Design, Pakete P4 Kunden und P7 Finanzen) baut parallel genau die betroffenen Dateien um.
## Entscheidungen des Nutzers (06.10.2026)
1. **Abrechnungsarten:** Festpreis, nach Aufwand (Arbeitsblätter × Stundensatz) und Anzahlung mit Schlussrechnung; ein
freier Festbetrag geht immer.
2. **Auch Privatkunden:** Kunden bekommen die Kundenart *Unternehmen / Privatperson*. Bei Privatpersonen enthält die AB
Widerrufsbelehrung und Muster-Widerrufsformular; Arbeitsbeginn vor Fristende nur auf ausdrückliches Verlangen.
3. **AB-Versand:** Mail mit PDF und Knopf „Auftrag bestätigen“ (Link ohne Anmeldung, auch im Portal sichtbar); der
Klick wird protokolliert.
4. **Ansatz A:** Rechnungen bleiben Stripe-Rechnungen (erweitert); die AB ist ein eigenes Dokument des Portal-Servers.
5. **Abschnitte 1–4 dieses Entwurfs** (Oberfläche, AB, Rechnungen, Technik/Livegang) wie unten beschrieben.
6. **Reihenfolge:** Portal-Server und Kundenseiten zuerst; WOMS-Oberfläche im neuen weißen Design, sobald die
Redesign-Stränge Kunden (P4) und Finanzen (P7) zusammengeführt sind. **Zwei Pläne** (siehe unten).
## Begriffe
| Begriff | Bedeutung |
|---|---|
| **Auftrag / AB** | Eine Auftragsbestätigung an einen Kunden, meist zu einem Projekt. Art *Festpreis* (Positionen) oder *Aufwand* (Stundensatz, Schätzung). |
| **Projektkonto** | Zusammenfassung je Projekt: vereinbart, berechnet, bezahlt, noch offen, Zeit erfasst. |
| **Zeitquelle** | Die Tickets, deren Arbeitsblätter zu einem Projekt/Auftrag zählen (Standard: das mit dem Projekt verknüpfte Ticket). |
| **Rechnungsart** | `anzahlung`, `schluss`, `aufwand`, `festbetrag` (mit Projekt) oder `frei` (ohne Projekt, wie heute). |
| **Abo-Zeit** | Arbeitsblätter mit `countsToPlan = true`; sie laufen über das Abo-Stundenkonto und werden nie projektbezogen berechnet. |
## 1. Oberfläche in WOMS
**Ort:** Kunde → Reiter **„Aufträge und Rechnungen“** (bisher „Rechnungen“, `id` bleibt `invoices`), nur für Admins.
Gebaut im neuen weißen Design (Komponenten des Redesigns, z. B. `<Dialog>`).
Von oben nach unten:
1. **Projekte (Projektkonto):** je Projekt des Kunden eine Zeile mit Name, Art und Ticketnummer; Spalten *Vereinbart*
(Summe der gültigen Festpreis-ABs bzw. „nach Aufwand, 100 €/Std.“ bzw. „keine AB“), *Berechnet* (darunter „bezahlt
x €“), *Noch offen*, *Zeit erfasst* (Stunden, darunter „Wert x €“). Knöpfe **„Auftragsbestätigung“** und
**„Rechnung“**. Eine letzte Zeile **„Ohne Projekt“** sammelt Rechnungen ohne Projektbezug.
2. **Aufträge:** Liste der ABs (Nummer, Projekt/Titel, Datum, Summe bzw. „nach Aufwand“, Status, Aktionen: PDF,
Erinnerung senden, Neue Fassung, Stornieren, Entwurf bearbeiten/löschen).
3. **Rechnungen:** die bestehende Rechnungsliste mit den zusätzlichen Spalten *Projekt* und *Art*.
Der neue Dialog „Rechnung schreiben“ ersetzt den bisherigen überall. Seine Projektliste endet mit **„Ohne Projekt“**
(Art `frei`: freie Positionen wie bisher), so bleibt eine Rechnung ohne Projekt möglich.
**Kundenart:** In der Kunden-Übersicht ein Feld „Kundenart: Unternehmen / Privatperson“ (leer = unbekannt). Ist es
leer, fragt der AB-Dialog im ersten Schritt danach und speichert die Antwort am Kunden.
**Finanzen-Seite:** „Rechnung schreiben“ öffnet denselben neuen Dialog, mit Kundenwahl davor. Die Rechnungstabelle
bekommt die Spalten Projekt und Art.
**Arbeitsblätter (Ticketseite):** Ein abgerechnetes Arbeitsblatt zeigt das Abzeichen „abgerechnet (Rechnungsnummer)“.
Ändert jemand dessen Zeit, warnt der Dialog: „Dieses Arbeitsblatt ist abgerechnet (WEBKLAR-0009). Eine Änderung ändert
die Rechnung nicht.“ (Keine harte Sperre: WOMS schreibt Arbeitsblätter direkt in Appwrite.) Das Abzeichen sehen alle
Mitarbeiter (ohne Beträge).
### Dialog „Rechnung schreiben“ (neu)
1. **Projekt** (vorausgewählt, wenn vom Projektkonto geöffnet) oder „Ohne Projekt“.
2. **Zusammenfassung** des Projektkontos (siehe Abschnitt 4) und **Art**: Anzahlung, Schlussrechnung, Nach Aufwand,
Festbetrag. Anzahlung und Schlussrechnung sind nur mit gültiger Festpreis-AB wählbar (Hinweis statt Sperre ohne
Erklärung: „Anzahlung und Schlussrechnung brauchen eine Auftragsbestätigung.“).
3. **Positionen** (je Art vorausgefüllt, änderbar), **Leistungszeitraum** (optional, bei Aufwand vorausgefüllt),
Zahlungsziel (Standard 14 Tage), Notiz; Summe mit § 19-Hinweis.
4. **„Als Entwurf speichern“** oder **„Senden“** (mit Rückfrage wie heute; Adress-Sperre über 250 € wie heute).
### Dialog „Auftragsbestätigung“ (neu)
1. **Projekt** (oder freier Titel, wenn es noch kein Projekt gibt; später zuordenbar) und, falls leer, **Kundenart**.
2. **Auftrag:** Datum der Absprache und Weg (mündlich, telefonisch, per E-Mail, vor Ort, im Chat). Art **Festpreis**
(Positionen: Beschreibung, Menge, Einzelpreis; Grenzen wie bei Rechnungen) oder **Nach Aufwand** (Stundensatz,
Standard 100 €; Schätzung in Stunden, optional, in 15-Minuten-Schritten).
3. **Zahlung und Termine:** Zahlungsplan „Alles nach Abnahme“ · „Anzahlung x % bei Auftrag, Rest nach Abnahme“
(Standard 50 %) · „Monatlich nach Aufwand“ (nur Aufwand) · „Frei“ (Text, höchstens 500 Zeichen); Zahlungsziel
(Standard 14 Tage); Start und voraussichtliche Fertigstellung (optional); Zeitquelle (Tickets des Kunden, Standard:
Ticket des Projekts).
4. **Vorschau** (PDF, nicht gespeichert) → „Als Entwurf speichern“ oder „Senden“ (mit Rückfrage).
## 2. Auftragsbestätigung (AB)
### Nummer und Status
- Nummer **`AB-JJJJ-NNNN`**, fortlaufend je Kalenderjahr (Berliner Datum des Sendens), vergeben erst beim Senden unter
einer Sperre im Portal-Server (eine Instanz). Entwürfe haben keine Nummer. Scheitert das Senden vor dem Speichern des
PDFs, wird keine Nummer verbraucht.
- Status: `entwurf` → `versendet` → `bestaetigt` → `abgerechnet` (nach finalisierter Schlussrechnung); außerdem
`storniert` (von Hand, ohne Mail), `ersetzt` (durch eine neue Fassung) und `widerrufen` (Privatperson).
- **Unveränderlich nach dem Senden.** Änderung = „Neue Fassung“: Kopie als neuer Entwurf mit Vermerk
„Ersetzt AB-… vom …“; beim Senden der neuen AB wird die alte `ersetzt`. Rechnungen der alten AB wandern intern mit
(`projektRechnungen.auftragId` und Stripe-Metadaten auf die neue AB); der auf finalisierten Rechnungen aufgedruckte
Bezug bleibt unverändert.
### Inhalt des PDFs (A4)
- Kopf: WEBklar-Logo, WEBklar GbR, Anschrift, Kontakt, **Steuernummer** (Einstellung `financeSettings.steuernummer`).
- Empfänger: Firma bzw. Name, Anschrift (Straße, PLZ, Ort Pflicht für das Senden – auch unter 250 €).
- Titel „Auftragsbestätigung“, Nummer, Datum, Projekt bzw. Titel, Kundennummer (falls vorhanden).
- Einleitung: „Vielen Dank für Ihren Auftrag vom TT.MM.JJJJ (telefonisch). Wir bestätigen ihn wie folgt:“
- Leistungsumfang: Positionen mit Menge, Einzelpreis, Summe und Gesamtpreis **oder** Aufwandsregel („100 € pro
Stunde in Schritten von 15 Minuten; unterstützt eine weitere Person, berechnen wir für sie die Hälfte der
Arbeitszeit (AGB Nr. 9). Geschätzter Umfang: etwa X Stunden (unverbindlich). Zeichnet sich eine deutliche
Überschreitung ab, melden wir uns vorher.“); Hinweis „Gemäß § 19 UStG wird keine Umsatzsteuer berechnet.“
- Zahlungsplan mit Beträgen (z. B. „Anzahlung 50 % (400,00 €) nach Auftragsbestätigung, Rest (400,00 €) nach Abnahme;
zahlbar jeweils innerhalb von 14 Tagen nach Rechnung“), Termine.
- Mitwirkung und Abnahme laut AGB Nr. 7 und 8; „Es gelten unsere Allgemeinen Geschäftsbedingungen in der Fassung vom
…, die dieser Bestätigung beiliegen.“
- **Privatperson:** zusätzliche Seiten „Widerrufsbelehrung“ (Projektauftrag) und „Muster-Widerrufsformular“.
- Fuß: Bankverbindung (`financeSettings.bankDetailsText`), Kontakt, Seitenzahl.
- Texte und Fassung kommen aus einer Konstante im Server (`texteFassung`, Muster wie `config.termine`), die AB merkt sich
die Fassung.
### Erzeugung und Ablage
- PDF mit **pdfkit** (reines JavaScript; kein Chrome zur Laufzeit wegen knappem Arbeitsspeicher), Schrift Figtree aus
`public/fonts`, Logo aus `public/img`.
- Beim Senden einmal erzeugt und unter `upload-data/auftraege/<kundenId>/<abId>.pdf` abgelegt, SHA-256 im Dokument;
danach wird nur noch diese Datei ausgeliefert. Die Datei liegt in der Hetzner-Serversicherung; jede Mail geht zudem
per BCC an das Team-Postfach.
- Die **AGB** werden als versionierte PDF-Datei ins Portal gedruckt (`public/rechtliches/agb-<fassung>.pdf` mit
`.sha256`, über `scripts/build-legal-pdfs.sh`, Quelle: Website-AGB). Jede AB merkt sich `agbFassung`.
### Versand
- Mail **K31 „Auftragsbestätigung“** (Kunde, Design der Mailvorlagen, BCC Team): kurzer Text, Anhänge AB-PDF und
AGB-PDF, Knopf **„Auftrag bestätigen“** → `https://project.webklar.com/auftrag.html?t=<token>`; bei Privatpersonen
Hinweis auf Widerrufsbelehrung und Frühbeginn.
- Token: 32 Zufallsbytes (base64url), gespeichert nur als SHA-256, gültig 60 Tage ab dem letzten Versand.
**„Erinnerung senden“** (WOMS, von Hand) schickt K31 erneut mit Erinnerungs-Hinweis und neuem Token (der alte gilt
nicht mehr).
- Mail-Status am AB (`mailStatus`: gesendet / nicht zugestellt + Zeitpunkt); bei Fehler „Erneut senden“.
### Bestätigung
- **Öffentliche Seite** `auftrag.html?t=…` (ohne Anmeldung, `noindex`, Ratenbegrenzung je IP im Arbeitsspeicher wie
bei der Widerrufsseite): Zusammenfassung (Nummer, Datum, Projekt, Positionen bzw. Aufwandsregel, Summe,
Zahlungsplan, Termine), Links auf AB-PDF und AGB-PDF, Häkchen „Ich bestätige den Auftrag zu diesen Bedingungen.“,
Knopf „Auftrag bestätigen“. Nach der Bestätigung zeigt die Seite „Bestätigt am …“. Ungültiger oder abgelaufener Link:
Erklärung und Kontaktweg.
- **Privatperson:** zweites, freiwilliges Häkchen mit der Frühbeginn-Erklärung (Wortlaut aus der Rechtsrunde, Muster
der Funktionen). Ohne Häkchen zeigt WOMS „Arbeitsbeginn frühestens am TT.MM.“ = Tag nach
`withdrawalDeadline(gesendetAm)`.
- **Im Portal** (angemeldet): Reiter Rechnungen → Abschnitt „Aufträge“ mit Status, PDF und demselben Bestätigen-Ablauf.
- **Kundenansicht:** Bestätigen ist gesperrt (Erklärung im Namen des Kunden; der Endpunkt steht nicht auf der
Freigabeliste der Kundenansicht-Sperre).
- **Protokoll am AB:** `bestaetigtAm`, `bestaetigtUeber` (`link` | `portal`), `bestaetigtPdfSha256` (muss
`pdfSha256` entsprechen), `texteFassung`, `fruehbeginn` (ja/nein) und dessen Textfassung. **Keine IP-Adresse.**
Eine zweite Bestätigung ändert nichts (die erste zählt).
- Danach: Mail **K32 „Auftrag bestätigt“** an den Kunden (Eingangsbestätigung mit Datum, Uhrzeit, Frühbeginn ja/nein)
und **T22 „Auftrag bestätigt“** an das Team.
### Widerruf (Privatperson)
- `widerrufen.html` bekommt die Vertragsart **„Projektauftrag (Auftragsbestätigung)“** mit Feld AB-Nummer. Die
Erklärung läuft über den bestehenden Weg (Eingangsbestätigung an den Kunden, Team-Mail); passt die AB zu Nummer und
E-Mail-Adresse, wird sie `widerrufen` und WOMS zeigt es. Erstattung und Wertersatz bleiben Handarbeit.
## 3. Rechnungen
Alle Projektrechnungen bleiben **Stripe-Rechnungen** (`collection_method: send_invoice`, Mail mit PDF und Zahlungsseite,
Karte/SEPA, Bankabgleich und Finanzen-Übersicht unverändert).
### Arten
| Art | Voraussetzung | Positionen |
|---|---|---|
| `anzahlung` | AB *Festpreis*, Status `versendet` oder `bestaetigt` | Eine Zeile „Anzahlung 50 % gemäß Auftragsbestätigung AB-2026-0002 vom 06.10.2026 (Projekt)“; Betrag aus dem Zahlungsplan, änderbar in % oder €, höchstens AB-Summe minus bereits Berechnetes. Bezeichnung änderbar (auch „Teilrechnung“). |
| `schluss` | AB *Festpreis*, Status `versendet` oder `bestaetigt`; kein offener Entwurf zur AB | Alle AB-Positionen, zusätzliche Positionen (Nachträge) möglich, dann je früherer finalisierter, nicht stornierter Rechnung zur AB eine **Minuszeile** („abzüglich Anzahlung, Rechnung WEBKLAR-0007 vom 08.10.2026“, negativer `amount`). Summe muss > 0 sein, sonst „Nichts mehr offen“. Nach dem Finalisieren: AB `abgerechnet` (Storno setzt zurück). |
| `aufwand` | Projekt; Zeitquelle mit noch nicht abgerechneten Arbeitsblättern | Alle offenen Arbeitsblätter der Zeitquelle (ohne Abo-Zeit, ohne reine Kommentare, ohne 0 Minuten) sind vorausgewählt und abwählbar; **eine Zeile je Tag**: „06.10.2026: Startseite, Kontaktformular – 2,25 Std. à 100,00 €“ (Menge 1, Betrag = Stunden × Satz). Minuten je Arbeitsblatt wie `worksheetMinutes` (inkl. Unterstützung), je Eintrag auf 15 Minuten aufgerundet. Zeilentext = Kurzfassungen der Arbeitsblätter des Tages, gefiltert wie in der Kundenansicht (`customerProjectView`: ohne Git-Anhänge, SHAs, Kontonamen), auf 200 Zeichen gekürzt, im Dialog änderbar. Satz aus der jüngsten gültigen Aufwands-AB des Projekts, sonst 100 €. |
| `festbetrag` | Projekt | Freie Positionen wie heute (Frau Wolf: „Erstellung der Website anelia-wolf.com, Pauschalpreis laut Absprache“, 500 €). |
| `frei` | „Ohne Projekt“ gewählt | Freie Positionen wie bisher, ohne Zusatzfelder „Projekt“/„Auftrag“ (aber A4). |
Grenzen wie heute (1–20 Positionen inkl. Minuszeilen; Beschreibung ≤ 200 Zeichen). Ergäbe eine Aufwandsrechnung mehr
als 20 Zeilen (zusätzliche Positionen zählen mit), werden die ältesten Tage zu Kalenderwochen zusammengefasst
(„KW 41/2026: … – 6,5 Std. à 100,00 €“), bis höchstens 20 Zeilen bleiben.
### Auf jeder Projektrechnung
- **Zusatzfelder** (`custom_fields`): „Projekt“ (Name, auf 140 Zeichen gekürzt), „Auftrag“ („AB-2026-0002 vom
06.10.2026“, nur mit AB), „Leistungszeitraum“ (bei Aufwand erster bis letzter Tag, sonst optional; immer änderbar).
- **Metadaten:** `webklarCustomerId`, `quelle`, `erstelltVon` (wie heute) plus `projektId`, `projektName`, `auftragId`,
`auftragNummer`, `art`.
- **`rendering.pdf.page_size = 'a4'`** – auch im bisherigen Dialog.
- Fuß unverändert (§ 19-Hinweis, Bankverbindung, Verwendungszweck).
### Abgerechnete Arbeitsblätter, Storno
- Jede Projektrechnung bekommt ein Dokument in `projektRechnungen` (Kennung = Stripe-Rechnungs-ID) mit Projekt, AB,
Art, Betrag und den **abgerechneten Arbeitsblatt-IDs**. Beim Anlegen prüft der Server, dass keines der Blätter in einer
aktiven Projektrechnung steckt (sonst 409 „Arbeitsblatt bereits abgerechnet“).
- Entwurf löschen oder Rechnung stornieren setzt das Dokument auf `aktiv = false`: die Blätter sind wieder frei, der
Betrag zählt nicht mehr als berechnet; eine Schlussrechnung setzt die AB auf den vorigen Status zurück.
### Steuernummer
- **Sofort, unabhängig vom Bau (Nutzer):** Steuernummer der GbR im Stripe-Dashboard (Einstellungen → Billing →
Rechnungen → „Steuerinformationen für Rechnungen“) hinterlegen und als Standard setzen – dann steht sie auf allen
Rechnungen, auch Abo-Rechnungen.
- Beim Livegang an einem Entwurfs-PDF prüfen, wie Stripe sie beschriftet. Ist die Beschriftung unpassend, kommt sie
stattdessen in den Rechnungsfuß (`invoiceFooter`, und per Skript in `invoice_settings.footer` bestehender
Stripe-Kunden).
- Für das AB-PDF: neues Einstellungsfeld `steuernummer` in WOMS Finanzen → Einstellungen.
### Kundenportal
- Die Rechnungsliste zeigt zusätzlich das Projekt (`metadata.projektName`).
## 4. Projektkonto (Rechenregeln)
Je Projekt, berechnet im Portal-Server bei jeder Anfrage (Stripe-Daten aus dem bestehenden 60-Sekunden-Zwischenspeicher;
fehlt eine Rechnung dort, wird sie einzeln gelesen):
| Feld | Regel |
|---|---|
| Vereinbart | Summe `summeCents` aller Festpreis-ABs des Projekts mit Status `versendet`, `bestaetigt` oder `abgerechnet`. Aufwands-ABs: Text „nach Aufwand, <Satz> €/Std.“ (mit Schätzung). Ohne AB: „keine AB“. |
| Berechnet | Summe der Rechnungen aus aktiven `projektRechnungen` des Projekts, die finalisiert sind (offen, überfällig, bezahlt, uneinbringlich); Entwürfe und Stornos zählen nicht. |
| davon bezahlt | Summe `amount_paid` dieser Rechnungen. |
| Noch offen | Festpreis: Vereinbart − Berechnet (nie unter 0). Aufwand: Wert der noch nicht abgerechneten Zeit. Ohne AB: „–“. |
| Zeit erfasst | Minuten aller Arbeitsblätter der Zeitquelle ohne Abo-Zeit (Regeln wie Aufwand), darunter „Wert x €“ zum Satz der AB bzw. 100 €; Abo-Zeit getrennt als „über Abo: y Std.“. |
Zeitquelle eines Projekts: Vereinigung der Zeitquellen seiner ABs, sonst das Ticket des Projekts; im Rechnungsdialog
können weitere Tickets des Kunden dazugenommen werden. „Ohne Projekt“: Stripe-Rechnungen des Kunden ohne aktive
`projektRechnungen`.
## 5. Daten (Appwrite `woms-database`, nur Server-Schlüssel)
Neue Collections (Setup-Skript `scripts/auftraege-setup.mjs`, idempotent wie `setup-finanzen.mjs`):
- **`auftraege`**: `nummer`, `jahr`, `laufnummer`, `status`, `kundenId`, `kundeJson` (Name, Firma, Anschrift, E-Mail,
Kundenart zum Zeitpunkt des Sendens), `projektId`, `titel`, `zeitquelleTicketIds` (string[]), `art`
(`festpreis` | `aufwand`), `positionenJson`, `stundensatzCents`, `schaetzungMinuten`, `summeCents`,
`zahlungsplanJson`, `zahlungszielTage`, `auftragVom`, `auftragWeg`, `startAm`, `fertigBis`, `texteFassung`,
`agbFassung`, `pdfDatei`, `pdfSha256`, `gesendetAm`, `gesendetAn`, `mailStatus`, `mailFehler`, `tokenHash`,
`tokenGueltigBis`, `bestaetigtAm`, `bestaetigtUeber`, `bestaetigtPdfSha256`, `fruehbeginn`, `fruehbeginnFassung`,
`ersetztId`, `ersetztDurchId`, `storniertAm`, `widerrufenAm`, `erstelltVon`, `erstelltAm`, `geaendertAm`.
Indizes: `kundenId`, `projektId`, `nummer` (unique), `tokenHash`.
- **`projektRechnungen`**: Dokument-ID = Stripe-Rechnungs-ID; `kundenId`, `projektId`, `auftragId`, `art`,
`betragCents`, `arbeitsblattIds` (string[]), `aktiv` (bool), `erstelltVon`, `erstelltAm`. Indizes: `kundenId`,
`projektId`, `auftragId`.
Neue Attribute: `customers.kundenart` (`''` | `unternehmen` | `privat`), `financeSettings.steuernummer`.
Stripe-Rechnungen werden weiter nicht gespiegelt.
## 6. Schnittstellen (Portal-Server)
Admin (unter dem bestehenden Finanzen-Router, `requireAppwriteStaff`):
| Methode, Pfad | Zweck |
|---|---|
| GET `/api/admin/finanzen/projektkonten?kunde=` | Projektkonten des Kunden inkl. „Ohne Projekt“ |
| GET `/api/admin/finanzen/projektkonten/:projektId` | Ein Projektkonto mit ABs, Rechnungen, offenen Arbeitsblättern |
| GET, POST `/api/admin/finanzen/auftraege` (`?kunde=`) | ABs lesen, Entwurf anlegen |
| GET, PATCH, DELETE `/api/admin/finanzen/auftraege/:id` | Lesen; Entwurf ändern/löschen |
| GET `/api/admin/finanzen/auftraege/:id/pdf` | Gespeichertes PDF bzw. Vorschau eines Entwurfs |
| POST `/api/admin/finanzen/auftraege/:id/senden` | Nummer, PDF, Ablage, Mail |
| POST `/api/admin/finanzen/auftraege/:id/erinnern` | K31 erneut, neuer Token |
| POST `/api/admin/finanzen/auftraege/:id/neue-fassung` | Kopie als Entwurf |
| POST `/api/admin/finanzen/auftraege/:id/stornieren` | Status `storniert` |
| POST `/api/admin/finanzen/rechnungen` | erweitert: `{ customerId, projektId?, auftragId?, art, items, arbeitsblattIds?, leistungszeitraum?, daysUntilDue, memo, senden, idempotencyKey }`; Positionen für `anzahlung`/`schluss`/`aufwand` baut der Server nach (Minuszeilen nur serverseitig) |
| PATCH `/api/admin/finanzen/einstellungen` | erweitert um `steuernummer` |
Alle Mitarbeiter (`/api/team`, `requireStaffJwt`): GET `/api/team/arbeitsblaetter/abgerechnet?ticketId=` →
`{ worksheetId: rechnungsnummer }` (ohne Beträge).
Kunden: GET `/api/auftraege` (eigene ABs), GET `/api/auftraege/:id/pdf`, POST `/api/auftraege/:id/bestaetigen`
(Portal-Sitzung; in der Kundenansicht gesperrt).
Öffentlich (Token): GET `/api/auftrag/:token`, GET `/api/auftrag/:token/pdf`, POST `/api/auftrag/:token/bestaetigen`
(`{ bestaetigt: true, fruehbeginn }`).
Widerruf: `/api/abo/public/withdraw` akzeptiert `order = projekt` mit `abNummer`.
Kundenart: WOMS schreibt `customers.kundenart` direkt (wie die übrigen Kundenfelder).
## 7. Mails
Nächste freie Kennungen zum Zeitpunkt des Baus prüfen (derzeit K31, K32, T22):
- **K31 Auftragsbestätigung** (Kunde; Anhänge AB-PDF und AGB-PDF; Variante Erinnerung; Variante Privatperson).
- **K32 Auftrag bestätigt** (Kunde; Eingangsbestätigung, Frühbeginn ja/nein).
- **T22 Auftrag bestätigt** (Team).
- Widerruf eines Projektauftrags nutzt die bestehenden Widerrufs-Mails (K04/T09) mit der neuen Vertragsart.
## 8. Rechtsrunde (vor dem Livegang von Plan 2)
Der Nutzer gibt frei; danach lädt Claude die Fassungen in WOMS → Rechtliches hoch:
- Textbausteine der AB (Einleitung, Aufwandsregel, Zahlungsplan, Mitwirkung/Abnahme, AGB-Satz).
- Widerrufsbelehrung für Projektaufträge und Muster-Widerrufsformular (Bauart wie `widerrufsbelehrung-funktionen`).
- Frühbeginn-Erklärung (Wortlaut nach Muster der Funktionen, passende Norm prüfen).
- Datenschutzerklärung: neuer Abschnitt „Auftragsbestätigungen“ (verarbeitete Daten, Bestätigungslink, keine IP,
Aufbewahrung 6 bzw. 8 Jahre) und Ergänzung des Widerrufs-Abschnitts.
- AGB als PDF (Fassung, aus der Website gedruckt).
## 9. Fehlerverhalten
| Fall | Verhalten |
|---|---|
| Stripe nicht erreichbar | Rechnung scheitert mit „Stripe ist gerade nicht erreichbar.“; ABs und Projektkonto (ohne Stripe-Teil, markiert) gehen weiter |
| PDF-Erzeugung scheitert | AB bleibt Entwurf, keine Nummer verbraucht, Meldung im Dialog |
| Mail scheitert | AB ist `versendet`, `mailStatus` „nicht zugestellt“, Knopf „Erneut senden“ |
| Link ungültig/abgelaufen | Seite erklärt es, nennt den Kontaktweg; WOMS kann per „Erinnerung senden“ einen neuen Link schicken |
| Doppelte Bestätigung | folgenlos, die erste zählt |
| Arbeitsblatt schon abgerechnet | 409 mit Rechnungsnummer |
| Schlussrechnung mit offenem Entwurf zur AB | 409 „Erst den Entwurf … senden oder löschen.“ |
| Fehlende Anschrift beim AB-Senden | 409 „Adresse des Kunden unvollständig“ |
| Kundenart leer beim AB-Senden | 400 (der Dialog fragt vorher) |
## 10. Tests
- **Unit** (`node --test`): Projektkonto-Regeln; Positionsbau je Art (Minuszeilen, Tageszeilen, 15-Minuten-Rundung,
Unterstützung, Wochen-Zusammenfassung über 20 Tage); AB-Nummern (Jahreswechsel, Sperre); Token (Hash, Ablauf,
Erneuerung); Frühbeginn-Datum; PDF enthält Pflichtangaben (Text aus dem PDF gelesen) und bei Privatpersonen die
Belehrung; Stripe-Parameter (`custom_fields`, `page_size`, Metadaten) gegen das Fake-Stripe.
- **Server-Ablauf** mit Appwrite-Nachbau, Fake-Stripe und SMTP-Senke (Rezept `kundenportal-testing`): AB anlegen →
senden → bestätigen (Link und Portal) → Anzahlung → Schlussrechnung; Aufwand mit Arbeitsblättern; Storno gibt frei;
Kundenansicht kann nicht bestätigen; Widerruf eines Projektauftrags.
- **WOMS:** Lib-Tests der Ansichtslogik; Bildschirmfotos über das Prüfwerkzeug des Redesigns (Desktop und Handy).
- **Kundenseiten:** Playwright (Handy, Desktop) gegen Fixture-Server.
- **Live:** nur Entwürfe mit dem Testkunden des Nutzers; nichts an echte Kunden.
## 11. Pläne und Livegang
- **Plan 1 – Rechnungen mit Projektkonto:** `projektRechnungen`, Projektkonto, Arten `aufwand`, `festbetrag` und
`frei`, Zusatzfelder, Metadaten, A4, Abzeichen „abgerechnet“, Projekt in der Kunden-Rechnungsliste,
WOMS-Oberfläche (Projektkonto, neuer Rechnungsdialog, Reiter umbenannt). In Plan 1 zeigt *Vereinbart* immer
„keine AB“. Keine Rechtsrunde nötig.
- **Plan 2 – Auftragsbestätigung:** `auftraege`, Kundenart, Steuernummer-Einstellung, PDF, Mails, Bestätigungsseite
und Portal-Abschnitt, Verbraucher-Variante, Widerruf, Arten `anzahlung` und `schluss` (mit Minuszeilen),
WOMS-Dialog und -Liste, Rechtsrunde.
- **Reihenfolge je Plan:** Portal-Server und Kundenseiten zuerst; WOMS-Oberfläche im neuen Design auf Basis der
zusammengeführten Redesign-Stränge Kunden (P4) und Finanzen (P7). Sind die Stränge beim Start der WOMS-Aufgaben noch
nicht zusammengeführt, wird mit dem Nutzer entschieden (warten oder auf `test` bauen und eine Mitnahme-Notiz für das
Redesign hinterlegen).
- **Livegang:** (0) Nutzer hinterlegt die Steuernummer in Stripe; (1) Setup-Skript; (2) Nutzer pusht Portal `main`;
(3) Nutzer pusht WOMS `test`; (4) Prüfung mit Testkunde (Entwürfe, PDF-Ansicht); bei Plan 2 vorher Rechtsrunde und
Upload in Rechtliches.
## Nicht im Umfang
Angebote vor dem Auftrag, Mahnwesen (Stripe-Erinnerungen wie heute), E-Rechnung, Abrechnung des Abo-Mehraufwands
(`hourLedger`), Nachlass-/Rabattzeilen, eigene Archivkopien der Stripe-PDFs, englische ABs, mehrere Währungen,
Projektmarge und Auswertungen, automatische Erinnerungen.
## Quellen
- § 34a UStDV: https://lxgesetze.de/ustdv/34a
- § 14b UStG: https://lxgesetze.de/ustg/14b
- § 312f BGB: https://www.haufe.de/id/norm/buergerliches-gesetzbuch-312f-abschriften-und-bestaetigungen-HI1049155_p312f.html
- Auftragsbestätigung (Inhalt): https://www.lexware.de/wissen/faktura-warenwirtschaft/auftragsbestaetigung-bedeutung-und-inhalt/
- Stripe Rechnungen anpassen: https://docs.stripe.com/invoicing/customize
- Agentur-Abrechnung (Festpreis/Aufwand): https://everhour.com/de/invoices/rechnungssoftware-fuer-digitalagenturen