feat(core): improve in-memory validation, FatturaPA detection coverage, and published type compatibility
This commit is contained in:
@@ -70,41 +70,81 @@ export class FormatDetector {
|
||||
* @returns Detected format or UNKNOWN if more analysis is needed
|
||||
*/
|
||||
private static quickFormatCheck(xml: string): InvoiceFormat {
|
||||
const lowerXml = xml.toLowerCase();
|
||||
// Only scan a small prefix so large payloads do not create another full-size string copy.
|
||||
const sample = xml.slice(0, 65536);
|
||||
|
||||
// Root-element checks avoid a DOM parse for the common invoice formats.
|
||||
if (/<(?:[A-Za-z_][\w.-]*:)?(?:Invoice|CreditNote)\b/.test(sample)) {
|
||||
const customizationIdMatch = sample.match(
|
||||
/<[^>]*CustomizationID[^>]*>\s*([^<]+?)\s*<\/[^>]*CustomizationID>/i,
|
||||
);
|
||||
const customizationId = customizationIdMatch?.[1] ?? '';
|
||||
|
||||
if (/xrechnung/i.test(customizationId) || /urn:xoev-de:kosit:standard:xrechnung/i.test(customizationId)) {
|
||||
return InvoiceFormat.XRECHNUNG;
|
||||
}
|
||||
return InvoiceFormat.UBL;
|
||||
}
|
||||
|
||||
if (/<(?:[A-Za-z_][\w.-]*:)?CrossIndustryInvoice\b/.test(sample)) {
|
||||
const guidelineIdMatch = sample.match(
|
||||
/<[^>]*GuidelineSpecifiedDocumentContextParameter[^>]*>[\s\S]*?<[^>]*ID[^>]*>\s*([^<]+?)\s*<\/[^>]*ID>/i,
|
||||
);
|
||||
const guidelineId = guidelineIdMatch?.[1] ?? '';
|
||||
|
||||
if (/xrechnung/i.test(guidelineId)) {
|
||||
return InvoiceFormat.XRECHNUNG;
|
||||
}
|
||||
if (/factur-x/i.test(guidelineId) || /urn:cen\.eu:en16931:2017/i.test(guidelineId)) {
|
||||
return InvoiceFormat.FACTURX;
|
||||
}
|
||||
if (/zugferd/i.test(guidelineId) || /urn:ferd:/i.test(guidelineId) || /urn:zugferd/i.test(guidelineId)) {
|
||||
return InvoiceFormat.ZUGFERD;
|
||||
}
|
||||
return InvoiceFormat.CII;
|
||||
}
|
||||
|
||||
if (/<(?:[A-Za-z_][\w.-]*:)?CrossIndustryDocument\b/.test(sample)) {
|
||||
return InvoiceFormat.ZUGFERD;
|
||||
}
|
||||
|
||||
if (/<FatturaElettronica\b/.test(sample)) {
|
||||
return InvoiceFormat.FATTURAPA;
|
||||
}
|
||||
|
||||
// Check for obvious Factur-X indicators
|
||||
if (
|
||||
lowerXml.includes('factur-x.eu') ||
|
||||
lowerXml.includes('factur-x.xml') ||
|
||||
lowerXml.includes('factur-x:') ||
|
||||
lowerXml.includes('urn:cen.eu:en16931:2017') && lowerXml.includes('factur-x')
|
||||
/factur-x\.eu/i.test(sample) ||
|
||||
/factur-x\.xml/i.test(sample) ||
|
||||
/factur-x:/i.test(sample) ||
|
||||
(/urn:cen\.eu:en16931:2017/i.test(sample) && /factur-x/i.test(sample))
|
||||
) {
|
||||
return InvoiceFormat.FACTURX;
|
||||
}
|
||||
|
||||
// Check for obvious ZUGFeRD indicators
|
||||
if (
|
||||
lowerXml.includes('zugferd:') ||
|
||||
lowerXml.includes('zugferd-invoice.xml') ||
|
||||
lowerXml.includes('urn:ferd:') ||
|
||||
lowerXml.includes('urn:zugferd')
|
||||
/zugferd:/i.test(sample) ||
|
||||
/zugferd-invoice\.xml/i.test(sample) ||
|
||||
/urn:ferd:/i.test(sample) ||
|
||||
/urn:zugferd/i.test(sample)
|
||||
) {
|
||||
return InvoiceFormat.ZUGFERD;
|
||||
}
|
||||
|
||||
// Check for obvious XRechnung indicators
|
||||
if (
|
||||
lowerXml.includes('xrechnung') ||
|
||||
lowerXml.includes('urn:xoev-de:kosit:standard:xrechnung')
|
||||
/xrechnung/i.test(sample) ||
|
||||
/urn:xoev-de:kosit:standard:xrechnung/i.test(sample)
|
||||
) {
|
||||
return InvoiceFormat.XRECHNUNG;
|
||||
}
|
||||
|
||||
// Check for obvious FatturaPA indicators
|
||||
if (
|
||||
lowerXml.includes('fatturapa') ||
|
||||
lowerXml.includes('fattura elettronica') ||
|
||||
lowerXml.includes('fatturaelettronica')
|
||||
/fatturapa/i.test(sample) ||
|
||||
/fattura elettronica/i.test(sample) ||
|
||||
/fatturaelettronica/i.test(sample)
|
||||
) {
|
||||
return InvoiceFormat.FATTURAPA;
|
||||
}
|
||||
@@ -198,10 +238,8 @@ export class FormatDetector {
|
||||
* @returns True if it's a FatturaPA format
|
||||
*/
|
||||
private static isFatturaPAFormat(root: Element): boolean {
|
||||
return (
|
||||
root.nodeName === 'FatturaElettronica' ||
|
||||
(root.getAttribute('xmlns') && root.getAttribute('xmlns')!.includes('fatturapa.gov.it'))
|
||||
);
|
||||
const xmlns = root.getAttribute('xmlns') || '';
|
||||
return root.nodeName === 'FatturaElettronica' || xmlns.includes('fatturapa.gov.it');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,4 +341,4 @@ export class FormatDetector {
|
||||
// Generic CII if we can't determine more specifically
|
||||
return InvoiceFormat.CII;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user