Feinkonzept §4.5 — Bau-Abrechnung (XRechnung / ZUGFeRD / Peppol, VOB/B §14)
Feinkonzept §4.5 — Bau-Abrechnung (XRechnung / ZUGFeRD / Peppol, VOB/B §14)
Abschnitt betitelt „Feinkonzept §4.5 — Bau-Abrechnung (XRechnung / ZUGFeRD / Peppol, VOB/B §14)“1. Header / Meta
Abschnitt betitelt „1. Header / Meta“| Feld | Wert |
|---|---|
| feature_id | handwerk/05-bau-abrechnung |
| titel | Bau-Abrechnung: XRechnung 3.0, ZUGFeRD 2.3, Peppol BIS 3, VOB/B-Teilzahlung, §13b/§48 |
| version | V1.0 (MVP) — Stand 19.04.2026 |
| status | DOR angenommen → Implementierung Sprint 26-Q2-A |
| referenzkunde | musterbetrieb-maler (25-MA Maler Bayern, ~410 Rechnungen/Jahr) · shk-gebruder-schmidt (80-MA SHK NRW, ~1 800 Rechnungen/Jahr, Kommunal-Kunden Peppol-pflichtig) |
| modul | Handwerk → §4.5 (Produktreifegrad V1 Pflicht: GAEB §4.3 + Aufmaß §4.4 + Nachtrag §4.2) |
| business-owner | Paul Wagner (CFO Referenz-Beirat Werkszeit) |
| tech-owner | Ingenieursteam „Rechnung“ (2 Backend, 1 Frontend, 1 QA, 0,4 Compliance) |
| geschätzt | 84 Ingenieur-Tage (MVP) · +22 Tage Härtung · ∑ 106 Tage bis GA |
| kritikalität | Geschäftskritisch — ohne konforme eRechnung kein Geldfluss; §14 VOB/B Ausschluss möglich |
| compliance-flags | GoBD §147 AO (10 J.), §14 UStG, §14 Abs. 4 UStG, XRechnung 3.0 (KoSIT), EN 16931, §13b UStG, §48 EStG, §14 VOB/B, VOB/C DIN 18299, §4 Abs. 1 E-Rech-V, Peppol BIS 3.0, DSGVO Art. 6/17/30 |
| quellen | KoSIT XRechnung-Spezifikation 3.0.2 (Stand 2025-11-15) · EN 16931-1:2017+A1:2019+A2:2023 · Mustang-Library 2.14 · KoSIT-Validator 1.5.0 · OpenPeppol BIS Billing 3.0.17 · UStAE 13b.1 · § 48 EStG i.V.m. § 48b EStG · BFA-Merkblatt 2025 |
2. Kontext & Problem
Abschnitt betitelt „2. Kontext & Problem“2.1 Problemlage bei Handwerksbetrieben heute
Abschnitt betitelt „2.1 Problemlage bei Handwerksbetrieben heute“Der Bau ist im Pflicht-eRechnungs-Umstellungsjahr. Seit 01.01.2025 muss jeder Unternehmer in DE eine strukturierte eRechnung empfangen können (Wachstumschancengesetz, § 14 Abs. 1 Satz 2 UStG n.F.), die Pflicht zum Versand an B2B-Empfänger greift für KMU ab 01.01.2027 (Übergang 01.01.2028 für Kleinunternehmer < 800 k€ Vorjahresumsatz). Für öffentliche Auftraggeber gilt bereits seit 27.11.2020 die ERechV (Bundesverwaltung) plus 16 Landes-Verordnungen mit je eigenen Leitweg-IDs (Bund: 991-xxxxx-yyyy, Bayern: 04, NRW: eigene Regionsschlüssel via Mandanten-Leitweg).
Zwei Formate sind spezifikations-konform:
- UBL 2.1 (OASIS Universal Business Language) — XML-Namespace
urn:oasis:names:specification:ubl:schema:xsd:Invoice-2, empfohlen für Peppol-Flow. - UN/CEFACT CII (Cross Industry Invoice, D16B) — XML-Namespace
urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100, Basis von ZUGFeRD.
Beide werden durch CIUS XRechnung (Core Invoice Usage Specification) eingeschränkt, sind aber nicht deckungsgleich: So ist BT-115 Paid amount in UBL optional, in XRechnung 3.0 bei Teilzahlungen bedingt Pflicht; BG-20 Document level allowances in CII technisch anders modelliert.
Hybride: ZUGFeRD 2.3 legt CII-XML als Attachment in ein PDF/A-3 — Vorteil: Lesbarkeit + Struktur, Nachteil: PDF/A-3 ist nicht von allen DMS akzeptiert (Adobe rendert, Archiv-Systeme teils nur PDF/A-1/2). Profile: MINIMUM, BASIC, EN 16931, EXTENDED, XRECHNUNG (Zielprofil für DE-eRechnung).
Peppol BIS 3.0 Billing: Für Grenzüberschreitenden Versand (EU öffentliche Auftraggeber, Skandinavien mandatorisch, NL/BE teils Pflicht) und optional als 4-Corner-Model (Sender-AP → SMP-Lookup → Receiver-AP → Empfänger). DE-Peppol-Authority (KoSIT) schreibt ISO 20022 Schematron vor.
2.2 VOB/B §14 — Prüfbarkeit von Bau-Rechnungen
Abschnitt betitelt „2.2 VOB/B §14 — Prüfbarkeit von Bau-Rechnungen“VOB/B § 14 Abs. 1: “Die Rechnungen sind übersichtlich aufzustellen”. § 14 Abs. 2: “Teilrechnungen” — monatlich zulässig, kumulative Aufstellung (Bruttoleistung bis Stichtag ./. bereits geleistete Abschläge = Zahlungs-Saldo). § 14 Abs. 3: “Schlussrechnung” — prüfbar muss enthalten: vollständige Positions-Nachweise, Aufmaß-Referenzen (→ §4.4), Preisermittlung, Nachträge (→ §4.2). § 14 Abs. 4: Prüfungs-Frist Auftraggeber — Teilrechnung 21 Tage, Schlussrechnung 30 Tage (bei Bund 60 Tage).
Eine XRechnung, die VOB-Teilrechnung abbildet, muss kumulativ sein. EN 16931 kennt BT-113 Paid amount (bereits gezahlt), aber die kumulative Logik (bis-heute-Brutto ./. bis-gestern-gezahlt = heutiger Zahlbetrag) ist nicht im Core-Modell — wir lösen das über INVOICE_TYPE_CODE=386 Prepayment invoice bzw. CIUS-XRechnung-Erweiterung + BG-22 Document totals mit vertraglich vereinbarter Kumulations-Logik. Die Kumulation wird im Begleittext (BT-22 Invoice note) menschen-lesbar ausgewiesen — KoSIT-Validator meckert sonst nicht, aber der Architekt tut es.
2.3 §13b UStG — Reverse-Charge im Bau
Abschnitt betitelt „2.3 §13b UStG — Reverse-Charge im Bau“Reverse-Charge (Umkehr der Steuerschuldnerschaft) gilt nach §13b Abs. 2 Nr. 4 i.V.m. Abs. 5 Satz 2 UStG für Bauleistungen an Bau-Unternehmer. Kriterium: Der Leistungsempfänger führt selbst nachhaltig Bauleistungen aus (> 10 % Umsatzanteil) und legt Bescheinigung USt 1 TG (gültig 3 Jahre) vor. Rechtsfolge:
- Rechnungssteller schreibt Netto aus, kein USt-Ausweis.
- Hinweis nach § 14 Abs. 4 Nr. 8 UStG zwingend: “Steuerschuldnerschaft des Leistungsempfängers”.
- In XRechnung:
BT-96 VAT category code = AE(VAT Reverse Charge) auf EbeneBG-23 VAT breakdown. BT-118 VAT category rate= 0,00 Prozent,BT-120 VAT exemption reason text= „§ 13b UStG“.
Bei fehlerhafter Anwendung haftet der Rechnungssteller für die USt. Bei fehlerhafter Nicht-Anwendung (Ausweis von USt bei Reverse-Charge) droht dem Empfänger der Vorsteuer-Ausschluss.
2.4 §48 EStG — Bauabzugssteuer 15 %
Abschnitt betitelt „2.4 §48 EStG — Bauabzugssteuer 15 %“Leistungsempfänger (Unternehmer oder JurPerson öffentl. Rechts) muss 15 % des Rechnungsbetrags einbehalten und ans FA des Leistenden abführen — es sei denn, Leistender legt gültige Freistellungsbescheinigung nach § 48b EStG vor. Bagatellgrenze: Rechnung ≤ 5 000 € (Bau-Unternehmer) bzw. ≤ 15 000 € (Vermieter mit ≤ 2 Wohnungen). In XRechnung nicht abbildbar (keine BT-Referenz); wir informieren über BT-22 Invoice note mit Bescheinigungs-Nr + Gültigkeit. Im Abrechnungs-Workflow blockieren wir den Versand, wenn Freistellung fehlt ∧ Betrag > Bagatelle.
2.5 Warum das im Handwerk ungelöst ist
Abschnitt betitelt „2.5 Warum das im Handwerk ungelöst ist“- DATEV Lexware, Sage u. a. können XRechnung — aber nicht kumulative Teilrechnungen nach VOB/B §14 Abs. 2 (Workaround: Handschlag-PDFs).
- Aufmaß-→-Rechnung-Kopplung fehlt in fast allen Office-Suiten; LV-Positionen werden doppelt gepflegt.
- Peppol-AP-Anbindung ist Kapitän-schwierig (Zertifikat-Management, SMP-Lookup, Delivery-Receipts).
- KoSIT-Validator lokal zu betreiben ist Java-lastig (JRE 17+) — Handwerk will SaaS, nicht Java-Installer.
2.6 Unser Lösungsraum
Abschnitt betitelt „2.6 Unser Lösungsraum“Wir bauen eine Rechnung-Engine, die aus LV-Positionen (→ §4.3) + freigegebenem Aufmaß (→ §4.4) + akzeptierten Nachträgen (→ §4.2) XRechnung-UBL + PDF/A-3 ZUGFeRD-Profil XRECHNUNG erzeugt, mit kumulativer VOB-Teil-/Schlussrechnungs-Logik, Reverse-Charge-Pflichthinweis-Automatik und Peppol-Versand via Access-Point. Empfangene eRechnungen werden automatisch validiert (KoSIT 1.5.0 + Mustang 2.14 + Peppol-Schematron) und als prüffähig gegen den korrespondierenden LV-Plan abgeglichen.
Ziel: 99,5 % KoSIT-Konformität beim Erstversuch, TTR < 3 Minuten vom Aufmaß-Freigabe bis versandbereite XRechnung, Zero-Manual-Entry bei VOB-Teilrechnungs-Kumulation.
3. Personas
Abschnitt betitelt „3. Personas“3.1 Primär: Paul Wagner — CFO shk-gebruder-schmidt
Abschnitt betitelt „3.1 Primär: Paul Wagner — CFO shk-gebruder-schmidt“- Kontext: 80-MA-SHK-Betrieb, 1 800 Rechnungen/Jahr, davon ~210 Kommunal (Peppol-pflichtig ab Leitweg), Rest Gewerbe / Privat.
- Gerät: Web am 27“-Bildschirm, Dual-Monitor; Outlook als zweites Fenster für PDF-Empfang.
- Schmerzen: Bislang 3,5 VZÄ in Buchhaltung, davon 1,5 NUR für Rechnungs-PDF-Aufbereitung + manuelle XRechnung-Erstellung in OpenXRechnung-Tool.
- Erfolg: < 10 Minuten/Rechnung, inkl. Prüfung. Peppol-Mandaten-Onboarding ohne IT-Dienstleister.
3.2 Primär: Anja Beltz — Leiterin Buchhaltung musterbetrieb-maler
Abschnitt betitelt „3.2 Primär: Anja Beltz — Leiterin Buchhaltung musterbetrieb-maler“- Kontext: 25-MA-Maler, ~410 Rechnungen/Jahr, kaum Peppol-Bedarf, aber §13b + §48 häufig (Subunternehmer-Ketten).
- Gerät: Laptop + Second-Screen. Ggf. Handy für Freigabe vom Inhaber.
- Schmerzen: §48 EStG vergessen → Haftung. Freistellungs-Bescheinigungen auf Papier im Ordner.
- Erfolg: System warnt vor Versand, wenn Bescheinigung abgelaufen oder fehlt; automatische Prüfung USt 1 TG bei §13b.
3.3 Sekundär: Mehmet Yılmaz — Monteur
Abschnitt betitelt „3.3 Sekundär: Mehmet Yılmaz — Monteur“- Kontext: Erstellt keine Rechnungen, aber signiert Aufmaß-Freigaben vor Ort (→ §4.4), die als Basis der Rechnung dienen. Bekommt Benachrichtigung, wenn Architekt die Rechnung zurückweist, weil Aufmaß unklar.
- Erfolg: Sieht Rechnungs-Status mit seinem Aufmaß-Block zugeordnet, kann binnen 24 h Rückfrage telefonisch klären.
3.4 Sekundär: Architektin Dr. Ina Pietsch (Rechnungs-Empfängerin Architektin des Bauherrn)
Abschnitt betitelt „3.4 Sekundär: Architektin Dr. Ina Pietsch (Rechnungs-Empfängerin Architektin des Bauherrn)“- Kontext: Prüft VOB-Teilrechnungen der Auftragnehmer. Will XRechnung + PDF/A-3-Visualisierung, Aufmaß-Referenz pro Position, Nachtrags-Positionen sauber ausgewiesen.
- Erfolg: Rechnung lässt sich maschinell ins Prüf-Programm (z. B. „California“ von RIB) importieren + VOB-konform prüfen.
4. User Stories (INVEST, DOR-konform)
Abschnitt betitelt „4. User Stories (INVEST, DOR-konform)“- US-05-01 · Als Buchhalterin will ich aus freigegebenen Aufmaß-Positionen (→ §4.4) eine XRechnung erzeugen, sodass ich keine LV-Position doppelt eintippe. (DOR: §1.1 Referenzkunde
shk-gebruder-schmidt· §1.2 Modul 4.5 · §1.3 §14 UStG, XRechnung 3.0) - US-05-02 · Als CFO will ich kumulative VOB/B-§14-Abs.-2-Teilrechnungen mit Abzug der bisherigen Abschläge, sodass der Architekt die Prüfbarkeit bestätigt.
- US-05-03 · Als Buchhalterin will ich bei §13b-Empfängern automatisch Reverse-Charge (USt=0, Hinweistext), sodass ich nicht haftbar werde.
- US-05-04 · Als CFO will ich vor Versand einen Block, wenn § 48 EStG Freistellung fehlt oder abgelaufen ist (und Betrag > 5 000 €), sodass Bauabzugssteuer korrekt abgeführt wird.
- US-05-05 · Als Buchhalterin will ich Versand via Peppol Access Point mit Delivery-Receipt, sodass Kommunal-Empfänger die Rechnung rechtssicher erhalten.
- US-05-06 · Als CFO will ich jede ausgehende XRechnung mit KoSIT 1.5.0 validiert bekommen (Schematron + Codeliste), sodass Retouren < 0,5 %.
- US-05-07 · Als Buchhalterin will ich eingehende XRechnungen automatisch parsen + mit LV abgleichen, sodass Abweichungen binnen 24 h auffallen.
- US-05-08 · Als Monteur will ich Push-Nachricht, wenn mein Aufmaß in eine Rechnung eingeflossen und bezahlt wurde, sodass ich die Baustelle abschließen kann.
- US-05-09 · Als Buchhalterin will ich Storno via § 14 Abs. 4 UStG + Gutschrift-Hinweis (INVOICE_TYPE_CODE=381), sodass GoBD-konform.
- US-05-10 · Als Datenschutzbeauftragte will ich Art.-17-DSGVO-Löschung nach Ablauf der 10-Jahres-GoBD-Frist, sodass Privatpersonen-Adressen nicht ewig vorgehalten werden.
- US-05-11 · Als IT-Admin will ich Peppol-Zertifikat-Rotation automatisch 30 Tage vor Ablauf, sodass Versand nicht stoppt.
- US-05-12 · Als CFO will ich DATEV-Export (DATEV-Format
Buchungsstapel-CSVv7 + XRechnung-XML als Beleg-Link), sodass Steuerberater arbeiten kann. - US-05-13 · Als Referenzkunde will ich Mandanten-Freigabe-Workflow (CFO sieht, Inhaber signiert) vor Versand bei Rechnungen > 25 000 €.
5. Funktionale Anforderungen
Abschnitt betitelt „5. Funktionale Anforderungen“5.1 Rechnungs-Erstellung (outbound)
Abschnitt betitelt „5.1 Rechnungs-Erstellung (outbound)“R-01 Engine generiert XRechnung 3.0.2 UBL 2.1 (default) ODER CII D16B (je Empfänger-Präferenz, Feld debitor.ausgabeformat).
R-02 Engine liest LV-Version (immutable_at > 0) + Aufmaß-Blöcke (immutable_at > 0) + akzeptierte Nachträge (status=akzeptiert). Keine anderen Quellen (keine „freien“ Positionen außer Abschlags-Einbehalte).
R-03 Kumulation VOB §14 Abs. 2: Engine zieht alle bisherigen Teilrechnungen derselben bauvorhaben_id heran, summiert bezahlte_beträge und einbehaltene_beträge (Sicherheits-Einbehalt 5 %, Gewährleistungs-Einbehalt 3 %), bildet zahlungsanspruch_aktuell = brutto_kumuliert - bereits_bezahlt_kumuliert - einbehalten_kumuliert.
R-04 Reverse-Charge-Automatik: Wenn debitor.ust_1_tg_bescheinigung.gueltig_bis >= heute → BT-96=AE, kein USt-Ausweis, Hinweistext Pflicht; GoBD-Log.
R-05 §48-EStG-Pre-Check: Wenn leistender.freistellungsbescheinigung_48b.gueltig_bis < versand_datum ∧ brutto > 5 000 € → Versand blockiert, Meldung mit Handlungsempfehlung.
R-06 Leitweg-ID: Pflichtfeld BT-10 Buyer reference für öff. Auftraggeber. System prüft Format via Regex pro Land (Bund \d{3}-[A-Z\d]{1,30}-\d{2}, Bayern 2-stellig, NRW mehrstellig). Fehler → Block.
R-07 PDF/A-3-Anhang (ZUGFeRD): Engine rendert Rechnung nach XSL-FO → FOP 2.9 → PDF/A-3 + embedded CII-XML (Name factur-x.xml).
R-08 Peppol-Versand: Wenn debitor.peppol_id vorhanden → SMP-Lookup (OpenPeppol Directory), AS4-Push zu Access Point (V1: B2BRouter Managed-AP, V2: eigenes Zertifikat). Delivery-Receipt persistent speichern.
R-09 E-Mail-Versand (Fallback): PDF/A-3 als Attachment, signiert via S/MIME optional. UBL-XML auf Wunsch als zweites Attachment.
R-10 KoSIT-Validierung erfolgt für jede ausgehende Rechnung vor Versand (nicht asynchron). Fehler = FATAL → Versand blockiert. Fehler = WARNING → Versand erlaubt, Log-Eintrag.
5.2 Rechnungs-Empfang (inbound)
Abschnitt betitelt „5.2 Rechnungs-Empfang (inbound)“R-11 Eingangs-Kanäle: E-Mail (IMAP-Import oder MX-Forward an rechnung@<tenant>.werkszeit.io), Peppol-AP (inbound push), Manueller Upload (PDF oder XML).
R-12 Format-Detection: MIME-Sniff + XML-Root-Element; ZUGFeRD extrahiert CII-XML aus PDF/A-3 via iText 8.0 (BouncyCastle für Signaturen).
R-13 Validierung: KoSIT 1.5.0 (ohne PDF/A-Check) + Mustang 2.14 (PDF/A-3 Conformance) + Peppol-Schematron (optional).
R-14 LV-Abgleich: Bei vorhandenem bauvorhaben_id (aus BT-11 Project reference oder manueller Zuordnung) versucht System, Rechnungs-Positionen den LV-Positionen per OZ-Match (→ §4.3) zuzuordnen. Abweichung > 2 % → Warnung.
R-15 §13b-Empfang-Check: Wenn wir Leistungsempfänger sind ∧ Rechnung weist § 13b aus ∧ wir erfüllen Bau-Kriterium → System schlägt Reverse-Charge-Buchung vor (0 % USt + Meldung in USt-VA Zeile 87).
5.3 Storno & Korrektur
Abschnitt betitelt „5.3 Storno & Korrektur“R-16 Storno: INVOICE_TYPE_CODE=381 (credit note) mit BT-25 Preceding invoice reference. Originalrechnung erhält Status=storniert_durch, nicht hart gelöscht (GoBD).
R-17 Korrektur per Neu-Ausstellung: Originalrechnung stornieren (R-16), neue Rechnung mit Bezug. System legt beides atomar an.
5.4 Archiv & GoBD
Abschnitt betitelt „5.4 Archiv & GoBD“R-18 Jede ausgehende XRechnung (UBL/CII-XML) + PDF/A-3 wird in S3 mit Object Lock Compliance Mode, 10 Jahre abgelegt. SHA-256 im rechnung_blob + Hash-Chain mit Vorgänger.
R-19 KoSIT-Validator-Report als report.xml beigefügt, gleiche Hash-Chain.
R-20 Append-only in DB; Änderungen nur über Storno+Neu-Ausstellung.
5.5 Nicht-funktionale Anforderungen
Abschnitt betitelt „5.5 Nicht-funktionale Anforderungen“- Performance: XRechnung-Generierung < 900 ms p95 für bis zu 200 Positionen.
- Durchsatz: 500 Rechnungen/Tag pro Tenant bei
shk-gebruder-schmidt. - Verfügbarkeit: 99,5 % (Werktage 06–20 Uhr); Peppol-AP darf nachts 4 h Wartung haben.
- Offline: Nein. Rechnungserstellung ist desk-office, Online-Pflicht für KoSIT-Validator.
- Auditierbarkeit: 100 % der Rechnungen mit Hash-Chain, Revisions-Log, Validator-Bericht.
- A11y: PDF/A-3 muss WCAG 2.1 AA sein (tagged PDF).
6. Mockups / Flows (ASCII)
Abschnitt betitelt „6. Mockups / Flows (ASCII)“6.1 Hauptscreen Web — „Rechnung erstellen“ (Desk-Office, Paul Wagner)
Abschnitt betitelt „6.1 Hauptscreen Web — „Rechnung erstellen“ (Desk-Office, Paul Wagner)“ ┌──────────────────────────────────────────────────────────────────────────────────────────┐ │ WERKSZEIT · Gebrüder Schmidt GmbH (NRW) [Paul Wagner · CFO] Mo 19.04.2026 [⚙] [?] │ ├──────────────────────────────────────────────────────────────────────────────────────────┤ │ Sidebar │ Handwerk › Abrechnung › BV "Stadthalle Krefeld" › Teilrechnung Nr. 4 │ │ Zeit │──────────────────────────────────────────────────────────────────────────── │ │ Dispo │ [Status: ENTWURF] [⚠ Reverse-Charge aktiv · USt 1 TG bis 30.06.2027] │ │ LV │ │ │ »Rechnung« │ Rechnung: TR-2026-00471 BV-ID: 000123 Leitweg-ID: – │ │ Nachtrag │ Debitor: Stadt Krefeld, Hochbauamt Peppol-ID: 0204:DE220612345 │ │ Material │ Empfänger-Präferenz: ⓘ XRechnung UBL 2.1 │ │ Mängel │─────────────────── VOB §14 Abs. 2 — Kumulation ────────────────────────────│ │ Doku │ Brutto bisher kumuliert (TR 1–3) € 182 340,22 │ │ │ + Leistung diesen Zeitraum (TR 4) € 58 117,90 │ │ │ = Brutto kumuliert € 240 458,12 │ │ │ – Bereits bezahlt (TR 1 + TR 3, inkl.) € 168 406,40 │ │ │ – Sicherheits-Einbehalt 5 % € 12 022,91 │ │ │ = Auszahlungsbetrag diese Rechnung € 60 028,81 │ │ │──────────────────────────────────────────────────────────────────────────── │ │ │ Positionen (aus LV v7 + Aufmaß-Block #A-118 freigegeben 17.04.2026): │ │ │ ┌────┬───────────────────────────┬──────┬─────┬──────────┬──────────────┐ │ │ │ │ OZ │ Kurztext │ Menge│ Ein.│ EP € │ GP € │ │ │ │ ├────┼───────────────────────────┼──────┼─────┼──────────┼──────────────┤ │ │ │ │01.02.030│ Putz-Innenwand abreißen│ 214,7│ m² │ 12,40│ 2 662,28 │ │ │ │ │01.02.040│ Gipsputz Q2 auftragen │ 214,7│ m² │ 28,90│ 6 204,83 │ │ │ │ │01.03.010│ Malerarbeiten lattenl.│ 214,7│ m² │ 11,20│ 2 404,64 │ │ │ │ │02.NT.001│ Nachtrag Dämmung EPS │ 48,0│ m² │ 42,00│ 2 016,00 │ │ │ │ │… (27 weitere Positionen sichtbar mit Scroll) │ │ │ │ └────┴───────────────────────────┴──────┴─────┴──────────┴──────────────┘ │ │ │ │ │ │ ⓘ Reverse-Charge §13b: USt-Betrag = 0,00 € │ │ │ ⚠ §48 EStG: Freistellungsbescheinigung Ihrer Firma gültig bis 31.12.2028 ✓ │ │ │ │ │ │ [ ] XRechnung UBL 2.1 + [x] PDF/A-3 (ZUGFeRD XRECHNUNG-Profil) │ │ │ │ │ │ [Validieren KoSIT 1.5.0 →] [Versand ➜ Peppol] │ │ │ │ ├──────────────────────────────────────────────────────────────────────────────────────────┤ │ Paul Wagner · CFO · [email protected] [Audit-Log] [Exports] [Support] │ └──────────────────────────────────────────────────────────────────────────────────────────┘6.2 Validierungs-Dialog (Vor-Versand-Modal)
Abschnitt betitelt „6.2 Validierungs-Dialog (Vor-Versand-Modal)“ ┌───────────────── KoSIT-Validator 1.5.0 · Szenario „XRechnung 3.0.2 UBL" ──────────────────┐ │ │ │ ✓ 0 FATAL · 0 ERROR · 1 WARNING · 0 INFO Dauer 642 ms │ │ │ │ ⚠ BR-CO-15 Aufmaßreferenz `BT-DEX-30` empfohlen │ │ Kontext: Position 01.02.030 enthält keine REB-DA11-Referenz im Annotation-Block. │ │ Wirkung: Kein Versand-Block. Architekt prüft manuell. │ │ [Ignore] [Fix & erneut validieren] │ │ │ │ ✓ Schema: UBL-Invoice-2.xsd │ │ ✓ Schematron: xrechnung-UBL-validation.sch (v3.0.2) │ │ ✓ Codelisten: EAS, Currency (ISO 4217), UNTDID 1001 Document Type │ │ ✓ PDF/A-3 Conformance (Mustang 2.14) · factur-x.xml eingebettet, XMP-Metadaten ok │ │ │ │ Report-Download: [XML] [HTML] Hash-Chain: 4f9c8e…2a7e ← [vorige: bb21…c6a1] │ │ │ │ [Abbrechen] [Zurück] [Versand bestätigen]│ └────────────────────────────────────────────────────────────────────────────────────────────┘6.3 Mobile — Status-Push für Monteur (Mehmet)
Abschnitt betitelt „6.3 Mobile — Status-Push für Monteur (Mehmet)“ ┌────────────────────────┐ │ 11:42 ●●●●● 100 % │ ├────────────────────────┤ │ ← BV Stadthalle Kref.│ ├────────────────────────┤ │ │ │ [Aufmaß-Block A-118] │ │ Freigegeben 17.04. │ │ 214,7 m² Innenwand │ │ │ │ Status: │ │ ┌──────────────────┐ │ │ │● Aufmaß │ │ │ │● Rechnung erst. │ │ │ │● KoSIT ok │ │ │ │● Versand Peppol │ │ │ │ Architekt prüft │ │ │ │ ⏳ noch 9 Tage │ │ │ └──────────────────┘ │ │ │ │ TR-2026-00471 │ │ 60 028,81 € netto │ │ │ │ [Rückfrage öffnen] │ │ │ ├────────────────────────┤ │ [Heute] [BV] [Prof.] │ └────────────────────────┘6.4 Flow (Sequenzdiagramm, linearer Happy Path)
Abschnitt betitelt „6.4 Flow (Sequenzdiagramm, linearer Happy Path)“ Anja (Buchhaltung) Werkszeit-Backend KoSIT-Lambda Peppol-AP (B2BRouter) Debitor │ │ │ │ │ │ 1. „Neue Rechnung" │ │ │ │ ├────────────────────►│ │ │ │ │ │ 2. lädt LV-v7 │ │ │ │ │ + Aufmaß-Block │ │ │ │ │ + Nachträge │ │ │ │ │ 3. berechnet │ │ │ │ │ Kumulation │ │ │ │ │ + Reverse-Chg │ │ │ │ 4. UI zeigt Entwurf │ │ │ │ │◄────────────────────┤ │ │ │ │ 5. „Validieren" │ │ │ │ ├────────────────────►│ 6. generiert UBL │ │ │ │ │ + PDF/A-3 │ │ │ │ ├─────────────────► │ 7. validate │ │ │ │ │ (0 FATAL) │ │ │ │◄──────────────────┤ │ │ │ 8. „Versand" │ │ │ │ ├────────────────────►│ 9. SMP-Lookup │ │ │ │ ├───────────────────────────────────────►│ │ │ │ │ 10. AS4-Push │ │ │ ├────────────────────►│ │ │ │◄────────────────────┤ │ │◄───────────────────────────────────────┤ 11. Receipt │ │ 12. Status=VERSANDT │ │ │ │ │◄────────────────────┤ │ │ │7. Datenmodell (Drizzle + RLS)
Abschnitt betitelt „7. Datenmodell (Drizzle + RLS)“7.1 Core-Tabellen
Abschnitt betitelt „7.1 Core-Tabellen“export const rechnung = pgTable("rechnung", { id: uuid("id").primaryKey().defaultRandom(), // UUID v7 tenant_id: uuid("tenant_id").notNull(), bauvorhaben_id: uuid("bauvorhaben_id"), // null bei Nicht-Bau debitor_id: uuid("debitor_id").notNull(), rechnungs_nr: text("rechnungs_nr").notNull(), // fortlaufend, Tenant-scoped, §14 (4) Nr. 4 UStG typ: rechnungstyp("typ").notNull(), // 'teilrechnung' | 'schlussrechnung' | 'abschlag' | 'storno' | 'gutschrift' invoice_type_code: text("invoice_type_code").notNull(), // UNCL 1001: 380, 381, 386 ... waehrung: char("waehrung", { length: 3 }).notNull().default("EUR"), ausstellungs_datum: date("ausstellungs_datum").notNull(), leistungs_zeitraum_von: date("leistungs_zeitraum_von"), leistungs_zeitraum_bis: date("leistungs_zeitraum_bis"), faelligkeit: date("faelligkeit").notNull(), leitweg_id: text("leitweg_id"), // BT-10 netto_aktuell_cent: bigint("netto_aktuell_cent", { mode: "bigint" }).notNull(), ust_cent: bigint("ust_cent", { mode: "bigint" }).notNull(), brutto_aktuell_cent: bigint("brutto_aktuell_cent", { mode: "bigint" }).notNull(), brutto_kumuliert_cent: bigint("brutto_kumuliert_cent", { mode: "bigint" }).notNull(), // VOB §14 Abs. 2 bereits_gezahlt_cent: bigint("bereits_gezahlt_cent", { mode: "bigint" }).notNull(), sicherheits_einbehalt_cent: bigint("sicherheits_einbehalt_cent", { mode: "bigint" }).notNull(), gewaehrleistungs_einbehalt_cent: bigint("gewaehrleistungs_einbehalt_cent", { mode: "bigint" }).notNull(), zahlungsanspruch_cent: bigint("zahlungsanspruch_cent", { mode: "bigint" }).notNull(), reverse_charge: boolean("reverse_charge").notNull().default(false), reverse_charge_grund: text("reverse_charge_grund"), // "§ 13b UStG" oder BT-120-Text paragraph_48_estg_pruefung: jsonb("paragraph_48_estg_pruefung").notNull(), // {freistellung: true, gueltig_bis: '...'} oder {abzug_cent: 150000} lv_version_id: uuid("lv_version_id"), // FK auf §4.3 ausgabeformat: text("ausgabeformat").notNull(), // 'xrechnung-ubl' | 'xrechnung-cii' | 'zugferd-xrechnung' | 'zugferd-en16931' versandkanal: text("versandkanal"), // 'peppol' | 'email' | 'manual' | 'download' status: text("status").notNull(), // 'entwurf' | 'validiert' | 'versandt' | 'bezahlt' | 'storniert_durch' | 'mahnung_1' | 'mahnung_2' | 'gerichtlich' storniert_durch_rechnung_id: uuid("storniert_durch_rechnung_id"), vorheriger_rechnung_id: uuid("vorheriger_rechnung_id"), // Kette der Teilrechnungen (BV-scope) erstellt_von: uuid("erstellt_von").notNull(), erstellt_am: timestamp("erstellt_am", { withTimezone: true }).notNull().defaultNow(), immutable_at: timestamp("immutable_at", { withTimezone: true }), // gesetzt bei Versand hash_prev: bytea("hash_prev").notNull(), // 32 Byte SHA-256 hash_self: bytea("hash_self").notNull(), // 32 Byte SHA-256}, (t) => ({ tenantIdx: index("rechnung_tenant_idx").on(t.tenant_id, t.erstellt_am.desc()), bvIdx: index("rechnung_bv_idx").on(t.tenant_id, t.bauvorhaben_id, t.typ, t.erstellt_am), rnrUnique: uniqueIndex("rechnung_rnr_tenant_uq").on(t.tenant_id, t.rechnungs_nr), debitorIdx: index("rechnung_debitor_idx").on(t.tenant_id, t.debitor_id, t.status),}));
export const rechnung_position = pgTable("rechnung_position", { id: uuid("id").primaryKey().defaultRandom(), tenant_id: uuid("tenant_id").notNull(), rechnung_id: uuid("rechnung_id").notNull(), position_nr: integer("position_nr").notNull(), oz: text("oz").notNull(), // 01.02.030 (→ §4.3) lv_knoten_id: uuid("lv_knoten_id"), // FK auf lv_knoten aufmass_block_id: uuid("aufmass_block_id"), // FK auf §4.4 kurztext: text("kurztext").notNull(), langtext_md: text("langtext_md"), menge: numeric("menge", { precision: 18, scale: 6 }).notNull(), // negative bei Gutschrift einheit: text("einheit").notNull(), // m², m, Stk, Psch ep_cent: bigint("ep_cent", { mode: "bigint" }).notNull(), gp_cent: bigint("gp_cent", { mode: "bigint" }).notNull(), ust_satz_bps: integer("ust_satz_bps").notNull(), // 0 bei Reverse-Charge, 1900 = 19,00 % ust_category: text("ust_category").notNull(), // 'S', 'AE', 'Z', 'E', 'G', 'K', 'O' ust_exemption_reason: text("ust_exemption_reason"), // BT-120 nachtrag_id: uuid("nachtrag_id"), ist_abschlag: boolean("ist_abschlag").notNull().default(false),}, (t) => ({ rechIdx: index("rp_rech_idx").on(t.tenant_id, t.rechnung_id, t.position_nr),}));
export const rechnung_blob = pgTable("rechnung_blob", { id: uuid("id").primaryKey().defaultRandom(), tenant_id: uuid("tenant_id").notNull(), rechnung_id: uuid("rechnung_id").notNull(), art: text("art").notNull(), // 'ubl' | 'cii' | 'pdfa3' | 'report-kosit' | 'report-mustang' | 'peppol-receipt' mime: text("mime").notNull(), s3_key: text("s3_key").notNull(), s3_object_lock_retain_until: timestamp("s3_object_lock_retain_until", { withTimezone: true }).notNull(), sha256: bytea("sha256").notNull(), groesse_bytes: bigint("groesse_bytes", { mode: "bigint" }).notNull(), erzeugt_am: timestamp("erzeugt_am", { withTimezone: true }).notNull().defaultNow(), hash_prev: bytea("hash_prev").notNull(), hash_self: bytea("hash_self").notNull(),}, (t) => ({ rechIdx: index("rb_rech_idx").on(t.tenant_id, t.rechnung_id, t.art),}));
export const peppol_zertifikat = pgTable("peppol_zertifikat", { id: uuid("id").primaryKey().defaultRandom(), tenant_id: uuid("tenant_id").notNull(), peppol_id: text("peppol_id").notNull(), // 0204:DE123... kn_truststore_alias: text("kn_truststore_alias").notNull(), // Schlüsselname KMS ausgestellt_am: timestamp("ausgestellt_am").notNull(), gueltig_bis: timestamp("gueltig_bis").notNull(), status: text("status").notNull(), // 'aktiv' | 'rotation_geplant' | 'abgelaufen' rotations_job_id: uuid("rotations_job_id"),});
export const freistellungsbescheinigung_48b = pgTable("freistellungsbescheinigung_48b", { id: uuid("id").primaryKey().defaultRandom(), tenant_id: uuid("tenant_id").notNull(), subjekt_id: uuid("subjekt_id").notNull(), // leistender / debitor subjekt_typ: text("subjekt_typ").notNull(), // 'wir' | 'debitor' | 'sub' bescheinigungs_nr: text("bescheinigungs_nr").notNull(), ausgestellt_von: text("ausgestellt_von").notNull(), // Finanzamt gueltig_bis: date("gueltig_bis").notNull(), pdf_s3_key: text("pdf_s3_key"), erfasst_von: uuid("erfasst_von").notNull(), erfasst_am: timestamp("erfasst_am", { withTimezone: true }).notNull().defaultNow(),}, (t) => ({ subjIdx: index("fb48b_subj_idx").on(t.tenant_id, t.subjekt_id, t.gueltig_bis.desc()),}));
export const ust_1_tg = pgTable("ust_1_tg", { id: uuid("id").primaryKey().defaultRandom(), tenant_id: uuid("tenant_id").notNull(), debitor_id: uuid("debitor_id").notNull(), bescheinigungs_nr: text("bescheinigungs_nr").notNull(), gueltig_bis: date("gueltig_bis").notNull(), pdf_s3_key: text("pdf_s3_key"), erfasst_von: uuid("erfasst_von").notNull(), erfasst_am: timestamp("erfasst_am", { withTimezone: true }).notNull().defaultNow(),});7.2 Row-Level-Security (Auszug)
Abschnitt betitelt „7.2 Row-Level-Security (Auszug)“-- MandantentrennungALTER TABLE rechnung ENABLE ROW LEVEL SECURITY;CREATE POLICY rechnung_tenant_isolation ON rechnung USING (tenant_id = current_setting('app.tenant_id')::uuid);
-- Immutability: nach Versand (immutable_at gesetzt) kein UPDATE / DELETE mehrCREATE POLICY rechnung_no_update_after_versand ON rechnung FOR UPDATE USING (immutable_at IS NULL);CREATE POLICY rechnung_no_delete ON rechnung FOR DELETE USING (false);
-- rechnung_blob nur Insert, nie Delete / UpdateALTER TABLE rechnung_blob ENABLE ROW LEVEL SECURITY;CREATE POLICY rb_no_update ON rechnung_blob FOR UPDATE USING (false);CREATE POLICY rb_no_delete ON rechnung_blob FOR DELETE USING (false);
-- Rollenbasierte Sichten: Monteur sieht nur Rechnungs-Status, nicht PreiseCREATE POLICY rechnung_monteur_read ON rechnung FOR SELECT USING ( current_setting('app.role') <> 'monteur' OR (bauvorhaben_id IN (SELECT bv_id FROM mitarbeiter_bv WHERE mitarbeiter_id = current_setting('app.user_id')::uuid)));7.3 Hash-Chain (GoBD Append-Only)
Abschnitt betitelt „7.3 Hash-Chain (GoBD Append-Only)“CREATE OR REPLACE FUNCTION rechnung_hash_self() RETURNS trigger LANGUAGE plpgsql AS $$DECLARE prev bytea;BEGIN SELECT COALESCE(hash_self, '\x00'::bytea) INTO prev FROM rechnung WHERE tenant_id = NEW.tenant_id ORDER BY erstellt_am DESC, id DESC LIMIT 1; NEW.hash_prev := COALESCE(prev, '\x00'); NEW.hash_self := digest( NEW.tenant_id::text || '|' || NEW.rechnungs_nr || '|' || NEW.brutto_aktuell_cent::text || '|' || NEW.ausstellungs_datum::text || '|' || encode(NEW.hash_prev, 'hex'), 'sha256'); RETURN NEW;END $$;
CREATE TRIGGER rechnung_hash_chain BEFORE INSERT ON rechnung FOR EACH ROW EXECUTE FUNCTION rechnung_hash_self();8. API-Spezifikation
Abschnitt betitelt „8. API-Spezifikation“Basis-Pfad: /v1/handwerk/rechnung · Auth: OAuth2 Bearer · Mandant per Header X-Tenant.
8.1 Rechnung erstellen (Draft)
Abschnitt betitelt „8.1 Rechnung erstellen (Draft)“POST /v1/handwerk/rechnung
{ "typ": "teilrechnung", "bauvorhaben_id": "01H..._krefeld", "debitor_id": "01H..._stadt_krefeld", "aufmass_block_ids": ["01H..._aufmassA118"], "nachtrag_ids": ["01H..._nt01"], "leistungs_zeitraum_von": "2026-04-01", "leistungs_zeitraum_bis": "2026-04-17", "faelligkeit": "2026-05-10", "ausgabeformat": "xrechnung-ubl", "vorheriger_rechnung_id": "01H..._tr3", "idempotency_key": "01H..."}201 → { rechnung_id, netto_aktuell_cent, brutto_kumuliert_cent, warnings: [...] }
409 → INVOICE_NUMBER_GAP (fortlaufende Numerierung verletzt), UST_1_TG_EXPIRED
8.2 Validieren
Abschnitt betitelt „8.2 Validieren“POST /v1/handwerk/rechnung/{id}/validate → ruft KoSIT-Lambda asynchron, antwortet mit Job-ID + WebSocket-Channel. Job-Ergebnis landet als rechnung_blob (art='report-kosit').
8.3 Versand
Abschnitt betitelt „8.3 Versand“POST /v1/handwerk/rechnung/{id}/send
{ "kanal": "peppol", "delivery_mode": "reliable" }200 → Delivery-Receipt (mit message_id, timestamp, ap_signature) als rechnung_blob persistiert. 422 → PEPPOL_CERT_EXPIRED, LEITWEG_FORMAT_INVALID, KOSIT_FATAL, FREISTELLUNG_48B_MISSING, REVERSE_CHARGE_CONFLICT.
8.4 Stornieren
Abschnitt betitelt „8.4 Stornieren“POST /v1/handwerk/rechnung/{id}/cancel
Body: { "grund": "falsche Menge", "gutschrift_rechnungs_nr_vorschlag": "TR-2026-00471-S" }
→ Atomar: Status der Original=storniert_durch, neue Storno-Rechnung mit invoice_type_code=381, BT-25=....
8.5 Eingangs-Rechnung empfangen
Abschnitt betitelt „8.5 Eingangs-Rechnung empfangen“POST /v1/handwerk/rechnung/inbound (Multipart)
Felder: xml (UBL/CII), pdf (ZUGFeRD), source (email-from, peppol-inbound, manual).
→ Validierung + Parsing + LV-Abgleich + Event rechnung.inbound.parsed.
8.6 Webhooks (Outbound)
Abschnitt betitelt „8.6 Webhooks (Outbound)“rechnung.erstelltrechnung.validiertrechnung.versandtrechnung.bezahltrechnung.storniertrechnung.inbound.abweichung_erkannt
Webhooks mit HMAC-SHA256-Signatur (Secret pro Tenant rotierbar).
8.7 Idempotency
Abschnitt betitelt „8.7 Idempotency“Idempotency-Key Header (UUID v7, 24 h Geltung). Bei Duplikat → 200 mit original_response_cached.
9. Offline-Profil
Abschnitt betitelt „9. Offline-Profil“Rechnungs-Erstellung: OFFLINE NICHT SUPPORTED. Grund: KoSIT-Validator muss synchron laufen, Peppol-SMP-Lookup erfordert Internet, Fehl-Validierungen können nicht bei schwacher Verbindung „zwischengespeichert“ werden ohne Risiko der Doppel-Nummer-Vergabe (→ GoBD-Verstoß).
Lese-Modus offline (Mobile für Monteur, Referenz §4.1 Bautagebuch-Cache):
- Rechnungs-Status (
entwurf|validiert|versandt|bezahlt|storniert) wird via SQLite-Cache (Drift) gespiegelt (read-only). - Rechnungs-Details werden nicht offline verfügbar gemacht (Preise, Debitor-Daten) — DSGVO Min-Prinzip für Monteure.
Aufmaß-Signatur offline ist separater Flow (→ §4.4), entsteht nicht in diesem Modul.
Konflikt-Strategie: Da nur read, entfallen Konflikte. Kein Last-Write-Wins — hier irrelevant, da nur schreibender Teil online ist.
10. Compliance-Mapping
Abschnitt betitelt „10. Compliance-Mapping“| Ident | Norm | Umsetzung im Feature | Artefakt |
|---|---|---|---|
| C-01 | § 14 UStG (inkl. § 14 Abs. 4 Pflichtangaben) | Pflichtfelder harte Validation vor Draft→Validiert | Unit-Test Pflichtfeld-Matrix |
| C-02 | § 14 Abs. 1 Satz 2 UStG n.F. (E-Rech-V) | Ausgabeformat default=xrechnung-ubl für B2B | R-01 |
| C-03 | EN 16931-1:2017+A2:2023 | Kern-Rechnungsdatenmodell | Mapping-Tabelle BT-IDs ↔ DB-Spalten |
| C-04 | XRechnung 3.0.2 (KoSIT) | CIUS-Schematron Validation | C-TEST-01 |
| C-05 | § 13b UStG + UStAE 13b.1 | Reverse-Charge-Automatik + USt 1 TG-Pflege | R-04, US-05-03 |
| C-06 | § 48 / § 48b EStG | Pre-Check + Versand-Block + Dokumentation Bescheinigung | R-05, Test Gherkin 3 |
| C-07 | § 14 Abs. 2/3 VOB/B | Kumulation + Schlussrechnungs-Prüfbarkeit | R-03, UI §6.1 |
| C-08 | GoBD § 147 AO | Append-only + Hash-Chain + S3 Object Lock 10 J | §7.3, R-18 |
| C-09 | DSGVO Art. 6 (1) lit. b, c | Rechtsgrundlage Vertrag + gesetzl. Pflicht | Verfahrensverzeichnis Art. 30 |
| C-10 | DSGVO Art. 17 (Löschung) | Löschauftrag erst nach 10 J ab Ausstellung; vorher Sperre | §7.1 + Job „retention-sweeper“ |
| C-11 | Peppol BIS 3.0.17 Billing | AS4-Push + Schematron + SMP-Lookup | R-08 |
| C-12 | § 27b UStG (Umsatzsteuer-Nachschau) | Audit-Export in 24 h abrufbar | API §8.6 Exports |
| C-13 | § 93 AO (Vorlage) | Betriebsprüfer-Export (XRechnung + Signaturen) | Standard-Export |
| C-14 | BSI-Grundschutz APP.3.3 (Webanwendungen) | Session-Handling, CSRF, CSP | Pen-Test-Report |
| C-15 | ZUGFeRD 2.3 (FeRD) | Mustang 2.14 Profile XRECHNUNG | C-TEST-02 |
10.1 Retention-Regeln
Abschnitt betitelt „10.1 Retention-Regeln“| Art | Retention | Grund |
|---|---|---|
rechnung (Hauptsatz) |
10 Jahre ab Ende Kalenderjahr | § 147 Abs. 3 AO |
rechnung_blob (UBL/CII/PDF/Reports) |
10 Jahre ab Erzeugung | § 147 Abs. 3 AO + § 14b UStG |
peppol_zertifikat (abgelaufen) |
30 J | Strafverfolgungs-Verjährung |
ust_1_tg (abgelaufen) |
6 Jahre nach Ablauf | § 147 Abs. 3 Nr. 5 AO (analog Geschäftsbriefe) |
11. Edge-Cases
Abschnitt betitelt „11. Edge-Cases“- EC-01 Rechnungs-Nummer-Lücke (Netzwerk-Fehler zw. ID-Reserve und Commit): System nutzt HiLo-Sequenz pro Tenant; Lücken können entstehen, werden als GoBD-konforme Begründung im Audit-Log dokumentiert (zulässig nach AEAO § 146 „fortlaufend, nicht lückenlos“).
- EC-02 Leitweg-ID Format falsch (Bund 991-xxx-yy): Regex-Prüfung ja — aber Bundesland-Regex variiert. V1: Bund, Bayern, NRW, BW explizit; andere per generischem
[A-Z0-9\-]{4,50}-Fallback + WARN. - EC-03 Debitor weigert sich, Peppol-ID herauszugeben → Fallback E-Mail + PDF/A-3 ZUGFeRD (UBL ist trotzdem im PDF).
- EC-04 Empfänger will UBL ohne PDF → Option:
"ausgabeformat":"xrechnung-ubl", kein PDF/A-3. - EC-05 ZUGFeRD-PDF/A-3 schlägt Mustang-Check fehl (XMP-Metadaten), weil Kunden-Logo PNG mit ICC-Profil-Fehler → Pre-Generator konvertiert Logo via ImageMagick 7 in PDF/A-3-tauglich (sRGB, kein Alpha).
- EC-06 Kunde zahlt zu viel → Erstattungsrechnung
invoice_type_code=381(credit note) mit negativem Gesamtbetrag, Referenz auf Originalrechnung. - EC-07 Summen-Divergenz Aufmaß ↔ Rechnung-Position (Rundungsfehler): Nur Bank-Rounding HALF_EVEN auf Cent, Check
|Σ_pos − brutto_aktuell| ≤ 0,02 €, sonstINTERNAL_CONSISTENCY_ERROR500. - EC-08 USt 1 TG läuft morgen ab — aktive Banner im UI 30 Tage vorher, Versand erlaubt solange heute < gueltig_bis.
- EC-09 USt-Satz-Wechsel (Corona-Historie 16 %): Historische Rechnungen mit altem Satz bleiben unverändert (immutable), neue Rechnungen nutzen tagesaktuellen Satz aus
ust_satz_historie. - EC-10 Peppol-AP unreachable (Fehler 503): Retry mit exp. Backoff (1 min, 5, 15, 60, 240 min). Nach 4 h Alarm an Admin.
- EC-11 Eingangs-Rechnung in FatturaPA oder FranceRechnung statt XRechnung → V1: Ablehnung mit freundlicher Meldung; V2: Multi-CIUS-Parser.
- EC-12 Doppel-Versand (Kunde erhält zweimal) → Idempotency-Key auf API-Ebene; auf Peppol-Ebene Message-ID dedupliziert.
- EC-13 Rechnung-Nr-Prefix-Wechsel zum Jahreswechsel: System nutzt Template
{prefix}-{YYYY}-{running:05d}, Sequenz reset 01.01. - EC-14 Debitor zahlt mit falscher Referenz (BT-83 Payment reference fehlt) → Bank-Matching nur unscharf möglich; manuelle Zuordnung UI.
- EC-15 Reverse-Charge gilt nur für Bauleistungen; Material-Lieferung allein nicht → System prüft, ob
lv_knoten.art = 'leistung'vs.'material'und markiert gemischte Positionen rot: Splitting nötig. - EC-16 Positions-weise ust_category-Mix: §13b (AE) + Regelsatz (S) auf einer Rechnung technisch erlaubt, aber inhaltlich fast nie — WARN, kein Block.
- EC-17 Kunde-Tenant wird gelöscht (DSGVO-Art.-17-Gesuch) → Rechnung bleibt (gesetzl. Pflicht > Löschanspruch), Debitor-PII wird pseudonymisiert (
Privatperson A), Adresse anonymisiert. - EC-18 Sicherheitseinbehalt-Auflösung (Gewährleistungszeit-Ende 4 Jahre nach Abnahme): Scheduler-Job schlägt Auflösungsrechnung vor.
12. Gherkin — Acceptance (Happy + 3 Grenzfälle)
Abschnitt betitelt „12. Gherkin — Acceptance (Happy + 3 Grenzfälle)“12.1 Happy Path — XRechnung erzeugen + Peppol-Versand
Abschnitt betitelt „12.1 Happy Path — XRechnung erzeugen + Peppol-Versand“Feature: Teilrechnung VOB §14 Abs. 2 als XRechnung UBL 2.1 an Peppol-Empfänger Background: Given der Tenant "shk-gebruder-schmidt" And eine CFO "Paul Wagner" mit Rolle "cfo" And ein Bauvorhaben "Stadthalle Krefeld" mit 3 bezahlten Teilrechnungen 182.340,22 € brutto And ein Aufmaß-Block "A-118" freigegeben mit 214,7 m² Innenwand-Malerarbeiten And der Debitor "Stadt Krefeld" mit Peppol-ID "0204:DE220612345" und USt 1 TG gültig bis 30.06.2027 And eine gültige Freistellungsbescheinigung § 48b für uns bis 31.12.2028
Scenario: Erfolgreicher Versand einer Teilrechnung When Paul eine neue Teilrechnung Typ "teilrechnung" für BV "Stadthalle Krefeld" erstellt Then berechnet das System eine Brutto-Summe diesen Zeitraum von 58.117,90 € And eine kumulierte Brutto-Summe 240.458,12 € And einen Zahlungsanspruch 60.028,81 € (nach 5 % Sicherheits-Einbehalt) And USt-Betrag = 0,00 € mit Kategorie "AE" (Reverse-Charge) And Hinweistext "Steuerschuldnerschaft des Leistungsempfängers nach § 13b UStG" When Paul auf "Validieren" klickt Then ruft das System die KoSIT-Lambda mit Szenario "XRechnung 3.0.2 UBL" And der Validator-Report enthält 0 FATAL und 0 ERROR When Paul auf "Versand ➜ Peppol" klickt Then macht das System einen SMP-Lookup auf "0204:DE220612345" And sendet via AS4-Push an den Receiver-AP And persistiert die Delivery-Receipt als "rechnung_blob" mit art="peppol-receipt" And setzt "rechnung.status" auf "versandt" And füllt "rechnung.immutable_at" mit NOW() And der Hash-Chain-Eintrag ist konsistent mit dem Vorgänger12.2 Grenzfall 1 — §48-EStG-Versand-Block
Abschnitt betitelt „12.2 Grenzfall 1 — §48-EStG-Versand-Block“Scenario: Versand blockiert, wenn Freistellungsbescheinigung abgelaufen und Betrag > 5000 € Given eine Rechnung mit Brutto 12.000 € And die Freistellungsbescheinigung § 48b unseres Betriebs ist abgelaufen am 31.03.2026 When Paul auf "Versand" klickt Then liefert die API 422 mit Fehler-Code "FREISTELLUNG_48B_MISSING" And zeigt eine prominente Warnung mit Handlungsempfehlungen: "Neue Bescheinigung beim Finanzamt beantragen" And das Debitor-System muss 15 % Bauabzugssteuer (1800,00 €) einbehalten und ans FA abführen And der Versand-Button ist deaktiviert, bis eine neue Bescheinigung eingetragen wurde And ein Audit-Log-Eintrag dokumentiert den Block mit Grund + User + Zeit12.3 Grenzfall 2 — KoSIT-Validator liefert FATAL (Leitweg-ID fehlt bei öffentlichem Auftraggeber)
Abschnitt betitelt „12.3 Grenzfall 2 — KoSIT-Validator liefert FATAL (Leitweg-ID fehlt bei öffentlichem Auftraggeber)“Scenario: Validator schlägt fehl, Versand unmöglich Given ein Debitor "Land Berlin" mit Kategorie "öffentlicher Auftraggeber" And die Rechnung hat keine Leitweg-ID When Paul auf "Validieren" klickt Then antwortet KoSIT-Lambda mit 1 FATAL (Code "BR-DE-15: Kauferreferenz Pflicht") And der UI-Dialog zeigt den Fehler mit Lösungsvorschlag And der Versand-Button bleibt deaktiviert And die API-Antwort auf POST /v1/handwerk/rechnung/{id}/send ist 422 mit "KOSIT_FATAL" And der Validator-Report wird als "rechnung_blob" art="report-kosit" persistiert (auch Fehl-Reports für Audit)12.4 Grenzfall 3 — Doppel-Versand (Idempotency)
Abschnitt betitelt „12.4 Grenzfall 3 — Doppel-Versand (Idempotency)“Scenario: Zweiter POST mit gleichem Idempotency-Key liefert gecachte Antwort, kein Doppel-Versand Given ein Idempotency-Key "01H8A..." wurde in den letzten 24 h für diese Rechnung verwendet And der erste Request hat Status "versandt" zurückgegeben When ein zweiter POST /v1/handwerk/rechnung/{id}/send mit gleichem Idempotency-Key eingeht Then liefert das System 200 mit Body identisch zum Erstergebnis And das Peppol-AP bekommt keinen zweiten AS4-Push And im Audit-Log erscheint ein "idempotency_cache_hit"-Event12.5 Grenzfall 4 — Reverse-Charge-Konflikt
Abschnitt betitelt „12.5 Grenzfall 4 — Reverse-Charge-Konflikt“Scenario: Debitor hat USt 1 TG abgelaufen, Rechnung aber mit §13b vorausgesetzt Given der Debitor hat USt 1 TG bis 31.12.2025 And heute ist der 19.04.2026 And der Rechnungs-Entwurf setzt reverse_charge=true When Paul "Validieren" klickt Then zeigt das UI WARN (nicht FATAL) "Reverse-Charge riskant, USt 1 TG nicht mehr aktuell" And der Versand ist nicht automatisch blockiert, aber Paul muss die Rechnung mit Häkchen "Ich bestätige, dass der Leistungsempfänger weiterhin Bauleistender i.S.v. § 13b UStG ist" bestätigen And ein Audit-Eintrag dokumentiert diese Bestätigung mit User + Zeit + User-Agent13. Test-Cases (funktional + compliance + performance)
Abschnitt betitelt „13. Test-Cases (funktional + compliance + performance)“| ID | Typ | Titel | Tooling |
|---|---|---|---|
| T-01 | Unit | EN-16931-BT-Mapping LV→UBL (alle 20 Core-Profile-Felder) | Vitest + xmllint |
| T-02 | Unit | VOB-Kumulations-Algorithmus (5-Teilrechnungs-Kaskade) | Vitest |
| T-03 | Unit | Rundungs-Property-Test (Σ_pos == brutto_aktuell ± 0,02 €) für 10 000 Zufalls-LVs |
fast-check |
| T-04 | Unit | Reverse-Charge-Automatik an/aus je Debitor-Status | Vitest |
| T-05 | Unit | §48-Pre-Check (Freistellung abgelaufen · Betrag > 5000 €) → Block | Vitest |
| C-TEST-01 | Compliance | KoSIT-Validator 1.5.0 gegen 200 generierte Rechnungen (0 FATAL) | KoSIT CLI + GitHub-Action nightly |
| C-TEST-02 | Compliance | Mustang-Project-Validator gegen PDF/A-3 (alle Beispiele Profile=XRECHNUNG) | Mustang 2.14 Jar |
| C-TEST-03 | Compliance | Peppol-Schematron für EU-Versand (BE, NL, FR, NO, SE) | OpenPeppol TestBed |
| C-TEST-04 | Compliance | BMF-Muster-XRechnungen round-trip (lesen + erzeugen) | Datensatz KoSIT-XRechnung-Testsuite |
| C-TEST-05 | Security | XXE + Billion-Laughs bei Eingangs-XML | secure XML parser (disable DOCTYPE) |
| C-TEST-06 | Security | KMS-Zertifikat-Rotation simuliert (30-Tage-Alarm) | Terraform-Test |
| C-TEST-07 | Compliance | GoBD-Hash-Chain-Integrität nach 1 000 Rechnungen | Python-Script Nightly |
| C-TEST-08 | Compliance | Retention-Sweeper (> 10 Jahre alte Privatrechnungen werden anonymisiert) | Pytest + Testcontainers |
| T-10 | Integration | End-to-End: Aufmaß-Freigabe → Rechnung → KoSIT → Peppol-AP-Sandbox | Playwright + B2BRouter Test-AP |
| T-11 | Load | 500 Rechnungen/Tag · Generierung p95 < 900 ms | k6 |
| T-12 | Chaos | KoSIT-Lambda Timeout → Retry + Alarm | AWS FIS |
| T-13 | UAT | Referenzkunde shk-gebruder-schmidt erstellt 10 reale Rechnungen (Anonymisierung) |
Sprint-Review |
13.1 Property-Test-Auszug (TypeScript)
Abschnitt betitelt „13.1 Property-Test-Auszug (TypeScript)“import { fc } from "fast-check";import { berechneKumulation } from "@/lib/vob-kumulation";
test("P-01 Kumulation ist monoton", () => { fc.assert(fc.property( fc.array(fc.record({ nettoCent: fc.bigInt({ min: 1n, max: 10_000_000n }), einbehalteneCent: fc.bigInt({ min: 0n, max: 2_000_000n }), }), { minLength: 1, maxLength: 50 }), (teilrechnungen) => { const kum = berechneKumulation(teilrechnungen); // Brutto kumuliert darf nie sinken for (let i = 1; i < kum.length; i++) { expect(kum[i].bruttoKumuliert).toBeGreaterThanOrEqual(kum[i - 1].bruttoKumuliert); } }));});14. Nicht-Ziele / Abgrenzungen
Abschnitt betitelt „14. Nicht-Ziele / Abgrenzungen“- Keine Buchungs-Engine: Wir integrieren DATEV-CSV-Export und GDPdU-Schnittstelle — aber wir führen keine eigene Kostenrechnung (kein Kontenplan, keine BWA).
- Keine Mahn-Automatik in V1: Status-Feld existiert, automatisches Mahnwesen V2.
- Keine Factoring-Integration: Vorbereitet (Daten-Export JSON), aber kein Live-Factoring-Anbieter in V1.
- Keine Lohnsteueranmeldung für §48: Nur Pflicht-Erinnerung; Abführung erfolgt extern.
- Keine Kassen-Nachschau-Anbindung: Bau ≠ Gastro/Handel, TSE-Pflicht irrelevant.
- Kein WebPortal für Debitor: V1 nur Versand + PDF; Self-Service-Portal für Debitor erst V2.
- Keine Multi-Währung: EUR only; GBP/CHF erst V3.
- Keine FatturaPA / FR-E-Rech-Parser inbound: Nur XRechnung/ZUGFeRD/Peppol BIS in V1.
15. Risiken
Abschnitt betitelt „15. Risiken“| ID | Risiko | Wahrsch. | Wirkung | Mitigation |
|---|---|---|---|---|
| R-01 | KoSIT-Validator-Spec-Change (v3.1 in 2026) bricht XSDs | hoch | mittel | Validator-Version im Feature-Flag, Shadow-Run neuer Version parallel 90 Tage |
| R-02 | Peppol-Managed-AP (B2BRouter) Ausfall | mittel | hoch | Multi-AP-Strategie (B2BRouter + Unimaze + eigener AP ab V2); Failover-Config |
| R-03 | §14-UStG-Auslegung FA uneinheitlich | mittel | mittel | Begleitschreiben als Fußnote in PDF/A-3; Klausel-Bibliothek pflegbar |
| R-04 | VOB/B-§14-Kumulation vom Architekten abgelehnt (“nicht prüfbar”) | mittel | hoch | Muster-Kumulationsdarstellung mit BauSoft-Bayern Best-Practice-Format als Template |
| R-05 | Reverse-Charge-Fehlanwendung → Steuerhaftung | gering | hoch | Double-Check-Workflow bei Erstanwendung je Debitor; jährliche USt 1 TG-Prüfung via FA-Abfrage |
| R-06 | PDF/A-3-Erzeugung resource-intensiv (große LVs > 500 Pos.) | mittel | mittel | FOP-Stream-Rendering, 2-GB-Lambda-Tier, Async-Ready-Notification |
| R-07 | DSGVO-Löschungsanspruch kollidiert mit 10 J AO-Aufbewahrung | mittel | mittel | Sperre + Pseudonymisierung dokumentiert, DPO-Freigabe Schreibweise |
| R-08 | Zertifikat-Rotation Peppol vergessen | mittel | hoch | 30-Tage-Alarm, 14-Tage-Eskalation, 7-Tage-Block-Gefahr |
| R-09 | §13b-Auslegung bei Mischrechnung (Leistung+Material) | mittel | mittel | Position-Splitting-UI mit Steuerberater-Review-Flow |
| R-10 | Fortlaufende Numerierung bricht bei Tenant-Migration | gering | mittel | Migration-Playbook: Einmal-Nummer-Prefix-Wechsel, Audit-Begründung |
| R-11 | ZUGFeRD-PDF/A-3 zu groß (> 20 MB Attachment-Limit mancher Systeme) | gering | mittel | Bild-Kompression, Logo-Caching, Pre-Generator-Hinweis |
| R-12 | Geringe Adoption Peppol in DE (Rückfall auf E-Mail) | hoch | niedrig | E-Mail-Fallback robust, Peppol als USP vermarkten |
16. Abhängigkeiten
Abschnitt betitelt „16. Abhängigkeiten“16.1 Modulintern
Abschnitt betitelt „16.1 Modulintern“- §4.3 GAEB-LV — Pflicht. LV-Version_id ist Quelle für alle Positions-Texte (Immutable Ref).
- §4.4 Mobiles Aufmaß — Pflicht. Aufmaß-Block-IDs sind Pflicht für Mengen-Herkunft; ohne freigegebenen Aufmaß keine Teilrechnung.
- §4.2 Nachtrag — Pflicht, wenn
lv_knoten.art == 'nachtrag'involviert ist. - §4.1 Bautagebuch — optional (Querverweis auf Stundenbelege bei Regieleistung).
- §5 Zeit/Personal — optional (Stunden als Nachweis bei Regieleistung in Anlage).
16.2 Externe Systeme
Abschnitt betitelt „16.2 Externe Systeme“- KoSIT-Validator 1.5.0 (Docker-Image als AWS Lambda im Single-Tenant-Account der Plattform, nicht pro Mandant).
- Mustang-Project 2.14 (Java 21, SnapStart-Lambda).
- Peppol Access Point (V1: B2BRouter Managed-AP, Vertrag Q1/2026 geplant).
- AWS S3 Object Lock Compliance Mode Bucket
werkszeit-rechnung-archiv-<region>(10-J-Retention). - AWS KMS für Peppol-Client-Zertifikat (CMK mit Rotation 365 Tage).
- Bundes-DeMail-Adapter (optional, für §14 Steuer-Bescheide).
- FA-Abfrage-Schnittstelle (via DATEV oder manuell) für §48b-Bescheinigungs-Validität.
16.3 Intern
Abschnitt betitelt „16.3 Intern“packages/oz-core(gemeinsam mit §4.3) — OZ-Parsing / -Formatting.packages/xrechnung-generator(neu, TypeScript) — BT-ID-Mapping, UBL/CII-XML-Serialization via fast-xml-builder.packages/zugferd-renderer(neu, Java 21) — FOP-XSLT → PDF/A-3 via Mustang.packages/peppol-client(neu, TypeScript) — SMP-Lookup (HTTP+XSD), AS4-Push via jgroups-like oasis-as4-ts (ADR noch offen).packages/einkommensteuer(neu, klein) — §48-Pre-Check-Logik.services/apiHono-Backend.services/frontendReact 19 + TanStack Router.apps/mobileFlutter (nur read-Views).
16.4 Team & Rollen
Abschnitt betitelt „16.4 Team & Rollen“| Rolle | Aufwand | Wer |
|---|---|---|
| Backend-Lead | 40 % × 12 Wochen | J. Peters |
| Backend-Dev | 100 % × 10 Wochen | L. Okafor |
| Frontend-Dev | 100 % × 6 Wochen | K. Schmidt |
| QA | 50 % × 10 Wochen | F. Reiss |
| Compliance-Berater (extern) | 0,4 VZÄ × 4 Wochen | Dr. M. Blank (StB Lübeck) |
| DevOps/Lambda/KMS | 20 % × 4 Wochen | A. Bauer |
17. Referenzkunden-Slot
Abschnitt betitelt „17. Referenzkunden-Slot“17.1 shk-gebruder-schmidt — Rollout-Plan
Abschnitt betitelt „17.1 shk-gebruder-schmidt — Rollout-Plan“Woche 1–2 Mandanten-Onboarding: Leitweg-ID Kommunal-Kunden einspielen (CSV-Import 210 Debitoren), Peppol-ID-Mapping, USt 1 TG-Bescheinigungen (via E-Mail-Weiterleitung → KI-OCR). Woche 3 Schatten-Betrieb: Rechnungen werden zusätzlich in Werkszeit erstellt, aber alter Workflow (OpenXRechnung) bleibt primär. Differenzen werden automatisch dokumentiert. Woche 4 Umschwenk bei 25 % des Rechnungsvolumens (kleine Beträge, nicht-kritische Debitoren). Woche 5–6 100 %-Übernahme, alter Workflow archiviert. Woche 7 Retrospektive mit Paul Wagner + CFO-Beirat; Lessons Learned → LESSONS-LEARNED.md.
17.2 Metriken
Abschnitt betitelt „17.2 Metriken“- Time-to-Rechnung (Aufmaß-Freigabe → Versand): Baseline 32 min, Zielwert 8 min.
- Retour-Quote (Rechnungen vom Debitor abgelehnt): Baseline 1,8 %, Zielwert < 0,5 %.
- VZÄ in Buchhaltung: Baseline 3,5, Zielwert 2,5 (Freisetzung 1 VZÄ für Kundenbetreuung).
- Peppol-Adoption Kommunal: Start 0 %, Woche 12 Zielwert 60 %.
17.3 musterbetrieb-maler — Spezifika
Abschnitt betitelt „17.3 musterbetrieb-maler — Spezifika“Wesentlich kleineres Volumen, Fokus auf §13b und §48b-Pflege. Shadow-Betrieb entfällt (zu klein), stattdessen Direkt-Umschwenk mit Rollback-Garantie (7 Tage).
18. Senior-Berater-Empfehlung
Abschnitt betitelt „18. Senior-Berater-Empfehlung“18.1 Strategische Leitplanken
Abschnitt betitelt „18.1 Strategische Leitplanken“-
Validator-Version nicht hart codieren. KoSIT wechselt alle 6 Monate. Jede Rechnung speichert die Validator-Version, mit der sie validiert wurde (
rechnung_blob.art='report-kosit'Hash-Chain). Rückwirkende Prüfung alter Rechnungen mit neuer Validator-Version ist optional (Audit-Komfort), nicht Pflicht. -
PDF/A-3 ist Komfort, UBL ist Wahrheit. Bei jeder Diskrepanz zwischen PDF-Anzeige und UBL-Struktur hat UBL Vorrang. Das muss im UI explizit kommuniziert werden (“Rechtsverbindlich ist die XML-Struktur; das PDF dient der menschenlesbaren Darstellung”). Sonst gibt es Ärger mit Finanzamt-Prüfern, die nur das PDF lesen.
-
Peppol-AP via B2BRouter in V1, eigener AP in V2. Ein eigener Peppol-AP kostet ~ 80 k€ Zertifizierung + 4 k€/Monat Betrieb + 0,3 VZÄ. Lohnt sich erst ab > 5 000 Rechnungen/Monat via Peppol. Bis dahin managed.
-
§48 EStG ist kein Rechnungsmerkmal, sondern ein Compliance-Gate. Die Versand-Blockade bei fehlender Freistellung ist radikal, aber richtig — jede andere Lösung führt in die Haftung. Kommuniziere das explizit im Onboarding.
-
VOB §14 Kumulation ist Maßarbeit je Architekt. Die Kumulations-Darstellung in der PDF-Seite muss pro Auftraggeber konfigurierbar sein (BauSoft-Bayern-Format ≠ RIB-iTWO-Format ≠ Excel-Handschlag). Template-Editor ab V1.1.
18.2 Go/No-Go Gates
Abschnitt betitelt „18.2 Go/No-Go Gates“- G1 (Sprint 26-Q2-A Ende): 100 % der KoSIT-Testsuite-Musterrechnungen werden korrekt erzeugt. No-Go → verschiebt Sprint 26-Q2-B um 2 Wochen.
- G2 (Sprint 26-Q2-B Ende): B2BRouter-Sandbox-AP liefert 10 aufeinanderfolgende erfolgreiche Deliveries ohne manuellen Eingriff. No-Go → Peppol verschoben auf V1.1 (MVP mit E-Mail-Only).
- G3 (Sprint 26-Q2-C Ende): Referenzkunde
shk-gebruder-schmidtSchatten-Betrieb 0 Differenzen vs. Altsystem. No-Go → Rollback-Plan aktivieren. - G4 (vor GA): Pen-Test + DSGVO-DSFA + Steuerberater-Gutachten (Dr. Blank). No-Go → GA verschoben.
18.3 Quick-Wins für erste Kundendemos
Abschnitt betitelt „18.3 Quick-Wins für erste Kundendemos“- Vorgefertigte „Schöner-Rechnen-Teilrechnung-nach-VOB“-Demo mit echt aussehenden Zahlen (
shk-gebruder-schmidtSample). - Live-KoSIT-Validator-Dialog (“schaut, es findet den Fehler in 600 ms”) — sehr überzeugend in Sales-Calls.
- PDF/A-3 mit gut lesbarem Bau-Layout (Referenz: RIB-iTWO-Schema) statt Accountant-Stil.
- Reverse-Charge-Automatik live umschalten per Toggle (“mit/ohne §13b”) — Wow-Effekt bei CFOs.
18.4 Langfristige Wette
Abschnitt betitelt „18.4 Langfristige Wette“Das ePeppol-Ökosystem wird in 2027/2028 der defacto-Rechnungsweg im EU-Bau, getrieben durch ViDA („VAT in the Digital Age“, EU-Direktive 2025/XYZ, Peppol-CTC-Reporting). Wer heute saubere Peppol-Pipeline baut, kann den 2027/2028-Realtime-Reporting-Turn bedienen, ohne Architektur-Umbau. Das ist der strategische Graben (Moat) dieses Moduls.
Empfehlung: GA-Freigabe sobald G1–G4 grün. Parallel Entwicklung V1.1 (Mahnwesen, eigener Peppol-AP, Template-Editor) im Folge-Quartal.
19. Wave 2 — XRechnung-Bau-Extension (2026-04-23)
Abschnitt betitelt „19. Wave 2 — XRechnung-Bau-Extension (2026-04-23)“Umsetzung von drei zusätzlichen Endpoints und zwei neuen TypeScript-Modulen, die den kern-05 XRechnung-Exporter um Bau-spezifische Anteile erweitern und eine offline Validierung gegen einen Teil der EN-16931/KoSIT-Schematron-Regeln durchführen. Keine externen Services, keine Java-Runtime, rein TypeScript.
19.1 Neue Endpoints
Abschnitt betitelt „19.1 Neue Endpoints“| Methode | Pfad | Zweck |
|---|---|---|
| GET | /v1/handwerk/bau-abrechnung/rechnungen/{id}/xrechnung.xml |
UBL 2.1 XML inkl. Bau-Extensions + X-XRechnung-Validation Header |
| GET | /v1/handwerk/bau-abrechnung/rechnungen/{id}/leitweg-id-check |
Regex + mod-97-10 Offline-Check + Klassifikation (Bund/Land/…) |
| POST | /v1/handwerk/bau-abrechnung/xrechnung-validate |
Ad-hoc Validierung eines beliebigen XRechnung-XMLs (≤ 5 MB) |
19.2 Bau-spezifische XML-Extensions
Abschnitt betitelt „19.2 Bau-spezifische XML-Extensions“- BT-3 InvoiceTypeCode ←
rechnungstyp(UNCL1001):abschlagszahlung=386,teilrechnung=387,schlussrechnung=380,storno=381(bzw.384bei gesetztemoriginRechnungsnummer). - §13b UStG Reverse-Charge → VAT-Kategorie
S→AE,Percent=0.00,TaxExemptionReasonCode=VATEX-EU-AE, Hinweistext (BT-120),TaxAmount=0,TaxInclusive = TaxExclusive(Payable = Netto). - §48 EStG Bauabzug → BG-20 Document-Level-Allowance mit
AllowanceChargeReasonCode=95(UNCL5189 Deduction) undAllowanceChargeReason="§48 EStG Bauabzugssteuer 15 %...". - VOB/B §14 Abs. 2 Kumulation → je vorherige Rechnungsnummer
ein
cac:AdditionalDocumentReferencemitDocumentType="VOB-Teilrechnung-Kumulation".
19.3 Offline KoSIT-Check (rund 30 BR-Regeln)
Abschnitt betitelt „19.3 Offline KoSIT-Check (rund 30 BR-Regeln)“Implementiert in kosit-offline-validator.ts. Abgedeckt:
BR-01..BR-17, BR-21, BR-22, BR-24..BR-28, BR-CO-04/10/13/15/25/26,
BR-S-01, BR-AE-01, BR-Z-01, BR-DE-15, BR-DE-17, BR-DE-21, BR-B-01, BR-33.
Severity error oder warning. Report wird im Response-Header der
XRechnung-Download-Route mitgeliefert (X-XRechnung-Validation: valid=<bool>;errors=<n>;warnings=<n>).
19.4 Leitweg-ID (BT-10)
Abschnitt betitelt „19.4 Leitweg-ID (BT-10)“leitweg-id-validator.ts prüft das Schema
^\d{1,12}(-[A-Z0-9]{1,30})?(-\d{1,5})?$ und — wenn ein Prüfziffern-Suffix
vorhanden ist — ISO/IEC 7064 mod-97-10 (gleiche Arithmetik wie IBAN,
berechnet in 9-Stellen-Chunks ohne BigInt-Dependency).
19.5 Migration 0034
Abschnitt betitelt „19.5 Migration 0034“Fügt bau_rechnung_meta.ust1tg_bescheinigung_id uuid (lose Ref, keine DB-FK),
CHECK (reverse_charge_13b = false OR ust1tg_bescheinigung_id IS NOT NULL)
und brm_tenant_leitweg_idx / brm_tenant_ust1tg_idx hinzu.
leitweg_id und reverse_charge_13b existierten bereits in Migration 0021.
19.6 Tests
Abschnitt betitelt „19.6 Tests“xrechnung-bau-extension.test.ts— 12 Caseskosit-offline-validator.test.ts— 33 Cases (pro Regel ≥ 1 pass + 1 fail)leitweg-id-validator.test.ts— 15 Cases (inkl. mod-97-10 happy/fail)
— Ende Feinkonzept §4.5 — Version 1.0 · 19.04.2026 · Senior-Berater-Verantwortung: Team “Rechnung” (Werkszeit GmbH) · Compliance-Review: Dr. M. Blank, StB —
— Wave 2 · 23.04.2026 · XRechnung-Bau-Extension · rein TS / offline · Migration 0034 —
Für Entwickler — API-Endpoints13
| Methode | Pfad | Auth | Zweck |
|---|---|---|---|
| GET | /v1/handwerk/bau-abrechnung/freistellungsbescheinigungen | bearerAuth | §48 EStG Freistellungsbescheinigungen — Liste |
| POST | /v1/handwerk/bau-abrechnung/freistellungsbescheinigungen | bearerAuth | §48 EStG Freistellungsbescheinigung anlegen |
| PUT | /v1/handwerk/bau-abrechnung/freistellungsbescheinigungen/{id} | bearerAuth | §48 EStG Freistellungsbescheinigung aktualisieren |
| GET | /v1/handwerk/bau-abrechnung/rechnungen | bearerAuth | Bau-Rechnungen — Liste |
| POST | /v1/handwerk/bau-abrechnung/rechnungen | bearerAuth | Bau-Rechnung anlegen (transaktional rechnungen + bau_rechnung_meta) |
| GET | /v1/handwerk/bau-abrechnung/rechnungen/{id} | bearerAuth | Bau-Rechnung Detail (inkl. VOB-Status-History) |
| POST | /v1/handwerk/bau-abrechnung/rechnungen/{id}/kumulation-berechnen | bearerAuth | Kumulative VOB/B §14 Saldo-Werte berechnen (kein DB-Change) |
| GET | /v1/handwerk/bau-abrechnung/rechnungen/{id}/leitweg-id-check | bearerAuth | Leitweg-ID einer Bau-Rechnung prüfen |
| POST | /v1/handwerk/bau-abrechnung/rechnungen/{id}/stornieren | bearerAuth | Bau-Rechnung stornieren (US-05-09) |
| GET | /v1/handwerk/bau-abrechnung/rechnungen/{id}/xrechnung.xml | bearerAuth | Bau-Rechnung als XRechnung-XML |
| GET | /v1/handwerk/bau-abrechnung/ust1tg-bescheinigungen | bearerAuth | §13b UStG USt-1-TG-Bescheinigungen — Liste |
| POST | /v1/handwerk/bau-abrechnung/ust1tg-bescheinigungen | bearerAuth | §13b UStG USt-1-TG-Bescheinigung anlegen |
| POST | /v1/handwerk/bau-abrechnung/xrechnung-validate | bearerAuth | XRechnung-XML validieren |