@fin.cx/einvoice
Version:
A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.
320 lines • 20.1 kB
JavaScript
import * as plugins from './plugins.js';
import * as xmldom from 'xmldom';
/**
* A class to convert a given XML string (ZUGFeRD/Factur-X, UBL or fatturaPA)
* into a structured ILetter with invoice data.
*
* Handles different invoice XML formats:
* - ZUGFeRD/Factur-X (CII)
* - UBL
* - FatturaPA
*/
export class FacturXDecoder {
constructor(xmlString) {
this.xmlDoc = null;
if (!xmlString) {
throw new Error('No XML string provided to decoder');
}
this.xmlString = xmlString;
// Simple format detection based on string contents
this.xmlFormat = this.detectFormat();
// Parse XML to DOM
try {
const parser = new xmldom.DOMParser();
this.xmlDoc = parser.parseFromString(this.xmlString, 'text/xml');
}
catch (error) {
console.error('Error parsing XML:', error);
}
}
/**
* Detects the XML invoice format using simple string checks
*/
detectFormat() {
// ZUGFeRD/Factur-X (CII format)
if (this.xmlString.includes('CrossIndustryInvoice') ||
this.xmlString.includes('un/cefact') ||
this.xmlString.includes('rsm:')) {
return 'CII';
}
// UBL format
if (this.xmlString.includes('Invoice') ||
this.xmlString.includes('oasis:names:specification:ubl')) {
return 'UBL';
}
// FatturaPA format
if (this.xmlString.includes('FatturaElettronica') ||
this.xmlString.includes('fatturapa.gov.it')) {
return 'FatturaPA';
}
// Default to generic
return 'unknown';
}
/**
* Extracts text from the first element matching the XPath-like selector
*/
getElementText(tagName) {
if (!this.xmlDoc) {
return '';
}
try {
// Basic handling for namespaced tags
let namespace = '';
let localName = tagName;
if (tagName.includes(':')) {
const parts = tagName.split(':');
namespace = parts[0];
localName = parts[1];
}
// Find all elements with this name
const elements = this.xmlDoc.getElementsByTagName(tagName);
if (elements.length > 0) {
return elements[0].textContent || '';
}
// Try with just the local name if we didn't find it with the namespace
if (namespace) {
const elements = this.xmlDoc.getElementsByTagName(localName);
if (elements.length > 0) {
return elements[0].textContent || '';
}
}
return '';
}
catch (error) {
console.error(`Error extracting element ${tagName}:`, error);
return '';
}
}
/**
* Converts XML to a structured letter object
*/
async getLetterData() {
try {
if (this.xmlFormat === 'CII') {
return this.parseCII();
}
else if (this.xmlFormat === 'UBL') {
// For now, use the default implementation
return this.parseGeneric();
}
else if (this.xmlFormat === 'FatturaPA') {
// For now, use the default implementation
return this.parseGeneric();
}
else {
return this.parseGeneric();
}
}
catch (error) {
console.error('Error converting XML to letter data:', error);
// If all else fails, return a minimal letter object
return this.createDefaultLetter();
}
}
/**
* Parse CII (ZUGFeRD/Factur-X) formatted XML
*/
parseCII() {
// Extract invoice ID
let invoiceId = this.getElementText('ram:ID');
if (!invoiceId) {
// Try alternative locations
invoiceId = this.getElementText('rsm:ExchangedDocument ram:ID') || 'Unknown';
}
// Extract seller name
let sellerName = this.getElementText('ram:Name');
if (!sellerName) {
sellerName = this.getElementText('ram:SellerTradeParty ram:Name') || 'Unknown Seller';
}
// Extract buyer name
let buyerName = '';
// Try to find BuyerTradeParty Name specifically
if (this.xmlDoc) {
const buyerParties = this.xmlDoc.getElementsByTagName('ram:BuyerTradeParty');
if (buyerParties.length > 0) {
const nameElements = buyerParties[0].getElementsByTagName('ram:Name');
if (nameElements.length > 0) {
buyerName = nameElements[0].textContent || '';
}
}
}
if (!buyerName) {
buyerName = 'Unknown Buyer';
}
// Create seller
const seller = {
name: sellerName,
type: 'company',
description: sellerName,
address: {
streetName: this.getElementText('ram:LineOne') || 'Unknown',
houseNumber: '0', // Required by IAddress interface
city: this.getElementText('ram:CityName') || 'Unknown',
country: this.getElementText('ram:CountryID') || 'Unknown',
postalCode: this.getElementText('ram:PostcodeCode') || 'Unknown',
},
};
// Create buyer
const buyer = {
name: buyerName,
type: 'company',
description: buyerName,
address: {
streetName: 'Unknown',
houseNumber: '0',
city: 'Unknown',
country: 'Unknown',
postalCode: 'Unknown',
},
};
// Extract invoice type
let invoiceType = 'debitnote';
const typeCode = this.getElementText('ram:TypeCode');
if (typeCode === '381') {
invoiceType = 'creditnote';
}
// Create invoice data
const invoiceData = {
id: invoiceId,
status: null,
type: invoiceType,
billedBy: seller,
billedTo: buyer,
deliveryDate: Date.now(),
dueInDays: 30,
periodOfPerformance: null,
printResult: null,
currency: (this.getElementText('ram:InvoiceCurrencyCode') || 'EUR'),
notes: [],
items: [
{
name: 'Item from XML',
unitQuantity: 1,
unitNetPrice: 0,
vatPercentage: 0,
position: 0,
unitType: 'units',
}
],
reverseCharge: false,
};
// Return a letter
return {
versionInfo: {
type: 'draft',
version: '1.0.0',
},
type: 'invoice',
date: Date.now(),
subject: `Invoice: ${invoiceId}`,
from: seller,
to: buyer,
content: {
invoiceData: invoiceData,
textData: null,
timesheetData: null,
contractData: null,
},
needsCoverSheet: false,
objectActions: [],
pdf: null,
incidenceId: null,
language: null,
legalContact: null,
logoUrl: null,
pdfAttachments: null,
accentColor: null,
};
}
/**
* Parse generic XML using default approach
*/
parseGeneric() {
// Create a default letter with some extraction attempts
return this.createDefaultLetter();
}
/**
* Creates a default letter object with minimal data
*/
createDefaultLetter() {
// Create a default seller
const seller = {
name: 'Unknown Seller',
type: 'company',
description: 'Unknown Seller', // Required by IContact interface
address: {
streetName: 'Unknown',
houseNumber: '0', // Required by IAddress interface
city: 'Unknown',
country: 'Unknown',
postalCode: 'Unknown',
},
};
// Create a default buyer
const buyer = {
name: 'Unknown Buyer',
type: 'company',
description: 'Unknown Buyer', // Required by IContact interface
address: {
streetName: 'Unknown',
houseNumber: '0', // Required by IAddress interface
city: 'Unknown',
country: 'Unknown',
postalCode: 'Unknown',
},
};
// Create default invoice data
const invoiceData = {
id: 'Unknown',
status: null,
type: 'debitnote',
billedBy: seller,
billedTo: buyer,
deliveryDate: Date.now(),
dueInDays: 30,
periodOfPerformance: null,
printResult: null,
currency: 'EUR',
notes: [],
items: [
{
name: 'Unknown Item',
unitQuantity: 1,
unitNetPrice: 0,
vatPercentage: 0,
position: 0,
unitType: 'units',
}
],
reverseCharge: false,
};
// Return a default letter
return {
versionInfo: {
type: 'draft',
version: '1.0.0',
},
type: 'invoice',
date: Date.now(),
subject: `Extracted Invoice (${this.xmlFormat} format)`,
from: seller,
to: buyer,
content: {
invoiceData: invoiceData,
textData: null,
timesheetData: null,
contractData: null,
},
needsCoverSheet: false,
objectActions: [],
pdf: null,
incidenceId: null,
language: null,
legalContact: null,
logoUrl: null,
pdfAttachments: null,
accentColor: null,
};
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5kZWNvZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5kZWNvZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sS0FBSyxNQUFNLE1BQU0sUUFBUSxDQUFDO0FBRWpDOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxPQUFPLGNBQWM7SUFLekIsWUFBWSxTQUFpQjtRQUZyQixXQUFNLEdBQW9CLElBQUksQ0FBQztRQUdyQyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDZixNQUFNLElBQUksS0FBSyxDQUFDLG1DQUFtQyxDQUFDLENBQUM7UUFDdkQsQ0FBQztRQUVELElBQUksQ0FBQyxTQUFTLEdBQUcsU0FBUyxDQUFDO1FBRTNCLG1EQUFtRDtRQUNuRCxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUVyQyxtQkFBbUI7UUFDbkIsSUFBSSxDQUFDO1lBQ0gsTUFBTSxNQUFNLEdBQUcsSUFBSSxNQUFNLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDdEMsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDbkUsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixPQUFPLENBQUMsS0FBSyxDQUFDLG9CQUFvQixFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQzdDLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxZQUFZO1FBQ2xCLGdDQUFnQztRQUNoQyxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLHNCQUFzQixDQUFDO1lBQy9DLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQztZQUNwQyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENB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