UNPKG

n8n-nodes-einvoice

Version:

n8n.io node to handle E-Invoices with PDF or XML Files (ZUGFeRD / XRechnung / Factur-X / EN-16931)

202 lines 12.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.extractEInvoiceFromPDF = extractEInvoiceFromPDF; exports.extractEInvoiceFromXML = extractEInvoiceFromXML; const n8n_workflow_1 = require("n8n-workflow"); const util_1 = require("pdfjs-dist/lib/shared/util"); const pdfjs_dist_1 = require("pdfjs-dist"); const xml2js_1 = require("xml2js"); const documentTypes_1 = require("../types/documentTypes"); const FACTUR_X_FILENAMES = ["factur-x.xml", "factur\\055x\\056xml", "zugferd-invoice.xml", "zugferd\\055invoice\\056xml", "ZUGFeRD-invoice.xml", "ZUGFeRD\\055invoice\\056xml", "xrechnung.xml", "xrechnung\\056xml"].map((name) => (0, util_1.stringToPDFString)(name)); async function extractEInvoiceFromPDF(binaryPropertyName, password, mode, itemIndex = 0) { const binaryData = this.helpers.assertBinaryData(itemIndex, binaryPropertyName); const params = { password, isEvalSupported: false }; if (binaryData.id) { params.data = await this.helpers.binaryToBuffer(await this.helpers.getBinaryStream(binaryData.id)); } else { params.data = Buffer.from(binaryData.data, n8n_workflow_1.BINARY_ENCODING).buffer; } const pdf = await (0, pdfjs_dist_1.getDocument)(params).promise; const attachments = await pdf.getAttachments(); for (const [filename, attachment] of Object.entries(attachments)) { if (FACTUR_X_FILENAMES.includes(filename)) { const xml = Buffer.from(attachment.content).toString('utf-8'); if (xml === "") { throw new Error("empty xml-attachment in pdf"); } if (mode === 'xml') { return { xml: Buffer.from(xml).toString('base64'), filename: attachment.filename, }; } return parseEInvoiceXML(xml, mode); } } throw new Error("Could not find xml-attachment in pdf"); } async function extractEInvoiceFromXML(binaryPropertyName, mode, itemIndex = 0) { const binaryData = this.helpers.assertBinaryData(itemIndex, binaryPropertyName); let data = {}; if (binaryData.id) { data = await this.helpers.binaryToBuffer(await this.helpers.getBinaryStream(binaryData.id)); } else { data = Buffer.from(binaryData.data, n8n_workflow_1.BINARY_ENCODING).toString('utf-8'); } if (mode === 'xml') { return { xml: data, filename: binaryData.filename, }; } return parseEInvoiceXML(data, mode); } async function parseEInvoiceXML(xml, mode) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11; const parserOptions = { mergeAttrs: true, explicitArray: false, tagNameProcessors: [ (name) => name.replace(/^.*:/, '') ], attrNameProcessors: [ (name) => name.replace(/^.*:/, '') ], ignoreAttrs: true }; const parser = new xml2js_1.Parser(parserOptions); const json = await parser.parseStringPromise(xml); if (mode === 'json') { return json; } if (!json.CrossIndustryInvoice) { throw new Error("no CrossIndustryInvoice after parsing found in xml"); } const profileId = (_c = (_b = (_a = json.CrossIndustryInvoice) === null || _a === void 0 ? void 0 : _a.ExchangedDocumentContext) === null || _b === void 0 ? void 0 : _b.GuidelineSpecifiedDocumentContextParameter) === null || _c === void 0 ? void 0 : _c.ID; var profile = ""; if (!profileId) { throw new Error("missing profile identifier"); } switch (profileId) { case "urn:factur-x.eu:1p0:minimum": profile = "minimum"; break; case "urn:factur-x.eu:1p0:basicwl": profile = "basicwl"; break; case "urn:factur-x.eu:1p0:basic": case "urn:cen.eu:en16931:2017:compliant:factur-x.eu:1p0:basic": case "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:basic": profile = "basic"; break; case "urn:cen.eu:en16931:2017": profile = "en16931"; break; case "urn:factur-x.eu:1p0:extended": case "urn:cen.eu:en16931:2017:compliant:factur-x.eu:1p0:extended": case "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended": profile = "extended"; break; default: throw new Error(`unknown profile: ${profileId}`); } const simplified = {}; const ci = json.CrossIndustryInvoice; simplified.meta = { specificationProfile: profile, businessProcessType: ((_e = (_d = ci.ExchangedDocumentContext) === null || _d === void 0 ? void 0 : _d.BusinessProcessSpecifiedDocumentContext) === null || _e === void 0 ? void 0 : _e.ID) || 'A1' }; simplified.documentId = (_f = ci.ExchangedDocument) === null || _f === void 0 ? void 0 : _f.ID; simplified.documentType = (_g = ci.ExchangedDocument) === null || _g === void 0 ? void 0 : _g.TypeCode; simplified.documentDate = (_j = (_h = ci.ExchangedDocument) === null || _h === void 0 ? void 0 : _h.IssueDateTime) === null || _j === void 0 ? void 0 : _j.DateTimeString; simplified.notes = ((_k = ci.ExchangedDocument) === null || _k === void 0 ? void 0 : _k.IncludedNote) ? [{ text: ci.ExchangedDocument.IncludedNote.Content, code: ci.ExchangedDocument.IncludedNote.SubjectCode }] : []; const agreement = (_l = ci.SupplyChainTradeTransaction) === null || _l === void 0 ? void 0 : _l.ApplicableHeaderTradeAgreement; const sellerParty = agreement === null || agreement === void 0 ? void 0 : agreement.SellerTradeParty; if (sellerParty) { simplified.seller = { sellerId: sellerParty.ID, sellerName: sellerParty.Name, postalAddress: { address: [ (_m = sellerParty.PostalTradeAddress) === null || _m === void 0 ? void 0 : _m.LineOne, (_o = sellerParty.PostalTradeAddress) === null || _o === void 0 ? void 0 : _o.LineTwo, (_p = sellerParty.PostalTradeAddress) === null || _p === void 0 ? void 0 : _p.LineThree ], postCode: (_q = sellerParty.PostalTradeAddress) === null || _q === void 0 ? void 0 : _q.PostcodeCode, city: (_r = sellerParty.PostalTradeAddress) === null || _r === void 0 ? void 0 : _r.CityName, countryCode: (_s = sellerParty.PostalTradeAddress) === null || _s === void 0 ? void 0 : _s.CountryID, countrySubdivision: (_t = sellerParty.PostalTradeAddress) === null || _t === void 0 ? void 0 : _t.CountrySubDivisionName }, taxRegistrations: sellerParty.SpecifiedTaxRegistration ? [{ type: (_u = sellerParty.SpecifiedTaxRegistration.ID) === null || _u === void 0 ? void 0 : _u.schemeID, value: sellerParty.SpecifiedTaxRegistration.ID }] : [] }; } const buyerParty = agreement === null || agreement === void 0 ? void 0 : agreement.BuyerTradeParty; if (buyerParty) { simplified.buyer = { buyerId: buyerParty.ID, buyerName: buyerParty.Name, postalAddress: { address: [ (_v = buyerParty.PostalTradeAddress) === null || _v === void 0 ? void 0 : _v.LineOne, (_w = buyerParty.PostalTradeAddress) === null || _w === void 0 ? void 0 : _w.LineTwo, (_x = buyerParty.PostalTradeAddress) === null || _x === void 0 ? void 0 : _x.LineThree ], postCode: (_y = buyerParty.PostalTradeAddress) === null || _y === void 0 ? void 0 : _y.PostcodeCode, city: (_z = buyerParty.PostalTradeAddress) === null || _z === void 0 ? void 0 : _z.CityName, countryCode: (_0 = buyerParty.PostalTradeAddress) === null || _0 === void 0 ? void 0 : _0.CountryID, countrySubdivision: (_1 = buyerParty.PostalTradeAddress) === null || _1 === void 0 ? void 0 : _1.CountrySubDivisionName }, taxRegistrations: buyerParty.SpecifiedTaxRegistration ? [{ type: (_2 = buyerParty.SpecifiedTaxRegistration.ID) === null || _2 === void 0 ? void 0 : _2.schemeID, value: buyerParty.SpecifiedTaxRegistration.ID }] : [] }; } const settlement = (_3 = ci.SupplyChainTradeTransaction) === null || _3 === void 0 ? void 0 : _3.ApplicableHeaderTradeSettlement; simplified.transaction = { currency: settlement === null || settlement === void 0 ? void 0 : settlement.InvoiceCurrencyCode, totalGross: parseFloat(((_4 = settlement === null || settlement === void 0 ? void 0 : settlement.SpecifiedTradeSettlementHeaderMonetarySummation) === null || _4 === void 0 ? void 0 : _4.GrandTotalAmount) || '0'), totalNet: parseFloat(((_5 = settlement === null || settlement === void 0 ? void 0 : settlement.SpecifiedTradeSettlementHeaderMonetarySummation) === null || _5 === void 0 ? void 0 : _5.LineTotalAmount) || '0'), totalVat: parseFloat(((_6 = settlement === null || settlement === void 0 ? void 0 : settlement.SpecifiedTradeSettlementHeaderMonetarySummation) === null || _6 === void 0 ? void 0 : _6.TaxTotalAmount) || '0'), totalPrepaid: parseFloat(((_7 = settlement === null || settlement === void 0 ? void 0 : settlement.SpecifiedTradeSettlementHeaderMonetarySummation) === null || _7 === void 0 ? void 0 : _7.TotalPrepaidAmount) || '0'), totalPayable: parseFloat(((_8 = settlement === null || settlement === void 0 ? void 0 : settlement.SpecifiedTradeSettlementHeaderMonetarySummation) === null || _8 === void 0 ? void 0 : _8.DuePayableAmount) || '0'), paymentReference: (settlement === null || settlement === void 0 ? void 0 : settlement.PaymentReference) || '', taxes: ((_9 = settlement === null || settlement === void 0 ? void 0 : settlement.ApplicableTradeTax) === null || _9 === void 0 ? void 0 : _9.map((tax) => ({ taxType: 'VAT', taxPercent: parseFloat(tax.RateApplicablePercent || '0'), taxAmount: parseFloat(tax.CalculatedAmount || '0'), totalNet: parseFloat(tax.BasisAmount || '0') }))) || [], positions: ((_11 = (_10 = ci.SupplyChainTradeTransaction) === null || _10 === void 0 ? void 0 : _10.IncludedSupplyChainTradeLineItem) === null || _11 === void 0 ? void 0 : _11.map((item) => { var _a, _b, _c, _d, _e, _f, _g, _h; return ({ lineId: (_a = item.AssociatedDocumentLineDocument) === null || _a === void 0 ? void 0 : _a.LineID, gtin: (_b = item.SpecifiedTradeProduct) === null || _b === void 0 ? void 0 : _b.GlobalID, name: (_c = item.SpecifiedTradeProduct) === null || _c === void 0 ? void 0 : _c.Name, quantity: parseFloat(((_d = item.SpecifiedLineTradeDelivery) === null || _d === void 0 ? void 0 : _d.BilledQuantity) || '0'), netItemPrice: parseFloat(((_f = (_e = item.SpecifiedLineTradeAgreement) === null || _e === void 0 ? void 0 : _e.NetPriceProductTradePrice) === null || _f === void 0 ? void 0 : _f.ChargeAmount) || '0'), total: parseFloat(((_h = (_g = item.SpecifiedLineTradeSettlement) === null || _g === void 0 ? void 0 : _g.SpecifiedTradeSettlementLineMonetarySummation) === null || _h === void 0 ? void 0 : _h.LineTotalAmount) || '0') }); })) || [] }; if (!Object.values(documentTypes_1.DOCUMENT_TYPES).includes(simplified.documentType)) { throw new Error("XML contains invalid Invoice type code: " + simplified.documentType); } simplified.documentTypeCode = documentTypes_1.DOCUMENT_TYPES[simplified.documentType]; if (!simplified.seller) { throw new Error("XML is missing Seller Entity"); } if (!simplified.buyer) { throw new Error("XML is missing Buyer Entity"); } return simplified; } //# sourceMappingURL=einvoice.js.map