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.2 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5lbmNvZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5lbmNvZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBRXhDOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLE9BQU8sY0FBYztJQUV6QixnQkFBZSxDQUFDO0lBRWhCOztPQUVHO0lBQ0ksZ0JBQWdCLENBQUMsU0FBMkM7UUFDakUsT0FBTyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDMUMsQ0FBQztJQUVEOzs7T0FHRztJQUNJLGdCQUFnQixDQUFDLFNBQTJDO1FBQ2pFLG1EQUFtRDtRQUNuRCxNQUFNLGdCQUFnQixHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUV6RCxJQUFJLENBQUMsU0FBUyxFQUFFLE9BQU8sRUFBRSxXQUFXLEVBQUUsQ0FBQztZQUNyQyxNQUFNLElBQUksS0FBSyxDQUFDLHVDQUF1QyxDQUFDLENBQUM7UUFDM0QsQ0FBQztRQUVELE1BQU0sT0FBTyxHQUFxQyxTQUFTLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQztRQUNoRixNQUFNLFFBQVEsR0FBc0MsT0FBTyxDQUFDLFFBQVEsQ0FBQztRQUNyRSxNQUFNLFFBQVEsR0FBc0MsT0FBTyxDQUFDLFFBQVEsQ0FBQztRQUVyRSxpQ0FBaUM7UUFDakMsTUFBTSxHQUFHLEdBQUcsZ0JBQWdCO2FBQ3pCLE1BQU0sQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxDQUFDO2FBQzdDLEdBQUcsQ0FBQywwQkFBMEIsRUFBRTtZQUMvQixXQUFXLEVBQUUsOERBQThEO1lBQzNFLFdBQVcsRUFBRSw2REFBNkQ7WUFDMUUsV0FBVyxFQUFFLDJEQUEyRDtZQUN4RSxXQUFXLEVBQUUsb0ZBQW9GO1NBQ2xHLENBQUMsQ0FBQztRQUVM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