einvoice/test/suite/einvoice_standards-compliance/test.std-03.peppol-bis.ts

838 lines
27 KiB
TypeScript
Raw Normal View History

2025-05-26 04:04:51 +00:00
import { tap } from '@git.zone/tstest/tapbundle';
import * as plugins from '../plugins.js';
import { EInvoice } from '../../../ts/index.js';
import { PerformanceTracker } from '../performance.tracker.js';
import { CorpusLoader } from '../corpus.loader.js';
const performanceTracker = new PerformanceTracker('STD-03: PEPPOL BIS 3.0 Compliance');
tap.test('STD-03: PEPPOL BIS 3.0 Compliance - should validate PEPPOL Business Interoperability Specifications', async (t) => {
const einvoice = new EInvoice();
const corpusLoader = new CorpusLoader();
// Test 1: PEPPOL BIS 3.0 mandatory elements
const peppolMandatoryElements = await performanceTracker.measureAsync(
'peppol-mandatory-elements',
async () => {
const peppolRequirements = [
'CustomizationID', // Must be specific PEPPOL value
'ProfileID', // Must reference PEPPOL process
'EndpointID', // Both buyer and seller must have endpoints
'CompanyID', // VAT registration required
'SchemeID', // Proper scheme identifiers
'InvoicePeriod', // When applicable
'OrderReference', // Strongly recommended
];
const testCases = [
{
name: 'complete-peppol-invoice',
xml: createCompletePEPPOLInvoice()
},
{
name: 'missing-endpoint-ids',
xml: createPEPPOLWithoutEndpoints()
},
{
name: 'invalid-customization-id',
xml: createPEPPOLWithInvalidCustomization()
},
{
name: 'missing-scheme-ids',
xml: createPEPPOLWithoutSchemeIds()
}
];
const results = [];
for (const test of testCases) {
try {
const parsed = await einvoice.parseDocument(test.xml);
const validation = await einvoice.validatePEPPOLBIS(parsed);
results.push({
testCase: test.name,
valid: validation?.isValid || false,
peppolCompliant: validation?.peppolCompliant || false,
missingElements: validation?.missingElements || [],
invalidElements: validation?.invalidElements || [],
warnings: validation?.warnings || []
});
} catch (error) {
results.push({
testCase: test.name,
valid: false,
error: error.message
});
}
}
return results;
}
);
const completeTest = peppolMandatoryElements.find(r => r.testCase === 'complete-peppol-invoice');
t.ok(completeTest?.peppolCompliant, 'Complete PEPPOL invoice should be compliant');
peppolMandatoryElements.filter(r => r.testCase !== 'complete-peppol-invoice').forEach(result => {
t.notOk(result.peppolCompliant, `${result.testCase} should not be PEPPOL compliant`);
});
// Test 2: PEPPOL Participant Identifier validation
const participantIdentifierValidation = await performanceTracker.measureAsync(
'participant-identifier-validation',
async () => {
const identifierTests = [
{
name: 'valid-gln',
scheme: '0088',
identifier: '7300010000001',
expected: { valid: true, type: 'GLN' }
},
{
name: 'valid-duns',
scheme: '0060',
identifier: '123456789',
expected: { valid: true, type: 'DUNS' }
},
{
name: 'valid-orgnr',
scheme: '0007',
identifier: '123456789',
expected: { valid: true, type: 'SE:ORGNR' }
},
{
name: 'invalid-scheme',
scheme: '9999',
identifier: '123456789',
expected: { valid: false, error: 'Unknown scheme' }
},
{
name: 'invalid-checksum',
scheme: '0088',
identifier: '7300010000000', // Invalid GLN checksum
expected: { valid: false, error: 'Invalid checksum' }
}
];
const results = [];
for (const test of identifierTests) {
const invoice = createPEPPOLWithParticipant(test.scheme, test.identifier);
const validation = await einvoice.validatePEPPOLParticipant(invoice);
results.push({
test: test.name,
scheme: test.scheme,
identifier: test.identifier,
valid: validation?.isValid || false,
identifierType: validation?.identifierType,
checksumValid: validation?.checksumValid,
schemeRecognized: validation?.schemeRecognized
});
}
return results;
}
);
participantIdentifierValidation.forEach(result => {
const expected = identifierTests.find(t => t.name === result.test)?.expected;
t.equal(result.valid, expected?.valid,
`Participant identifier ${result.test} validation should match expected`);
});
// Test 3: PEPPOL Document Type validation
const documentTypeValidation = await performanceTracker.measureAsync(
'peppol-document-type-validation',
async () => {
const documentTypes = [
{
name: 'invoice',
customizationId: 'urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0',
profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:01:1.0',
valid: true
},
{
name: 'credit-note',
customizationId: 'urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0',
profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:01:1.0',
typeCode: '381',
valid: true
},
{
name: 'old-bis2',
customizationId: 'urn:www.cenbii.eu:transaction:biitrns010:ver2.0',
profileId: 'urn:www.cenbii.eu:profile:bii05:ver2.0',
valid: false // Old version
}
];
const results = [];
for (const docType of documentTypes) {
const invoice = createPEPPOLWithDocumentType(docType);
const validation = await einvoice.validatePEPPOLDocumentType(invoice);
results.push({
documentType: docType.name,
customizationId: docType.customizationId,
profileId: docType.profileId,
recognized: validation?.recognized || false,
supported: validation?.supported || false,
version: validation?.version,
deprecated: validation?.deprecated || false
});
}
return results;
}
);
documentTypeValidation.forEach(result => {
const expected = documentTypes.find(d => d.name === result.documentType);
if (expected?.valid) {
t.ok(result.supported, `Document type ${result.documentType} should be supported`);
} else {
t.notOk(result.supported || result.deprecated,
`Document type ${result.documentType} should not be supported`);
}
});
// Test 4: PEPPOL Business Rules validation
const businessRulesValidation = await performanceTracker.measureAsync(
'peppol-business-rules',
async () => {
const peppolRules = [
{
rule: 'PEPPOL-EN16931-R001',
description: 'Business process MUST be provided',
violation: createInvoiceViolatingPEPPOLRule('R001')
},
{
rule: 'PEPPOL-EN16931-R002',
description: 'Supplier electronic address MUST be provided',
violation: createInvoiceViolatingPEPPOLRule('R002')
},
{
rule: 'PEPPOL-EN16931-R003',
description: 'Customer electronic address MUST be provided',
violation: createInvoiceViolatingPEPPOLRule('R003')
},
{
rule: 'PEPPOL-EN16931-R004',
description: 'Specification identifier MUST have correct value',
violation: createInvoiceViolatingPEPPOLRule('R004')
},
{
rule: 'PEPPOL-EN16931-R007',
description: 'Payment means code must be valid',
violation: createInvoiceViolatingPEPPOLRule('R007')
}
];
const results = [];
for (const ruleTest of peppolRules) {
try {
const parsed = await einvoice.parseDocument(ruleTest.violation);
const validation = await einvoice.validatePEPPOLBusinessRules(parsed);
const violation = validation?.violations?.find(v => v.rule === ruleTest.rule);
results.push({
rule: ruleTest.rule,
description: ruleTest.description,
violated: !!violation,
severity: violation?.severity || 'unknown',
flag: violation?.flag || 'unknown' // fatal/warning
});
} catch (error) {
results.push({
rule: ruleTest.rule,
error: error.message
});
}
}
return results;
}
);
businessRulesValidation.forEach(result => {
t.ok(result.violated, `PEPPOL rule ${result.rule} violation should be detected`);
});
// Test 5: PEPPOL Code List validation
const codeListValidation = await performanceTracker.measureAsync(
'peppol-code-list-validation',
async () => {
const codeTests = [
{
list: 'ICD',
code: '0088',
description: 'GLN',
valid: true
},
{
list: 'EAS',
code: '9906',
description: 'IT:VAT',
valid: true
},
{
list: 'UNCL1001',
code: '380',
description: 'Commercial invoice',
valid: true
},
{
list: 'ISO3166',
code: 'NO',
description: 'Norway',
valid: true
},
{
list: 'UNCL4461',
code: '42',
description: 'Payment to bank account',
valid: true
}
];
const results = [];
for (const test of codeTests) {
const validation = await einvoice.validatePEPPOLCode(test.list, test.code);
results.push({
list: test.list,
code: test.code,
description: test.description,
valid: validation?.isValid || false,
recognized: validation?.recognized || false,
deprecated: validation?.deprecated || false
});
}
return results;
}
);
codeListValidation.forEach(result => {
t.ok(result.valid && result.recognized,
`PEPPOL code ${result.code} in list ${result.list} should be valid`);
});
// Test 6: PEPPOL Transport validation
const transportValidation = await performanceTracker.measureAsync(
'peppol-transport-validation',
async () => {
const transportTests = [
{
name: 'as4-compliant',
endpoint: 'https://ap.example.com/as4',
certificate: 'valid-peppol-cert',
encryption: 'required'
},
{
name: 'smp-lookup',
participantId: '0007:123456789',
documentType: 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0::2.1'
},
{
name: 'certificate-validation',
cert: 'PEPPOL-SMP-cert',
ca: 'PEPPOL-Root-CA'
}
];
const results = [];
for (const test of transportTests) {
const validation = await einvoice.validatePEPPOLTransport(test);
results.push({
test: test.name,
transportReady: validation?.transportReady || false,
endpointValid: validation?.endpointValid || false,
certificateValid: validation?.certificateValid || false,
smpResolvable: validation?.smpResolvable || false
});
}
return results;
}
);
transportValidation.forEach(result => {
t.ok(result.transportReady, `PEPPOL transport ${result.test} should be ready`);
});
// Test 7: PEPPOL MLR (Message Level Response) handling
const mlrHandling = await performanceTracker.measureAsync(
'peppol-mlr-handling',
async () => {
const mlrScenarios = [
{
name: 'invoice-response-accept',
responseCode: 'AP',
status: 'Accepted'
},
{
name: 'invoice-response-reject',
responseCode: 'RE',
status: 'Rejected',
reasons: ['Missing mandatory field', 'Invalid VAT calculation']
},
{
name: 'invoice-response-conditional',
responseCode: 'CA',
status: 'Conditionally Accepted',
conditions: ['Payment terms clarification needed']
}
];
const results = [];
for (const scenario of mlrScenarios) {
const mlr = createPEPPOLMLR(scenario);
const validation = await einvoice.validatePEPPOLMLR(mlr);
results.push({
scenario: scenario.name,
responseCode: scenario.responseCode,
valid: validation?.isValid || false,
structureValid: validation?.structureValid || false,
semanticsValid: validation?.semanticsValid || false
});
}
return results;
}
);
mlrHandling.forEach(result => {
t.ok(result.valid, `PEPPOL MLR ${result.scenario} should be valid`);
});
// Test 8: PEPPOL Directory integration
const directoryIntegration = await performanceTracker.measureAsync(
'peppol-directory-integration',
async () => {
const directoryTests = [
{
name: 'participant-lookup',
identifier: '0007:987654321',
country: 'NO'
},
{
name: 'capability-lookup',
participant: '0088:7300010000001',
documentTypes: ['Invoice', 'CreditNote', 'OrderResponse']
},
{
name: 'smp-metadata',
endpoint: 'https://smp.example.com',
participant: '0184:IT01234567890'
}
];
const results = [];
for (const test of directoryTests) {
const lookup = await einvoice.lookupPEPPOLParticipant(test);
results.push({
test: test.name,
found: lookup?.found || false,
active: lookup?.active || false,
capabilities: lookup?.capabilities || [],
metadata: lookup?.metadata || {}
});
}
return results;
}
);
directoryIntegration.forEach(result => {
t.ok(result.found !== undefined,
`PEPPOL directory lookup ${result.test} should return result`);
});
// Test 9: Corpus PEPPOL validation
const corpusPEPPOLValidation = await performanceTracker.measureAsync(
'corpus-peppol-validation',
async () => {
const peppolFiles = await corpusLoader.getFilesByPattern('**/PEPPOL/**/*.xml');
const results = {
total: peppolFiles.length,
valid: 0,
invalid: 0,
errors: [],
profiles: {}
};
for (const file of peppolFiles.slice(0, 10)) { // Test first 10
try {
const content = await corpusLoader.readFile(file);
const parsed = await einvoice.parseDocument(content);
const validation = await einvoice.validatePEPPOLBIS(parsed);
if (validation?.isValid) {
results.valid++;
const profile = validation.profileId || 'unknown';
results.profiles[profile] = (results.profiles[profile] || 0) + 1;
} else {
results.invalid++;
results.errors.push({
file: file.name,
errors: validation?.errors?.slice(0, 3)
});
}
} catch (error) {
results.invalid++;
results.errors.push({
file: file.name,
error: error.message
});
}
}
return results;
}
);
t.ok(corpusPEPPOLValidation.valid > 0, 'Some corpus files should be valid PEPPOL');
// Test 10: PEPPOL Country Specific Rules
const countrySpecificRules = await performanceTracker.measureAsync(
'peppol-country-specific-rules',
async () => {
const countryTests = [
{
country: 'IT',
name: 'Italy',
specificRules: ['Codice Fiscale required', 'SDI code mandatory'],
invoice: createPEPPOLItalianInvoice()
},
{
country: 'NO',
name: 'Norway',
specificRules: ['Organization number format', 'Foretaksregisteret validation'],
invoice: createPEPPOLNorwegianInvoice()
},
{
country: 'NL',
name: 'Netherlands',
specificRules: ['KvK number validation', 'OB number format'],
invoice: createPEPPOLDutchInvoice()
}
];
const results = [];
for (const test of countryTests) {
try {
const parsed = await einvoice.parseDocument(test.invoice);
const validation = await einvoice.validatePEPPOLCountryRules(parsed, test.country);
results.push({
country: test.country,
name: test.name,
valid: validation?.isValid || false,
countryRulesApplied: validation?.countryRulesApplied || false,
specificValidations: validation?.specificValidations || [],
violations: validation?.violations || []
});
} catch (error) {
results.push({
country: test.country,
name: test.name,
error: error.message
});
}
}
return results;
}
);
countrySpecificRules.forEach(result => {
t.ok(result.countryRulesApplied,
`Country specific rules for ${result.name} should be applied`);
});
// Print performance summary
performanceTracker.printSummary();
});
// Helper functions
function createCompletePEPPOLInvoice(): string {
return `<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>PEPPOL-INV-001</cbc:ID>
<cbc:IssueDate>2024-01-15</cbc:IssueDate>
<cbc:DueDate>2024-02-15</cbc:DueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cac:OrderReference>
<cbc:ID>PO-12345</cbc:ID>
</cac:OrderReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cbc:EndpointID schemeID="0088">7300010000001</cbc:EndpointID>
<cac:PartyIdentification>
<cbc:ID schemeID="0088">7300010000001</cbc:ID>
</cac:PartyIdentification>
<cac:PartyName>
<cbc:Name>Supplier Company AS</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Main Street 1</cbc:StreetName>
<cbc:CityName>Oslo</cbc:CityName>
<cbc:PostalZone>0001</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>NO</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>NO999888777</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Supplier Company AS</cbc:RegistrationName>
<cbc:CompanyID schemeID="0007">999888777</cbc:CompanyID>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cbc:EndpointID schemeID="0007">123456789</cbc:EndpointID>
<cac:PartyIdentification>
<cbc:ID schemeID="0007">123456789</cbc:ID>
</cac:PartyIdentification>
<cac:PartyName>
<cbc:Name>Customer Company AB</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Storgatan 1</cbc:StreetName>
<cbc:CityName>Stockholm</cbc:CityName>
<cbc:PostalZone>10001</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>SE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:PaymentMeans>
<cbc:PaymentMeansCode>42</cbc:PaymentMeansCode>
<cac:PayeeFinancialAccount>
<cbc:ID>NO9386011117947</cbc:ID>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">1000.00</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">1250.00</cbc:TaxInclusiveAmount>
<cbc:PayableAmount currencyID="EUR">1250.00</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="C62">10</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Product A</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>25</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
</Invoice>`;
}
function createPEPPOLWithoutEndpoints(): string {
let invoice = createCompletePEPPOLInvoice();
// Remove endpoint IDs
invoice = invoice.replace(/<cbc:EndpointID[^>]*>.*?<\/cbc:EndpointID>/g, '');
return invoice;
}
function createPEPPOLWithInvalidCustomization(): string {
let invoice = createCompletePEPPOLInvoice();
return invoice.replace(
'urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0',
'urn:cen.eu:en16931:2017'
);
}
function createPEPPOLWithoutSchemeIds(): string {
let invoice = createCompletePEPPOLInvoice();
// Remove schemeID attributes
invoice = invoice.replace(/ schemeID="[^"]*"/g, '');
return invoice;
}
function createPEPPOLWithParticipant(scheme: string, identifier: string): any {
return {
supplierEndpointID: { schemeID: scheme, value: identifier },
supplierPartyIdentification: { schemeID: scheme, value: identifier }
};
}
function createPEPPOLWithDocumentType(docType: any): string {
let invoice = createCompletePEPPOLInvoice();
invoice = invoice.replace(
/<cbc:CustomizationID>.*?<\/cbc:CustomizationID>/,
`<cbc:CustomizationID>${docType.customizationId}</cbc:CustomizationID>`
);
invoice = invoice.replace(
/<cbc:ProfileID>.*?<\/cbc:ProfileID>/,
`<cbc:ProfileID>${docType.profileId}</cbc:ProfileID>`
);
if (docType.typeCode) {
invoice = invoice.replace(
'<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>',
`<cbc:InvoiceTypeCode>${docType.typeCode}</cbc:InvoiceTypeCode>`
);
}
return invoice;
}
function createInvoiceViolatingPEPPOLRule(rule: string): string {
let invoice = createCompletePEPPOLInvoice();
switch (rule) {
case 'R001':
// Remove ProfileID
return invoice.replace(/<cbc:ProfileID>.*?<\/cbc:ProfileID>/, '');
case 'R002':
// Remove supplier endpoint
return invoice.replace(/<cbc:EndpointID schemeID="0088">7300010000001<\/cbc:EndpointID>/, '');
case 'R003':
// Remove customer endpoint
return invoice.replace(/<cbc:EndpointID schemeID="0007">123456789<\/cbc:EndpointID>/, '');
case 'R004':
// Invalid CustomizationID
return invoice.replace(
'urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0',
'invalid-customization-id'
);
case 'R007':
// Invalid payment means code
return invoice.replace(
'<cbc:PaymentMeansCode>42</cbc:PaymentMeansCode>',
'<cbc:PaymentMeansCode>99</cbc:PaymentMeansCode>'
);
default:
return invoice;
}
}
function createPEPPOLMLR(scenario: any): string {
return `<?xml version="1.0" encoding="UTF-8"?>
<ApplicationResponse xmlns="urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2">
<cbc:CustomizationID>urn:fdc:peppol.eu:poacc:trns:invoice_response:3</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:poacc:bis:invoice_response:3</cbc:ProfileID>
<cbc:ID>MLR-${scenario.name}</cbc:ID>
<cbc:IssueDate>2024-01-16</cbc:IssueDate>
<cbc:ResponseCode>${scenario.responseCode}</cbc:ResponseCode>
<cac:DocumentResponse>
<cac:Response>
<cbc:ResponseCode>${scenario.responseCode}</cbc:ResponseCode>
<cbc:Description>${scenario.status}</cbc:Description>
</cac:Response>
</cac:DocumentResponse>
</ApplicationResponse>`;
}
function createPEPPOLItalianInvoice(): string {
let invoice = createCompletePEPPOLInvoice();
// Add Italian specific fields
const italianFields = `
<cac:PartyIdentification>
<cbc:ID schemeID="IT:CF">RSSMRA85M01H501Z</cbc:ID>
</cac:PartyIdentification>
<cac:PartyIdentification>
<cbc:ID schemeID="IT:IPA">UFY9MH</cbc:ID>
</cac:PartyIdentification>`;
return invoice.replace('</cac:Party>', italianFields + '\n </cac:Party>');
}
function createPEPPOLNorwegianInvoice(): string {
// Already uses Norwegian example
return createCompletePEPPOLInvoice();
}
function createPEPPOLDutchInvoice(): string {
let invoice = createCompletePEPPOLInvoice();
// Change to Dutch context
invoice = invoice.replace('NO999888777', 'NL123456789B01');
invoice = invoice.replace('<cbc:IdentificationCode>NO</cbc:IdentificationCode>',
'<cbc:IdentificationCode>NL</cbc:IdentificationCode>');
invoice = invoice.replace('Oslo', 'Amsterdam');
invoice = invoice.replace('0001', '1011AB');
// Add KvK number
const kvkNumber = '<cbc:CompanyID schemeID="NL:KVK">12345678</cbc:CompanyID>';
invoice = invoice.replace('</cac:PartyLegalEntity>',
kvkNumber + '\n </cac:PartyLegalEntity>');
return invoice;
}
const identifierTests = [
{ name: 'valid-gln', scheme: '0088', identifier: '7300010000001', expected: { valid: true } },
{ name: 'valid-duns', scheme: '0060', identifier: '123456789', expected: { valid: true } },
{ name: 'valid-orgnr', scheme: '0007', identifier: '123456789', expected: { valid: true } },
{ name: 'invalid-scheme', scheme: '9999', identifier: '123456789', expected: { valid: false } },
{ name: 'invalid-checksum', scheme: '0088', identifier: '7300010000000', expected: { valid: false } }
];
const documentTypes = [
{
name: 'invoice',
customizationId: 'urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0',
profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:01:1.0',
valid: true
},
{
name: 'credit-note',
customizationId: 'urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0',
profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:01:1.0',
typeCode: '381',
valid: true
},
{
name: 'old-bis2',
customizationId: 'urn:www.cenbii.eu:transaction:biitrns010:ver2.0',
profileId: 'urn:www.cenbii.eu:profile:bii05:ver2.0',
valid: false
}
];
// Run the test
tap.start();