@fin.cx/einvoice
Version:
A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.
352 lines • 24.6 kB
JavaScript
import * as plugins from './plugins.js';
import { business, finance } from './plugins.js';
import { InvoiceFormat, ValidationLevel } from './interfaces/common.js';
// PDF-related imports are handled by the PDF utilities
// 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
* Implements TInvoice interface for seamless integration with existing systems
*/
export class XInvoice {
/**
* Creates a new XInvoice instance
* @param options Configuration options
*/
constructor(options) {
// TInvoice interface properties
this.id = '';
this.invoiceId = '';
this.invoiceType = 'debitnote';
this.versionInfo = {
type: 'draft',
version: '1.0.0'
};
this.type = 'invoice';
this.date = Date.now();
this.status = 'invoice';
this.subject = '';
this.incidenceId = '';
this.language = 'en';
this.objectActions = [];
this.pdf = null;
this.pdfAttachments = null;
this.accentColor = null;
this.logoUrl = null;
// Additional properties for invoice data
this.items = [];
this.dueInDays = 30;
this.reverseCharge = false;
this.currency = 'EUR';
this.notes = [];
// XInvoice specific properties
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 {
name: '',
type: 'company',
description: '',
address: {
streetName: '',
houseNumber: '0',
city: '',
country: '',
postalCode: ''
},
status: 'active',
foundedDate: {
year: 2000,
month: 1,
day: 1
},
registrationDetails: {
vatId: '',
registrationId: '',
registrationName: ''
}
};
}
/**
* Creates a new XInvoice instance from XML
* @param xmlString XML content
* @param options Configuration options
* @returns XInvoice instance
*/
static async fromXml(xmlString, options) {
const xinvoice = new XInvoice(options);
// Load XML data
await xinvoice.loadXml(xmlString);
return xinvoice;
}
/**
* Creates a new XInvoice instance from PDF
* @param pdfBuffer PDF buffer
* @param options Configuration options
* @returns XInvoice instance
*/
static async fromPdf(pdfBuffer, options) {
const xinvoice = new XInvoice(options);
// Load PDF data
await xinvoice.loadPdf(pdfBuffer);
return xinvoice;
}
/**
* Loads XML data into the XInvoice instance
* @param xmlString XML content
* @param validate Whether to validate the XML
* @returns This instance for chaining
*/
async loadXml(xmlString, validate = false) {
this.xmlString = xmlString;
// Detect format
this.detectedFormat = FormatDetector.detectFormat(xmlString);
try {
// Initialize the decoder with the XML string using the factory
const decoder = DecoderFactory.createDecoder(xmlString);
// Decode the XML into a TInvoice object
const invoice = await decoder.decode();
// Copy data from the decoded invoice
this.copyInvoiceData(invoice);
// Validate the XML if requested or if validateOnLoad is true
if (validate || this.options.validateOnLoad) {
await this.validate(this.options.validationLevel);
}
}
catch (error) {
console.error('Error loading XML:', error);
throw error;
}
return this;
}
/**
* Loads PDF data into the XInvoice instance
* @param pdfBuffer PDF buffer
* @param validate Whether to validate the extracted XML
* @returns This instance for chaining
*/
async loadPdf(pdfBuffer, validate = false) {
try {
// Extract XML from PDF using the consolidated extractor
const extractResult = await this.pdfExtractor.extractXml(pdfBuffer);
// Store the PDF buffer
this.pdf = {
name: 'invoice.pdf',
id: `invoice-${Date.now()}`,
metadata: {
textExtraction: '',
format: extractResult.success ? extractResult.format?.toString() : undefined
},
buffer: pdfBuffer instanceof Buffer ? new Uint8Array(pdfBuffer) : pdfBuffer
};
// Handle extraction result
if (!extractResult.success || !extractResult.xml) {
const errorMessage = extractResult.error ? extractResult.error.message : 'Unknown error extracting XML from PDF';
console.warn('XML extraction failed:', errorMessage);
throw new Error(`No XML found in PDF: ${errorMessage}`);
}
// Load the extracted XML
await this.loadXml(extractResult.xml, validate);
// Store the detected format
this.detectedFormat = extractResult.format || InvoiceFormat.UNKNOWN;
return this;
}
catch (error) {
console.error('Error loading PDF:', error);
throw error;
}
}
/**
* Copies data from a TInvoice object
* @param invoice Source invoice data
*/
copyInvoiceData(invoice) {
// Copy basic properties
this.id = invoice.id;
this.invoiceId = invoice.invoiceId || invoice.id;
this.invoiceType = invoice.invoiceType;
this.versionInfo = { ...invoice.versionInfo };
this.type = invoice.type;
this.date = invoice.date;
this.status = invoice.status;
this.subject = invoice.subject;
this.from = { ...invoice.from };
this.to = { ...invoice.to };
this.incidenceId = invoice.incidenceId;
this.language = invoice.language;
this.legalContact = invoice.legalContact ? { ...invoice.legalContact } : undefined;
this.objectActions = [...invoice.objectActions];
this.pdf = invoice.pdf;
this.pdfAttachments = invoice.pdfAttachments;
// Copy invoice-specific properties
if (invoice.items)
this.items = [...invoice.items];
if (invoice.dueInDays)
this.dueInDays = invoice.dueInDays;
if (invoice.reverseCharge !== undefined)
this.reverseCharge = invoice.reverseCharge;
if (invoice.currency)
this.currency = invoice.currency;
if (invoice.notes)
this.notes = [...invoice.notes];
if (invoice.periodOfPerformance)
this.periodOfPerformance = { ...invoice.periodOfPerformance };
if (invoice.deliveryDate)
this.deliveryDate = invoice.deliveryDate;
if (invoice.buyerReference)
this.buyerReference = invoice.buyerReference;
if (invoice.electronicAddress)
this.electronicAddress = { ...invoice.electronicAddress };
if (invoice.paymentOptions)
this.paymentOptions = { ...invoice.paymentOptions };
}
/**
* Validates the XML against the appropriate format rules
* @param level Validation level (syntax, semantic, business)
* @returns Validation result
*/
async validate(level = ValidationLevel.SYNTAX) {
if (!this.xmlString) {
throw new Error('No XML to validate');
}
try {
// Initialize the validator with the XML string
const validator = ValidatorFactory.createValidator(this.xmlString);
// Run validation
const result = validator.validate(level);
// Store validation errors
this.validationErrors = result.errors;
return result;
}
catch (error) {
console.error('Error validating XML:', error);
const errorResult = {
valid: false,
errors: [{
code: 'VAL-ERROR',
message: `Validation error: ${error instanceof Error ? error.message : String(error)}`
}],
level
};
this.validationErrors = errorResult.errors;
return errorResult;
}
}
/**
* Checks if the invoice is valid
* @returns True if no validation errors were found
*/
isValid() {
return this.validationErrors.length === 0;
}
/**
* Gets validation errors from the last validation
* @returns Array of validation errors
*/
getValidationErrors() {
return this.validationErrors;
}
/**
* Exports the invoice as XML in the specified format
* @param format Target format (e.g., 'facturx', 'xrechnung')
* @returns XML string in the specified format
*/
async exportXml(format = 'facturx') {
// Create encoder for the specified format
const encoder = EncoderFactory.createEncoder(format);
// Generate XML
return await encoder.encode(this);
}
/**
* Exports the invoice as a PDF with embedded XML
* @param format Target format (e.g., 'facturx', 'zugferd', 'xrechnung', 'ubl')
* @returns PDF object with embedded XML
*/
async exportPdf(format = 'facturx') {
if (!this.pdf) {
throw new Error('No PDF data available. Use loadPdf() first or set the pdf property.');
}
// Generate XML in the specified format
const xmlContent = await this.exportXml(format);
// Determine filename based on format
let filename = 'invoice.xml';
let description = 'XML Invoice';
switch (format.toLowerCase()) {
case 'facturx':
filename = 'factur-x.xml';
description = 'Factur-X XML Invoice';
break;
case 'zugferd':
filename = 'zugferd-invoice.xml';
description = 'ZUGFeRD XML Invoice';
break;
case 'xrechnung':
filename = 'xrechnung.xml';
description = 'XRechnung XML Invoice';
break;
case 'ubl':
filename = 'ubl-invoice.xml';
description = 'UBL XML Invoice';
break;
}
// Embed XML into PDF
const result = await this.pdfEmbedder.createPdfWithXml(this.pdf.buffer, xmlContent, filename, description, this.pdf.name, this.pdf.id);
// Handle potential errors
if (!result.success || !result.pdf) {
const errorMessage = result.error ? result.error.message : 'Unknown error embedding XML into PDF';
console.error('Error exporting PDF:', errorMessage);
throw new Error(`Failed to export PDF: ${errorMessage}`);
}
return result.pdf;
}
/**
* Gets the raw XML content
* @returns XML string
*/
getXml() {
return this.xmlString;
}
/**
* Gets the invoice format as an enum value
* @returns InvoiceFormat enum value
*/
getFormat() {
return this.detectedFormat;
}
/**
* Checks if the invoice is in the specified format
* @param format Format to check
* @returns True if the invoice is in the specified format
*/
isFormat(format) {
return this.detectedFormat === format;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy54aW52b2ljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMueGludm9pY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFFeEMsT0FBTyxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFakQsT0FBTyxFQUFFLGFBQWEsRUFBRSxlQUFlLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUV4RSx1REFBdUQ7QUFFdkQsbUJBQW1CO0FBQ25CLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUN4RSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sd0NBQXdDLENBQUM7QUFDeEUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMENBQTBDLENBQUM7QUFFNUUsdUJBQXVCO0FBQ3ZCLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUM1RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFOUQseUJBQXlCO0FBQ3pCLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUVwRTs7OztHQUlHO0FBQ0gsTUFBTSxPQUFPLFFBQVE7SUFpRG5COzs7T0FHRztJQUNILFlBQVksT0FBeUI7UUFwRHJDLGdDQUFnQztRQUN6QixPQUFFLEdBQVcsRUFBRSxDQUFDO1FBQ2hCLGNBQVMsR0FBVyxFQUFFLENBQUM7UUFDdkIsZ0JBQVcsR0FBK0IsV0FBVyxDQUFDO1FBQ3RELGdCQUFXLEdBQTJEO1lBQzNFLElBQUksRUFBRSxPQUFPO1lBQ2IsT0FBTyxFQUFFLE9BQU87U0FDakIsQ0FBQztRQUNLLFNBQUksR0FBYyxTQUFTLENBQUM7UUFDNUIsU0FBSSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUNsQixXQUFNLEdBQThDLFNBQVMsQ0FBQztRQUM5RCxZ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