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:
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
|
||||
Reference in new issue
Block a user