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.

352 lines 24.6 kB
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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