update
This commit is contained in:
715
test/suite/einvoice_edge-cases/test.edge-08.mixed-formats.ts
Normal file
715
test/suite/einvoice_edge-cases/test.edge-08.mixed-formats.ts
Normal file
@ -0,0 +1,715 @@
|
||||
import { tap } from '@git.zone/tstest/tapbundle';
|
||||
import * as plugins from '../plugins.js';
|
||||
import { EInvoice } from '../../../ts/index.js';
|
||||
import { PerformanceTracker } from '../performance.tracker.js';
|
||||
|
||||
const performanceTracker = new PerformanceTracker('EDGE-08: Mixed Format Documents');
|
||||
|
||||
tap.test('EDGE-08: Mixed Format Documents - should handle documents with mixed or ambiguous formats', async (t) => {
|
||||
const einvoice = new EInvoice();
|
||||
|
||||
// Test 1: Documents with elements from multiple standards
|
||||
const multiStandardElements = await performanceTracker.measureAsync(
|
||||
'multi-standard-elements',
|
||||
async () => {
|
||||
const mixedXML = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
|
||||
xmlns:cii="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
|
||||
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
|
||||
<!-- UBL elements -->
|
||||
<ubl:ID>MIXED-001</ubl:ID>
|
||||
<cbc:IssueDate>2024-01-15</cbc:IssueDate>
|
||||
|
||||
<!-- CII elements -->
|
||||
<cii:ExchangedDocument>
|
||||
<ram:ID>MIXED-001-CII</ram:ID>
|
||||
</cii:ExchangedDocument>
|
||||
|
||||
<!-- Custom elements -->
|
||||
<CustomField>Custom Value</CustomField>
|
||||
|
||||
<!-- Mix of both -->
|
||||
<LineItems>
|
||||
<ubl:InvoiceLine>
|
||||
<cbc:ID>1</cbc:ID>
|
||||
</ubl:InvoiceLine>
|
||||
<cii:SupplyChainTradeLineItem>
|
||||
<ram:AssociatedDocumentLineDocument>
|
||||
<ram:LineID>2</ram:LineID>
|
||||
</ram:AssociatedDocumentLineDocument>
|
||||
</cii:SupplyChainTradeLineItem>
|
||||
</LineItems>
|
||||
</Invoice>`;
|
||||
|
||||
try {
|
||||
const detection = await einvoice.detectFormat(mixedXML);
|
||||
const parsed = await einvoice.parseDocument(mixedXML);
|
||||
|
||||
return {
|
||||
detected: true,
|
||||
primaryFormat: detection?.format,
|
||||
confidence: detection?.confidence,
|
||||
mixedElements: detection?.mixedElements || [],
|
||||
standardsFound: detection?.detectedStandards || [],
|
||||
parsed: !!parsed
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
detected: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
t.ok(multiStandardElements.detected, 'Multi-standard document was processed');
|
||||
t.ok(multiStandardElements.standardsFound?.length > 1, 'Multiple standards detected');
|
||||
|
||||
// Test 2: Namespace confusion
|
||||
const namespaceConfusion = await performanceTracker.measureAsync(
|
||||
'namespace-confusion',
|
||||
async () => {
|
||||
const confusedNamespaces = [
|
||||
{
|
||||
name: 'wrong-namespace-binding',
|
||||
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||||
<!-- Using CII elements in UBL namespace -->
|
||||
<ExchangedDocument>
|
||||
<ID>CONFUSED-001</ID>
|
||||
</ExchangedDocument>
|
||||
</Invoice>`
|
||||
},
|
||||
{
|
||||
name: 'conflicting-default-namespaces',
|
||||
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root>
|
||||
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||||
<ID>UBL-001</ID>
|
||||
</Invoice>
|
||||
<Invoice xmlns="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100">
|
||||
<ExchangedDocument>
|
||||
<ID>CII-001</ID>
|
||||
</ExchangedDocument>
|
||||
</Invoice>
|
||||
</root>`
|
||||
},
|
||||
{
|
||||
name: 'namespace-switching',
|
||||
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||||
<ID>START-UBL</ID>
|
||||
<Items xmlns="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100">
|
||||
<SupplyChainTradeLineItem>
|
||||
<LineID xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">1</LineID>
|
||||
</SupplyChainTradeLineItem>
|
||||
</Items>
|
||||
</Invoice>`
|
||||
}
|
||||
];
|
||||
|
||||
const results = [];
|
||||
|
||||
for (const test of confusedNamespaces) {
|
||||
try {
|
||||
const detection = await einvoice.detectFormat(test.xml);
|
||||
const parsed = await einvoice.parseDocument(test.xml);
|
||||
const validation = await einvoice.validate(parsed);
|
||||
|
||||
results.push({
|
||||
scenario: test.name,
|
||||
detected: true,
|
||||
format: detection?.format,
|
||||
hasNamespaceIssues: detection?.namespaceIssues || false,
|
||||
valid: validation?.isValid || false,
|
||||
warnings: validation?.warnings || []
|
||||
});
|
||||
} catch (error) {
|
||||
results.push({
|
||||
scenario: test.name,
|
||||
detected: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
);
|
||||
|
||||
namespaceConfusion.forEach(result => {
|
||||
t.ok(result.detected || result.error, `Namespace confusion ${result.scenario} was handled`);
|
||||
if (result.detected) {
|
||||
t.ok(result.hasNamespaceIssues || result.warnings.length > 0,
|
||||
'Namespace issues should be detected');
|
||||
}
|
||||
});
|
||||
|
||||
// Test 3: Hybrid PDF documents
|
||||
const hybridPDFDocuments = await performanceTracker.measureAsync(
|
||||
'hybrid-pdf-documents',
|
||||
async () => {
|
||||
const hybridScenarios = [
|
||||
{
|
||||
name: 'multiple-xml-attachments',
|
||||
description: 'PDF with both UBL and CII XML attachments'
|
||||
},
|
||||
{
|
||||
name: 'conflicting-metadata',
|
||||
description: 'PDF metadata says ZUGFeRD but contains Factur-X'
|
||||
},
|
||||
{
|
||||
name: 'mixed-version-attachments',
|
||||
description: 'PDF with ZUGFeRD v1 and v2 attachments'
|
||||
},
|
||||
{
|
||||
name: 'non-standard-attachment',
|
||||
description: 'PDF with standard XML plus custom format'
|
||||
}
|
||||
];
|
||||
|
||||
const results = [];
|
||||
|
||||
for (const scenario of hybridScenarios) {
|
||||
// Create mock hybrid PDF
|
||||
const hybridPDF = createHybridPDF(scenario.name);
|
||||
|
||||
try {
|
||||
const extraction = await einvoice.extractFromPDF(hybridPDF);
|
||||
|
||||
results.push({
|
||||
scenario: scenario.name,
|
||||
extracted: true,
|
||||
attachmentCount: extraction?.attachments?.length || 0,
|
||||
formats: extraction?.detectedFormats || [],
|
||||
primaryFormat: extraction?.primaryFormat,
|
||||
hasConflicts: extraction?.hasFormatConflicts || false
|
||||
});
|
||||
} catch (error) {
|
||||
results.push({
|
||||
scenario: scenario.name,
|
||||
extracted: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
);
|
||||
|
||||
hybridPDFDocuments.forEach(result => {
|
||||
t.ok(result.extracted || result.error,
|
||||
`Hybrid PDF ${result.scenario} was processed`);
|
||||
});
|
||||
|
||||
// Test 4: Schema version mixing
|
||||
const schemaVersionMixing = await performanceTracker.measureAsync(
|
||||
'schema-version-mixing',
|
||||
async () => {
|
||||
const versionMixes = [
|
||||
{
|
||||
name: 'ubl-version-mix',
|
||||
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
|
||||
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
|
||||
xmlns:cbc1="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-1">
|
||||
<cbc:UBLVersionID>2.1</cbc:UBLVersionID>
|
||||
<cbc1:ID>OLD-STYLE-ID</cbc1:ID>
|
||||
<cbc:IssueDate>2024-01-15</cbc:IssueDate>
|
||||
</Invoice>`
|
||||
},
|
||||
{
|
||||
name: 'zugferd-version-mix',
|
||||
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rsm:CrossIndustryDocument>
|
||||
<!-- ZUGFeRD 1.0 structure -->
|
||||
<rsm:SpecifiedExchangedDocumentContext>
|
||||
<ram:GuidelineSpecifiedDocumentContextParameter>
|
||||
<ram:ID>urn:ferd:CrossIndustryDocument:invoice:1p0</ram:ID>
|
||||
</ram:GuidelineSpecifiedDocumentContextParameter>
|
||||
</rsm:SpecifiedExchangedDocumentContext>
|
||||
|
||||
<!-- ZUGFeRD 2.1 elements -->
|
||||
<rsm:ExchangedDocument>
|
||||
<ram:ID>MIXED-VERSION</ram:ID>
|
||||
</rsm:ExchangedDocument>
|
||||
</rsm:CrossIndustryDocument>`
|
||||
}
|
||||
];
|
||||
|
||||
const results = [];
|
||||
|
||||
for (const mix of versionMixes) {
|
||||
try {
|
||||
const detection = await einvoice.detectFormat(mix.xml);
|
||||
const parsed = await einvoice.parseDocument(mix.xml);
|
||||
|
||||
results.push({
|
||||
scenario: mix.name,
|
||||
processed: true,
|
||||
detectedVersion: detection?.version,
|
||||
versionConflicts: detection?.versionConflicts || [],
|
||||
canMigrate: detection?.migrationPath !== undefined
|
||||
});
|
||||
} catch (error) {
|
||||
results.push({
|
||||
scenario: mix.name,
|
||||
processed: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
);
|
||||
|
||||
schemaVersionMixing.forEach(result => {
|
||||
t.ok(result.processed || result.error,
|
||||
`Version mix ${result.scenario} was handled`);
|
||||
});
|
||||
|
||||
// Test 5: Invalid format combinations
|
||||
const invalidFormatCombos = await performanceTracker.measureAsync(
|
||||
'invalid-format-combinations',
|
||||
async () => {
|
||||
const invalidCombos = [
|
||||
{
|
||||
name: 'ubl-with-cii-structure',
|
||||
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||||
<!-- CII structure in UBL namespace -->
|
||||
<rsm:ExchangedDocumentContext>
|
||||
<ram:BusinessProcessSpecifiedDocumentContextParameter/>
|
||||
</rsm:ExchangedDocumentContext>
|
||||
<rsm:ExchangedDocument>
|
||||
<ram:ID>INVALID-001</ram:ID>
|
||||
</rsm:ExchangedDocument>
|
||||
</Invoice>`
|
||||
},
|
||||
{
|
||||
name: 'html-invoice-hybrid',
|
||||
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<html>
|
||||
<body>
|
||||
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||||
<ID>HTML-WRAPPED</ID>
|
||||
</Invoice>
|
||||
</body>
|
||||
</html>`
|
||||
},
|
||||
{
|
||||
name: 'json-xml-mix',
|
||||
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Invoice>
|
||||
<ID>JSON-MIX</ID>
|
||||
<JSONData>
|
||||
{"amount": 100, "currency": "EUR"}
|
||||
</JSONData>
|
||||
<XMLData>
|
||||
<Amount>100</Amount>
|
||||
<Currency>EUR</Currency>
|
||||
</XMLData>
|
||||
</Invoice>`
|
||||
}
|
||||
];
|
||||
|
||||
const results = [];
|
||||
|
||||
for (const combo of invalidCombos) {
|
||||
try {
|
||||
const detection = await einvoice.detectFormat(combo.xml);
|
||||
const parsed = await einvoice.parseDocument(combo.xml);
|
||||
const validation = await einvoice.validate(parsed);
|
||||
|
||||
results.push({
|
||||
combo: combo.name,
|
||||
detected: !!detection,
|
||||
format: detection?.format || 'unknown',
|
||||
valid: validation?.isValid || false,
|
||||
recoverable: detection?.canRecover || false
|
||||
});
|
||||
} catch (error) {
|
||||
results.push({
|
||||
combo: combo.name,
|
||||
detected: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
);
|
||||
|
||||
invalidFormatCombos.forEach(result => {
|
||||
t.notOk(result.valid, `Invalid combo ${result.combo} should not validate`);
|
||||
});
|
||||
|
||||
// Test 6: Partial format documents
|
||||
const partialFormatDocuments = await performanceTracker.measureAsync(
|
||||
'partial-format-documents',
|
||||
async () => {
|
||||
const partials = [
|
||||
{
|
||||
name: 'ubl-header-cii-body',
|
||||
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||||
<cbc:ID>PARTIAL-001</cbc:ID>
|
||||
<cbc:IssueDate>2024-01-15</cbc:IssueDate>
|
||||
|
||||
<!-- Switch to CII for line items -->
|
||||
<IncludedSupplyChainTradeLineItem>
|
||||
<ram:AssociatedDocumentLineDocument>
|
||||
<ram:LineID>1</ram:LineID>
|
||||
</ram:AssociatedDocumentLineDocument>
|
||||
</IncludedSupplyChainTradeLineItem>
|
||||
</Invoice>`
|
||||
},
|
||||
{
|
||||
name: 'incomplete-migration',
|
||||
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Invoice>
|
||||
<!-- Old format -->
|
||||
<InvoiceNumber>OLD-001</InvoiceNumber>
|
||||
|
||||
<!-- New format -->
|
||||
<ID>NEW-001</ID>
|
||||
|
||||
<!-- Mixed date formats -->
|
||||
<InvoiceDate>2024-01-15</InvoiceDate>
|
||||
<IssueDate>2024-01-15T00:00:00</IssueDate>
|
||||
</Invoice>`
|
||||
}
|
||||
];
|
||||
|
||||
const results = [];
|
||||
|
||||
for (const partial of partials) {
|
||||
try {
|
||||
const analysis = await einvoice.analyzeDocument(partial.xml);
|
||||
|
||||
results.push({
|
||||
scenario: partial.name,
|
||||
analyzed: true,
|
||||
completeness: analysis?.completeness || 0,
|
||||
missingElements: analysis?.missingElements || [],
|
||||
formatConsistency: analysis?.formatConsistency || 0,
|
||||
migrationNeeded: analysis?.requiresMigration || false
|
||||
});
|
||||
} catch (error) {
|
||||
results.push({
|
||||
scenario: partial.name,
|
||||
analyzed: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
);
|
||||
|
||||
partialFormatDocuments.forEach(result => {
|
||||
t.ok(result.analyzed || result.error,
|
||||
`Partial document ${result.scenario} was analyzed`);
|
||||
if (result.analyzed) {
|
||||
t.ok(result.formatConsistency < 100,
|
||||
'Format inconsistency should be detected');
|
||||
}
|
||||
});
|
||||
|
||||
// Test 7: Encoding format conflicts
|
||||
const encodingFormatConflicts = await performanceTracker.measureAsync(
|
||||
'encoding-format-conflicts',
|
||||
async () => {
|
||||
const encodingConflicts = [
|
||||
{
|
||||
name: 'utf8-with-utf16-content',
|
||||
declared: 'UTF-8',
|
||||
actual: 'UTF-16',
|
||||
content: Buffer.from('<?xml version="1.0" encoding="UTF-8"?><Invoice><ID>TEST</ID></Invoice>', 'utf16le')
|
||||
},
|
||||
{
|
||||
name: 'wrong-decimal-separator',
|
||||
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Invoice>
|
||||
<!-- European format in US-style document -->
|
||||
<TotalAmount>1.234,56</TotalAmount>
|
||||
<TaxAmount>234.56</TaxAmount>
|
||||
</Invoice>`
|
||||
},
|
||||
{
|
||||
name: 'date-format-mixing',
|
||||
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Invoice>
|
||||
<Dates>
|
||||
<IssueDate>2024-01-15</IssueDate>
|
||||
<DueDate>15/01/2024</DueDate>
|
||||
<DeliveryDate>01-15-2024</DeliveryDate>
|
||||
<PaymentDate>20240115</PaymentDate>
|
||||
</Dates>
|
||||
</Invoice>`
|
||||
}
|
||||
];
|
||||
|
||||
const results = [];
|
||||
|
||||
for (const conflict of encodingConflicts) {
|
||||
try {
|
||||
let parseResult;
|
||||
|
||||
if (conflict.content) {
|
||||
parseResult = await einvoice.parseDocument(conflict.content);
|
||||
} else {
|
||||
parseResult = await einvoice.parseDocument(conflict.xml);
|
||||
}
|
||||
|
||||
const analysis = await einvoice.analyzeFormatConsistency(parseResult);
|
||||
|
||||
results.push({
|
||||
scenario: conflict.name,
|
||||
handled: true,
|
||||
encodingIssues: analysis?.encodingIssues || [],
|
||||
formatIssues: analysis?.formatIssues || [],
|
||||
normalized: analysis?.normalized || false
|
||||
});
|
||||
} catch (error) {
|
||||
results.push({
|
||||
scenario: conflict.name,
|
||||
handled: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
);
|
||||
|
||||
encodingFormatConflicts.forEach(result => {
|
||||
t.ok(result.handled || result.error,
|
||||
`Encoding conflict ${result.scenario} was handled`);
|
||||
});
|
||||
|
||||
// Test 8: Format autodetection challenges
|
||||
const autodetectionChallenges = await performanceTracker.measureAsync(
|
||||
'format-autodetection-challenges',
|
||||
async () => {
|
||||
const challenges = [
|
||||
{
|
||||
name: 'minimal-structure',
|
||||
xml: '<Invoice><ID>123</ID></Invoice>'
|
||||
},
|
||||
{
|
||||
name: 'generic-xml',
|
||||
xml: `<?xml version="1.0"?>
|
||||
<Document>
|
||||
<Header>
|
||||
<ID>DOC-001</ID>
|
||||
<Date>2024-01-15</Date>
|
||||
</Header>
|
||||
<Items>
|
||||
<Item>
|
||||
<Description>Product</Description>
|
||||
<Amount>100</Amount>
|
||||
</Item>
|
||||
</Items>
|
||||
</Document>`
|
||||
},
|
||||
{
|
||||
name: 'custom-namespace',
|
||||
xml: `<?xml version="1.0"?>
|
||||
<inv:Invoice xmlns:inv="http://custom.company.com/invoice">
|
||||
<inv:Number>INV-001</inv:Number>
|
||||
<inv:Total>1000</inv:Total>
|
||||
</inv:Invoice>`
|
||||
}
|
||||
];
|
||||
|
||||
const results = [];
|
||||
|
||||
for (const challenge of challenges) {
|
||||
const detectionResult = await einvoice.detectFormat(challenge.xml);
|
||||
|
||||
results.push({
|
||||
scenario: challenge.name,
|
||||
format: detectionResult?.format || 'unknown',
|
||||
confidence: detectionResult?.confidence || 0,
|
||||
isGeneric: detectionResult?.isGeneric || false,
|
||||
suggestedFormats: detectionResult?.possibleFormats || []
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
);
|
||||
|
||||
autodetectionChallenges.forEach(result => {
|
||||
t.ok(result.confidence < 100 || result.isGeneric,
|
||||
`Challenge ${result.scenario} should have detection uncertainty`);
|
||||
});
|
||||
|
||||
// Test 9: Legacy format mixing
|
||||
const legacyFormatMixing = await performanceTracker.measureAsync(
|
||||
'legacy-format-mixing',
|
||||
async () => {
|
||||
const legacyMixes = [
|
||||
{
|
||||
name: 'edifact-xml-hybrid',
|
||||
content: `UNB+UNOC:3+SENDER+RECEIVER+240115:1200+1++INVOIC'
|
||||
<?xml version="1.0"?>
|
||||
<AdditionalData>
|
||||
<Invoice>
|
||||
<ID>HYBRID-001</ID>
|
||||
</Invoice>
|
||||
</AdditionalData>
|
||||
UNZ+1+1'`
|
||||
},
|
||||
{
|
||||
name: 'csv-xml-combination',
|
||||
content: `INVOICE_HEADER
|
||||
ID,Date,Amount
|
||||
INV-001,2024-01-15,1000.00
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<InvoiceDetails>
|
||||
<LineItems>
|
||||
<Item>Product A</Item>
|
||||
</LineItems>
|
||||
</InvoiceDetails>`
|
||||
}
|
||||
];
|
||||
|
||||
const results = [];
|
||||
|
||||
for (const mix of legacyMixes) {
|
||||
try {
|
||||
const detection = await einvoice.detectFormat(mix.content);
|
||||
const extraction = await einvoice.extractStructuredData(mix.content);
|
||||
|
||||
results.push({
|
||||
scenario: mix.name,
|
||||
processed: true,
|
||||
formatsFound: detection?.multipleFormats || [],
|
||||
primaryFormat: detection?.primaryFormat,
|
||||
dataExtracted: !!extraction?.data
|
||||
});
|
||||
} catch (error) {
|
||||
results.push({
|
||||
scenario: mix.name,
|
||||
processed: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
);
|
||||
|
||||
legacyFormatMixing.forEach(result => {
|
||||
t.ok(result.processed || result.error,
|
||||
`Legacy mix ${result.scenario} was handled`);
|
||||
});
|
||||
|
||||
// Test 10: Format conversion conflicts
|
||||
const formatConversionConflicts = await performanceTracker.measureAsync(
|
||||
'format-conversion-conflicts',
|
||||
async () => {
|
||||
// Create invoice with format-specific features
|
||||
const sourceInvoice = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||||
<ID>CONVERT-001</ID>
|
||||
<!-- UBL-specific extension -->
|
||||
<UBLExtensions>
|
||||
<UBLExtension>
|
||||
<ExtensionContent>
|
||||
<CustomField>UBL-Only-Data</CustomField>
|
||||
</ExtensionContent>
|
||||
</UBLExtension>
|
||||
</UBLExtensions>
|
||||
|
||||
<!-- Format-specific calculation -->
|
||||
<AllowanceCharge>
|
||||
<ChargeIndicator>false</ChargeIndicator>
|
||||
<AllowanceChargeReason>Discount</AllowanceChargeReason>
|
||||
<Amount currencyID="EUR">50.00</Amount>
|
||||
</AllowanceCharge>
|
||||
</Invoice>`;
|
||||
|
||||
const targetFormats = ['cii', 'xrechnung', 'fatturapa'];
|
||||
const results = [];
|
||||
|
||||
for (const target of targetFormats) {
|
||||
try {
|
||||
const converted = await einvoice.convertFormat(sourceInvoice, target);
|
||||
const analysis = await einvoice.analyzeConversion(sourceInvoice, converted);
|
||||
|
||||
results.push({
|
||||
targetFormat: target,
|
||||
converted: true,
|
||||
dataLoss: analysis?.dataLoss || [],
|
||||
unsupportedFeatures: analysis?.unsupportedFeatures || [],
|
||||
warnings: analysis?.warnings || []
|
||||
});
|
||||
} catch (error) {
|
||||
results.push({
|
||||
targetFormat: target,
|
||||
converted: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
);
|
||||
|
||||
formatConversionConflicts.forEach(result => {
|
||||
t.ok(result.converted || result.error,
|
||||
`Conversion to ${result.targetFormat} was attempted`);
|
||||
if (result.converted) {
|
||||
t.ok(result.dataLoss.length > 0 || result.warnings.length > 0,
|
||||
'Format-specific features should cause warnings');
|
||||
}
|
||||
});
|
||||
|
||||
// Print performance summary
|
||||
performanceTracker.printSummary();
|
||||
});
|
||||
|
||||
// Helper function to create hybrid PDF
|
||||
function createHybridPDF(scenario: string): Buffer {
|
||||
// Simplified mock - in reality would create actual PDF structure
|
||||
const mockStructure = {
|
||||
'multiple-xml-attachments': {
|
||||
attachments: [
|
||||
{ name: 'invoice.ubl.xml', type: 'application/xml' },
|
||||
{ name: 'invoice.cii.xml', type: 'application/xml' }
|
||||
]
|
||||
},
|
||||
'conflicting-metadata': {
|
||||
metadata: { format: 'ZUGFeRD' },
|
||||
attachments: [{ name: 'facturx.xml', type: 'application/xml' }]
|
||||
},
|
||||
'mixed-version-attachments': {
|
||||
attachments: [
|
||||
{ name: 'zugferd_v1.xml', version: '1.0' },
|
||||
{ name: 'zugferd_v2.xml', version: '2.1' }
|
||||
]
|
||||
},
|
||||
'non-standard-attachment': {
|
||||
attachments: [
|
||||
{ name: 'invoice.xml', type: 'application/xml' },
|
||||
{ name: 'custom.json', type: 'application/json' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
return Buffer.from(JSON.stringify(mockStructure[scenario] || {}));
|
||||
}
|
||||
|
||||
// Run the test
|
||||
tap.start();
|
Reference in New Issue
Block a user