- Change tap test signatures from async (t) => to async () => - Replace t.ok(), t.notOk(), t.equal() with expect() assertions - Fix import paths for helpers to use correct ../../helpers/ path - Update PerformanceTracker to use instance version - Add missing expect imports from tapbundle - Remove t.end() calls that are no longer needed - Ensure all tests have tap.start() for proper execution
320 lines
11 KiB
TypeScript
320 lines
11 KiB
TypeScript
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(); |