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
JavaScript
;
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