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.

229 lines 17.1 kB
import { CIIBaseDecoder } from '../cii.decoder.js'; import { ZUGFERD_V1_NAMESPACES } from '../cii.types.js'; import { business, finance } from '../../../plugins.js'; import { EN16931Validator } from '../../validation/en16931.validator.js'; /** * Decoder for ZUGFeRD v1 invoice format */ export class ZUGFeRDV1Decoder extends CIIBaseDecoder { /** * Constructor * @param xml XML string to decode * @param skipValidation Whether to skip EN16931 validation */ constructor(xml, skipValidation = false) { super(xml, skipValidation); // Override namespaces for ZUGFeRD v1 this.namespaces = { rsm: ZUGFERD_V1_NAMESPACES.RSM, ram: ZUGFERD_V1_NAMESPACES.RAM, udt: ZUGFERD_V1_NAMESPACES.UDT }; } /** * Decodes a ZUGFeRD v1 credit note * @returns Promise resolving to a TCreditNote object */ async decodeCreditNote() { // Get common invoice data const commonData = await this.extractCommonData(); // Create a credit note with the common data return { ...commonData, accountingDocType: 'creditnote' }; } /** * Decodes a ZUGFeRD v1 debit note (invoice) * @returns Promise resolving to a TDebitNote object */ async decodeDebitNote() { // Get common invoice data const commonData = await this.extractCommonData(); // Create a debit note with the common data return { ...commonData, accountingDocType: 'debitnote' }; } /** * Extracts common invoice data from ZUGFeRD v1 XML * @returns Common invoice data */ async extractCommonData() { // Extract invoice ID const invoiceId = this.getText('//ram:ID'); // Extract issue date const issueDateStr = this.getText('//ram:IssueDateTime/udt:DateTimeString'); const issueDateFormat = this.getText('//ram:IssueDateTime/udt:DateTimeString/@format'); const issueDate = this.parseCIIDate(issueDateStr, issueDateFormat); // Extract seller information const seller = this.extractParty('//ram:SellerTradeParty'); // Extract buyer information const buyer = this.extractParty('//ram:BuyerTradeParty'); // Extract items const items = this.extractItems(); // Extract due date const dueDateStr = this.getText('//ram:SpecifiedTradePaymentTerms/ram:DueDateDateTime/udt:DateTimeString'); const dueDate = dueDateStr ? new Date(dueDateStr).getTime() : Date.now(); const dueInDays = Math.round((dueDate - issueDate) / (1000 * 60 * 60 * 24)); // Extract currency const currencyCode = this.getText('//ram:InvoiceCurrencyCode') || 'EUR'; // Extract total amount (not used in this implementation but could be useful) // const totalAmount = this.getNumber('//ram:GrandTotalAmount'); // Extract notes const allNotes = this.extractNotes(); // Extract subject and notes separately // If we have notes, the first one might be the subject let subject = `Invoice ${invoiceId}`; let notes = [...allNotes]; // If the first note doesn't look like a payment term or other standard note, // treat it as the subject if (allNotes.length > 0 && !allNotes[0].toLowerCase().includes('due in') && !allNotes[0].toLowerCase().includes('payment')) { subject = allNotes[0]; notes = allNotes.slice(1); // Remove subject from notes } // Check for reverse charge const reverseCharge = this.exists('//ram:SpecifiedTradeAllowanceCharge/ram:ReasonCode[text()="62"]'); // Create the common invoice data const invoiceData = { type: 'accounting-doc', accountingDocType: 'invoice', id: invoiceId, accountingDocId: invoiceId, date: issueDate, accountingDocStatus: 'issued', versionInfo: { type: 'final', version: '1.0.0' }, language: 'en', incidenceId: invoiceId, from: seller, to: buyer, subject: subject, items: items, dueInDays: dueInDays, reverseCharge: reverseCharge, currency: currencyCode, notes: notes, deliveryDate: issueDate, objectActions: [] }; return invoiceData; } /** * Extracts party information from ZUGFeRD v1 XML * @param partyXPath XPath to the party node * @returns Party information as TContact */ extractParty(partyXPath) { // Extract name const name = this.getText(`${partyXPath}/ram:Name`); // Extract address const streetName = this.getText(`${partyXPath}/ram:PostalTradeAddress/ram:LineOne`); const city = this.getText(`${partyXPath}/ram:PostalTradeAddress/ram:CityName`); const postalCode = this.getText(`${partyXPath}/ram:PostalTradeAddress/ram:PostcodeCode`); const country = this.getText(`${partyXPath}/ram:PostalTradeAddress/ram:CountryID`); // Try to extract house number from street if possible let houseNumber = ''; const streetParts = streetName.match(/^(.*?)\s+(\d+.*)$/); if (streetParts) { // If we can split into street name and house number houseNumber = streetParts[2]; } // Create address object const address = { streetName: streetName, houseNumber: houseNumber, city: city, postalCode: postalCode, countryCode: country }; // Extract VAT ID const vatId = this.getText(`${partyXPath}/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="VA"]`) || ''; // Extract registration ID const registrationId = this.getText(`${partyXPath}/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="FC"]`) || ''; // Create contact object return { type: 'company', name: name, description: '', address: address, status: 'active', foundedDate: this.createDefaultDate(), registrationDetails: { vatId: vatId, registrationId: registrationId, registrationName: '' } }; } /** * Extracts invoice items from ZUGFeRD v1 XML * @returns Array of invoice items */ extractItems() { const items = []; // Get all item nodes const itemNodes = this.select('//ram:IncludedSupplyChainTradeLineItem', this.doc); // Process each item if (Array.isArray(itemNodes)) { for (let i = 0; i < itemNodes.length; i++) { const itemNode = itemNodes[i]; // Extract item data const name = this.getText('ram:SpecifiedTradeProduct/ram:Name', itemNode); const articleNumber = this.getText('ram:SpecifiedTradeProduct/ram:SellerAssignedID', itemNode); const unitQuantity = this.getNumber('ram:SpecifiedLineTradeDelivery/ram:BilledQuantity', itemNode); const unitType = this.getText('ram:SpecifiedLineTradeDelivery/ram:BilledQuantity/@unitCode', itemNode) || 'EA'; const unitNetPrice = this.getNumber('ram:SpecifiedLineTradeAgreement/ram:NetPriceProductTradePrice/ram:ChargeAmount', itemNode); const vatPercentage = this.getNumber('ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent', itemNode); // Create item object items.push({ position: i + 1, name: name, articleNumber: articleNumber, unitType: unitType, unitQuantity: unitQuantity, unitNetPrice: unitNetPrice, vatPercentage: vatPercentage }); } } return items; } /** * Extracts notes from ZUGFeRD v1 XML * @returns Array of notes */ extractNotes() { const notes = []; // Get all note nodes const noteNodes = this.select('//ram:IncludedNote', this.doc); // Process each note if (Array.isArray(noteNodes)) { for (let i = 0; i < noteNodes.length; i++) { const noteNode = noteNodes[i]; const noteText = this.getText('ram:Content', noteNode); if (noteText) { notes.push(noteText); } } } return notes; } /** * Creates a default date for empty date fields * @returns Default date object compatible with TContact */ createDefaultDate() { // Create a date object that will be compatible with TContact return { year: 2000, month: 1, day: 1 }; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoienVnZmVyZC52MS5kZWNvZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHMvZm9ybWF0cy9jaWkvenVnZmVyZC96dWdmZXJkLnYxLmRlY29kZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRW5ELE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3hELE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sdUNBQXVDLENBQUM7QUFFekU7O0dBRUc7QUFDSCxNQUFNLE9BQU8sZ0JBQWlCLFNBQVEsY0FBYztJQUNsRDs7OztPQUlHO0lBQ0gsWUFBWSxHQUFXLEVBQUUsaUJBQTBCLEtBQUs7UUFDdEQsS0FBSyxDQUFDLEdBQUcsRUFBRSxjQUFjLENBQUMsQ0FBQztRQUMzQixxQ0FBcUM7UUFDckMsSUFBSSxDQUFDLFVBQVUsR0FBRztZQUNoQixHQUFHLEVBQUUscUJBQXFCLENBQUMsR0FBRztZQUM5QixHQUFHLEVBQUUscUJBQXFCLENBQUMsR0FBRztZQUM5QixHQUFHLEVBQUUscUJBQXFCLENBQUMsR0FBRztTQUMvQixDQUFDO0lBQ0osQ0FBQztJQUVEOzs7T0FHRztJQUNPLEtBQUssQ0FBQyxnQkFBZ0I7UUFDOUIsMEJBQTBCO1FBQzFCLE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFFbEQsNENBQTRDO1FBQzVDLE9BQU87WUFDTCxHQUFHLFVBQVU7WUFDYixpQkFBaUIsRUFBRSxZQUFxQjtTQUNmLENBQUM7SUFDOUIsQ0FBQztJQUVEOzs7T0FHRztJQUNPLEtBQUssQ0FBQyxlQUFlO1FBQzdCLDBCQUEwQjtRQUMxQixNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUksQ0FBQyxpQkFBaUIs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