import { tap, expect } from '@git.zone/tstest/tapbundle'; import * as plugins from '../../../ts/plugins.ts'; import { EInvoice } from '../../../ts/classes.xinvoice.ts'; import { CorpusLoader } from '../../helpers/corpus.loader.ts'; import { PerformanceTracker } from '../../helpers/performance.tracker.ts'; const testTimeout = 300000; // 5 minutes timeout for conversion processing // CONV-03: ZUGFeRD to XRechnung Conversion // Tests conversion from ZUGFeRD format to XRechnung (German CIUS of EN16931) // including profile adaptation, compliance checking, and German-specific requirements tap.test('CONV-03: ZUGFeRD to XRechnung Conversion - Basic Conversion', async (tools) => { const startTime = Date.now(); try { // Create a sample ZUGFeRD invoice for conversion testing const sampleZugferdXml = ` urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p1:comfort ZUGFERD-TO-XRECHNUNG-001 380 20240115 ZUGFeRD to XRechnung conversion test 1 ZUGFeRD Test Product Product for ZUGFeRD to XRechnung conversion 50.00 2 VAT 19.00 100.00 ZUGFeRD Test Supplier GmbH 10115 Friedrichstraße 123 Berlin DE DE123456789 XRechnung Test Customer GmbH 80331 Marienplatz 1 München DE 20240115 EUR 19.00 VAT 100.00 19.00 100.00 100.00 19.00 119.00 119.00 `; const invoice = new EInvoice(); const parseResult = await invoice.fromXmlString(sampleZugferdXml); expect(parseResult).toBeTruthy(); // Test ZUGFeRD to XRechnung conversion if supported if (typeof invoice.convertTo === 'function') { tools.log('Testing ZUGFeRD to XRechnung conversion...'); try { const conversionResult = await invoice.convertTo('XRECHNUNG'); if (conversionResult) { tools.log('✓ ZUGFeRD to XRechnung conversion completed'); // Verify the converted format const convertedXml = await conversionResult.toXmlString(); expect(convertedXml).toBeTruthy(); expect(convertedXml.length).toBeGreaterThan(100); // Check for XRechnung format characteristics const xrechnungChecks = { hasXrechnungCustomization: convertedXml.includes('urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung') || convertedXml.includes('XRechnung') || convertedXml.includes('xrechnung'), hasUblNamespace: convertedXml.includes('urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'), hasPeppolProfile: convertedXml.includes('urn:fdc:peppol.eu:2017:poacc:billing:01:1.0'), hasOriginalId: convertedXml.includes('ZUGFERD-TO-XRECHNUNG-001'), hasGermanVat: convertedXml.includes('DE123456789'), hasEurocurrency: convertedXml.includes('EUR') }; tools.log('XRechnung Format Verification:'); tools.log(` XRechnung Customization: ${xrechnungChecks.hasXrechnungCustomization}`); tools.log(` UBL Namespace: ${xrechnungChecks.hasUblNamespace}`); tools.log(` PEPPOL Profile: ${xrechnungChecks.hasPeppolProfile}`); tools.log(` Original ID preserved: ${xrechnungChecks.hasOriginalId}`); tools.log(` German VAT preserved: ${xrechnungChecks.hasGermanVat}`); tools.log(` Euro currency preserved: ${xrechnungChecks.hasEurourrency}`); if (xrechnungChecks.hasUblNamespace || xrechnungChecks.hasXrechnungCustomization) { tools.log('✓ Valid XRechnung format structure detected'); } else { tools.log('⚠ XRechnung format structure not clearly detected'); } // Validate the converted invoice try { const validationResult = await conversionResult.validate(); if (validationResult.valid) { tools.log('✓ Converted XRechnung invoice passes validation'); } else { tools.log(`⚠ Converted XRechnung validation issues: ${validationResult.errors?.length || 0} errors`); if (validationResult.errors && validationResult.errors.length > 0) { tools.log(` First error: ${validationResult.errors[0].message}`); } } } catch (validationError) { tools.log(`⚠ Converted XRechnung validation failed: ${validationError.message}`); } } else { tools.log('⚠ ZUGFeRD to XRechnung conversion returned no result'); } } catch (conversionError) { tools.log(`⚠ ZUGFeRD to XRechnung conversion failed: ${conversionError.message}`); } } else { tools.log('⚠ ZUGFeRD to XRechnung conversion not supported (convertTo method not available)'); // Test alternative conversion approach if available if (typeof invoice.toXRechnung === 'function') { try { const xrechnungResult = await invoice.toXRechnung(); if (xrechnungResult) { tools.log('✓ Alternative ZUGFeRD to XRechnung conversion successful'); } } catch (alternativeError) { tools.log(`⚠ Alternative conversion failed: ${alternativeError.message}`); } } } } catch (error) { tools.log(`Basic ZUGFeRD to XRechnung conversion test failed: ${error.message}`); } const duration = Date.now() - startTime; PerformanceTracker.recordMetric('conversion-zugferd-to-xrechnung-basic', duration); }); tap.test('CONV-03: ZUGFeRD to XRechnung Conversion - Profile Adaptation', async (tools) => { const startTime = Date.now(); // Test conversion of different ZUGFeRD profiles to XRechnung const profileTests = [ { name: 'ZUGFeRD MINIMUM to XRechnung', zugferdXml: ` urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p1:minimum MIN-TO-XRECHNUNG-001 380 20240115 EUR 119.00 ` }, { name: 'ZUGFeRD BASIC to XRechnung', zugferdXml: ` urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p1:basic BASIC-TO-XRECHNUNG-001 380 20240115 BASIC Supplier GmbH BASIC Customer GmbH EUR 100.00 19.00 119.00 119.00 ` }, { name: 'ZUGFeRD COMFORT to XRechnung', zugferdXml: ` urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p1:comfort COMFORT-TO-XRECHNUNG-001 380 20240115 1 COMFORT Test Product 100.00 EUR 100.00 100.00 19.00 119.00 119.00 ` } ]; for (const profileTest of profileTests) { tools.log(`Testing ${profileTest.name}...`); try { const invoice = new EInvoice(); const parseResult = await invoice.fromXmlString(profileTest.zugferdXml); if (parseResult) { if (typeof invoice.convertTo === 'function') { const conversionResult = await invoice.convertTo('XRECHNUNG'); if (conversionResult) { tools.log(`✓ ${profileTest.name} conversion completed`); const convertedXml = await conversionResult.toXmlString(); // Check profile-specific adaptations const profileAdaptations = { hasXrechnungProfile: convertedXml.includes('xrechnung') || convertedXml.includes('XRechnung'), retainsOriginalId: convertedXml.includes('TO-XRECHNUNG-001'), hasRequiredStructure: convertedXml.includes(' { const startTime = Date.now(); // Test German-specific compliance requirements for XRechnung const germanComplianceXml = ` urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p1:comfort DE-COMPLIANCE-001 380 20240115 BUYER-REF-12345 Deutsche Lieferant GmbH 10115 Unter den Linden 1 Berlin DE DE987654321 Deutscher Kunde GmbH 80331 Maximilianstraße 1 München DE PAYMENT-REF-67890 EUR 19.00 VAT 100.00 19.00 S Zahlbar innerhalb 30 Tagen ohne Abzug 20240214 100.00 100.00 19.00 119.00 119.00 `; try { const invoice = new EInvoice(); const parseResult = await invoice.fromXmlString(germanComplianceXml); if (parseResult) { tools.log('Testing German compliance requirements during conversion...'); if (typeof invoice.convertTo === 'function') { const conversionResult = await invoice.convertTo('XRECHNUNG'); if (conversionResult) { const convertedXml = await conversionResult.toXmlString(); // Check German-specific compliance requirements const germanComplianceChecks = { hasBuyerReference: convertedXml.includes('BUYER-REF-12345'), hasPaymentReference: convertedXml.includes('PAYMENT-REF-67890'), hasGermanVatNumber: convertedXml.includes('DE987654321'), hasGermanAddresses: convertedXml.includes('Berlin') && convertedXml.includes('München'), hasGermanPostCodes: convertedXml.includes('10115') && convertedXml.includes('80331'), hasEuroCurrency: convertedXml.includes('EUR'), hasStandardVatRate: convertedXml.includes('19.00'), hasPaymentTerms: convertedXml.includes('30 Tagen') || convertedXml.includes('payment') }; tools.log('German Compliance Verification:'); tools.log(` Buyer reference preserved: ${germanComplianceChecks.hasBuyerReference}`); tools.log(` Payment reference preserved: ${germanComplianceChecks.hasPaymentReference}`); tools.log(` German VAT number preserved: ${germanComplianceChecks.hasGermanVatNumber}`); tools.log(` German addresses preserved: ${germanComplianceChecks.hasGermanAddresses}`); tools.log(` German postal codes preserved: ${germanComplianceChecks.hasGermanPostCodes}`); tools.log(` Euro currency preserved: ${germanComplianceChecks.hasEuroCurrency}`); tools.log(` Standard VAT rate preserved: ${germanComplianceChecks.hasStandardVatRate}`); tools.log(` Payment terms preserved: ${germanComplianceChecks.hasPaymentTerms}`); const complianceScore = Object.values(germanComplianceChecks).filter(Boolean).length; const totalChecks = Object.values(germanComplianceChecks).length; const compliancePercentage = (complianceScore / totalChecks) * 100; tools.log(`German compliance score: ${complianceScore}/${totalChecks} (${compliancePercentage.toFixed(1)}%)`); if (compliancePercentage >= 80) { tools.log('✓ Good German compliance maintained'); } else { tools.log('⚠ German compliance issues detected'); } } else { tools.log('⚠ German compliance conversion returned no result'); } } else { tools.log('⚠ German compliance conversion not supported'); } } else { tools.log('⚠ German compliance test - ZUGFeRD parsing failed'); } } catch (error) { tools.log(`German compliance test failed: ${error.message}`); } const duration = Date.now() - startTime; PerformanceTracker.recordMetric('conversion-zugferd-to-xrechnung-german-compliance', duration); }); tap.test('CONV-03: ZUGFeRD to XRechnung Conversion - Corpus Testing', { timeout: testTimeout }, async (tools) => { const startTime = Date.now(); let processedFiles = 0; let successfulConversions = 0; let conversionErrors = 0; let totalConversionTime = 0; try { const zugferdFiles = await CorpusLoader.getFiles('ZUGFERD_V2'); tools.log(`Testing ZUGFeRD to XRechnung conversion with ${zugferdFiles.length} ZUGFeRD files`); if (zugferdFiles.length === 0) { tools.log('⚠ No ZUGFeRD files found in corpus for conversion testing'); return; } // Process a subset of files for performance const filesToProcess = zugferdFiles.slice(0, Math.min(6, zugferdFiles.length)); for (const filePath of filesToProcess) { const fileName = plugins.path.basename(filePath); const fileConversionStart = Date.now(); try { processedFiles++; const invoice = new EInvoice(); const parseResult = await invoice.fromFile(filePath); if (parseResult) { // Attempt conversion to XRechnung if (typeof invoice.convertTo === 'function') { const conversionResult = await invoice.convertTo('XRECHNUNG'); const fileConversionTime = Date.now() - fileConversionStart; totalConversionTime += fileConversionTime; if (conversionResult) { successfulConversions++; tools.log(`✓ ${fileName}: Converted to XRechnung (${fileConversionTime}ms)`); // Quick validation of converted content const convertedXml = await conversionResult.toXmlString(); if (convertedXml && convertedXml.length > 100) { tools.log(` Converted content length: ${convertedXml.length} chars`); // Check for XRechnung characteristics const xrechnungMarkers = { hasXrechnungId: convertedXml.includes('xrechnung') || convertedXml.includes('XRechnung'), hasUblStructure: convertedXml.includes('Invoice') && convertedXml.includes('urn:oasis:names'), hasGermanElements: convertedXml.includes('DE') || convertedXml.includes('EUR') }; if (Object.values(xrechnungMarkers).some(Boolean)) { tools.log(` ✓ XRechnung characteristics detected`); } } } else { conversionErrors++; tools.log(`⚠ ${fileName}: Conversion returned no result`); } } else { conversionErrors++; tools.log(`⚠ ${fileName}: Conversion method not available`); } } else { conversionErrors++; tools.log(`⚠ ${fileName}: Failed to parse original ZUGFeRD`); } } catch (error) { conversionErrors++; const fileConversionTime = Date.now() - fileConversionStart; totalConversionTime += fileConversionTime; tools.log(`✗ ${fileName}: Conversion failed - ${error.message}`); } } // Calculate statistics const successRate = processedFiles > 0 ? (successfulConversions / processedFiles) * 100 : 0; const averageConversionTime = processedFiles > 0 ? totalConversionTime / processedFiles : 0; tools.log(`\nZUGFeRD to XRechnung Conversion Summary:`); tools.log(`- Files processed: ${processedFiles}`); tools.log(`- Successful conversions: ${successfulConversions} (${successRate.toFixed(1)}%)`); tools.log(`- Conversion errors: ${conversionErrors}`); tools.log(`- Average conversion time: ${averageConversionTime.toFixed(1)}ms`); // Performance expectations if (processedFiles > 0) { expect(averageConversionTime).toBeLessThan(4000); // 4 seconds max per file } // We expect some conversions to work if (processedFiles > 0) { expect(successRate).toBeGreaterThan(0); // At least one conversion should work } } catch (error) { tools.log(`ZUGFeRD to XRechnung corpus testing failed: ${error.message}`); throw error; } const totalDuration = Date.now() - startTime; PerformanceTracker.recordMetric('conversion-zugferd-to-xrechnung-corpus', totalDuration); tools.log(`ZUGFeRD to XRechnung corpus testing completed in ${totalDuration}ms`); }); tap.test('CONV-03: Performance Summary', async (tools) => { const operations = [ 'conversion-zugferd-to-xrechnung-basic', 'conversion-zugferd-to-xrechnung-profiles', 'conversion-zugferd-to-xrechnung-german-compliance', 'conversion-zugferd-to-xrechnung-corpus' ]; tools.log(`\n=== ZUGFeRD to XRechnung Conversion Performance Summary ===`); for (const operation of operations) { const summary = await PerformanceTracker.getSummary(operation); if (summary) { tools.log(`${operation}:`); tools.log(` avg=${summary.average}ms, min=${summary.min}ms, max=${summary.max}ms, p95=${summary.p95}ms`); } } tools.log(`\nZUGFeRD to XRechnung conversion testing completed.`); });