UNPKG

@fin.cx/einvoice

Version:

A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.

293 lines 27.3 kB
import * as plugins from '../plugins.js'; /** * A class to convert a given ILetter with invoice data * into a Factur-X compliant XML (also compatible with ZUGFeRD and EN16931). * * Factur-X is the French implementation of the European e-invoicing standard EN16931, * which is also implemented in Germany as ZUGFeRD. Both formats are based on * UN/CEFACT Cross Industry Invoice (CII) XML schemas. */ export class FacturXEncoder { constructor() { } /** * Alias for createFacturXXml to maintain backward compatibility */ createZugferdXml(letterArg) { return this.createFacturXXml(letterArg); } /** * Creates a Factur-X compliant XML based on the provided letter data. * This XML is also compliant with ZUGFeRD and EN16931 standards. */ createFacturXXml(letterArg) { // 1) Get your "SmartXml" or "xmlbuilder2" instance const smartxmlInstance = new plugins.smartxml.SmartXml(); if (!letterArg?.content?.invoiceData) { throw new Error('Letter does not contain invoice data.'); } const invoice = letterArg.content.invoiceData; const billedBy = invoice.billedBy; const billedTo = invoice.billedTo; // 2) Start building the document const doc = smartxmlInstance .create({ version: '1.0', encoding: 'UTF-8' }) .ele('rsm:CrossIndustryInvoice', { 'xmlns:rsm': 'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100', 'xmlns:udt': 'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100', 'xmlns:qdt': 'urn:un:unece:uncefact:data:standard:QualifiedDataType:100', 'xmlns:ram': 'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100' }); // 3) Exchanged Document Context const docContext = doc.ele('rsm:ExchangedDocumentContext'); // Add test indicator docContext.ele('ram:TestIndicator') .ele('udt:Indicator') .txt(this.isDraft(letterArg) ? 'true' : 'false') .up() .up(); // Add Factur-X profile information // EN16931 profile is compliant with both Factur-X and ZUGFeRD docContext.ele('ram:GuidelineSpecifiedDocumentContextParameter') .ele('ram:ID') .txt('urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:en16931') .up() .up(); docContext.up(); // </rsm:ExchangedDocumentContext> // 4) Exchanged Document (Invoice Header Info) const exchangedDoc = doc.ele('rsm:ExchangedDocument'); // Invoice ID exchangedDoc.ele('ram:ID').txt(invoice.id).up(); // Document type code // 380 = commercial invoice, 381 = credit note const documentTypeCode = invoice.type === 'creditnote' ? '381' : '380'; exchangedDoc.ele('ram:TypeCode').txt(documentTypeCode).up(); // Issue date exchangedDoc .ele('ram:IssueDateTime') .ele('udt:DateTimeString', { format: '102' }) // Format 'YYYYMMDD' as per Factur-X specification .txt(this.formatDate(letterArg.date)) .up() .up(); // Document name - Factur-X recommended field const documentName = invoice.type === 'creditnote' ? 'CREDIT NOTE' : 'INVOICE'; exchangedDoc.ele('ram:Name').txt(documentName).up(); // Optional: Add language indicator (recommended for Factur-X) // Use document language if specified, default to 'en' const languageCode = letterArg.language?.toUpperCase() || 'EN'; exchangedDoc .ele('ram:IncludedNote') .ele('ram:Content').txt('Invoice created with Factur-X compliant software').up() .ele('ram:SubjectCode').txt('REG').up() // REG = regulatory information .up(); exchangedDoc.up(); // </rsm:ExchangedDocument> // 5) Supply Chain Trade Transaction const supplyChainEle = doc.ele('rsm:SupplyChainTradeTransaction'); // 5.1) Included Supply Chain Trade Line Items invoice.items.forEach((item) => { const lineItemEle = supplyChainEle.ele('ram:IncludedSupplyChainTradeLineItem'); lineItemEle.ele('ram:SpecifiedTradeProduct') .ele('ram:Name') .txt(item.name) .up() .up(); // </ram:SpecifiedTradeProduct> lineItemEle.ele('ram:SpecifiedLineTradeAgreement') .ele('ram:GrossPriceProductTradePrice') .ele('ram:ChargeAmount') .txt(item.unitNetPrice.toFixed(2)) .up() .up() .up(); // </ram:SpecifiedLineTradeAgreement> lineItemEle.ele('ram:SpecifiedLineTradeDelivery') .ele('ram:BilledQuantity') .txt(item.unitQuantity.toString()) .up() .up(); // </ram:SpecifiedLineTradeDelivery> lineItemEle.ele('ram:SpecifiedLineTradeSettlement') .ele('ram:ApplicableTradeTax') .ele('ram:RateApplicablePercent') .txt(item.vatPercentage.toFixed(2)) .up() .up() .ele('ram:SpecifiedTradeSettlementLineMonetarySummation') .ele('ram:LineTotalAmount') .txt((item.unitQuantity * item.unitNetPrice * (1 + item.vatPercentage / 100)).toFixed(2)) .up() .up() .up(); // </ram:SpecifiedLineTradeSettlement> }); // 5.2) Applicable Header Trade Agreement const headerTradeAgreementEle = supplyChainEle.ele('ram:ApplicableHeaderTradeAgreement'); // Seller const sellerPartyEle = headerTradeAgreementEle.ele('ram:SellerTradeParty'); sellerPartyEle.ele('ram:Name').txt(billedBy.name).up(); // Example: If it's a company, put company name, etc. const sellerAddressEle = sellerPartyEle.ele('ram:PostalTradeAddress'); sellerAddressEle.ele('ram:PostcodeCode').txt(billedBy.address.postalCode).up(); sellerAddressEle.ele('ram:LineOne').txt(billedBy.address.streetName).up(); sellerAddressEle.ele('ram:CityName').txt(billedBy.address.city).up(); // Typically you'd include 'ram:CountryID' with ISO2 code, e.g. "DE" sellerAddressEle.up(); // </ram:PostalTradeAddress> sellerPartyEle.up(); // </ram:SellerTradeParty> // Buyer const buyerPartyEle = headerTradeAgreementEle.ele('ram:BuyerTradeParty'); buyerPartyEle.ele('ram:Name').txt(billedTo.name).up(); const buyerAddressEle = buyerPartyEle.ele('ram:PostalTradeAddress'); buyerAddressEle.ele('ram:PostcodeCode').txt(billedTo.address.postalCode).up(); buyerAddressEle.ele('ram:LineOne').txt(billedTo.address.streetName).up(); buyerAddressEle.ele('ram:CityName').txt(billedTo.address.city).up(); buyerAddressEle.up(); // </ram:PostalTradeAddress> buyerPartyEle.up(); // </ram:BuyerTradeParty> headerTradeAgreementEle.up(); // </ram:ApplicableHeaderTradeAgreement> // 5.3) Applicable Header Trade Delivery const headerTradeDeliveryEle = supplyChainEle.ele('ram:ApplicableHeaderTradeDelivery'); const actualDeliveryEle = headerTradeDeliveryEle.ele('ram:ActualDeliverySupplyChainEvent'); const occurrenceEle = actualDeliveryEle.ele('ram:OccurrenceDateTime') .ele('udt:DateTimeString', { format: '102' }); const deliveryDate = invoice.deliveryDate || letterArg.date; occurrenceEle.txt(this.formatDate(deliveryDate)).up(); actualDeliveryEle.up(); // </ram:ActualDeliverySupplyChainEvent> headerTradeDeliveryEle.up(); // </ram:ApplicableHeaderTradeDelivery> // 5.4) Applicable Header Trade Settlement const headerTradeSettlementEle = supplyChainEle.ele('ram:ApplicableHeaderTradeSettlement'); // Tax currency code, doc currency code, etc. headerTradeSettlementEle.ele('ram:InvoiceCurrencyCode').txt(invoice.currency).up(); // Example single tax breakdown const tradeTaxEle = headerTradeSettlementEle.ele('ram:ApplicableTradeTax'); tradeTaxEle.ele('ram:TypeCode').txt('VAT').up(); tradeTaxEle.ele('ram:CalculatedAmount').txt(this.sumAllVat(invoice).toFixed(2)).up(); tradeTaxEle .ele('ram:RateApplicablePercent') .txt(this.extractMainVatRate(invoice.items).toFixed(2)) .up(); tradeTaxEle.up(); // </ram:ApplicableTradeTax> // Payment Terms const paymentTermsEle = headerTradeSettlementEle.ele('ram:SpecifiedTradePaymentTerms'); // Payment description paymentTermsEle.ele('ram:Description').txt(`Payment due in ${invoice.dueInDays} days.`).up(); // Due date calculation const dueDate = new Date(letterArg.date); dueDate.setDate(dueDate.getDate() + invoice.dueInDays); // Add due date as per Factur-X spec paymentTermsEle .ele('ram:DueDateDateTime') .ele('udt:DateTimeString', { format: '102' }) .txt(this.formatDate(dueDate.getTime())) .up() .up(); // Add payment means if available if (invoice.billedBy.sepaConnection) { // Add SEPA information as per Factur-X standard const paymentMeans = headerTradeSettlementEle.ele('ram:SpecifiedTradeSettlementPaymentMeans'); paymentMeans.ele('ram:TypeCode').txt('58').up(); // 58 = SEPA credit transfer // Payment reference (for bank statement reconciliation) paymentMeans.ele('ram:Information').txt(`Reference: ${invoice.id}`).up(); // Payee account (IBAN) if (invoice.billedBy.sepaConnection.iban) { const payeeAccount = paymentMeans.ele('ram:PayeePartyCreditorFinancialAccount'); payeeAccount.ele('ram:IBANID').txt(invoice.billedBy.sepaConnection.iban).up(); payeeAccount.up(); } // Bank BIC if (invoice.billedBy.sepaConnection.bic) { const payeeBank = paymentMeans.ele('ram:PayeeSpecifiedCreditorFinancialInstitution'); payeeBank.ele('ram:BICID').txt(invoice.billedBy.sepaConnection.bic).up(); payeeBank.up(); } paymentMeans.up(); } paymentTermsEle.up(); // </ram:SpecifiedTradePaymentTerms> // Monetary Summation const monetarySummationEle = headerTradeSettlementEle.ele('ram:SpecifiedTradeSettlementHeaderMonetarySummation'); monetarySummationEle .ele('ram:LineTotalAmount') .txt(this.calcLineTotalNet(invoice).toFixed(2)) .up(); monetarySummationEle .ele('ram:TaxTotalAmount') .txt(this.sumAllVat(invoice).toFixed(2)) .up(); monetarySummationEle .ele('ram:GrandTotalAmount') .txt(this.calcGrandTotal(invoice).toFixed(2)) .up(); monetarySummationEle.up(); // </ram:SpecifiedTradeSettlementHeaderMonetarySummation> headerTradeSettlementEle.up(); // </ram:ApplicableHeaderTradeSettlement> supplyChainEle.up(); // </rsm:SupplyChainTradeTransaction> doc.up(); // </rsm:CrossIndustryInvoice> // 6) Return the final XML string return doc.end({ prettyPrint: true }); } /** * Helper: Determine if the letter is in draft or final. */ isDraft(letterArg) { return letterArg.versionInfo?.type === 'draft'; } /** * Helper: Format date to certain patterns (very minimal example). * e.g. 'yyyyMMdd' => '20231231' */ formatDate(timestampMs) { const date = new Date(timestampMs); const yyyy = date.getFullYear(); const mm = String(date.getMonth() + 1).padStart(2, '0'); const dd = String(date.getDate()).padStart(2, '0'); return `${yyyy}${mm}${dd}`; } /** * Helper: Map your custom 'unitType' to an ISO code or similar. */ mapUnitType(unitType) { switch (unitType.toLowerCase()) { case 'hour': return 'HUR'; case 'piece': return 'C62'; default: return 'C62'; // fallback } } /** * Example: Sum all VAT amounts from items. */ sumAllVat(invoice) { return invoice.items.reduce((acc, item) => { const net = item.unitNetPrice * item.unitQuantity; const vat = net * (item.vatPercentage / 100); return acc + vat; }, 0); } /** * Example: Extract main (or highest) VAT rate from items as representative. * In reality, you might list multiple 'ApplicableTradeTax' blocks by group. */ extractMainVatRate(items) { let max = 0; items.forEach((item) => { if (item.vatPercentage > max) max = item.vatPercentage; }); return max; } /** * Example: Sum net amounts (without VAT). */ calcLineTotalNet(invoice) { return invoice.items.reduce((acc, item) => { const net = item.unitNetPrice * item.unitQuantity; return acc + net; }, 0); } /** * Example: net + VAT = grand total */ calcGrandTotal(invoice) { const net = this.calcLineTotalNet(invoice); const vat = this.sumAllVat(invoice); return net + vat; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdHVyeC5lbmNvZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvZm9ybWF0cy9mYWN0dXJ4LmVuY29kZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFFekM7Ozs7Ozs7R0FPRztBQUNILE1BQU0sT0FBTyxjQUFjO0lBRXpCLGdCQUFlLENBQUM7SUFFaEI7O09BRUc7SUFDSSxnQkFBZ0IsQ0FBQyxTQUEyQztRQUNqRSxPQUFPLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUMxQyxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksZ0JBQWdCLENBQUMsU0FBMkM7UUFDakUsbURBQW1EO1FBQ25ELE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBRXpELElBQUksQ0FBQyxTQUFTLEVBQUUsT0FBTyxFQUFFLFdBQVcsRUFBRSxDQUFDO1lBQ3JDLE1BQU0sSUFBSSxLQUFLLENBQUMsdUNBQXVDLENBQUMsQ0FBQztRQUMzRCxDQUFDO1FBRUQsTUFBTSxPQUFPLEdBQXFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDO1FBQ2hGLE1BQU0sUUFBUSxHQUFzQyxPQUFPLENBQUMsUUFBUSxDQUFDO1FBQ3JFLE1BQU0sUUFBUSxHQUFzQyxPQUFPLENBQUMsUUFBUSxDQUFDO1FBRXJFLGlDQUFpQztRQUNqQyxNQUFNLEdBQUcsR0FBRyxnQkFBZ0I7YUFDekIsTUFBTSxDQUFDLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLENBQUM7YUFDN0MsR0FBRyxDQUFDLDBCQUEwQixFQUFFO1lBQy9CLFdBQVcsRUFBRSw4REFBOEQ7WUFDM0UsV0FBVyxFQUFFLDZEQUE2RDtZQUMxRSxXQUFXLEVBQUUsMkRBQTJEO1lBQ3hFLFdBQVcsRUFBRSxvRkFBb0Y7U0FDbEcsQ0FB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