63e3a02212
renderInvoiceEInvoice()/renderCorrectionInvoiceEInvoice() produce a Factur-X-EN16931 hybrid PDF/A-3 (existing @react-pdf/renderer PDF + embedded EN16931 XML) via @e-invoice-eu/core, instead of a plain PDF. The plain renderInvoicePdf()/renderCorrectionInvoicePdf() stay unchanged and are still what Live Preview uses — this only adds a post-processing step for the actual send/download paths. buildEInvoiceData()/buildCorrectionEInvoiceData() map InvoiceOrder/ CorrectionInvoiceOrder + InvoiceSeller into the library's raw UBL-shaped Invoice object, reusing computeTaxBreakdown() for the tax math — one implementation feeding both the human-readable and machine-readable side of the same document. Verified end-to-end: rendered a sample invoice and correction invoice, inflated the embedded XML stream out of the resulting PDF/A-3 by hand (the library has no attachment-reading API to check against), confirmed correct CrossIndustryInvoice XML, EN16931 guideline reference, per-rate tax breakdown matching the order totals exactly, and payment means with the IBAN from Phase 2 — not just "it didn't throw." Found only through actually running it (not documented anywhere): every EN16931 amount field requires a sibling `*@currencyID` key via the library's runtime ajv validation, invisible in its TypeScript types. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
50 lines
2.9 KiB
TypeScript
50 lines
2.9 KiB
TypeScript
import { InvoiceService } from "@e-invoice-eu/core";
|
|
import { renderInvoicePdf, type InvoiceOrder } from "../invoicePdf";
|
|
import { renderCorrectionInvoicePdf, type CorrectionInvoiceKind, type CorrectionInvoiceOrder } from "../correctionInvoicePdf";
|
|
import type { InvoiceSeller } from "../seller";
|
|
import { buildEInvoiceData, buildCorrectionEInvoiceData } from "./buildEInvoiceData";
|
|
|
|
// `console` satisfies @e-invoice-eu/core's minimal Logger interface
|
|
// (debug/info/warn/error) without needing a dedicated logger dependency —
|
|
// this only ever runs server-side (Node.js), console output lands in the
|
|
// same place `payload.logger`/plain `console.error` calls already do
|
|
// elsewhere in both consuming repos.
|
|
const invoiceService = new InvoiceService(console);
|
|
|
|
// Renders the existing @react-pdf/renderer visual PDF unchanged, then
|
|
// passes that buffer through @e-invoice-eu/core's Factur-X embed step to
|
|
// get back a PDF/A-3 file with the EN16931 XML attached (`factur-x.xml`)
|
|
// — same visual document a human opens, but AP software that understands
|
|
// Factur-X/ZUGFeRD can also extract and process it automatically. Format
|
|
// 'Factur-X-EN16931' — the "Comfort" profile, the minimum EN16931-compliant
|
|
// Factur-X level (see this package's README/the project's e-invoicing
|
|
// migration plan for why EN16931 over a lower profile like BASIC).
|
|
export async function renderInvoiceEInvoice(order: InvoiceOrder, seller: InvoiceSeller): Promise<Uint8Array> {
|
|
const pdf = await renderInvoicePdf(order, seller);
|
|
const invoiceData = buildEInvoiceData(order, seller);
|
|
const result = await invoiceService.generate(invoiceData, {
|
|
format: "Factur-X-EN16931",
|
|
lang: "de",
|
|
pdf: { buffer: pdf, filename: `Rechnung-${order.invoiceNumber}.pdf`, mimetype: "application/pdf" },
|
|
});
|
|
// Factur-X formats always return the PDF/A-3 bytes, never the bare XML
|
|
// string variant `generate()`'s return type also allows for XML-only
|
|
// formats (XRechnung/UBL/CII) — this function only ever requests a
|
|
// Factur-X format, so this case is unreachable in practice, just
|
|
// satisfying the union type.
|
|
if (typeof result === "string") throw new Error("Expected a PDF/A-3 buffer from Factur-X generation, got a string.");
|
|
return result;
|
|
}
|
|
|
|
export async function renderCorrectionInvoiceEInvoice(kind: CorrectionInvoiceKind, order: CorrectionInvoiceOrder, seller: InvoiceSeller): Promise<Uint8Array> {
|
|
const pdf = await renderCorrectionInvoicePdf(kind, order, seller);
|
|
const invoiceData = buildCorrectionEInvoiceData(kind, order, seller);
|
|
const result = await invoiceService.generate(invoiceData, {
|
|
format: "Factur-X-EN16931",
|
|
lang: "de",
|
|
pdf: { buffer: pdf, filename: `${kind === "storno" ? "Stornorechnung" : "Gutschrift"}-${order.correctionInvoiceNumber}.pdf`, mimetype: "application/pdf" },
|
|
});
|
|
if (typeof result === "string") throw new Error("Expected a PDF/A-3 buffer from Factur-X generation, got a string.");
|
|
return result;
|
|
}
|