import { InvoiceFormat } from '../../interfaces/common.js'; import { DOMParser } from 'xmldom'; /** * Utility class for detecting invoice formats */ export class FormatDetector { /** * Detects the format of an XML document * @param xml XML content to analyze * @returns Detected invoice format */ public static detectFormat(xml: string): InvoiceFormat { try { const doc = new DOMParser().parseFromString(xml, 'application/xml'); const root = doc.documentElement; if (!root) { return InvoiceFormat.UNKNOWN; } // UBL detection (Invoice or CreditNote root element) if (root.nodeName === 'Invoice' || root.nodeName === 'CreditNote') { // Check if it's XRechnung by looking at CustomizationID const customizationNodes = root.getElementsByTagName('cbc:CustomizationID'); if (customizationNodes.length > 0) { const customizationId = customizationNodes[0].textContent || ''; if (customizationId.includes('xrechnung') || customizationId.includes('XRechnung')) { return InvoiceFormat.XRECHNUNG; } } return InvoiceFormat.UBL; } // Factur-X/ZUGFeRD detection (CrossIndustryInvoice root element) if (root.nodeName === 'rsm:CrossIndustryInvoice' || root.nodeName === 'CrossIndustryInvoice') { // Check for profile to determine if it's Factur-X or ZUGFeRD const profileNodes = root.getElementsByTagName('ram:ID'); for (let i = 0; i < profileNodes.length; i++) { const profileText = profileNodes[i].textContent || ''; if (profileText.includes('factur-x') || profileText.includes('Factur-X')) { return InvoiceFormat.FACTURX; } if (profileText.includes('zugferd') || profileText.includes('ZUGFeRD')) { return InvoiceFormat.ZUGFERD; } } // If no specific profile found, default to CII return InvoiceFormat.CII; } // FatturaPA detection would be implemented here if (root.nodeName === 'FatturaElettronica' || (root.getAttribute('xmlns') && root.getAttribute('xmlns')!.includes('fatturapa.gov.it'))) { return InvoiceFormat.FATTURAPA; } return InvoiceFormat.UNKNOWN; } catch (error) { console.error('Error detecting format:', error); return InvoiceFormat.UNKNOWN; } } }