@fin.cx/einvoice
Version:
A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.
505 lines • 35.6 kB
JavaScript
import * as plugins from './plugins.js';
import { business, finance } from './plugins.js';
import { InvoiceFormat, ValidationLevel } from './interfaces/common.js';
// Import error classes
import { EInvoiceError, EInvoiceParsingError, EInvoiceValidationError, EInvoicePDFError, EInvoiceFormatError, ErrorContext } from './errors.js';
// Import factories
import { DecoderFactory } from './formats/factories/decoder.factory.js';
import { EncoderFactory } from './formats/factories/encoder.factory.js';
import { ValidatorFactory } from './formats/factories/validator.factory.js';
// Import PDF utilities
import { PDFEmbedder } from './formats/pdf/pdf.embedder.js';
import { PDFExtractor } from './formats/pdf/pdf.extractor.js';
// Import format detector
import { FormatDetector } from './formats/utils/format.detector.js';
/**
* Main class for working with electronic invoices.
* Supports various invoice formats including Factur-X, ZUGFeRD, UBL, and XRechnung
* Extends the TInvoice interface for seamless integration with existing systems
*/
export class EInvoice {
/**
* Creates an EInvoice instance from XML string
* @param xmlString XML string to parse
* @returns EInvoice instance
*/
static async fromXml(xmlString) {
const invoice = new EInvoice();
await invoice.fromXmlString(xmlString);
return invoice;
}
/**
* Creates an EInvoice instance from file
* @param filePath Path to the file
* @returns EInvoice instance
*/
static async fromFile(filePath) {
const invoice = new EInvoice();
await invoice.fromFile(filePath);
return invoice;
}
/**
* Creates an EInvoice instance from PDF
* @param pdfBuffer PDF buffer
* @returns EInvoice instance
*/
static async fromPdf(pdfBuffer) {
const invoice = new EInvoice();
if (typeof pdfBuffer === 'string') {
// If given a file path
await invoice.fromPdfFile(pdfBuffer);
}
else {
// If given a buffer, extract XML and parse it
const extractResult = await invoice.pdfExtractor.extractXml(pdfBuffer);
if (!extractResult.success || !extractResult.xml) {
throw new EInvoicePDFError('No invoice XML found in PDF', 'extract');
}
await invoice.fromXmlString(extractResult.xml);
}
return invoice;
}
// Backward compatibility properties
get invoiceId() { return this.accountingDocId; }
set invoiceId(value) { this.accountingDocId = value; }
get invoiceType() {
return this.accountingDocType === 'invoice' ? 'invoice' :
this.accountingDocType === 'creditnote' ? 'creditnote' : 'debitnote';
}
set invoiceType(value) {
this.accountingDocType = 'invoice'; // Always set to invoice for TInvoice type
}
// Computed properties for convenience
get issueDate() {
return new Date(this.date);
}
set issueDate(value) {
this.date = value.getTime();
}
get totalNet() {
return this.calculateTotalNet();
}
get totalVat() {
return this.calculateTotalVat();
}
get totalGross() {
return this.totalNet + this.totalVat;
}
get taxBreakdown() {
return this.calculateTaxBreakdown();
}
/**
* Creates a new EInvoice instance
* @param options Configuration options
*/
constructor(options) {
// TInvoice interface properties - accounting document structure
this.type = 'accounting-doc';
this.accountingDocType = 'invoice';
this.accountingDocId = '';
this.accountingDocStatus = 'issued';
// Business envelope properties
this.id = '';
this.date = Date.now();
this.status = 'issued';
this.subject = '';
this.versionInfo = {
type: 'draft',
version: '1.0.0'
};
// Additional envelope properties
this.incidenceId = '';
this.language = 'en';
this.objectActions = [];
this.pdf = null;
this.pdfAttachments = null;
this.accentColor = null;
this.logoUrl = null;
// Accounting document specific properties
this.items = [];
this.dueInDays = 30;
this.reverseCharge = false;
this.currency = 'EUR';
this.notes = [];
this.xmlString = '';
this.detectedFormat = InvoiceFormat.UNKNOWN;
this.validationErrors = [];
this.options = {
validateOnLoad: false,
validationLevel: ValidationLevel.SYNTAX
};
// PDF utilities
this.pdfEmbedder = new PDFEmbedder();
this.pdfExtractor = new PDFExtractor();
// Initialize empty contact objects
this.from = this.createEmptyContact();
this.to = this.createEmptyContact();
// Apply options if provided
if (options) {
this.options = { ...this.options, ...options };
}
}
/**
* Creates an empty TContact object
*/
createEmptyContact() {
return {
type: 'company',
name: '',
description: '',
address: {
streetName: '',
houseNumber: '',
city: '',
postalCode: '',
country: ''
},
registrationDetails: {
vatId: '',
registrationId: '',
registrationName: ''
},
status: 'active',
foundedDate: {
year: new Date().getFullYear(),
month: new Date().getMonth() + 1,
day: new Date().getDate()
}
};
}
/**
* Exports the invoice as XML in the specified format
* @param format The export format
* @returns XML string
*/
async exportXml(format) {
return this.toXmlString(format);
}
/**
* Loads invoice data from XML (alias for fromXmlString)
* @param xmlString The XML string to parse
* @returns The EInvoice instance for chaining
*/
async loadXml(xmlString) {
return this.fromXmlString(xmlString);
}
/**
* Loads invoice data from an XML string
* @param xmlString The XML string to parse
* @returns The EInvoice instance for chaining
*/
async fromXmlString(xmlString) {
try {
this.xmlString = xmlString;
// Detect format
this.detectedFormat = FormatDetector.detectFormat(xmlString);
if (this.detectedFormat === InvoiceFormat.UNKNOWN) {
throw new EInvoiceFormatError('Unknown invoice format', { sourceFormat: 'unknown' });
}
// Get appropriate decoder
const decoder = DecoderFactory.createDecoder(xmlString, !this.options.validateOnLoad);
const invoice = await decoder.decode();
// Map the decoded invoice to our properties
this.mapFromTInvoice(invoice);
// Validate if requested
if (this.options.validateOnLoad) {
await this.validate(this.options.validationLevel);
}
return this;
}
catch (error) {
if (error instanceof EInvoiceError) {
throw error;
}
throw new EInvoiceParsingError(`Failed to parse XML: ${error.message}`, {}, error);
}
}
/**
* Loads invoice data from a file
* @param filePath Path to the file to load
* @returns The EInvoice instance for chaining
*/
async fromFile(filePath) {
try {
const fileBuffer = await plugins.fs.readFile(filePath);
// Check if it's a PDF
if (filePath.toLowerCase().endsWith('.pdf') || fileBuffer.subarray(0, 4).toString() === '%PDF') {
return this.fromPdfFile(filePath);
}
// Otherwise treat as XML
const xmlString = fileBuffer.toString('utf-8');
return this.fromXmlString(xmlString);
}
catch (error) {
throw new EInvoiceError(`Failed to load file: ${error.message}`, 'FILE_LOAD_ERROR', { filePath });
}
}
/**
* Loads invoice data from a PDF file
* @param filePath Path to the PDF file
* @returns The EInvoice instance for chaining
*/
async fromPdfFile(filePath) {
try {
const pdfBuffer = await plugins.fs.readFile(filePath);
const extractResult = await this.pdfExtractor.extractXml(pdfBuffer);
const extractedXml = extractResult.success ? extractResult.xml : null;
if (!extractedXml) {
throw new EInvoicePDFError('No invoice XML found in PDF', 'extract', { filePath });
}
// Store the PDF for later use
this.pdf = {
name: plugins.path.basename(filePath),
id: plugins.crypto.createHash('md5').update(pdfBuffer).digest('hex'),
buffer: new Uint8Array(pdfBuffer),
metadata: {
textExtraction: '',
format: 'PDF/A-3',
embeddedXml: {
filename: 'factur-x.xml',
description: 'Factur-X Invoice'
}
}
};
return this.fromXmlString(extractedXml);
}
catch (error) {
if (error instanceof EInvoiceError) {
throw error;
}
throw new EInvoicePDFError(`Failed to extract invoice from PDF: ${error.message}`, 'extract', {}, error);
}
}
/**
* Maps data from a TInvoice to this EInvoice instance
*/
mapFromTInvoice(invoice) {
// Map all properties from the decoded invoice
Object.assign(this, invoice);
// Ensure backward compatibility
if (!this.id && this.accountingDocId) {
this.id = this.accountingDocId;
}
}
/**
* Maps this EInvoice instance to a TInvoice
*/
mapToTInvoice() {
const invoice = {
type: 'accounting-doc',
accountingDocType: this.accountingDocType,
accountingDocId: this.accountingDocId || this.id,
accountingDocStatus: this.accountingDocStatus,
id: this.id,
date: this.date,
status: this.status,
subject: this.subject,
versionInfo: this.versionInfo,
from: this.from,
to: this.to,
legalContact: this.legalContact,
incidenceId: this.incidenceId,
language: this.language,
objectActions: this.objectActions,
items: this.items,
dueInDays: this.dueInDays,
reverseCharge: this.reverseCharge,
currency: this.currency,
notes: this.notes,
periodOfPerformance: this.periodOfPerformance,
deliveryDate: this.deliveryDate,
buyerReference: this.buyerReference,
electronicAddress: this.electronicAddress,
paymentOptions: this.paymentOptions,
relatedDocuments: this.relatedDocuments,
printResult: this.printResult
};
// Preserve metadata for enhanced spec compliance
if (this.metadata) {
invoice.metadata = this.metadata;
}
return invoice;
}
/**
* Exports the invoice to an XML string in the specified format
* @param format The target format
* @returns The XML string
*/
async toXmlString(format) {
try {
const encoder = EncoderFactory.createEncoder(format);
const invoice = this.mapToTInvoice();
// Import EN16931Validator dynamically to avoid circular dependency
const { EN16931Validator } = await import('./formats/validation/en16931.validator.js');
// Validate mandatory fields before encoding
EN16931Validator.validateMandatoryFields(invoice);
return await encoder.encode(invoice);
}
catch (error) {
throw new EInvoiceFormatError(`Failed to encode to ${format}: ${error.message}`, { targetFormat: format });
}
}
/**
* Validates the invoice
* @param level The validation level to use
* @returns The validation result
*/
async validate(level = ValidationLevel.BUSINESS) {
try {
const format = this.detectedFormat || InvoiceFormat.UNKNOWN;
if (format === InvoiceFormat.UNKNOWN) {
throw new EInvoiceValidationError('Cannot validate: format unknown', []);
}
const validator = ValidatorFactory.createValidator(this.xmlString);
const result = validator.validate(level);
this.validationErrors = result.errors;
return result;
}
catch (error) {
if (error instanceof EInvoiceError) {
throw error;
}
throw new EInvoiceValidationError(`Validation failed: ${error.message}`, [], { validationLevel: level });
}
}
/**
* Embeds the invoice XML into a PDF
* @param pdfBuffer The PDF buffer to embed into
* @param format The format to use for embedding
* @returns The PDF buffer with embedded XML
*/
async embedInPdf(pdfBuffer, format = 'facturx') {
try {
const xmlString = await this.toXmlString(format);
const embedResult = await this.pdfEmbedder.embedXml(pdfBuffer, xmlString, 'invoice.xml', `${format} Invoice`);
if (!embedResult.success) {
throw new EInvoicePDFError('Failed to embed XML in PDF', 'embed', { format });
}
return embedResult.data;
}
catch (error) {
throw new EInvoicePDFError(`Failed to embed XML in PDF: ${error.message}`, 'embed', { format }, error);
}
}
/**
* Saves the invoice to a file
* @param filePath The path to save to
* @param format The format to save in
*/
async saveToFile(filePath, format) {
try {
// Determine format from file extension if not provided
if (!format && filePath.toLowerCase().endsWith('.xml')) {
format = this.detectedFormat === InvoiceFormat.UBL ? 'ubl' :
this.detectedFormat === InvoiceFormat.ZUGFERD ? 'zugferd' :
this.detectedFormat === InvoiceFormat.FACTURX ? 'facturx' :
'xrechnung';
}
if (filePath.toLowerCase().endsWith('.pdf')) {
// Save as PDF with embedded XML
if (!this.pdf) {
throw new EInvoiceError('No PDF available to save', 'NO_PDF_ERROR');
}
const pdfWithXml = await this.embedInPdf(Buffer.from(this.pdf.buffer), format);
await plugins.fs.writeFile(filePath, pdfWithXml);
}
else {
// Save as XML
const xmlString = await this.toXmlString(format || 'xrechnung');
await plugins.fs.writeFile(filePath, xmlString, 'utf-8');
}
}
catch (error) {
if (error instanceof EInvoiceError) {
throw error;
}
throw new EInvoiceError(`Failed to save file: ${error.message}`, 'FILE_SAVE_ERROR', { filePath });
}
}
/**
* Gets the validation errors
* @returns Array of validation errors
*/
getValidationErrors() {
return this.validationErrors;
}
/**
* Checks if the invoice is valid
* @returns True if valid, false otherwise
*/
isValid() {
return this.validationErrors.length === 0;
}
/**
* Gets the detected format
* @returns The detected invoice format
*/
getFormat() {
return this.detectedFormat;
}
/**
* Gets the original XML string
* @returns The XML string
*/
getXml() {
return this.xmlString;
}
/**
* Calculates the total net amount
*/
calculateTotalNet() {
return this.items.reduce((sum, item) => {
return sum + (item.unitQuantity * item.unitNetPrice);
}, 0);
}
/**
* Calculates the total VAT amount
*/
calculateTotalVat() {
return this.items.reduce((sum, item) => {
const net = item.unitQuantity * item.unitNetPrice;
return sum + (net * item.vatPercentage / 100);
}, 0);
}
/**
* Calculates tax breakdown by rate
*/
calculateTaxBreakdown() {
const breakdown = new Map();
this.items.forEach(item => {
const net = item.unitQuantity * item.unitNetPrice;
const tax = net * item.vatPercentage / 100;
const current = breakdown.get(item.vatPercentage) || { net: 0, tax: 0 };
breakdown.set(item.vatPercentage, {
net: current.net + net,
tax: current.tax + tax
});
});
return Array.from(breakdown.entries()).map(([rate, amounts]) => ({
taxPercent: rate,
netAmount: amounts.net,
taxAmount: amounts.tax
}));
}
/**
* Creates a new invoice item
*/
createItem(data) {
return {
position: data.position || this.items.length + 1,
name: data.name || '',
articleNumber: data.articleNumber,
unitType: data.unitType || 'unit',
unitQuantity: data.unitQuantity || 1,
unitNetPrice: data.unitNetPrice || 0,
vatPercentage: data.vatPercentage || 0
};
}
/**
* Adds an item to the invoice
*/
addItem(item) {
this.items.push(this.createItem(item));
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWludm9pY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9laW52b2ljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUV4QyxPQUFPLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUVqRCxPQUFPLEVBQUUsYUFBYSxFQUFFLGVBQWUsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBR3hFLHVCQUF1QjtBQUN2QixPQUFPLEVBQ0wsYUFBYSxFQUNiLG9CQUFvQixFQUNwQix1QkFBdUIsRUFDdkIsZ0JBQWdCLEVBQ2hCLG1CQUFtQixFQUNuQixZQUFZLEVBQ2IsTUFBTSxhQUFhLENBQUM7QUFFckIsbUJBQW1CO0FBQ25CLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUN4RSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sd0NBQXdDLENBQUM7QUFDeEUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMENBQTBDLENBQUM7QUFFNUUsdUJBQXVCO0FBQ3ZCLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUM1RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFOUQseUJBQXlCO0FBQ3pCLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUVwRTs7OztHQUlHO0FBQ0gsTUFBTSxPQUFPLFFBQVE7SUFDbkI7Ozs7T0FJRztJQUNJLE1BQU0sQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFNBQWlCO1FBQzNDLE1BQU0sT0FBTyxHQUFHLElBQUksUUFBUSxFQUFFLENBQUM7UUFDL0IsTUFBTSxPQUFPLENBQUMsYUFBYSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ3ZDLE9BQU8sT0FBTyxDQUFDO0lBQ2pCLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksTUFBTSxDQUFDLEtBQUss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