Files
einvoice/test/test.xrechnung-validator.ts
Juergen Kunz cbb297b0b1 feat: Implement PEPPOL and XRechnung validators for compliance with e-invoice specifications
- Added PeppolValidator class to validate PEPPOL BIS 3.0 invoices, including checks for endpoint IDs, document type IDs, process IDs, party identification, and business rules.
- Implemented validation for GLN check digits, document types, and transport protocols specific to PEPPOL.
- Added XRechnungValidator class to validate XRechnung 3.0 invoices, focusing on German-specific requirements such as Leitweg-ID, payment details, seller contact, and tax registration.
- Included validation for IBAN and BIC formats, ensuring compliance with SEPA regulations.
- Established methods for checking B2G invoice indicators and validating mandatory fields for both validators.
2025-08-11 18:07:01 +00:00

368 lines
11 KiB
TypeScript

import { tap, expect } from '@git.zone/tstest/tapbundle';
import { XRechnungValidator } from '../ts/formats/validation/xrechnung.validator.js';
import type { EInvoice } from '../ts/einvoice.js';
tap.test('XRechnungValidator - Leitweg-ID validation', async () => {
const validator = XRechnungValidator.create();
// Create test invoice with XRechnung profile
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-001',
metadata: {
profileId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
buyerReference: '04-123456789012-01'
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Valid Leitweg-ID should pass
const leitwegErrors = results.filter(r => r.ruleId === 'XR-DE-01');
expect(leitwegErrors).toHaveLength(0);
});
tap.test('XRechnungValidator - Invalid Leitweg-ID', async () => {
const validator = XRechnungValidator.create();
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-002',
metadata: {
profileId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
buyerReference: '4-12345-1' // Invalid format
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Should have Leitweg-ID format error
const leitwegErrors = results.filter(r => r.ruleId === 'XR-DE-01');
expect(leitwegErrors).toHaveLength(1);
expect(leitwegErrors[0].severity).toEqual('error');
});
tap.test('XRechnungValidator - IBAN validation', async () => {
const validator = XRechnungValidator.create();
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-003',
metadata: {
profileId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
buyerReference: 'REF-123',
extensions: {
paymentMeans: [
{
type: 'SEPA',
iban: 'DE89370400440532013000', // Valid German IBAN
bic: 'COBADEFFXXX'
}
]
}
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Valid IBAN should pass
const ibanErrors = results.filter(r => r.ruleId === 'XR-DE-19');
expect(ibanErrors).toHaveLength(0);
});
tap.test('XRechnungValidator - Invalid IBAN checksum', async () => {
const validator = XRechnungValidator.create();
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-004',
metadata: {
profileId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
buyerReference: 'REF-124',
extensions: {
paymentMeans: [
{
type: 'SEPA',
iban: 'DE89370400440532013001' // Invalid checksum
}
]
}
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Should have IBAN checksum error
const ibanErrors = results.filter(r => r.ruleId === 'XR-DE-19');
expect(ibanErrors).toHaveLength(1);
expect(ibanErrors[0].message).toInclude('Invalid IBAN checksum');
});
tap.test('XRechnungValidator - BIC validation', async () => {
const validator = XRechnungValidator.create();
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-005',
metadata: {
profileId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
buyerReference: 'REF-125',
extensions: {
paymentMeans: [
{
type: 'SEPA',
iban: 'DE89370400440532013000',
bic: 'COBADEFF' // Valid 8-character BIC
}
]
}
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Valid BIC should pass
const bicErrors = results.filter(r => r.ruleId === 'XR-DE-20');
expect(bicErrors).toHaveLength(0);
});
tap.test('XRechnungValidator - Invalid BIC format', async () => {
const validator = XRechnungValidator.create();
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-006',
metadata: {
profileId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
buyerReference: 'REF-126',
extensions: {
paymentMeans: [
{
type: 'SEPA',
iban: 'DE89370400440532013000',
bic: 'INVALID' // Invalid BIC format
}
]
}
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Should have BIC format error
const bicErrors = results.filter(r => r.ruleId === 'XR-DE-20');
expect(bicErrors).toHaveLength(1);
expect(bicErrors[0].message).toInclude('Invalid BIC format');
});
tap.test('XRechnungValidator - Mandatory buyer reference', async () => {
const validator = XRechnungValidator.create();
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-007',
metadata: {
profileId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0'
// Missing buyerReference
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Should have mandatory buyer reference error
const refErrors = results.filter(r => r.ruleId === 'XR-DE-15');
expect(refErrors).toHaveLength(1);
expect(refErrors[0].severity).toEqual('error');
});
tap.test('XRechnungValidator - Seller contact validation', async () => {
const validator = XRechnungValidator.create();
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-008',
metadata: {
profileId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
buyerReference: 'REF-127',
extensions: {
sellerContact: {
name: 'John Doe',
email: 'john.doe@example.com',
phone: '+49 30 12345678'
}
}
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Valid seller contact should pass
const contactErrors = results.filter(r => r.ruleId === 'XR-DE-02');
expect(contactErrors).toHaveLength(0);
});
tap.test('XRechnungValidator - Missing seller contact', async () => {
const validator = XRechnungValidator.create();
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-009',
metadata: {
profileId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
buyerReference: 'REF-128'
// Missing sellerContact
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Should have missing seller contact error
const contactErrors = results.filter(r => r.ruleId === 'XR-DE-02');
expect(contactErrors).toHaveLength(1);
expect(contactErrors[0].severity).toEqual('error');
});
tap.test('XRechnungValidator - German VAT ID validation', async () => {
const validator = XRechnungValidator.create();
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-010',
from: {
type: 'company' as const,
name: 'Test Company',
registrationDetails: {
vatId: 'DE123456789' // Valid German VAT ID format
}
},
metadata: {
profileId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
buyerReference: 'REF-129',
sellerTaxId: 'DE123456789'
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Valid German VAT ID should pass
const vatErrors = results.filter(r => r.ruleId === 'XR-DE-04');
expect(vatErrors).toHaveLength(0);
});
tap.test('XRechnungValidator - Invalid German VAT ID', async () => {
const validator = XRechnungValidator.create();
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-011',
metadata: {
profileId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
buyerReference: 'REF-130',
sellerTaxId: 'DE12345' // Invalid - too short
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Should have invalid VAT ID error
const vatErrors = results.filter(r => r.ruleId === 'XR-DE-04');
expect(vatErrors).toHaveLength(1);
expect(vatErrors[0].message).toInclude('Invalid German VAT ID format');
});
tap.test('XRechnungValidator - Non-XRechnung invoice', async () => {
const validator = XRechnungValidator.create();
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-012',
metadata: {
profileId: 'urn:cen.eu:en16931:2017' // Not XRechnung
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Should not validate non-XRechnung invoices
expect(results).toHaveLength(0);
});
tap.test('XRechnungValidator - SEPA country validation', async () => {
const validator = XRechnungValidator.create();
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-013',
metadata: {
profileId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
buyerReference: 'REF-131',
extensions: {
paymentMeans: [
{
type: 'SEPA',
iban: 'US12345678901234567890123456789' // Non-SEPA country
}
]
}
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Should have warning for non-SEPA country
const sepaWarnings = results.filter(r => r.ruleId === 'XR-DE-19' && r.severity === 'warning');
expect(sepaWarnings.length).toBeGreaterThan(0);
expect(sepaWarnings[0].message).toInclude('not in SEPA zone');
});
tap.test('XRechnungValidator - B2G Leitweg-ID requirement', async () => {
const validator = XRechnungValidator.create();
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-014',
to: {
name: 'Bundesamt für Migration' // Public entity
},
metadata: {
profileId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
// Missing buyerReference for B2G
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Should require Leitweg-ID for B2G
const b2gErrors = results.filter(r => r.ruleId === 'XR-DE-15');
expect(b2gErrors).toHaveLength(1);
expect(b2gErrors[0].message).toInclude('mandatory for B2G invoices');
});
tap.test('XRechnungValidator - Complete valid XRechnung invoice', async () => {
const validator = XRechnungValidator.create();
const invoice: Partial<EInvoice> = {
invoiceNumber: 'INV-2025-015',
from: {
type: 'company' as const,
name: 'Example GmbH',
registrationDetails: {
vatId: 'DE123456789'
}
},
metadata: {
profileId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
buyerReference: '991-12345678901-23',
sellerTaxId: 'DE123456789',
extensions: {
sellerContact: {
name: 'Sales Department',
email: 'sales@example.de',
phone: '+49 30 98765432'
},
paymentMeans: [
{
type: 'SEPA',
iban: 'DE89370400440532013000',
bic: 'COBADEFFXXX',
accountName: 'Example GmbH'
}
]
}
}
};
const results = validator.validateXRechnung(invoice as EInvoice);
// Complete valid invoice should have no errors
const errors = results.filter(r => r.severity === 'error');
expect(errors).toHaveLength(0);
});
export default tap.start();