// ==================== ZUGFeRD / FACTUR-X XML (CII, EN 16931) ==================== // Erzeugt das Cross-Industry-Invoice-XML (UN/CEFACT CII) für eine Gutschrift // (Dokumenttyp 381). Profil: EN 16931 ("urn:cen.eu:en16931:2017"). // // WICHTIG: Muss vor produktivem Einsatz gegen einen ZUGFeRD-/Factur-X- // Validator geprüft werden. Verkäufer = ausstellende Firma (CompanyProfile), // Käufer = Kunde. Beträge positiv (der Typcode 381 kennzeichnet die Gutschrift). export interface ZugferdParty { name: string; street: string; houseNumber: string; postalCode: string; city: string; country: string; // ISO-2 vatId?: string; taxNumber?: string; } export interface ZugferdData { number: string; issueDate: Date; currency: string; seller: ZugferdParty; buyer: Omit & { vatId?: string }; lineName: string; vatRelevant: boolean; vatRatePercent: number; amountNet: number; amountVat: number; amountGross: number; } function esc(s: string | undefined | null): string { return (s ?? '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } const n2 = (n: number) => (Math.round((n + Number.EPSILON) * 100) / 100).toFixed(2); const date102 = (d: Date) => `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, '0')}${String(d.getDate()).padStart(2, '0')}`; function addressBlock(p: { street: string; houseNumber: string; postalCode: string; city: string; country: string }): string { const lineOne = `${p.street} ${p.houseNumber}`.trim(); return ` ${esc(p.postalCode)} ${esc(lineOne)} ${esc(p.city)} ${esc(p.country || 'DE')} `; } export function buildZugferdXml(d: ZugferdData): string { // Steuerkategorie: S = Regelsatz, E = steuerbefreit (ohne USt-Ausweis). const categoryCode = d.vatRelevant ? 'S' : 'E'; const ratePct = d.vatRelevant ? d.vatRatePercent : 0; const exemptionReason = d.vatRelevant ? '' : 'Kein gesonderter Umsatzsteuerausweis'; const sellerTax: string[] = []; if (d.seller.vatId) { sellerTax.push(` ${esc(d.seller.vatId)} `); } if (d.seller.taxNumber) { sellerTax.push(` ${esc(d.seller.taxNumber)} `); } const buyerTax = d.buyer.vatId ? ` ${esc(d.buyer.vatId)} ` : ''; const lineTaxCategory = ` VAT ${categoryCode} ${n2(ratePct)} `; const headerTax = ` ${n2(d.amountVat)} VAT${exemptionReason ? `\n ${esc(exemptionReason)}` : ''} ${n2(d.amountNet)} ${categoryCode} ${n2(ratePct)} `; return ` urn:cen.eu:en16931:2017 ${esc(d.number)} 381 ${date102(d.issueDate)} 1 ${esc(d.lineName)} ${n2(d.amountNet)} 1 ${lineTaxCategory} ${n2(d.amountNet)} ${esc(d.seller.name)} ${addressBlock(d.seller)} ${sellerTax.join('\n')} ${esc(d.buyer.name)} ${addressBlock(d.buyer)}${buyerTax ? `\n${buyerTax}` : ''} ${esc(d.currency)} ${headerTax} ${n2(d.amountNet)} ${n2(d.amountNet)} ${n2(d.amountVat)} ${n2(d.amountGross)} ${n2(d.amountGross)} `; }