- Added validation types for EN16931 compliance in `validation.types.ts`, including interfaces for `ValidationResult`, `ValidationOptions`, and `ValidationReport`. - Introduced `VATCategoriesValidator` in `vat-categories.validator.ts` to validate VAT categories according to EN16931 rules, including detailed checks for standard, zero-rated, exempt, reverse charge, intra-community, export, and out-of-scope services. - Enhanced `IEInvoiceMetadata` interface in `en16931-metadata.ts` to include additional fields required for full standards compliance, such as delivery information, payment information, allowances, and charges. - Implemented helper methods for VAT calculations and validation logic to ensure accurate compliance with EN16931 standards.
172 lines
6.0 KiB
TypeScript
172 lines
6.0 KiB
TypeScript
import { expect, tap } from '@git.zone/tstest/tapbundle/index.js';
|
|
import * as path from 'path';
|
|
import * as fs from 'fs';
|
|
|
|
// Import conformance harness
|
|
import { ConformanceTestHarness, runConformanceTests } from '../ts/formats/validation/conformance.harness.js';
|
|
|
|
tap.test('Conformance Test Harness - initialization', async () => {
|
|
const harness = new ConformanceTestHarness();
|
|
expect(harness).toBeInstanceOf(ConformanceTestHarness);
|
|
});
|
|
|
|
tap.test('Conformance Test Harness - load test samples', async () => {
|
|
const harness = new ConformanceTestHarness();
|
|
|
|
// Check if test-samples directory exists
|
|
const samplesDir = path.join(process.cwd(), 'test-samples');
|
|
if (fs.existsSync(samplesDir)) {
|
|
await harness.loadTestSamples(samplesDir);
|
|
console.log('Test samples loaded successfully');
|
|
} else {
|
|
console.log('Test samples directory not found - skipping');
|
|
}
|
|
});
|
|
|
|
tap.test('Conformance Test Harness - run minimal test', async (tools) => {
|
|
const harness = new ConformanceTestHarness();
|
|
|
|
// Create a minimal test sample
|
|
const minimalUBL = `<?xml version="1.0" encoding="UTF-8"?>
|
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
|
|
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
|
|
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
|
|
<cbc:CustomizationID>urn:cen.eu:en16931:2017</cbc:CustomizationID>
|
|
<cbc:ID>TEST-001</cbc:ID>
|
|
<cbc:IssueDate>2025-01-11</cbc:IssueDate>
|
|
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
|
|
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
|
|
<cac:AccountingSupplierParty>
|
|
<cac:Party>
|
|
<cac:PartyName>
|
|
<cbc:Name>Test Seller</cbc:Name>
|
|
</cac:PartyName>
|
|
<cac:PostalAddress>
|
|
<cbc:StreetName>Test Street 1</cbc:StreetName>
|
|
<cbc:CityName>Test City</cbc:CityName>
|
|
<cbc:PostalZone>12345</cbc:PostalZone>
|
|
<cac:Country>
|
|
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
|
|
</cac:Country>
|
|
</cac:PostalAddress>
|
|
</cac:Party>
|
|
</cac:AccountingSupplierParty>
|
|
<cac:AccountingCustomerParty>
|
|
<cac:Party>
|
|
<cac:PartyName>
|
|
<cbc:Name>Test Buyer</cbc:Name>
|
|
</cac:PartyName>
|
|
<cac:PostalAddress>
|
|
<cbc:StreetName>Test Street 2</cbc:StreetName>
|
|
<cbc:CityName>Test City</cbc:CityName>
|
|
<cbc:PostalZone>54321</cbc:PostalZone>
|
|
<cac:Country>
|
|
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
|
|
</cac:Country>
|
|
</cac:PostalAddress>
|
|
</cac:Party>
|
|
</cac:AccountingCustomerParty>
|
|
<cac:TaxTotal>
|
|
<cbc:TaxAmount currencyID="EUR">19.00</cbc:TaxAmount>
|
|
<cac:TaxSubtotal>
|
|
<cbc:TaxableAmount currencyID="EUR">100.00</cbc:TaxableAmount>
|
|
<cbc:TaxAmount currencyID="EUR">19.00</cbc:TaxAmount>
|
|
<cac:TaxCategory>
|
|
<cbc:ID>S</cbc:ID>
|
|
<cbc:Percent>19</cbc:Percent>
|
|
</cac:TaxCategory>
|
|
</cac:TaxSubtotal>
|
|
</cac:TaxTotal>
|
|
<cac:LegalMonetaryTotal>
|
|
<cbc:TaxExclusiveAmount currencyID="EUR">100.00</cbc:TaxExclusiveAmount>
|
|
<cbc:TaxInclusiveAmount currencyID="EUR">119.00</cbc:TaxInclusiveAmount>
|
|
</cac:LegalMonetaryTotal>
|
|
<cac:InvoiceLine>
|
|
<cbc:ID>1</cbc:ID>
|
|
<cbc:InvoicedQuantity unitCode="C62">1</cbc:InvoicedQuantity>
|
|
<cbc:LineExtensionAmount currencyID="EUR">100.00</cbc:LineExtensionAmount>
|
|
<cac:Item>
|
|
<cbc:Name>Test Product</cbc:Name>
|
|
<cac:ClassifiedTaxCategory>
|
|
<cbc:ID>S</cbc:ID>
|
|
<cbc:Percent>19</cbc:Percent>
|
|
</cac:ClassifiedTaxCategory>
|
|
</cac:Item>
|
|
<cac:Price>
|
|
<cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount>
|
|
</cac:Price>
|
|
</cac:InvoiceLine>
|
|
</Invoice>`;
|
|
|
|
// Create temporary test directory
|
|
const tempDir = path.join(process.cwd(), '.nogit', 'test-conformance');
|
|
if (!fs.existsSync(tempDir)) {
|
|
fs.mkdirSync(tempDir, { recursive: true });
|
|
}
|
|
|
|
// Write test file
|
|
const testFile = path.join(tempDir, 'minimal-test.xml');
|
|
fs.writeFileSync(testFile, minimalUBL);
|
|
|
|
// Create test sample metadata
|
|
const testSamples = [{
|
|
id: 'minimal-test',
|
|
name: 'minimal-test.xml',
|
|
path: testFile,
|
|
format: 'UBL' as const,
|
|
standard: 'EN16931',
|
|
expectedValid: false, // We expect some validation errors
|
|
description: 'Minimal test invoice'
|
|
}];
|
|
|
|
// Load test samples manually
|
|
(harness as any).testSamples = testSamples;
|
|
|
|
// Run conformance test
|
|
await harness.runConformanceTests();
|
|
|
|
// Generate coverage matrix
|
|
const coverage = harness.generateCoverageMatrix();
|
|
console.log(`Coverage: ${coverage.coveragePercentage.toFixed(1)}%`);
|
|
console.log(`Rules covered: ${coverage.coveredRules}/${coverage.totalRules}`);
|
|
|
|
// Clean up
|
|
fs.unlinkSync(testFile);
|
|
});
|
|
|
|
tap.test('Conformance Test Harness - coverage report generation', async () => {
|
|
const harness = new ConformanceTestHarness();
|
|
|
|
// Generate empty coverage report
|
|
const coverage = harness.generateCoverageMatrix();
|
|
|
|
expect(coverage.totalRules).toBeGreaterThan(100);
|
|
expect(coverage.coveredRules).toBeGreaterThanOrEqual(0);
|
|
expect(coverage.coveragePercentage).toBeGreaterThanOrEqual(0);
|
|
expect(coverage.byCategory.document.total).toBeGreaterThan(0);
|
|
expect(coverage.byCategory.calculation.total).toBeGreaterThan(0);
|
|
expect(coverage.byCategory.vat.total).toBeGreaterThan(0);
|
|
});
|
|
|
|
tap.test('Conformance Test Harness - full test suite', async (tools) => {
|
|
tools.timeout(60000); // 60 seconds timeout for full test
|
|
|
|
const samplesDir = path.join(process.cwd(), 'test-samples');
|
|
if (!fs.existsSync(samplesDir)) {
|
|
console.log('Test samples not found - skipping full conformance test');
|
|
console.log('Run: npm run download-test-samples');
|
|
return;
|
|
}
|
|
|
|
// Run full conformance test
|
|
console.log('\n=== Running Full Conformance Test Suite ===\n');
|
|
await runConformanceTests(samplesDir, true);
|
|
|
|
// Check if HTML report was generated
|
|
const reportPath = path.join(process.cwd(), 'coverage-report.html');
|
|
if (fs.existsSync(reportPath)) {
|
|
console.log(`\n✅ HTML report generated: ${reportPath}`);
|
|
}
|
|
});
|
|
|
|
export default tap; |