@fin.cx/einvoice
Version:
A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.
332 lines • 25.1 kB
JavaScript
import * as plugins from '../plugins.js';
import * as xmldom from 'xmldom';
import { BaseDecoder } from './base.decoder.js';
/**
* A decoder specifically for XInvoice/XRechnung format.
* XRechnung is the German implementation of the European standard EN16931
* for electronic invoices to the German public sector.
*/
export class XInvoiceDecoder extends BaseDecoder {
constructor(xmlString) {
super(xmlString);
this.xmlDoc = null;
this.namespaces = {
cbc: 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2',
cac: 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2',
ubl: 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'
};
// Parse XML to DOM
try {
const parser = new xmldom.DOMParser();
this.xmlDoc = parser.parseFromString(this.xmlString, 'text/xml');
// Try to detect if this is actually UBL (which XRechnung is based on)
if (this.xmlString.includes('oasis:names:specification:ubl')) {
// Set up appropriate namespaces
this.setupNamespaces();
}
}
catch (error) {
console.error('Error parsing XInvoice XML:', error);
}
}
/**
* Set up namespaces from the document
*/
setupNamespaces() {
if (!this.xmlDoc)
return;
// Try to extract namespaces from the document
const root = this.xmlDoc.documentElement;
if (root) {
// Look for common UBL namespaces
for (let i = 0; i < root.attributes.length; i++) {
const attr = root.attributes[i];
if (attr.name.startsWith('xmlns:')) {
const prefix = attr.name.substring(6);
this.namespaces[prefix] = attr.value;
}
}
}
}
/**
* Extract element text by tag name with namespace awareness
*/
getElementText(tagName) {
if (!this.xmlDoc) {
return '';
}
try {
// Handle namespace prefixes
if (tagName.includes(':')) {
const [nsPrefix, localName] = tagName.split(':');
// Find elements with this tag name
const elements = this.xmlDoc.getElementsByTagNameNS(this.namespaces[nsPrefix] || '', localName);
if (elements.length > 0) {
return elements[0].textContent || '';
}
}
// Fallback to direct tag name lookup
const elements = this.xmlDoc.getElementsByTagName(tagName);
if (elements.length > 0) {
return elements[0].textContent || '';
}
return '';
}
catch (error) {
console.error(`Error extracting XInvoice element ${tagName}:`, error);
return '';
}
}
/**
* Converts XInvoice/XRechnung XML to a structured letter object
*/
async getLetterData() {
try {
// Extract invoice ID - typically in cbc:ID or Invoice/cbc:ID
let invoiceId = this.getElementText('cbc:ID');
if (!invoiceId) {
invoiceId = this.getElementText('Invoice/cbc:ID') || 'Unknown';
}
// Extract invoice issue date
const issueDateStr = this.getElementText('cbc:IssueDate') || '';
const issueDate = issueDateStr ? new Date(issueDateStr).getTime() : Date.now();
// Extract seller information
const sellerName = this.getElementText('cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name') ||
this.getElementText('cac:SellerSupplierParty/cac:Party/cac:PartyName/cbc:Name') ||
'Unknown Seller';
// Extract seller address
const sellerStreet = this.getElementText('cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:StreetName') || 'Unknown';
const sellerCity = this.getElementText('cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:CityName') || 'Unknown';
const sellerPostcode = this.getElementText('cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:PostalZone') || 'Unknown';
const sellerCountry = this.getElementText('cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cac:Country/cbc:IdentificationCode') || 'Unknown';
// Extract buyer information
const buyerName = this.getElementText('cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name') ||
this.getElementText('cac:BuyerCustomerParty/cac:Party/cac:PartyName/cbc:Name') ||
'Unknown Buyer';
// Create seller contact
const seller = {
name: sellerName,
type: 'company',
description: sellerName,
address: {
streetName: sellerStreet,
houseNumber: '0',
city: sellerCity,
country: sellerCountry,
postalCode: sellerPostcode,
},
registrationDetails: {
vatId: this.getElementText('cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID') || 'Unknown',
registrationId: this.getElementText('cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID') || 'Unknown',
registrationName: sellerName
},
foundedDate: {
year: 2000,
month: 1,
day: 1
},
closedDate: {
year: 9999,
month: 12,
day: 31
},
status: 'active'
};
// Create buyer contact
const buyer = {
name: buyerName,
type: 'company',
description: buyerName,
address: {
streetName: 'Unknown',
houseNumber: '0',
city: 'Unknown',
country: 'Unknown',
postalCode: 'Unknown',
},
registrationDetails: {
vatId: this.getElementText('cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID') || 'Unknown',
registrationId: this.getElementText('cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID') || 'Unknown',
registrationName: buyerName
},
foundedDate: {
year: 2000,
month: 1,
day: 1
},
closedDate: {
year: 9999,
month: 12,
day: 31
},
status: 'active'
};
// Extract invoice type
let invoiceType = 'debitnote';
const typeCode = this.getElementText('cbc:InvoiceTypeCode');
if (typeCode === '380') {
invoiceType = 'debitnote'; // Standard invoice
}
else if (typeCode === '381') {
invoiceType = 'creditnote'; // Credit note
}
// Create invoice data
const invoiceData = {
id: invoiceId,
status: null,
type: invoiceType,
billedBy: seller,
billedTo: buyer,
deliveryDate: issueDate,
dueInDays: 30,
periodOfPerformance: null,
printResult: null,
currency: (this.getElementText('cbc:DocumentCurrencyCode') || 'EUR'),
notes: [],
items: this.extractInvoiceItems(),
reverseCharge: false,
};
// Return a letter
return {
versionInfo: {
type: 'draft',
version: '1.0.0',
},
type: 'invoice',
date: issueDate,
subject: `XInvoice: ${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,
};
}
catch (error) {
console.error('Error converting XInvoice XML to letter data:', error);
return this.createDefaultLetter();
}
}
/**
* Extracts invoice items from XInvoice document
*/
extractInvoiceItems() {
if (!this.xmlDoc) {
return [
{
name: 'Unknown Item',
unitQuantity: 1,
unitNetPrice: 0,
vatPercentage: 0,
position: 0,
unitType: 'units',
}
];
}
try {
const items = [];
// Get all invoice line elements
const lines = this.xmlDoc.getElementsByTagName('cac:InvoiceLine');
if (!lines || lines.length === 0) {
// Fallback to a default item
return [
{
name: 'Item from XInvoice XML',
unitQuantity: 1,
unitNetPrice: 0,
vatPercentage: 0,
position: 0,
unitType: 'units',
}
];
}
// Process each line
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Extract item details
let name = '';
let quantity = 1;
let price = 0;
let vatRate = 0;
// Find description element
const descElements = line.getElementsByTagName('cbc:Description');
if (descElements.length > 0) {
name = descElements[0].textContent || '';
}
// Fallback to item name if description is empty
if (!name) {
const itemNameElements = line.getElementsByTagName('cbc:Name');
if (itemNameElements.length > 0) {
name = itemNameElements[0].textContent || '';
}
}
// Find quantity
const quantityElements = line.getElementsByTagName('cbc:InvoicedQuantity');
if (quantityElements.length > 0) {
const quantityText = quantityElements[0].textContent || '1';
quantity = parseFloat(quantityText) || 1;
}
// Find price
const priceElements = line.getElementsByTagName('cbc:PriceAmount');
if (priceElements.length > 0) {
const priceText = priceElements[0].textContent || '0';
price = parseFloat(priceText) || 0;
}
// Find VAT rate - this is a bit more complex in UBL/XRechnung
const taxCategoryElements = line.getElementsByTagName('cac:ClassifiedTaxCategory');
if (taxCategoryElements.length > 0) {
const rateElements = taxCategoryElements[0].getElementsByTagName('cbc:Percent');
if (rateElements.length > 0) {
const rateText = rateElements[0].textContent || '0';
vatRate = parseFloat(rateText) || 0;
}
}
// Add the item to the list
items.push({
name: name || `Item ${i + 1}`,
unitQuantity: quantity,
unitNetPrice: price,
vatPercentage: vatRate,
position: i,
unitType: 'units',
});
}
return items.length > 0 ? items : [
{
name: 'Item from XInvoice XML',
unitQuantity: 1,
unitNetPrice: 0,
vatPercentage: 0,
position: 0,
unitType: 'units',
}
];
}
catch (error) {
console.error('Error extracting XInvoice items:', error);
return [
{
name: 'Error extracting items',
unitQuantity: 1,
unitNetPrice: 0,
vatPercentage: 0,
position: 0,
unitType: 'units',
}
];
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoieGludm9pY2UuZGVjb2Rlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2Zvcm1hdHMveGludm9pY2UuZGVjb2Rlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQUN6QyxPQUFPLEtBQUssTUFBTSxNQUFNLFFBQVEsQ0FBQztBQUNqQyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFaEQ7Ozs7R0FJRztBQUNILE1BQU0sT0FBTyxlQUFnQixTQUFRLFdBQVc7SUFROUMsWUFBWSxTQUFpQjtRQUMzQixLQUFLLENBQUMsU0FBUyxDQUFDLENBQUM7UUFSWCxXQUFNLEdBQW9CLElBQUksQ0FBQztRQUMvQixlQUFVLEdBQThCO1lBQzlDLEdBQUcsRUFBRSxzRUFBc0U7WUFDM0UsR0FBRyxFQUFFLDBFQUEwRTtZQUMvRSxHQUFHLEVBQUUsd0RBQXdEO1NBQzlELENBQUM7UUFLQSxtQkFBbUI7UUFDbkIsSUFBSSxDQUFDO1lBQ0gsTUFBTSxNQUFNLEdBQUcsSUFBSSxNQUFNLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDdEMsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsVUFBVSxDQUFDLENBQUM7WUFFakUsc0VBQXNFO1lBQ3RFLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsK0JBQStCLENBQUMsRUFBRSxDQUFDO2dCQUM3RCxnQ0FBZ0M7Z0JBQ2hDLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztZQUN6QixDQUFDO1FBQ0gsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixPQUFPLENBQUMsS0FBSyxDQUFDLDZCQUE2QixFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ3RELENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxlQUFlO1FBQ3JCLElBQUksQ0FBQyxJ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