einvoice/test/suite/einvoice_conversion/test.conv-04.field-mapping.ts
2025-05-25 19:45:37 +00:00

621 lines
24 KiB
TypeScript

import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as plugins from '../plugins.js';
import { EInvoice } from '../../../ts/index.js';
import { CorpusLoader } from '../corpus.loader.js';
import { PerformanceTracker } from '../performance.tracker.js';
tap.test('CONV-04: Field Mapping - should correctly map fields between formats', async (t) => {
// CONV-04: Verify accurate field mapping during format conversion
// This test ensures data is correctly transferred between different formats
const performanceTracker = new PerformanceTracker('CONV-04: Field Mapping');
const corpusLoader = new CorpusLoader();
t.test('Basic field mapping UBL to CII', async () => {
const startTime = performance.now();
// UBL invoice with comprehensive fields
const ublInvoice = `<?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:UBLVersionID>2.1</cbc:UBLVersionID>
<cbc:CustomizationID>urn:cen.eu:en16931:2017</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>FIELD-MAP-001</cbc:ID>
<cbc:IssueDate>2025-01-25</cbc:IssueDate>
<cbc:DueDate>2025-02-25</cbc:DueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:Note>Field mapping test invoice</cbc:Note>
<cbc:TaxPointDate>2025-01-25</cbc:TaxPointDate>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cbc:TaxCurrencyCode>EUR</cbc:TaxCurrencyCode>
<cbc:BuyerReference>PO-2025-001</cbc:BuyerReference>
<cac:OrderReference>
<cbc:ID>ORDER-123</cbc:ID>
</cac:OrderReference>
<cac:BillingReference>
<cac:InvoiceDocumentReference>
<cbc:ID>PREV-INV-001</cbc:ID>
<cbc:IssueDate>2025-01-01</cbc:IssueDate>
</cac:InvoiceDocumentReference>
</cac:BillingReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cbc:EndpointID schemeID="0088">5790000435975</cbc:EndpointID>
<cac:PartyIdentification>
<cbc:ID schemeID="0184">DK12345678</cbc:ID>
</cac:PartyIdentification>
<cac:PartyName>
<cbc:Name>Supplier Company A/S</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Main Street</cbc:StreetName>
<cbc:BuildingNumber>1</cbc:BuildingNumber>
<cbc:CityName>Copenhagen</cbc:CityName>
<cbc:PostalZone>1234</cbc:PostalZone>
<cbc:CountrySubentity>Capital Region</cbc:CountrySubentity>
<cac:Country>
<cbc:IdentificationCode>DK</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>DK12345678</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Supplier Company A/S</cbc:RegistrationName>
<cbc:CompanyID schemeID="0184">DK12345678</cbc:CompanyID>
</cac:PartyLegalEntity>
<cac:Contact>
<cbc:Name>John Doe</cbc:Name>
<cbc:Telephone>+45 12345678</cbc:Telephone>
<cbc:ElectronicMail>john@supplier.dk</cbc:ElectronicMail>
</cac:Contact>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cbc:EndpointID schemeID="0088">5790000435982</cbc:EndpointID>
<cac:PartyIdentification>
<cbc:ID schemeID="0184">DK87654321</cbc:ID>
</cac:PartyIdentification>
<cac:PartyName>
<cbc:Name>Customer Company B/V</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Market Street</cbc:StreetName>
<cbc:BuildingNumber>100</cbc:BuildingNumber>
<cbc:CityName>Aarhus</cbc:CityName>
<cbc:PostalZone>8000</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DK</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>DK87654321</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:Contact>
<cbc:Name>Jane Smith</cbc:Name>
<cbc:ElectronicMail>jane@customer.dk</cbc:ElectronicMail>
</cac:Contact>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:PaymentMeans>
<cbc:PaymentMeansCode name="Credit transfer">30</cbc:PaymentMeansCode>
<cbc:PaymentID>PAY-2025-001</cbc:PaymentID>
<cac:PayeeFinancialAccount>
<cbc:ID>DK5000400440116243</cbc:ID>
<cbc:Name>Supplier Bank Account</cbc:Name>
<cac:FinancialInstitutionBranch>
<cbc:ID>DANBDK22</cbc:ID>
<cbc:Name>Danske Bank</cbc:Name>
</cac:FinancialInstitutionBranch>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
</Invoice>`;
const einvoice = new EInvoice();
await einvoice.loadFromString(ublInvoice);
// Check if key fields are preserved
const invoiceData = einvoice.getInvoiceData();
if (invoiceData) {
// Basic fields
expect(invoiceData.invoiceNumber).toBe('FIELD-MAP-001');
expect(invoiceData.issueDate).toContain('2025-01-25');
expect(invoiceData.dueDate).toContain('2025-02-25');
expect(invoiceData.currency).toBe('EUR');
// Supplier fields
if (invoiceData.supplier) {
expect(invoiceData.supplier.name).toContain('Supplier Company');
expect(invoiceData.supplier.vatNumber).toContain('DK12345678');
expect(invoiceData.supplier.address?.street).toContain('Main Street');
expect(invoiceData.supplier.address?.city).toBe('Copenhagen');
expect(invoiceData.supplier.address?.postalCode).toBe('1234');
expect(invoiceData.supplier.address?.country).toBe('DK');
}
// Customer fields
if (invoiceData.customer) {
expect(invoiceData.customer.name).toContain('Customer Company');
expect(invoiceData.customer.vatNumber).toContain('DK87654321');
expect(invoiceData.customer.address?.city).toBe('Aarhus');
}
console.log('Basic field mapping verified');
} else {
console.log('Field mapping through invoice data not available');
}
const elapsed = performance.now() - startTime;
performanceTracker.addMeasurement('basic-mapping', elapsed);
});
t.test('Complex nested field mapping', async () => {
const startTime = performance.now();
// CII invoice with nested structures
const ciiInvoice = `<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
<rsm:ExchangedDocumentContext>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:cen.eu:en16931:2017</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>NESTED-MAP-001</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20250125</udt:DateTimeString>
</ram:IssueDateTime>
<ram:IncludedNote>
<ram:Content>Complex nested structure test</ram:Content>
<ram:SubjectCode>AAI</ram:SubjectCode>
</ram:IncludedNote>
<ram:IncludedNote>
<ram:Content>Second note for testing</ram:Content>
<ram:SubjectCode>REG</ram:SubjectCode>
</ram:IncludedNote>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<ram:IncludedSupplyChainTradeLineItem>
<ram:AssociatedDocumentLineDocument>
<ram:LineID>1</ram:LineID>
<ram:IncludedNote>
<ram:Content>Line item note</ram:Content>
</ram:IncludedNote>
</ram:AssociatedDocumentLineDocument>
<ram:SpecifiedTradeProduct>
<ram:GlobalID schemeID="0160">1234567890123</ram:GlobalID>
<ram:SellerAssignedID>PROD-001</ram:SellerAssignedID>
<ram:BuyerAssignedID>CUST-PROD-001</ram:BuyerAssignedID>
<ram:Name>Complex Product</ram:Name>
<ram:Description>Product with multiple identifiers and attributes</ram:Description>
<ram:ApplicableProductCharacteristic>
<ram:Description>Color</ram:Description>
<ram:Value>Blue</ram:Value>
</ram:ApplicableProductCharacteristic>
<ram:ApplicableProductCharacteristic>
<ram:Description>Size</ram:Description>
<ram:Value>Large</ram:Value>
</ram:ApplicableProductCharacteristic>
</ram:SpecifiedTradeProduct>
<ram:SpecifiedLineTradeAgreement>
<ram:BuyerOrderReferencedDocument>
<ram:LineID>PO-LINE-001</ram:LineID>
</ram:BuyerOrderReferencedDocument>
<ram:GrossPriceProductTradePrice>
<ram:ChargeAmount>120.00</ram:ChargeAmount>
<ram:AppliedTradeAllowanceCharge>
<ram:ChargeIndicator>
<udt:Indicator>false</udt:Indicator>
</ram:ChargeIndicator>
<ram:CalculationPercent>10.00</ram:CalculationPercent>
<ram:ActualAmount>12.00</ram:ActualAmount>
<ram:Reason>Volume discount</ram:Reason>
</ram:AppliedTradeAllowanceCharge>
</ram:GrossPriceProductTradePrice>
<ram:NetPriceProductTradePrice>
<ram:ChargeAmount>108.00</ram:ChargeAmount>
</ram:NetPriceProductTradePrice>
</ram:SpecifiedLineTradeAgreement>
<ram:SpecifiedLineTradeDelivery>
<ram:BilledQuantity unitCode="C62">10</ram:BilledQuantity>
</ram:SpecifiedLineTradeDelivery>
<ram:SpecifiedLineTradeSettlement>
<ram:ApplicableTradeTax>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradeSettlementLineMonetarySummation>
<ram:LineTotalAmount>1080.00</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation>
</ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem>
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>`;
const einvoice = new EInvoice();
await einvoice.loadFromString(ciiInvoice);
const xmlString = einvoice.getXmlString();
// Verify nested structures are preserved
expect(xmlString).toContain('NESTED-MAP-001');
expect(xmlString).toContain('Complex nested structure test');
expect(xmlString).toContain('PROD-001');
expect(xmlString).toContain('1234567890123');
expect(xmlString).toContain('Color');
expect(xmlString).toContain('Blue');
expect(xmlString).toContain('Volume discount');
console.log('Complex nested field mapping tested');
const elapsed = performance.now() - startTime;
performanceTracker.addMeasurement('nested-mapping', elapsed);
});
t.test('Field mapping with missing optional fields', async () => {
const startTime = performance.now();
// Minimal UBL invoice
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:ID>MINIMAL-001</cbc:ID>
<cbc:IssueDate>2025-01-25</cbc:IssueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Minimal Supplier</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Minimal Customer</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:LegalMonetaryTotal>
<cbc:PayableAmount currencyID="EUR">100.00</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
</Invoice>`;
const einvoice = new EInvoice();
await einvoice.loadFromString(minimalUbl);
const invoiceData = einvoice.getInvoiceData();
// Verify mandatory fields are mapped
expect(invoiceData?.invoiceNumber).toBe('MINIMAL-001');
expect(invoiceData?.issueDate).toContain('2025-01-25');
expect(invoiceData?.currency).toBe('EUR');
expect(invoiceData?.totalAmount).toBe(100.00);
// Optional fields should be undefined or have defaults
expect(invoiceData?.dueDate).toBeUndefined();
expect(invoiceData?.notes).toBeUndefined();
expect(invoiceData?.supplier?.vatNumber).toBeUndefined();
console.log('Minimal field mapping verified');
const elapsed = performance.now() - startTime;
performanceTracker.addMeasurement('minimal-mapping', elapsed);
});
t.test('Field type conversion mapping', async () => {
const startTime = performance.now();
// Invoice with various data types
const typeTestInvoice = `<?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>TYPE-TEST-001</cbc:ID>
<cbc:IssueDate>2025-01-25</cbc:IssueDate>
<cbc:IssueTime>14:30:00</cbc:IssueTime>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cbc:LineCountNumeric>5</cbc:LineCountNumeric>
<cbc:TaxPointDate>2025-01-25</cbc:TaxPointDate>
<cac:InvoicePeriod>
<cbc:StartDate>2025-01-01</cbc:StartDate>
<cbc:EndDate>2025-01-31</cbc:EndDate>
</cac:InvoicePeriod>
<cac:OrderReference>
<cbc:ID>ORDER-123</cbc:ID>
<cbc:SalesOrderID>SO-456</cbc:SalesOrderID>
</cac:OrderReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Type Test Supplier</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Type Test Customer</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:MultiplierFactorNumeric>0.05</cbc:MultiplierFactorNumeric>
<cbc:Amount currencyID="EUR">50.00</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">1000.00</cbc:BaseAmount>
</cac:AllowanceCharge>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19.00</cbc:Percent>
<cbc:TaxExemptionReasonCode>VATEX-EU-O</cbc:TaxExemptionReasonCode>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
</Invoice>`;
const einvoice = new EInvoice();
await einvoice.loadFromString(typeTestInvoice);
const xmlString = einvoice.getXmlString();
// Verify different data types are preserved
expect(xmlString).toContain('TYPE-TEST-001'); // String
expect(xmlString).toContain('2025-01-25'); // Date
expect(xmlString).toContain('14:30:00'); // Time
expect(xmlString).toContain('5'); // Integer
expect(xmlString).toContain('19.00'); // Decimal
expect(xmlString).toContain('false'); // Boolean
expect(xmlString).toContain('0.05'); // Float
console.log('Field type conversion mapping verified');
const elapsed = performance.now() - startTime;
performanceTracker.addMeasurement('type-conversion', elapsed);
});
t.test('Array field mapping', async () => {
const startTime = performance.now();
// Invoice with multiple repeated elements
const arrayInvoice = `<?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>ARRAY-TEST-001</cbc:ID>
<cbc:IssueDate>2025-01-25</cbc:IssueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:Note>First note</cbc:Note>
<cbc:Note>Second note</cbc:Note>
<cbc:Note>Third note with special chars: €£¥</cbc:Note>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cac:AdditionalDocumentReference>
<cbc:ID>DOC-001</cbc:ID>
<cbc:DocumentType>Contract</cbc:DocumentType>
</cac:AdditionalDocumentReference>
<cac:AdditionalDocumentReference>
<cbc:ID>DOC-002</cbc:ID>
<cbc:DocumentType>Purchase Order</cbc:DocumentType>
</cac:AdditionalDocumentReference>
<cac:AdditionalDocumentReference>
<cbc:ID>DOC-003</cbc:ID>
<cbc:DocumentType>Delivery Note</cbc:DocumentType>
</cac:AdditionalDocumentReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyIdentification>
<cbc:ID schemeID="GLN">1234567890123</cbc:ID>
</cac:PartyIdentification>
<cac:PartyIdentification>
<cbc:ID schemeID="VAT">DK12345678</cbc:ID>
</cac:PartyIdentification>
<cac:PartyIdentification>
<cbc:ID schemeID="DUNS">123456789</cbc:ID>
</cac:PartyIdentification>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Array Test Supplier</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Array Test Customer</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:PaymentMeans>
<cbc:PaymentMeansCode>30</cbc:PaymentMeansCode>
<cbc:PaymentID>PAY-001</cbc:PaymentID>
</cac:PaymentMeans>
<cac:PaymentMeans>
<cbc:PaymentMeansCode>31</cbc:PaymentMeansCode>
<cbc:PaymentID>PAY-002</cbc:PaymentID>
</cac:PaymentMeans>
</Invoice>`;
const einvoice = new EInvoice();
await einvoice.loadFromString(arrayInvoice);
const xmlString = einvoice.getXmlString();
// Verify arrays are preserved
expect(xmlString).toContain('First note');
expect(xmlString).toContain('Second note');
expect(xmlString).toContain('Third note with special chars: €£¥');
expect(xmlString).toContain('DOC-001');
expect(xmlString).toContain('DOC-002');
expect(xmlString).toContain('DOC-003');
expect(xmlString).toContain('1234567890123');
expect(xmlString).toContain('DK12345678');
expect(xmlString).toContain('123456789');
console.log('Array field mapping verified');
const elapsed = performance.now() - startTime;
performanceTracker.addMeasurement('array-mapping', elapsed);
});
t.test('Cross-reference field mapping', async () => {
const startTime = performance.now();
// Invoice with cross-references between sections
const crossRefInvoice = `<?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>XREF-TEST-001</cbc:ID>
<cbc:IssueDate>2025-01-25</cbc:IssueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cac:ProjectReference>
<cbc:ID>PROJ-2025-001</cbc:ID>
</cac:ProjectReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Cross Reference Supplier</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Cross Reference Customer</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:Delivery>
<cbc:ActualDeliveryDate>2025-01-20</cbc:ActualDeliveryDate>
<cac:DeliveryLocation>
<cbc:ID schemeID="GLN">5790000435999</cbc:ID>
<cac:Address>
<cbc:StreetName>Delivery Street</cbc:StreetName>
<cbc:CityName>Copenhagen</cbc:CityName>
</cac:Address>
</cac:DeliveryLocation>
</cac:Delivery>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:Note>Delivered to GLN: 5790000435999</cbc:Note>
<cbc:InvoicedQuantity unitCode="C62">10</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount>
<cac:OrderLineReference>
<cbc:LineID>ORDER-LINE-001</cbc:LineID>
</cac:OrderLineReference>
<cac:Item>
<cbc:Name>Product for PROJ-2025-001</cbc:Name>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
</Invoice>`;
const einvoice = new EInvoice();
await einvoice.loadFromString(crossRefInvoice);
const xmlString = einvoice.getXmlString();
// Verify cross-references are maintained
expect(xmlString).toContain('PROJ-2025-001');
expect(xmlString).toContain('5790000435999');
expect(xmlString).toContain('Delivered to GLN: 5790000435999');
expect(xmlString).toContain('Product for PROJ-2025-001');
expect(xmlString).toContain('ORDER-LINE-001');
console.log('Cross-reference field mapping verified');
const elapsed = performance.now() - startTime;
performanceTracker.addMeasurement('cross-reference', elapsed);
});
t.test('Corpus field mapping validation', async () => {
const startTime = performance.now();
let processedCount = 0;
let mappingIssues = 0;
const criticalFields = ['ID', 'IssueDate', 'DocumentCurrencyCode', 'AccountingSupplierParty', 'AccountingCustomerParty'];
const files = await corpusLoader.getAllFiles();
const xmlFiles = files.filter(f => f.endsWith('.xml') && !f.includes('.pdf'));
// Test field mapping on corpus files
const sampleSize = Math.min(30, xmlFiles.length);
const sample = xmlFiles.slice(0, sampleSize);
for (const file of sample) {
try {
const content = await corpusLoader.readFile(file);
const einvoice = new EInvoice();
if (typeof content === 'string') {
await einvoice.loadFromString(content);
} else {
await einvoice.loadFromBuffer(content);
}
const xmlString = einvoice.getXmlString();
const invoiceData = einvoice.getInvoiceData();
// Check critical field mapping
let hasIssue = false;
if (invoiceData) {
if (!invoiceData.invoiceNumber && xmlString.includes('<cbc:ID>')) {
console.log(`${file}: Invoice number not mapped`);
hasIssue = true;
}
if (!invoiceData.issueDate && xmlString.includes('<cbc:IssueDate>')) {
console.log(`${file}: Issue date not mapped`);
hasIssue = true;
}
if (!invoiceData.currency && xmlString.includes('<cbc:DocumentCurrencyCode>')) {
console.log(`${file}: Currency not mapped`);
hasIssue = true;
}
}
if (hasIssue) mappingIssues++;
processedCount++;
} catch (error) {
console.log(`Field mapping error in ${file}:`, error.message);
}
}
console.log(`Corpus field mapping validation (${processedCount} files):`);
console.log(`- Files with potential mapping issues: ${mappingIssues}`);
const elapsed = performance.now() - startTime;
performanceTracker.addMeasurement('corpus-validation', elapsed);
});
// Print performance summary
performanceTracker.printSummary();
// Performance assertions
const avgTime = performanceTracker.getAverageTime();
expect(avgTime).toBeLessThan(300); // Field mapping should be reasonably fast
});
tap.start();