409 lines
15 KiB
TypeScript
409 lines
15 KiB
TypeScript
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
import { EInvoice } from '../../../ts/index.js';
|
|
|
|
tap.test('ENC-06: Namespace Declarations - should handle XML namespace declarations correctly', async () => {
|
|
console.log('Testing XML namespace declaration handling...\n');
|
|
|
|
// Test 1: Default namespaces
|
|
const testDefaultNamespaces = async () => {
|
|
const einvoice = new EInvoice();
|
|
einvoice.id = 'NAMESPACE-DEFAULT-TEST';
|
|
einvoice.date = Date.now();
|
|
einvoice.currency = 'EUR';
|
|
einvoice.subject = 'Default namespace test';
|
|
|
|
einvoice.from = {
|
|
type: 'company',
|
|
name: 'Default Namespace Company',
|
|
description: 'Testing default namespaces',
|
|
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: 'Registry'
|
|
}
|
|
};
|
|
|
|
einvoice.to = {
|
|
type: 'company',
|
|
name: 'Customer',
|
|
description: 'Test customer',
|
|
address: {
|
|
streetName: 'Customer Street',
|
|
houseNumber: '2',
|
|
postalCode: '54321',
|
|
city: 'Customer City',
|
|
country: 'DE'
|
|
},
|
|
status: 'active',
|
|
foundedDate: { year: 2019, month: 1, day: 1 },
|
|
registrationDetails: {
|
|
vatId: 'DE987654321',
|
|
registrationId: 'HRB 54321',
|
|
registrationName: 'Registry'
|
|
}
|
|
};
|
|
|
|
einvoice.items = [{
|
|
position: 1,
|
|
name: 'Namespace Test Product',
|
|
unitType: 'C62',
|
|
unitQuantity: 1,
|
|
unitNetPrice: 100,
|
|
vatPercentage: 19
|
|
}];
|
|
|
|
const xmlString = await einvoice.toXmlString('ubl');
|
|
|
|
// Check if proper UBL namespaces are declared
|
|
const hasUblNamespace = xmlString.includes('xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"');
|
|
const hasCacNamespace = xmlString.includes('xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"');
|
|
const hasCbcNamespace = xmlString.includes('xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"');
|
|
|
|
// Check if elements use proper prefixes
|
|
const hasProperPrefixes = xmlString.includes('<cbc:ID>') &&
|
|
xmlString.includes('<cac:AccountingSupplierParty>') &&
|
|
xmlString.includes('<cac:AccountingCustomerParty>');
|
|
|
|
return {
|
|
hasUblNamespace,
|
|
hasCacNamespace,
|
|
hasCbcNamespace,
|
|
hasProperPrefixes,
|
|
xmlString
|
|
};
|
|
};
|
|
|
|
const defaultResult = await testDefaultNamespaces();
|
|
console.log('Test 1 - Default namespaces:');
|
|
console.log(` UBL namespace declared: ${defaultResult.hasUblNamespace ? 'Yes' : 'No'}`);
|
|
console.log(` CAC namespace declared: ${defaultResult.hasCacNamespace ? 'Yes' : 'No'}`);
|
|
console.log(` CBC namespace declared: ${defaultResult.hasCbcNamespace ? 'Yes' : 'No'}`);
|
|
console.log(` Proper prefixes used: ${defaultResult.hasProperPrefixes ? 'Yes' : 'No'}`);
|
|
|
|
// Test 2: Custom namespace handling
|
|
const testCustomNamespaces = async () => {
|
|
const customXml = `<?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"
|
|
xmlns:ext="urn:example:custom:extension">
|
|
<cbc:ID>CUSTOM-NS-TEST</cbc:ID>
|
|
<cbc:IssueDate>2025-01-25</cbc:IssueDate>
|
|
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
|
|
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
|
|
<cac:AccountingSupplierParty>
|
|
<cac:Party>
|
|
<cac:PartyName>
|
|
<cbc:Name>Custom Namespace Company</cbc:Name>
|
|
</cac:PartyName>
|
|
<cac:PostalAddress>
|
|
<cbc:StreetName>Test Street</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>Customer</cbc:Name>
|
|
</cac:PartyName>
|
|
<cac:PostalAddress>
|
|
<cbc:StreetName>Customer Street</cbc:StreetName>
|
|
<cbc:CityName>Customer City</cbc:CityName>
|
|
<cbc:PostalZone>54321</cbc:PostalZone>
|
|
<cac:Country>
|
|
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
|
|
</cac:Country>
|
|
</cac:PostalAddress>
|
|
</cac:Party>
|
|
</cac:AccountingCustomerParty>
|
|
<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 Item</cbc:Name>
|
|
</cac:Item>
|
|
</cac:InvoiceLine>
|
|
<ext:CustomExtension>
|
|
<ext:CustomField>Custom Value</ext:CustomField>
|
|
</ext:CustomExtension>
|
|
</Invoice>`;
|
|
|
|
try {
|
|
const invoice = await EInvoice.fromXml(customXml);
|
|
return {
|
|
success: invoice.id === 'CUSTOM-NS-TEST',
|
|
supplierName: invoice.from?.name || '',
|
|
customerName: invoice.to?.name || ''
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error: error.message
|
|
};
|
|
}
|
|
};
|
|
|
|
const customResult = await testCustomNamespaces();
|
|
console.log('\nTest 2 - Custom namespace handling:');
|
|
console.log(` Custom namespace XML parsed: ${customResult.success ? 'Yes' : 'No'}`);
|
|
if (customResult.error) {
|
|
console.log(` Error: ${customResult.error}`);
|
|
}
|
|
|
|
// Test 3: No namespace prefix handling
|
|
const testNoNamespacePrefix = async () => {
|
|
const noNsXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
|
<ID>NO-NS-PREFIX-TEST</ID>
|
|
<IssueDate>2025-01-25</IssueDate>
|
|
<InvoiceTypeCode>380</InvoiceTypeCode>
|
|
<DocumentCurrencyCode>EUR</DocumentCurrencyCode>
|
|
<AccountingSupplierParty xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
|
<Party>
|
|
<PartyName>
|
|
<Name xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">No Prefix Company</Name>
|
|
</PartyName>
|
|
<PostalAddress>
|
|
<StreetName xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">Test Street</StreetName>
|
|
<CityName xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">Test City</CityName>
|
|
<PostalZone xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">12345</PostalZone>
|
|
<Country>
|
|
<IdentificationCode xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">DE</IdentificationCode>
|
|
</Country>
|
|
</PostalAddress>
|
|
</Party>
|
|
</AccountingSupplierParty>
|
|
<AccountingCustomerParty xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
|
<Party>
|
|
<PartyName>
|
|
<Name xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">Customer</Name>
|
|
</PartyName>
|
|
<PostalAddress>
|
|
<StreetName xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">Customer Street</StreetName>
|
|
<CityName xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">Customer City</CityName>
|
|
<PostalZone xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">54321</PostalZone>
|
|
<Country>
|
|
<IdentificationCode xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">DE</IdentificationCode>
|
|
</Country>
|
|
</PostalAddress>
|
|
</Party>
|
|
</AccountingCustomerParty>
|
|
<InvoiceLine xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
|
<ID xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">1</ID>
|
|
<InvoicedQuantity xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" unitCode="C62">1</InvoicedQuantity>
|
|
<LineExtensionAmount xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" currencyID="EUR">100.00</LineExtensionAmount>
|
|
<Item>
|
|
<Name xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">Test Item</Name>
|
|
</Item>
|
|
</InvoiceLine>
|
|
</Invoice>`;
|
|
|
|
try {
|
|
const invoice = await EInvoice.fromXml(noNsXml);
|
|
return {
|
|
success: invoice.id === 'NO-NS-PREFIX-TEST',
|
|
supplierName: invoice.from?.name || '',
|
|
customerName: invoice.to?.name || ''
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error: error.message
|
|
};
|
|
}
|
|
};
|
|
|
|
const noNsResult = await testNoNamespacePrefix();
|
|
console.log('\nTest 3 - No namespace prefix handling:');
|
|
console.log(` No prefix XML parsed: ${noNsResult.success ? 'Yes' : 'No'}`);
|
|
if (noNsResult.error) {
|
|
console.log(` Error: ${noNsResult.error}`);
|
|
}
|
|
|
|
// Test 4: Namespace inheritance
|
|
const testNamespaceInheritance = async () => {
|
|
const inheritanceXml = `<?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:ID>INHERITANCE-TEST</cbc:ID>
|
|
<cbc:IssueDate>2025-01-25</cbc:IssueDate>
|
|
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
|
|
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
|
|
<cac:AccountingSupplierParty>
|
|
<cac:Party>
|
|
<cac:PartyName>
|
|
<cbc:Name>Inheritance Company</cbc:Name>
|
|
</cac:PartyName>
|
|
<cac:PostalAddress>
|
|
<cbc:StreetName>Test Street</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>Customer</cbc:Name>
|
|
</cac:PartyName>
|
|
<cac:PostalAddress>
|
|
<cbc:StreetName>Customer Street</cbc:StreetName>
|
|
<cbc:CityName>Customer City</cbc:CityName>
|
|
<cbc:PostalZone>54321</cbc:PostalZone>
|
|
<cac:Country>
|
|
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
|
|
</cac:Country>
|
|
</cac:PostalAddress>
|
|
</cac:Party>
|
|
</cac:AccountingCustomerParty>
|
|
<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 Item</cbc:Name>
|
|
</cac:Item>
|
|
</cac:InvoiceLine>
|
|
</Invoice>`;
|
|
|
|
try {
|
|
const invoice = await EInvoice.fromXml(inheritanceXml);
|
|
|
|
// Test round-trip to see if namespaces are preserved
|
|
const regeneratedXml = await invoice.toXmlString('ubl');
|
|
const reparsedInvoice = await EInvoice.fromXml(regeneratedXml);
|
|
|
|
return {
|
|
success: invoice.id === 'INHERITANCE-TEST',
|
|
roundTripSuccess: reparsedInvoice.id === 'INHERITANCE-TEST',
|
|
supplierName: invoice.from?.name || '',
|
|
regeneratedXml
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error: error.message
|
|
};
|
|
}
|
|
};
|
|
|
|
const inheritanceResult = await testNamespaceInheritance();
|
|
console.log('\nTest 4 - Namespace inheritance and round-trip:');
|
|
console.log(` Inheritance XML parsed: ${inheritanceResult.success ? 'Yes' : 'No'}`);
|
|
console.log(` Round-trip successful: ${inheritanceResult.roundTripSuccess ? 'Yes' : 'No'}`);
|
|
if (inheritanceResult.error) {
|
|
console.log(` Error: ${inheritanceResult.error}`);
|
|
}
|
|
|
|
// Test 5: Mixed namespace scenarios
|
|
const testMixedNamespaces = async () => {
|
|
const mixedXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
<ubl:Invoice xmlns:ubl="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"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
<cbc:ID>MIXED-NS-TEST</cbc:ID>
|
|
<cbc:IssueDate>2025-01-25</cbc:IssueDate>
|
|
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
|
|
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
|
|
<cac:AccountingSupplierParty>
|
|
<cac:Party>
|
|
<cac:PartyName>
|
|
<cbc:Name>Mixed Namespace Company</cbc:Name>
|
|
</cac:PartyName>
|
|
<cac:PostalAddress>
|
|
<cbc:StreetName>Test Street</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>Customer</cbc:Name>
|
|
</cac:PartyName>
|
|
<cac:PostalAddress>
|
|
<cbc:StreetName>Customer Street</cbc:StreetName>
|
|
<cbc:CityName>Customer City</cbc:CityName>
|
|
<cbc:PostalZone>54321</cbc:PostalZone>
|
|
<cac:Country>
|
|
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
|
|
</cac:Country>
|
|
</cac:PostalAddress>
|
|
</cac:Party>
|
|
</cac:AccountingCustomerParty>
|
|
<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 Item</cbc:Name>
|
|
</cac:Item>
|
|
</cac:InvoiceLine>
|
|
</ubl:Invoice>`;
|
|
|
|
try {
|
|
const invoice = await EInvoice.fromXml(mixedXml);
|
|
return {
|
|
success: invoice.id === 'MIXED-NS-TEST',
|
|
supplierName: invoice.from?.name || ''
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error: error.message
|
|
};
|
|
}
|
|
};
|
|
|
|
const mixedResult = await testMixedNamespaces();
|
|
console.log('\nTest 5 - Mixed namespace scenarios:');
|
|
console.log(` Mixed namespace XML parsed: ${mixedResult.success ? 'Yes' : 'No'}`);
|
|
if (mixedResult.error) {
|
|
console.log(` Error: ${mixedResult.error}`);
|
|
}
|
|
|
|
// Summary
|
|
console.log('\n=== XML Namespace Declarations Test Summary ===');
|
|
console.log(`Default namespaces: ${defaultResult.hasUblNamespace && defaultResult.hasCacNamespace && defaultResult.hasCbcNamespace ? 'Working' : 'Issues'}`);
|
|
console.log(`Custom namespaces: ${customResult.success ? 'Working' : 'Issues'}`);
|
|
console.log(`No prefix handling: ${noNsResult.success ? 'Working' : 'Issues'}`);
|
|
console.log(`Namespace inheritance: ${inheritanceResult.success && inheritanceResult.roundTripSuccess ? 'Working' : 'Issues'}`);
|
|
console.log(`Mixed namespaces: ${mixedResult.success ? 'Working' : 'Issues'}`);
|
|
|
|
// Tests pass if basic namespace functionality works
|
|
expect(defaultResult.hasUblNamespace).toEqual(true);
|
|
expect(defaultResult.hasCacNamespace).toEqual(true);
|
|
expect(defaultResult.hasCbcNamespace).toEqual(true);
|
|
expect(defaultResult.hasProperPrefixes).toEqual(true);
|
|
expect(customResult.success).toEqual(true);
|
|
expect(inheritanceResult.success).toEqual(true);
|
|
expect(inheritanceResult.roundTripSuccess).toEqual(true);
|
|
|
|
console.log('\n✓ XML namespace declarations test completed');
|
|
});
|
|
|
|
tap.start(); |