import { tap, expect } from '@git.zone/tstest/tapbundle'; import * as plugins from '../../../ts/plugins.js'; import { EInvoice } from '../../../ts/index.js'; import { CorpusLoader } from '../../helpers/corpus.loader.js'; 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) => { 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 BUYER-REF-123 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 console.log('Testing ZUGFeRD to XRechnung conversion...'); try { const convertedXml = await invoice.toXmlString('UBL'); if (convertedXml) { console.log('✓ ZUGFeRD to XRechnung conversion completed'); // Verify the converted format 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') }; console.log('XRechnung Format Verification:'); console.log(` XRechnung Customization: ${xrechnungChecks.hasXrechnungCustomization}`); console.log(` UBL Namespace: ${xrechnungChecks.hasUblNamespace}`); console.log(` PEPPOL Profile: ${xrechnungChecks.hasPeppolProfile}`); console.log(` Original ID preserved: ${xrechnungChecks.hasOriginalId}`); console.log(` German VAT preserved: ${xrechnungChecks.hasGermanVat}`); console.log(` Euro currency preserved: ${xrechnungChecks.hasEurourrency}`); if (xrechnungChecks.hasUblNamespace || xrechnungChecks.hasXrechnungCustomization) { console.log('✓ Valid XRechnung format structure detected'); } else { console.log('⚠ XRechnung format structure not clearly detected'); } // Validate the converted invoice by parsing it try { const convertedInvoice = new EInvoice(); await convertedInvoice.fromXmlString(convertedXml); const validationResult = await convertedInvoice.validate(); if (validationResult.valid) { console.log('✓ Converted XRechnung invoice passes validation'); } else { console.log(`⚠ Converted XRechnung validation issues: ${validationResult.errors?.length || 0} errors`); if (validationResult.errors && validationResult.errors.length > 0) { console.log(` First error: ${validationResult.errors[0].message}`); } } } catch (validationError) { console.log(`⚠ Converted XRechnung validation failed: ${validationError.message}`); } } else { console.log('⚠ ZUGFeRD to XRechnung conversion returned no result'); } } catch (conversionError) { console.log(`⚠ ZUGFeRD to XRechnung conversion failed: ${conversionError.message}`); } } catch (error) { console.log(`Basic ZUGFeRD to XRechnung conversion test failed: ${error.message}`); } // Conversion test completed }); tap.test('CONV-03: ZUGFeRD to XRechnung Conversion - Profile Adaptation', async (tools) => { // 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) { console.log(`Testing ${profileTest.name}...`); try { const invoice = new EInvoice(); const parseResult = await invoice.fromXmlString(profileTest.zugferdXml); if (parseResult) { try { const convertedXml = await invoice.toXmlString('UBL'); if (convertedXml) { console.log(`✓ ${profileTest.name} conversion completed`); // Check profile-specific adaptations const profileAdaptations = { hasXrechnungProfile: convertedXml.includes('xrechnung') || convertedXml.includes('XRechnung'), retainsOriginalId: convertedXml.includes('TO-XRECHNUNG-001'), hasRequiredStructure: convertedXml.includes(' { // 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) { console.log('Testing German compliance requirements during conversion...'); try { const convertedXml = await invoice.toXmlString('UBL'); if (convertedXml) { // 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') }; console.log('German Compliance Verification:'); console.log(` Buyer reference preserved: ${germanComplianceChecks.hasBuyerReference}`); console.log(` Payment reference preserved: ${germanComplianceChecks.hasPaymentReference}`); console.log(` German VAT number preserved: ${germanComplianceChecks.hasGermanVatNumber}`); console.log(` German addresses preserved: ${germanComplianceChecks.hasGermanAddresses}`); console.log(` German postal codes preserved: ${germanComplianceChecks.hasGermanPostCodes}`); console.log(` Euro currency preserved: ${germanComplianceChecks.hasEuroCurrency}`); console.log(` Standard VAT rate preserved: ${germanComplianceChecks.hasStandardVatRate}`); console.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; console.log(`German compliance score: ${complianceScore}/${totalChecks} (${compliancePercentage.toFixed(1)}%)`); if (compliancePercentage >= 80) { console.log('✓ Good German compliance maintained'); } else { console.log('⚠ German compliance issues detected'); } } else { console.log('⚠ German compliance conversion returned no result'); } } catch (convError) { console.log(`⚠ German compliance conversion failed: ${convError.message}`); } } else { console.log('⚠ German compliance test - ZUGFeRD parsing failed'); } } catch (error) { console.log(`German compliance test failed: ${error.message}`); } // German compliance test completed }); tap.test('CONV-03: ZUGFeRD to XRechnung Conversion - Corpus Testing', { timeout: testTimeout }, async (tools) => { let processedFiles = 0; let successfulConversions = 0; let conversionErrors = 0; let totalConversionTime = 0; try { const zugferdFiles = await CorpusLoader.getFiles('ZUGFERD_V2'); console.log(`Testing ZUGFeRD to XRechnung conversion with ${zugferdFiles.length} ZUGFeRD files`); if (zugferdFiles.length === 0) { console.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 try { const convertedXml = await invoice.toXmlString('UBL'); const fileConversionTime = Date.now() - fileConversionStart; totalConversionTime += fileConversionTime; if (convertedXml) { successfulConversions++; console.log(`✓ ${fileName}: Converted to XRechnung (${fileConversionTime}ms)`); // Quick validation of converted content if (convertedXml && convertedXml.length > 100) { console.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)) { console.log(` ✓ XRechnung characteristics detected`); } } } else { conversionErrors++; console.log(`⚠ ${fileName}: Conversion returned no result`); } } catch (convError) { conversionErrors++; console.log(`⚠ ${fileName}: Conversion failed - ${convError.message}`); } } else { conversionErrors++; console.log(`⚠ ${fileName}: Failed to parse original ZUGFeRD`); } } catch (error) { conversionErrors++; const fileConversionTime = Date.now() - fileConversionStart; totalConversionTime += fileConversionTime; console.log(`✗ ${fileName}: Conversion failed - ${error.message}`); } } // Calculate statistics const successRate = processedFiles > 0 ? (successfulConversions / processedFiles) * 100 : 0; const averageConversionTime = processedFiles > 0 ? totalConversionTime / processedFiles : 0; console.log(`\nZUGFeRD to XRechnung Conversion Summary:`); console.log(`- Files processed: ${processedFiles}`); console.log(`- Successful conversions: ${successfulConversions} (${successRate.toFixed(1)}%)`); console.log(`- Conversion errors: ${conversionErrors}`); console.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) { console.log(`ZUGFeRD to XRechnung corpus testing failed: ${error.message}`); throw error; } console.log(`ZUGFeRD to XRechnung corpus testing completed`); }); // Performance summary test removed - PerformanceTracker not configured for these tests export default tap.start();