feat(accounting): Pflichtangaben der E-Rechnung vor dem Fertigstellen durchsetzen
Fehlende Pflichtangaben (Firmendaten, Steuer-ID, Kundenanschrift, IBAN, § 13b-USt-Id, Lastschriftmandat) erscheinen im Editor als blockierende Fehler und verhindern Vorschau und Fertigstellung. Serienrechnungslauf, MCP-Finalisierung und PDF-Erzeugung prüfen serverseitig vor der Nummernvergabe bzw. Ablage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,11 +14,34 @@ import {
|
||||
tenants,
|
||||
units,
|
||||
} from "../../../db/schema"
|
||||
import { buildZugferdInvoiceXml, ZUGFERD_DOCUMENT_TYPES, ZugferdInvoiceContext } from "./buildZugferdInvoice"
|
||||
import { buildZugferdInvoiceXml, checkZugferdRequirements, ZUGFERD_DOCUMENT_TYPES, ZugferdInvoiceContext, ZugferdIssue } from "./buildZugferdInvoice"
|
||||
import { embedZugferdXml } from "./embedZugferd"
|
||||
|
||||
export const isZugferdDocument = (document: any) =>
|
||||
Boolean(document && ZUGFERD_DOCUMENT_TYPES.includes(document.type) && document.documentNumber)
|
||||
export const isZugferdDocumentType = (type: unknown) => ZUGFERD_DOCUMENT_TYPES.includes(String(type))
|
||||
export const isZugferdDocument = (document: any) => Boolean(document && isZugferdDocumentType(document.type) && document.documentNumber)
|
||||
|
||||
export class EInvoiceRequirementsError extends Error {
|
||||
statusCode = 422
|
||||
code = "EINVOICE_REQUIREMENTS"
|
||||
constructor(public issues: ZugferdIssue[]) {
|
||||
super(`Die E-Rechnung kann nicht erstellt werden: ${issues.map((issue) => issue.message.replace(/^E-Rechnung: /, "")).join("; ")}`)
|
||||
}
|
||||
}
|
||||
|
||||
export const breakingIssues = (issues: ZugferdIssue[]) => issues.filter((issue) => issue.type === "breaking")
|
||||
|
||||
/** Prüft die Pflichtangaben eines (auch ungespeicherten) Belegs. Andere Belegarten liefern keine Hinweise. */
|
||||
export async function checkOutgoingEInvoice(server: FastifyInstance, tenantId: number, document: any, options: { requireNumber?: boolean } = {}) {
|
||||
if (!document || !isZugferdDocumentType(document.type)) return [] as ZugferdIssue[]
|
||||
const context = await loadZugferdContext(server, tenantId, document)
|
||||
return checkZugferdRequirements(document, context, options)
|
||||
}
|
||||
|
||||
/** Wirft EInvoiceRequirementsError, wenn Pflichtangaben fehlen. */
|
||||
export async function assertOutgoingEInvoiceRequirements(server: FastifyInstance, tenantId: number, document: any, options: { requireNumber?: boolean } = {}) {
|
||||
const blocking = breakingIssues(await checkOutgoingEInvoice(server, tenantId, document, options))
|
||||
if (blocking.length) throw new EInvoiceRequirementsError(blocking)
|
||||
}
|
||||
|
||||
const byId = async (server: FastifyInstance, table: any, id: unknown, tenantId: number) => {
|
||||
if (id === null || id === undefined || id === "") return null
|
||||
@@ -69,19 +92,15 @@ export async function loadZugferdContext(server: FastifyInstance, tenantId: numb
|
||||
}
|
||||
|
||||
/**
|
||||
* Macht aus der gerenderten Rechnungs-PDF eine ZUGFeRD-Rechnung. Fehler bei der E-Rechnung blockieren
|
||||
* die Belegerstellung nicht: dann wird die unveränderte PDF mit einer Warnung zurückgegeben.
|
||||
* Macht aus der gerenderten Rechnungs-PDF eine ZUGFeRD-Rechnung. Fehlende Pflichtangaben führen zu
|
||||
* EInvoiceRequirementsError, damit keine Rechnung ohne gültige E-Rechnung abgelegt wird.
|
||||
*/
|
||||
export async function attachZugferdToPdf(server: FastifyInstance, tenantId: number, document: any, pdf: Buffer) {
|
||||
if (!isZugferdDocument(document)) return { pdf, warnings: [] as string[], einvoice: false }
|
||||
try {
|
||||
const context = await loadZugferdContext(server, tenantId, document)
|
||||
const { xml, warnings } = buildZugferdInvoiceXml(document, context)
|
||||
const result = await embedZugferdXml(pdf, xml, { title: document.title || `Rechnung ${document.documentNumber}` })
|
||||
if (warnings.length) server.log.warn({ documentId: document.id, warnings }, "E-Rechnung mit Hinweisen erzeugt")
|
||||
return { pdf: result, warnings, einvoice: true }
|
||||
} catch (error) {
|
||||
server.log.error({ err: error, documentId: document.id }, "E-Rechnung konnte nicht erzeugt werden")
|
||||
return { pdf, warnings: ["E-Rechnung konnte nicht erzeugt werden"], einvoice: false }
|
||||
}
|
||||
if (!isZugferdDocument(document)) return { pdf, issues: [] as ZugferdIssue[], einvoice: false }
|
||||
const context = await loadZugferdContext(server, tenantId, document)
|
||||
const { xml, issues } = buildZugferdInvoiceXml(document, context)
|
||||
const blocking = breakingIssues(issues)
|
||||
if (blocking.length) throw new EInvoiceRequirementsError(blocking)
|
||||
const result = await embedZugferdXml(pdf, xml, { title: document.title || `Rechnung ${document.documentNumber}` })
|
||||
return { pdf: result, issues, einvoice: true }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user