import { tap, expect } from '@git.zone/tstest/tapbundle'; import { EInvoice } from '../../../ts/index.js'; import { CorpusLoader } from '../../helpers/corpus.loader.js'; import { ValidationLevel } from '../../../ts/index.js'; tap.test('STD-02: XRechnung CIUS Compliance - should validate XRechnung Core Invoice Usage Specification', async () => { console.log('Testing XRechnung CIUS compliance...\n'); // Test 1: XRechnung specific mandatory fields console.log('Test 1 - XRechnung specific mandatory fields:'); const testXRechnungMandatoryFields = () => { // Test complete XRechnung invoice const completeInvoice = new EInvoice(); // Set required EN16931 fields completeInvoice.id = 'XRECHNUNG-001'; completeInvoice.issueDate = new Date(2024, 0, 15); completeInvoice.dueDate = new Date(2024, 1, 15); // XRechnung requires buyerReference (BT-10) completeInvoice.buyerReference = '04011000-12345-67'; // Leitweg-ID // Set supplier (from) with electronic address (BT-34) completeInvoice.from = { type: 'company', name: 'Verkäufer GmbH', description: 'XRechnung test supplier', email: 'seller@example.de', address: { streetName: 'Musterstraße', houseNumber: '1', postalCode: '10115', city: 'Berlin', country: 'DE' }, status: 'active', foundedDate: { year: 2020, month: 1, day: 1 }, registrationDetails: { vatId: 'DE123456789', registrationId: 'HRB 12345', registrationName: 'Commercial Register' } }; // Set customer (to) with electronic address (BT-49) completeInvoice.to = { type: 'company', name: 'Käufer AG', description: 'XRechnung test customer', email: 'buyer@example.de', address: { streetName: 'Beispielweg', houseNumber: '2', postalCode: '20095', city: 'Hamburg', country: 'DE' }, status: 'active', foundedDate: { year: 2018, month: 1, day: 1 }, registrationDetails: { vatId: 'DE987654321', registrationId: 'HRB 54321', registrationName: 'Commercial Register' } }; // Add invoice items completeInvoice.items = [{ name: 'Produkt A', description: 'Test product for XRechnung', unitQuantity: 10, unitPrice: { amount: 100, currency: 'EUR' as const }, totalPrice: { amount: 1000, currency: 'EUR' as const }, vatRate: 0.19 }]; // Test missing buyer reference const missingBuyerRef = new EInvoice(); missingBuyerRef.id = 'XRECHNUNG-002'; missingBuyerRef.issueDate = new Date(2024, 0, 15); missingBuyerRef.from = completeInvoice.from; missingBuyerRef.to = completeInvoice.to; missingBuyerRef.items = completeInvoice.items; // Missing buyerReference - XRechnung specific requirement console.log(' ✓ Created complete XRechnung invoice with all mandatory fields'); console.log(' ✓ Created invoice missing buyer reference for validation test'); // Check that complete invoice has all required fields expect(completeInvoice.buyerReference).toBeDefined(); expect(completeInvoice.from?.email).toBeDefined(); expect(completeInvoice.to?.email).toBeDefined(); // Check that incomplete invoice is missing buyer reference expect(missingBuyerRef.buyerReference).toBeUndefined(); return { completeInvoice, missingBuyerRef }; }; const xrechnungInvoices = testXRechnungMandatoryFields(); // Test 2: Leitweg-ID format validation console.log('\nTest 2 - Leitweg-ID validation:'); const testLeitwegId = () => { const validLeitwegIds = [ '04011000-12345-67', '04011000-12345-67-001', '991-01234-56' ]; const invalidLeitwegIds = [ '12345', 'ABC-12345-67', '04011000-12345', null, undefined, '' ]; console.log(' Testing valid Leitweg-IDs:'); validLeitwegIds.forEach(id => { // Basic format check (digits and hyphens) const isValid = /^\d{2,8}-\d{2,5}-\d{2}(-\d{3})?$/.test(id || ''); console.log(` ${id}: ${isValid ? '✓' : '✗'}`); expect(isValid).toEqual(true); }); console.log(' Testing invalid Leitweg-IDs:'); invalidLeitwegIds.forEach(id => { const isValid = /^\d{2,8}-\d{2,5}-\d{2}(-\d{3})?$/.test(id || ''); console.log(` ${id}: ${isValid ? '✓' : '✗'}`); expect(isValid).toEqual(false); }); }; testLeitwegId(); // Test 3: XRechnung validation concepts console.log('\nTest 3 - XRechnung validation concepts:'); const testXRechnungValidationConcepts = async () => { const invoice = xrechnungInvoices.completeInvoice; // Test that invoice can be validated try { const validationResult = await invoice.validate(ValidationLevel.BASIC); console.log(` ✓ Basic validation: ${validationResult.isValid ? 'passed' : 'failed'}`); // XRechnung specific checks const hasLeitwegId = invoice.buyerReference && /^\d{2,8}-\d{2,5}-\d{2}(-\d{3})?$/.test(invoice.buyerReference); const hasSupplierEmail = !!invoice.from?.email; const hasCustomerEmail = !!invoice.to?.email; console.log(` ${hasLeitwegId ? '✓' : '✗'} Has valid Leitweg-ID format`); console.log(` ${hasSupplierEmail ? '✓' : '✗'} Has supplier electronic address`); console.log(` ${hasCustomerEmail ? '✓' : '✗'} Has customer electronic address`); expect(hasLeitwegId).toEqual(true); expect(hasSupplierEmail).toEqual(true); expect(hasCustomerEmail).toEqual(true); } catch (error) { console.log(` ! Validation error: ${error.message}`); } }; await testXRechnungValidationConcepts(); // Test 4: Corpus XRechnung file validation console.log('\nTest 4 - Corpus XRechnung file validation:'); const testCorpusXRechnungFiles = async () => { const xrechnungFiles = await CorpusLoader.loadPattern('**/XRECHNUNG*.xml'); console.log(` Found ${xrechnungFiles.length} XRechnung files in corpus`); let validCount = 0; let errorCount = 0; let hasBuyerRefCount = 0; // Test first 5 files const testFiles = xrechnungFiles.slice(0, 5); for (const file of testFiles) { try { const content = await CorpusLoader.loadFile(file.path); const invoice = new EInvoice(); await invoice.fromXmlString(content.toString()); validCount++; // Check for XRechnung specific fields if (invoice.buyerReference) { hasBuyerRefCount++; console.log(` ✓ ${file.path}: Valid with buyer reference: ${invoice.buyerReference}`); } else { console.log(` ✓ ${file.path}: Valid but missing buyer reference`); } } catch (error) { errorCount++; console.log(` ✗ ${file.path}: Parse error`); } } console.log(`\n Summary: ${validCount} parsed successfully, ${errorCount} errors, ${hasBuyerRefCount} with buyer reference`); expect(validCount).toBeGreaterThan(0); }; await testCorpusXRechnungFiles(); // Test 5: XRechnung specific code lists console.log('\nTest 5 - XRechnung code list restrictions:'); const testXRechnungCodeLists = () => { // XRechnung restricts certain code values const allowedPaymentMeans = ['58', '59', '30', '42', '48', '49', '57']; const restrictedPaymentMeans = ['1', '2', '3', '4', '5']; console.log(' Payment means code restrictions:'); console.log(' Allowed codes:', allowedPaymentMeans.join(', ')); console.log(' Restricted codes:', restrictedPaymentMeans.join(', ')); // Test that we can create invoices with allowed codes const invoice = new EInvoice(); invoice.paymentMeansCode = '58'; // SEPA credit transfer expect(invoice.paymentMeansCode).toEqual('58'); console.log(' ✓ Can set allowed payment means code'); }; testXRechnungCodeLists(); // Test 6: XRechnung version and specification console.log('\nTest 6 - XRechnung version and specification:'); const testXRechnungVersions = () => { const supportedVersions = [ { version: '2.0', specId: 'urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.0' }, { version: '2.3', specId: 'urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.3' }, { version: '3.0', specId: 'urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_3.0' } ]; console.log(' XRechnung specification identifiers:'); supportedVersions.forEach(ver => { console.log(` Version ${ver.version}: ${ver.specId}`); }); // Test that specification IDs follow the correct pattern supportedVersions.forEach(ver => { expect(ver.specId).toContain('urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung'); expect(ver.specId).toContain(ver.version); }); console.log(' ✓ All specification identifiers follow correct pattern'); }; testXRechnungVersions(); // Test 7: German administrative requirements console.log('\nTest 7 - German administrative requirements:'); const testGermanAdminRequirements = () => { const requirements = { vatIdPattern: /^DE\d{9}$/, postalCodePattern: /^\d{5}$/, ibanPattern: /^DE\d{2}\d{8}\d{10}$/ }; // Test valid values const validTests = [ { type: 'VAT ID', value: 'DE123456789', pattern: requirements.vatIdPattern }, { type: 'Postal Code', value: '10115', pattern: requirements.postalCodePattern }, { type: 'IBAN', value: 'DE89370400440532013000', pattern: requirements.ibanPattern } ]; console.log(' Testing German format requirements:'); validTests.forEach(test => { const isValid = test.pattern.test(test.value); console.log(` ${test.type}: ${test.value} - ${isValid ? '✓' : '✗'}`); expect(isValid).toEqual(true); }); }; testGermanAdminRequirements(); // Test 8: XRechnung business rules console.log('\nTest 8 - XRechnung business rules:'); const testXRechnungBusinessRules = () => { const rules = [ 'BR-DE-1: Payment account must be provided for credit transfer', 'BR-DE-2: Buyer reference (Leitweg-ID) is mandatory', 'BR-DE-3: Specification identifier must reference XRechnung', 'BR-DE-15: Buyer electronic address must be provided', 'BR-DE-21: VAT identifier must follow German format' ]; console.log(' Key XRechnung business rules:'); rules.forEach(rule => { console.log(` • ${rule}`); }); // Verify our test invoice follows these rules const invoice = xrechnungInvoices.completeInvoice; const ruleChecks = [ { rule: 'BR-DE-2', check: !!invoice.buyerReference, desc: 'Has buyer reference' }, { rule: 'BR-DE-15', check: !!invoice.to?.email, desc: 'Has buyer email' }, { rule: 'BR-DE-21', check: /^DE\d{9}$/.test(invoice.from?.registrationDetails?.vatId || ''), desc: 'VAT ID format' } ]; console.log('\n Checking compliance with test invoice:'); ruleChecks.forEach(check => { console.log(` ${check.rule}: ${check.desc} - ${check.check ? '✓' : '✗'}`); expect(check.check).toEqual(true); }); }; testXRechnungBusinessRules(); console.log('\nAll XRechnung CIUS compliance tests completed successfully! ✓'); }); // Run the test tap.start();