Darstellung
10 – Rechnungen & E-Rechnung
Reifegrad: 🟢 produktiv · Schicht: invoices & Co., @work7/domain (Invoice-Funktionen), packages/domain/src/einvoice/*, src/app/invoices, src/app/api/invoices/*, src/templates/invoice-template.html
Zweck
Rechnungen erstellen (manuell, aus Angebot, aus Rapporten), Zahlungen verwalten, als PDF/E-Mail versenden und gesetzeskonforme E-Rechnungen für DE (ZUGFeRD/CII) und AT (ebInterface) exportieren.
Datenmodell (Migration 006 + 031)
invoices:invoice_number(UNIQUE(company_id, invoice_number)),type∈ {anzahlung,teilrechnung,schlussrechnung,standard},status∈ {draft,sent,paid,partial_paid,overdue,cancelled}, Finanzfelder als NUMERIC (subtotal,discount_*,tax_rate,tax_amount,total),already_paidund generiertesremaining = total - already_paid. Bezüge:customer_id,project_id,offer_id. E-Rechnungsfelder (031):service_date,service_period_start/_end,buyer_reference,reverse_charge,currency,original_invoice_number,sent_at,paid_at.invoice_items:position,source_type∈ {offer_item,rapport_entry,rapport_material,manual} +source_id(Herkunftsverfolgung),quantity,unit,unit_price, generiertestotal_price, zeilenweise Steuertax_rate/tax_category(DefaultS).invoice_rapport_links: m:n Rechnung ↔ Rapport mitbilling_percentage.invoice_counters: fortlaufende Nummer pro(company_id, year); DB-Funktiongenerate_invoice_number()→ FormatRE-2026-0001.invoice_exports: Export-Historie (format∈ {zugferd,ebinterface},status∈ {pending,valid,invalid,failed},validation_resultJSONB,file_xml,file_pdf,file_primary).labor_rates: Stundensätze jejob_category(für Abrechnung aus Rapporten).- DB-Trigger
recalculate_invoice_totals()summiert Beträge bei jeder Item-Änderung neu.
Status- & Zahlungslogik (@work7/domain)
- Statusübergänge (
INVOICE_STATUS_TRANSITIONS/assertInvoiceStatusTransition):draft→{sent,cancelled},sent→{paid,partial_paid,overdue,cancelled},partial_paid→{paid,overdue},overdue→{paid,partial_paid,cancelled};paid/cancelledterminal. - Teilzahlungen (
calculateInvoicePaymentState/recordInvoicePayment): validiert Betrag > 0 und ≤ Restbetrag, setztpartial_paidbzw.paid.markInvoicePaidfür Komplettzahlung.
Erzeugung aus Vorprozessen
createInvoiceFromOffer–POST /api/invoices/from-offer: übernimmt Angebotspositionen.createInvoiceFromRapports–POST /api/invoices/from-rapports: bündelt Rapportzeilen; Arbeitsstunden werden überlabor_rates(job_category)bepreist, Material als eigene Posten; setztinvoice_rapport_linksundrapports.billing_status.
E-Rechnung (packages/domain/src/einvoice/*)
Eigenständige, getestete E-Invoicing-Pipeline:
- Kanonisches Modell (
canonical-invoice.ts,types.ts): lädt Rechnung + Verkäufer/Käufer in einCanonicalInvoiceund validiert Pflichtfelder (EN 16931). - Formatwahl (
resolveEinvoiceFormat):DE → zugferd(CII-XML in PDF/A-3 eingebettet,zugferd-generator.ts), sonstebinterface(AT,ebinterface-generator.ts). - Human-PDF (
human-pdf.ts,invoice-html.ts): lesbares Rechnungs-PDF als Träger. - Validierung: Feld- und XML-Validierung (
validateCiiXml/validateEbInterfaceXml); Ergebnis (valid/invalid) + Fehlerliste landen ininvoice_exports.
Domain-Funktionen: validateInvoiceExport, exportInvoiceEinvoice, getInvoiceExportLink, getLatestExport.
API-Endpunkte
| Endpoint | Funktion |
|---|---|
GET/POST /api/invoices | Liste / anlegen |
GET/PUT/DELETE /api/invoices/[id] | Detail / ändern / löschen |
GET/POST /api/invoices/[id]/items | Positionen |
POST /api/invoices/[id]/pay | Zahlung erfassen |
POST /api/invoices/[id]/send | Rechnung versenden (E-Mail) |
POST /api/invoices/[id]/export | E-Rechnung erzeugen (ZUGFeRD/ebInterface) |
POST /api/invoices/[id]/export/validate | Export vorab validieren |
POST /api/invoices/from-offer · from-rapports | aus Angebot / Rapporten erzeugen |
GET/POST /api/invoices/labor-rates | Stundensätze verwalten |
GET /api/pdf/invoices/[id] | Rechnungs-PDF |
UI
/invoices (Liste mit Status/Restbetrag), /invoices/[id] (Detail, Positionen, Zahlungen, Export, Versand). Komponenten: src/components/invoices/*. Mandanten-Defaults (Nummernformat, E-Rechnungsformat, Steuer) aus companies (siehe 02-mandanten-company-settings).
Compliance-Hintergrund
Siehe ../compliance/compliance-e-rechnung-de-at.md und adr-0006-e-rechnung-zugferd-ebinterface.
Bekannte Befunde / offene Punkte
- Mahnwesen/Skonto sind in
companieskonfigurierbar (dunning_*,cash_discount_*), aber ein automatischer Mahnlauf ist (noch) nicht als Job implementiert. overdue-Status muss aktiv gesetzt werden (kein Auto-Job, der fällige Rechnungen umstuft).- Default-
tax_rate20 % (AT) – via Mandanten-Default überschreibbar.