einvoice/test/suite/einvoice_standards-compliance/test.std-01.en16931-core.ts

463 lines
15 KiB
TypeScript
Raw Normal View History

2025-05-28 18:46:18 +00:00
import { expect, tap } from '@git.zone/tstest/tapbundle';
2025-05-26 04:04:51 +00:00
import { EInvoice } from '../../../ts/index.js';
2025-05-28 18:46:18 +00:00
tap.test('STD-01: EN16931 Core Compliance - should validate EN16931 core standard compliance', async () => {
console.log('Testing EN16931 core standard compliance...\n');
2025-05-26 04:04:51 +00:00
2025-05-28 18:46:18 +00:00
// Test 1: Mandatory fields compliance
const testMandatoryFields = async () => {
console.log('Test 1 - Mandatory fields compliance:');
// Test with missing invoice ID (BR-01 violation)
let errorCaught = false;
try {
const einvoice = new EInvoice();
einvoice.issueDate = new Date(2024, 0, 1);
// Missing invoiceId - should fail BR-01
2025-05-26 04:04:51 +00:00
2025-05-28 18:46:18 +00:00
einvoice.from = {
type: 'company',
name: 'Test Company',
description: 'EN16931 compliance test',
address: {
streetName: 'Test Street',
houseNumber: '1',
postalCode: '12345',
city: 'Test City',
country: 'DE'
2025-05-26 04:04:51 +00:00
},
2025-05-28 18:46:18 +00:00
status: 'active',
foundedDate: { year: 2020, month: 1, day: 1 },
registrationDetails: {
vatId: 'DE123456789',
registrationId: 'HRB 12345',
registrationName: 'Commercial Register'
2025-05-26 04:04:51 +00:00
}
2025-05-28 18:46:18 +00:00
};
2025-05-26 04:04:51 +00:00
2025-05-28 18:46:18 +00:00
einvoice.to = {
type: 'person',
name: 'Test',
surname: 'Customer',
salutation: 'Mr' as const,
sex: 'male' as const,
title: 'Doctor' as const,
description: 'Test customer',
address: {
streetName: 'Customer Street',
houseNumber: '2',
postalCode: '54321',
city: 'Customer City',
country: 'DE'
2025-05-26 04:04:51 +00:00
}
2025-05-28 18:46:18 +00:00
};
2025-05-26 04:04:51 +00:00
2025-05-28 18:46:18 +00:00
einvoice.items = [{
position: 1,
name: 'Test Item',
articleNumber: 'TEST-001',
unitType: 'EA',
unitQuantity: 1,
unitNetPrice: 100,
vatPercentage: 19
}];
await einvoice.toXmlString('ubl');
} catch (error) {
errorCaught = true;
const errorMessage = error.message;
console.log(` BR-01 (Invoice number mandatory): ${errorMessage.includes('BR-01') ? 'Enforced' : 'Detected'}`);
2025-05-26 04:04:51 +00:00
}
2025-05-28 18:46:18 +00:00
// Test complete valid invoice
let validInvoiceProcessed = false;
try {
const einvoice = new EInvoice();
einvoice.issueDate = new Date(2024, 0, 1);
einvoice.invoiceId = 'EN16931-001';
einvoice.from = {
type: 'company',
name: 'EN16931 Compliant Company',
description: 'EN16931 compliance test',
address: {
streetName: 'Compliant Street',
houseNumber: '1',
postalCode: '12345',
city: 'Compliant City',
country: 'DE'
2025-05-26 04:04:51 +00:00
},
2025-05-28 18:46:18 +00:00
status: 'active',
foundedDate: { year: 2020, month: 1, day: 1 },
registrationDetails: {
vatId: 'DE123456789',
registrationId: 'HRB 12345',
registrationName: 'Commercial Register'
2025-05-26 04:04:51 +00:00
}
2025-05-28 18:46:18 +00:00
};
2025-05-26 04:04:51 +00:00
2025-05-28 18:46:18 +00:00
einvoice.to = {
type: 'person',
name: 'Compliant',
surname: 'Customer',
salutation: 'Mr' as const,
sex: 'male' as const,
title: 'Doctor' as const,
description: 'Compliant customer',
address: {
streetName: 'Customer Street',
houseNumber: '2',
postalCode: '54321',
city: 'Customer City',
country: 'DE'
2025-05-26 04:04:51 +00:00
}
2025-05-28 18:46:18 +00:00
};
2025-05-26 04:04:51 +00:00
2025-05-28 18:46:18 +00:00
einvoice.items = [{
position: 1,
name: 'Compliant Item',
articleNumber: 'EN16931-001',
unitType: 'EA',
unitQuantity: 1,
unitNetPrice: 100,
vatPercentage: 19
}];
const xml = await einvoice.toXmlString('ubl');
validInvoiceProcessed = xml.includes('EN16931-001');
console.log(` Complete valid invoice: ${validInvoiceProcessed ? 'Processed' : 'Failed'}`);
} catch (error) {
console.log(` Complete valid invoice: Failed - ${error.message}`);
2025-05-26 04:04:51 +00:00
}
2025-05-28 18:46:18 +00:00
return { errorCaught, validInvoiceProcessed };
};
2025-05-26 04:04:51 +00:00
2025-05-28 18:46:18 +00:00
// Test 2: Business rules compliance
const testBusinessRulesCompliance = async () => {
console.log('\nTest 2 - Business rules compliance:');
const businessRuleTests = [
{
name: 'BR-06 (Seller name mandatory)',
test: async () => {
try {
const einvoice = new EInvoice();
einvoice.issueDate = new Date(2024, 0, 1);
einvoice.invoiceId = 'BR-06-TEST';
// Missing seller name
einvoice.from = {
type: 'company',
name: '', // Empty name - should fail BR-06
description: 'BR-06 test',
address: {
streetName: 'Test Street',
houseNumber: '1',
postalCode: '12345',
city: 'Test City',
country: 'DE'
},
status: 'active',
foundedDate: { year: 2020, month: 1, day: 1 },
registrationDetails: {
vatId: 'DE123456789',
registrationId: 'HRB 12345',
registrationName: 'Commercial Register'
}
};
einvoice.to = {
type: 'person',
name: 'Test',
surname: 'Customer',
salutation: 'Mr' as const,
sex: 'male' as const,
title: 'Doctor' as const,
description: 'Test customer',
address: {
streetName: 'Customer Street',
houseNumber: '2',
postalCode: '54321',
city: 'Customer City',
country: 'DE'
}
};
einvoice.items = [{
position: 1,
name: 'Test Item',
articleNumber: 'TEST-001',
unitType: 'EA',
unitQuantity: 1,
unitNetPrice: 100,
vatPercentage: 19
}];
await einvoice.toXmlString('ubl');
return false; // Should not reach here
} catch (error) {
return error.message.includes('BR-06') || error.message.includes('name');
}
2025-05-26 04:04:51 +00:00
}
2025-05-28 18:46:18 +00:00
},
{
name: 'BR-07 (Buyer name mandatory)',
test: async () => {
try {
const einvoice = new EInvoice();
einvoice.issueDate = new Date(2024, 0, 1);
einvoice.invoiceId = 'BR-07-TEST';
einvoice.from = {
type: 'company',
name: 'Test Company',
description: 'BR-07 test',
address: {
streetName: 'Test Street',
houseNumber: '1',
postalCode: '12345',
city: 'Test City',
country: 'DE'
},
status: 'active',
foundedDate: { year: 2020, month: 1, day: 1 },
registrationDetails: {
vatId: 'DE123456789',
registrationId: 'HRB 12345',
registrationName: 'Commercial Register'
}
};
// Missing buyer name
einvoice.to = {
type: 'person',
name: '', // Empty name - should fail BR-07
surname: 'Customer',
salutation: 'Mr' as const,
sex: 'male' as const,
title: 'Doctor' as const,
description: 'Test customer',
address: {
streetName: 'Customer Street',
houseNumber: '2',
postalCode: '54321',
city: 'Customer City',
country: 'DE'
}
};
einvoice.items = [{
position: 1,
name: 'Test Item',
articleNumber: 'TEST-001',
unitType: 'EA',
unitQuantity: 1,
unitNetPrice: 100,
vatPercentage: 19
}];
await einvoice.toXmlString('ubl');
return false; // Should not reach here
} catch (error) {
return error.message.includes('BR-07') || error.message.includes('name');
}
2025-05-26 04:04:51 +00:00
}
}
2025-05-28 18:46:18 +00:00
];
let rulesEnforced = 0;
for (const rule of businessRuleTests) {
try {
const enforced = await rule.test();
console.log(` ${rule.name}: ${enforced ? 'Enforced' : 'Not enforced'}`);
if (enforced) rulesEnforced++;
} catch (error) {
console.log(` ${rule.name}: Error - ${error.message}`);
2025-05-26 04:04:51 +00:00
}
}
2025-05-28 18:46:18 +00:00
return { rulesEnforced, totalRules: businessRuleTests.length };
};
2025-05-26 04:04:51 +00:00
2025-05-28 18:46:18 +00:00
// Test 3: Format-specific compliance
const testFormatCompliance = async () => {
console.log('\nTest 3 - Format-specific compliance:');
const formats = ['ubl', 'cii', 'xrechnung'];
let formatsWorking = 0;
for (const format of formats) {
try {
const einvoice = new EInvoice();
einvoice.issueDate = new Date(2024, 0, 1);
einvoice.invoiceId = `${format.toUpperCase()}-COMPLIANCE-001`;
2025-05-26 04:04:51 +00:00
2025-05-28 18:46:18 +00:00
einvoice.from = {
type: 'company',
name: `${format.toUpperCase()} Test Company`,
description: `Testing ${format} format compliance`,
address: {
streetName: 'Compliance Street',
houseNumber: '1',
postalCode: '12345',
city: 'Compliance City',
country: 'DE'
},
status: 'active',
foundedDate: { year: 2020, month: 1, day: 1 },
registrationDetails: {
vatId: 'DE123456789',
registrationId: 'HRB 12345',
registrationName: 'Commercial Register'
}
};
2025-05-26 04:04:51 +00:00
2025-05-28 18:46:18 +00:00
einvoice.to = {
type: 'person',
name: 'Compliance',
surname: 'Customer',
salutation: 'Mr' as const,
sex: 'male' as const,
title: 'Doctor' as const,
description: 'Compliance customer',
address: {
streetName: 'Customer Street',
houseNumber: '2',
postalCode: '54321',
city: 'Customer City',
country: 'DE'
}
};
einvoice.items = [{
position: 1,
name: `${format.toUpperCase()} Compliance Item`,
articleNumber: `${format.toUpperCase()}-001`,
unitType: 'EA',
unitQuantity: 1,
unitNetPrice: 100,
vatPercentage: 19
}];
2025-05-26 04:04:51 +00:00
2025-05-28 18:46:18 +00:00
const xml = await einvoice.toXmlString(format as any);
const success = xml.includes(`${format.toUpperCase()}-COMPLIANCE-001`);
console.log(` ${format.toUpperCase()} format: ${success ? 'Compliant' : 'Failed'}`);
if (success) formatsWorking++;
} catch (error) {
console.log(` ${format.toUpperCase()} format: Failed - ${error.message}`);
2025-05-26 04:04:51 +00:00
}
}
2025-05-28 18:46:18 +00:00
return { formatsWorking, totalFormats: formats.length };
};
2025-05-26 04:04:51 +00:00
2025-05-28 18:46:18 +00:00
// Test 4: EN16931 mandatory elements presence
const testMandatoryElementsPresence = async () => {
console.log('\nTest 4 - Mandatory elements presence:');
const einvoice = new EInvoice();
einvoice.issueDate = new Date(2024, 0, 1);
einvoice.invoiceId = 'MANDATORY-ELEMENTS-001';
einvoice.from = {
type: 'company',
name: 'Mandatory Elements Company',
description: 'Testing mandatory elements',
address: {
streetName: 'Mandatory Street',
houseNumber: '1',
postalCode: '12345',
city: 'Mandatory City',
country: 'DE'
},
status: 'active',
foundedDate: { year: 2020, month: 1, day: 1 },
registrationDetails: {
vatId: 'DE123456789',
registrationId: 'HRB 12345',
registrationName: 'Commercial Register'
2025-05-26 04:04:51 +00:00
}
2025-05-28 18:46:18 +00:00
};
einvoice.to = {
type: 'person',
name: 'Mandatory',
surname: 'Customer',
salutation: 'Mr' as const,
sex: 'male' as const,
title: 'Doctor' as const,
description: 'Mandatory customer',
address: {
streetName: 'Customer Street',
houseNumber: '2',
postalCode: '54321',
city: 'Customer City',
country: 'DE'
2025-05-26 04:04:51 +00:00
}
2025-05-28 18:46:18 +00:00
};
einvoice.items = [{
position: 1,
name: 'Mandatory Item',
articleNumber: 'MANDATORY-001',
unitType: 'EA',
unitQuantity: 1,
unitNetPrice: 100,
vatPercentage: 19
}];
try {
const xml = await einvoice.toXmlString('ubl');
2025-05-26 04:04:51 +00:00
2025-05-28 18:46:18 +00:00
// Check for EN16931 mandatory elements
const mandatoryElements = [
'cbc:ID', // BT-1 Invoice number
'cbc:IssueDate', // BT-2 Invoice issue date
'cac:AccountingSupplierParty', // BG-4 Seller
'cac:AccountingCustomerParty', // BG-7 Buyer
'cac:InvoiceLine' // BG-25 Invoice line
2025-05-26 04:04:51 +00:00
];
2025-05-28 18:46:18 +00:00
let elementsPresent = 0;
for (const element of mandatoryElements) {
if (xml.includes(element)) {
elementsPresent++;
console.log(` ${element}: Present`);
} else {
console.log(` ${element}: Missing`);
2025-05-26 04:04:51 +00:00
}
}
2025-05-28 18:46:18 +00:00
return { elementsPresent, totalElements: mandatoryElements.length };
} catch (error) {
console.log(` Mandatory elements test failed: ${error.message}`);
return { elementsPresent: 0, totalElements: 5 };
2025-05-26 04:04:51 +00:00
}
};
2025-05-28 18:46:18 +00:00
// Run all tests
const result1 = await testMandatoryFields();
const result2 = await testBusinessRulesCompliance();
const result3 = await testFormatCompliance();
const result4 = await testMandatoryElementsPresence();
console.log('\n=== EN16931 Core Compliance Summary ===');
console.log(`Mandatory field validation: ${result1.errorCaught ? 'Working' : 'Not implemented'}`);
console.log(`Valid invoice processing: ${result1.validInvoiceProcessed ? 'Working' : 'Failed'}`);
console.log(`Business rules enforced: ${result2.rulesEnforced}/${result2.totalRules}`);
console.log(`Format compliance: ${result3.formatsWorking}/${result3.totalFormats}`);
console.log(`Mandatory elements: ${result4.elementsPresent}/${result4.totalElements}`);
// Test passes if core EN16931 functionality works
const coreValidationWorks = result1.errorCaught && result1.validInvoiceProcessed; // BR validation + valid processing
const businessRulesWork = result2.rulesEnforced === result2.totalRules; // All business rules enforced
const mandatoryElementsWork = result4.elementsPresent === result4.totalElements; // All mandatory elements present
expect(coreValidationWorks).toBeTrue(); // Core EN16931 validation must work
expect(businessRulesWork).toBeTrue(); // Business rules must be enforced
expect(mandatoryElementsWork).toBeTrue(); // All mandatory elements must be present
});
2025-05-26 04:04:51 +00:00
tap.start();