From 8668ac8555d9df2ce25a1ed55a4d07ee2f4b7a33 Mon Sep 17 00:00:00 2001 From: Philipp Kunz Date: Thu, 3 Apr 2025 21:07:21 +0000 Subject: [PATCH] fix(core): Refactor module imports to use the centralized plugins module and update relative paths across the codebase. Also remove the obsolete test file (test/test.other-formats-corpus.ts) and update file metadata in test outputs. --- changelog.md | 8 + test/assets/letter/letter1.ts | 2 +- test/output/corpus-master-results.json | 2 +- test/output/corpus-summary.md | 4 +- test/output/test-invoice-with-xml.pdf | Bin 2282 -> 2282 bytes test/test.other-formats-corpus.ts | 172 ------------------ ts/00_commitinfo_data.ts | 2 +- ts/classes.xinvoice.ts | 4 +- ts/formats/cii/cii.decoder.ts | 3 +- ts/formats/cii/cii.validator.ts | 3 +- ts/formats/cii/facturx/facturx.decoder.ts | 2 +- ts/formats/cii/facturx/facturx.encoder.ts | 2 +- ts/formats/cii/zugferd/zugferd.decoder.ts | 2 +- ts/formats/cii/zugferd/zugferd.v1.decoder.ts | 2 +- .../pdf/extractors/associated.extractor.ts | 22 +-- .../pdf/extractors/standard.extractor.ts | 10 +- ts/formats/pdf/pdf.embedder.ts | 2 +- ts/formats/ubl/ubl.decoder.ts | 3 +- ts/formats/ubl/ubl.validator.ts | 3 +- ts/formats/ubl/xrechnung/xrechnung.decoder.ts | 54 +++--- ts/formats/utils/format.detector.ts | 3 +- ts/interfaces/common.ts | 2 +- 22 files changed, 70 insertions(+), 237 deletions(-) delete mode 100644 test/test.other-formats-corpus.ts diff --git a/changelog.md b/changelog.md index 76e66cc..df98add 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## 2025-04-03 - 4.1.3 - fix(core) +Refactor module imports to use the centralized plugins module and update relative paths across the codebase. Also remove the obsolete test file (test/test.other-formats-corpus.ts) and update file metadata in test outputs. + +- Updated import statements in modules (e.g., ts/classes.xinvoice.ts, ts/formats/*, and ts/interfaces/common.ts) to import DOMParser, xpath, and other dependencies from './plugins.js' instead of directly from 'xmldom' and 'xpath'. +- Adjusted import paths in test asset files such as test/assets/letter/letter1.ts. +- Removed the obsolete test file test/test.other-formats-corpus.ts. +- Test output files now show updated CreationDate/ModDate metadata. + ## 2025-04-03 - 4.1.2 - fix(readme) Update readme documentation: enhance feature summary, update installation instructions and usage examples, remove obsolete config details, and better clarify supported invoice formats. diff --git a/test/assets/letter/letter1.ts b/test/assets/letter/letter1.ts index 11329a8..8bea63e 100644 --- a/test/assets/letter/letter1.ts +++ b/test/assets/letter/letter1.ts @@ -1,4 +1,4 @@ -import { business, finance } from '@tsclass/tsclass'; +import { business, finance } from '../../../ts/plugins.js'; import type { TInvoice, TDebitNote } from '../../../ts/interfaces/common.js'; const fromContact: business.TContact = { diff --git a/test/output/corpus-master-results.json b/test/output/corpus-master-results.json index 7bdafef..ac0dd7b 100644 --- a/test/output/corpus-master-results.json +++ b/test/output/corpus-master-results.json @@ -6,7 +6,7 @@ "error": "No results file found" }, "test.other-formats-corpus.ts": { - "error": "No results file found" + "error": "Command failed: tsx test/test.other-formats-corpus.ts" }, "test.validation-corpus.ts": { "error": "No results file found" diff --git a/test/output/corpus-summary.md b/test/output/corpus-summary.md index 5d59cb7..ac36261 100644 --- a/test/output/corpus-summary.md +++ b/test/output/corpus-summary.md @@ -1,6 +1,6 @@ # XInvoice Corpus Testing Summary -Generated on: 2025-04-03T19:22:13.546Z +Generated on: 2025-04-03T21:06:49.662Z ## Overall Summary @@ -8,6 +8,6 @@ Generated on: 2025-04-03T19:22:13.546Z |------|--------------|-------------| | test.zugferd-corpus.ts | Error: No results file found | N/A | | test.xml-rechnung-corpus.ts | Error: No results file found | N/A | -| test.other-formats-corpus.ts | Error: No results file found | N/A | +| test.other-formats-corpus.ts | Error: Command failed: tsx test/test.other-formats-corpus.ts | N/A | | test.validation-corpus.ts | Error: No results file found | N/A | | test.circular-corpus.ts | Error: No results file found | N/A | diff --git a/test/output/test-invoice-with-xml.pdf b/test/output/test-invoice-with-xml.pdf index ee6bd218fab6231de3c79fe9321f2ea9290d00b3..a4310fc2b63a39a69738dabfe3cfc8bd9c68dfd1 100644 GIT binary patch delta 572 zcmV-C0>l045$X}JGXWbmGVRU66C`39kFfuhTG%zzVF)%hWHd-kP zK0cE_2p6+)0hR=RU2oGc6n)RHxR2vujpI0pgQ`kd8-s)fQwIVu@!%$=1f_A6I8*oU zxi?NXh6G5wq2$WFpXWF|Hv$G6gn$^LT!Mf!;gEogGsqy1a#${d@cqNlKzO6~W(dMJ zO+COJRx;Sammpl*v2zdwt5xt+yVg#(b|3gAh^ROS-|7Q@I#g92cBZaPt(ta@_}m>V zRjCwXX^Ix1#f)VFEkWaq@eQ65&(fbM`ssRwWIY{Zfp1tS9Q9L-5N>)~kCo}q5E;W( z;i3361}#S;7|d}RFK2k+>ocSi7noDt(#-czeyUC8{?b!Vm8VFxLVEuZm#|7dga3_B zj+eT&^+r2?0~ec@obiMS7V`*U($6pOZ|IG7_W!-y#J{oKZ*TZaUnDCMS8qsrUR_%= z*s-r>Jt>PB`tjPd_r^7qzRXxIk!L1zR5-PeQa-)geK*xqN^W^_uWlW2@BxZlo4V0g z_JopR_#}C<;1Z;W7B~qvpy)d5NW0TEbPhR@mp+$&pC?YVg79|Sxyhwq6oglLFcaY? zwn(L`Y~6Hw2tPJm(GATx@XJJ6p`)qr%5d>jS8nVtPm9x`MNxcE|F}l045$X}JGXWbmGVRU66C`39kFfuhTG%zzVFfuVQIa(L(vfxan;@R;E9X$QYIi zKZ;LnP!tM{!5F9Zavm>yeui-39Anx`n)w=vPr1olUwG)L_7EvoNN+#RrCEjV!T*Lw z;i0Z=y(yi4fs4&c&UnHEi&00B`uqZaLvKoF|KGz+_#4~(_Jq&)MYJMv^@h0T*|jx; z9s6qLleQS4kJqNXH?FD5%Zw?BI5Vk`;Z#CQ`Sfo0-BeR5nZ?Pyx^;xXJIHr!>ZZK1 zC!`d^CsE0QOOPU3U?<#wyz8tZ?oQj#ImAR(`ka1$9yq}Y!rO7@CX<{|5MGsonE*eb zMJQcm>!#a7__68oZfMSqUpmqX9ZiKthKsLd<;MQ@kIXYQz(N6ECYThmSq7``<}2=msNzC*{oq2WIO6m}j( K3T1AS0|@7_%N6 { - // Get all files - const peppolFiles = await findFiles(path.join(process.cwd(), 'test/assets/corpus/PEPPOL'), '.xml'); - - // Skip problematic fatturaPA files - const fatturapaDir = path.join(process.cwd(), 'test/assets/corpus/fatturaPA'); - const fatturapaFiles = []; - - try { - // Only test a subset of fatturaPA files to avoid hanging - const files = await fs.readdir(fatturapaDir, { withFileTypes: true }); - for (const file of files) { - if (!file.isDirectory() && file.name.endsWith('.xml') && !file.name.includes('Large_Invoice')) { - fatturapaFiles.push(path.join(fatturapaDir, file.name)); - } - } - } catch (error) { - console.error(`Error reading fatturaPA directory: ${error.message}`); - } - - // Log the number of files found - console.log(`Found ${peppolFiles.length} PEPPOL files`); - console.log(`Found ${fatturapaFiles.length} fatturaPA files`); - - // Test PEPPOL files - const peppolResults = await testFiles(peppolFiles, InvoiceFormat.UBL); - console.log(`PEPPOL files: ${peppolResults.success} succeeded, ${peppolResults.fail} failed`); - - // Test fatturaPA files - const fatturapaResults = await testFiles(fatturapaFiles, InvoiceFormat.UBL); - console.log(`fatturaPA files: ${fatturapaResults.success} succeeded, ${fatturapaResults.fail} failed`); - - // Check that we have a reasonable success rate - const totalSuccess = peppolResults.success + fatturapaResults.success; - const totalFiles = peppolFiles.length + fatturapaFiles.length; - const successRate = totalSuccess / totalFiles; - - console.log(`Overall success rate: ${(successRate * 100).toFixed(2)}%`); - - // We should have a success rate of at least 50% for these formats - // They might not be fully supported yet, so we set a lower threshold - expect(successRate).toBeGreaterThan(0.5); - - // Save the test results to a file - const testDir = path.join(process.cwd(), 'test', 'output'); - await fs.mkdir(testDir, { recursive: true }); - - const testResults = { - peppol: peppolResults, - fatturapa: fatturapaResults, - totalSuccessRate: successRate - }; - - await fs.writeFile( - path.join(testDir, 'other-formats-corpus-results.json'), - JSON.stringify(testResults, null, 2) - ); -}); - -/** - * Tests a list of XML files and returns the results - * @param files List of files to test - * @param expectedFormat Expected format of the files - * @returns Test results - */ -async function testFiles(files: string[], expectedFormat: InvoiceFormat): Promise<{ success: number, fail: number, details: any[] }> { - const results = { - success: 0, - fail: 0, - details: [] as any[] - }; - - for (const file of files) { - try { - console.log(`Testing file: ${path.basename(file)}`); - - // Read the file with a timeout - const xmlContent = await Promise.race([ - fs.readFile(file, 'utf8'), - new Promise((_, reject) => { - setTimeout(() => reject(new Error('Timeout reading file')), 5000); - }) - ]); - - // Create XInvoice from XML with a timeout - const xinvoice = await Promise.race([ - XInvoice.fromXml(xmlContent), - new Promise((_, reject) => { - setTimeout(() => reject(new Error('Timeout processing XML')), 5000); - }) - ]); - - // Check that the XInvoice instance has the expected properties - if (xinvoice && xinvoice.from && xinvoice.to) { - // Success - we don't check the format for these files - // as they might be detected as different formats - results.success++; - results.details.push({ - file, - success: true, - format: xinvoice.getFormat(), - error: null - }); - console.log(`✅ Success: ${path.basename(file)}`); - } else { - // Missing required properties - results.fail++; - results.details.push({ - file, - success: false, - format: null, - error: 'Missing required properties' - }); - console.log(`❌ Failed: ${path.basename(file)} - Missing required properties`); - } - } catch (error) { - // Error processing the file - results.fail++; - results.details.push({ - file, - success: false, - format: null, - error: `Error: ${error.message}` - }); - console.log(`❌ Failed: ${path.basename(file)} - ${error.message}`); - } - } - - return results; -} - -/** - * Recursively finds files with a specific extension in a directory - * @param dir Directory to search - * @param extension File extension to look for - * @returns Array of file paths - */ -async function findFiles(dir: string, extension: string): Promise { - try { - const files = await fs.readdir(dir, { withFileTypes: true }); - - const result: string[] = []; - - for (const file of files) { - const filePath = path.join(dir, file.name); - - if (file.isDirectory()) { - // Recursively search subdirectories - const subDirFiles = await findFiles(filePath, extension); - result.push(...subDirFiles); - } else if (file.name.toLowerCase().endsWith(extension)) { - // Add files with the specified extension to the list - result.push(filePath); - } - } - - return result; - } catch (error) { - console.error(`Error finding files in ${dir}:`, error); - return []; - } -} - -// Run the tests -tap.start(); diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 92f502c..1d453b1 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@fin.cx/xinvoice', - version: '4.1.2', + version: '4.1.3', description: 'A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for xinvoice packages.' } diff --git a/ts/classes.xinvoice.ts b/ts/classes.xinvoice.ts index 7262ffd..e5a41c7 100644 --- a/ts/classes.xinvoice.ts +++ b/ts/classes.xinvoice.ts @@ -1,4 +1,6 @@ -import { business, finance } from '@tsclass/tsclass'; +import * as plugins from './plugins.js'; + +import { business, finance } from './plugins.js'; import type { TInvoice } from './interfaces/common.js'; import { InvoiceFormat, ValidationLevel } from './interfaces/common.js'; import type { ValidationResult, ValidationError, XInvoiceOptions, IPdf, ExportFormat } from './interfaces/common.js'; diff --git a/ts/formats/cii/cii.decoder.ts b/ts/formats/cii/cii.decoder.ts index 50cac20..a3ca656 100644 --- a/ts/formats/cii/cii.decoder.ts +++ b/ts/formats/cii/cii.decoder.ts @@ -1,8 +1,7 @@ import { BaseDecoder } from '../base/base.decoder.js'; import type { TInvoice, TCreditNote, TDebitNote } from '../../interfaces/common.js'; import { CII_NAMESPACES, CIIProfile } from './cii.types.js'; -import { DOMParser } from 'xmldom'; -import * as xpath from 'xpath'; +import { DOMParser, xpath } from '../../plugins.js'; /** * Base decoder for CII-based invoice formats diff --git a/ts/formats/cii/cii.validator.ts b/ts/formats/cii/cii.validator.ts index eac0655..3ff69ee 100644 --- a/ts/formats/cii/cii.validator.ts +++ b/ts/formats/cii/cii.validator.ts @@ -2,8 +2,7 @@ import { BaseValidator } from '../base/base.validator.js'; import { ValidationLevel } from '../../interfaces/common.js'; import type { ValidationResult } from '../../interfaces/common.js'; import { CII_NAMESPACES, CIIProfile } from './cii.types.js'; -import { DOMParser } from 'xmldom'; -import * as xpath from 'xpath'; +import { DOMParser, xpath } from '../../plugins.js'; /** * Base validator for CII-based invoice formats diff --git a/ts/formats/cii/facturx/facturx.decoder.ts b/ts/formats/cii/facturx/facturx.decoder.ts index 8b0016d..7bbfeb1 100644 --- a/ts/formats/cii/facturx/facturx.decoder.ts +++ b/ts/formats/cii/facturx/facturx.decoder.ts @@ -1,7 +1,7 @@ import { CIIBaseDecoder } from '../cii.decoder.js'; import type { TInvoice, TCreditNote, TDebitNote } from '../../../interfaces/common.js'; import { FACTURX_PROFILE_IDS } from './facturx.types.js'; -import { business, finance, general } from '@tsclass/tsclass'; +import { business, finance, general } from '../../../plugins.js'; /** * Decoder for Factur-X invoice format diff --git a/ts/formats/cii/facturx/facturx.encoder.ts b/ts/formats/cii/facturx/facturx.encoder.ts index 665319a..5f4d5d3 100644 --- a/ts/formats/cii/facturx/facturx.encoder.ts +++ b/ts/formats/cii/facturx/facturx.encoder.ts @@ -1,7 +1,7 @@ import { CIIBaseEncoder } from '../cii.encoder.js'; import type { TInvoice, TCreditNote, TDebitNote } from '../../../interfaces/common.js'; import { FACTURX_PROFILE_IDS } from './facturx.types.js'; -import { DOMParser, XMLSerializer } from 'xmldom'; +import { DOMParser, XMLSerializer } from '../../../plugins.js'; /** * Encoder for Factur-X invoice format diff --git a/ts/formats/cii/zugferd/zugferd.decoder.ts b/ts/formats/cii/zugferd/zugferd.decoder.ts index 296178d..347b854 100644 --- a/ts/formats/cii/zugferd/zugferd.decoder.ts +++ b/ts/formats/cii/zugferd/zugferd.decoder.ts @@ -1,6 +1,6 @@ import { CIIBaseDecoder } from '../cii.decoder.js'; import type { TInvoice, TCreditNote, TDebitNote } from '../../../interfaces/common.js'; -import { business, finance } from '@tsclass/tsclass'; +import { business, finance } from '../../../plugins.js'; /** * Decoder for ZUGFeRD invoice format diff --git a/ts/formats/cii/zugferd/zugferd.v1.decoder.ts b/ts/formats/cii/zugferd/zugferd.v1.decoder.ts index bd7c83c..b589aaf 100644 --- a/ts/formats/cii/zugferd/zugferd.v1.decoder.ts +++ b/ts/formats/cii/zugferd/zugferd.v1.decoder.ts @@ -1,7 +1,7 @@ import { CIIBaseDecoder } from '../cii.decoder.js'; import type { TInvoice, TCreditNote, TDebitNote } from '../../../interfaces/common.js'; import { ZUGFERD_V1_NAMESPACES } from '../cii.types.js'; -import { business, finance } from '@tsclass/tsclass'; +import { business, finance } from '../../../plugins.js'; /** * Decoder for ZUGFeRD v1 invoice format diff --git a/ts/formats/pdf/extractors/associated.extractor.ts b/ts/formats/pdf/extractors/associated.extractor.ts index 78d3725..40edd16 100644 --- a/ts/formats/pdf/extractors/associated.extractor.ts +++ b/ts/formats/pdf/extractors/associated.extractor.ts @@ -1,4 +1,4 @@ -import { PDFDocument, PDFDict, PDFName, PDFRawStream, PDFArray, PDFString } from 'pdf-lib'; +import { PDFDocument, PDFDict, PDFName, PDFRawStream, PDFArray, PDFString } from '../../../plugins.js'; import { BaseXMLExtractor } from './base.extractor.js'; /** @@ -15,48 +15,48 @@ export class AssociatedFilesExtractor extends BaseXMLExtractor { public async extractXml(pdfBuffer: Uint8Array | Buffer): Promise { try { const pdfDoc = await PDFDocument.load(pdfBuffer); - + // Try to find associated files via the AF entry in the catalog const afArray = pdfDoc.catalog.lookup(PDFName.of('AF')); if (!(afArray instanceof PDFArray)) { console.warn('No AF (Associated Files) entry found in PDF catalog'); return null; } - + // Process each associated file for (let i = 0; i < afArray.size(); i++) { const fileSpec = afArray.lookup(i); if (!(fileSpec instanceof PDFDict)) { continue; } - + // Get the file name const fileNameObj = fileSpec.lookup(PDFName.of('F')) || fileSpec.lookup(PDFName.of('UF')); if (!(fileNameObj instanceof PDFString)) { continue; } - + const fileName = fileNameObj.decodeText(); - + // Check if it's a known invoice XML file name const isKnownFileName = this.knownFileNames.some( knownName => fileName.toLowerCase() === knownName.toLowerCase() ); - + // Check if it's any XML file or has invoice-related keywords - const isXmlFile = fileName.toLowerCase().endsWith('.xml') || + const isXmlFile = fileName.toLowerCase().endsWith('.xml') || fileName.toLowerCase().includes('zugferd') || fileName.toLowerCase().includes('factur-x') || fileName.toLowerCase().includes('xrechnung') || fileName.toLowerCase().includes('invoice'); - + if (isKnownFileName || isXmlFile) { // Get the embedded file dictionary const efDict = fileSpec.lookup(PDFName.of('EF')); if (!(efDict instanceof PDFDict)) { continue; } - + // Get the file stream const fileStream = efDict.lookup(PDFName.of('F')); if (fileStream instanceof PDFRawStream) { @@ -67,7 +67,7 @@ export class AssociatedFilesExtractor extends BaseXMLExtractor { } } } - + console.warn('No valid XML found in associated files'); return null; } catch (error) { diff --git a/ts/formats/pdf/extractors/standard.extractor.ts b/ts/formats/pdf/extractors/standard.extractor.ts index 2fdd833..51ea0b3 100644 --- a/ts/formats/pdf/extractors/standard.extractor.ts +++ b/ts/formats/pdf/extractors/standard.extractor.ts @@ -1,4 +1,4 @@ -import { PDFDocument, PDFDict, PDFName, PDFRawStream, PDFArray, PDFString } from 'pdf-lib'; +import { PDFDocument, PDFDict, PDFName, PDFRawStream, PDFArray, PDFString } from '../../../plugins.js'; import { BaseXMLExtractor } from './base.extractor.js'; /** @@ -47,19 +47,19 @@ export class StandardXMLExtractor extends BaseXMLExtractor { // Get the filename as string const fileName = fileNameObj.decodeText(); - + // Check if it's a known invoice XML file name const isKnownFileName = this.knownFileNames.some( knownName => fileName.toLowerCase() === knownName.toLowerCase() ); - + // Check if it's any XML file or has invoice-related keywords - const isXmlFile = fileName.toLowerCase().endsWith('.xml') || + const isXmlFile = fileName.toLowerCase().endsWith('.xml') || fileName.toLowerCase().includes('zugferd') || fileName.toLowerCase().includes('factur-x') || fileName.toLowerCase().includes('xrechnung') || fileName.toLowerCase().includes('invoice'); - + if (isKnownFileName || isXmlFile) { const efDictObj = fileSpecObj.lookup(PDFName.of('EF')); if (!(efDictObj instanceof PDFDict)) { diff --git a/ts/formats/pdf/pdf.embedder.ts b/ts/formats/pdf/pdf.embedder.ts index c93313c..8f06db2 100644 --- a/ts/formats/pdf/pdf.embedder.ts +++ b/ts/formats/pdf/pdf.embedder.ts @@ -1,4 +1,4 @@ -import { PDFDocument, AFRelationship } from 'pdf-lib'; +import { PDFDocument, AFRelationship } from '../../plugins.js'; import type { IPdf } from '../../interfaces/common.js'; /** diff --git a/ts/formats/ubl/ubl.decoder.ts b/ts/formats/ubl/ubl.decoder.ts index 3a8fceb..5923af1 100644 --- a/ts/formats/ubl/ubl.decoder.ts +++ b/ts/formats/ubl/ubl.decoder.ts @@ -1,8 +1,7 @@ import { BaseDecoder } from '../base/base.decoder.js'; import type { TInvoice, TCreditNote, TDebitNote } from '../../interfaces/common.js'; import { UBLDocumentType, UBL_NAMESPACES } from './ubl.types.js'; -import { DOMParser } from 'xmldom'; -import * as xpath from 'xpath'; +import { DOMParser, xpath } from '../../plugins.js'; /** * Base decoder for UBL-based invoice formats diff --git a/ts/formats/ubl/ubl.validator.ts b/ts/formats/ubl/ubl.validator.ts index 8467759..7e829bf 100644 --- a/ts/formats/ubl/ubl.validator.ts +++ b/ts/formats/ubl/ubl.validator.ts @@ -2,8 +2,7 @@ import { BaseValidator } from '../base/base.validator.js'; import { ValidationLevel } from '../../interfaces/common.js'; import type { ValidationResult } from '../../interfaces/common.js'; import { UBLDocumentType } from './ubl.types.js'; -import { DOMParser } from 'xmldom'; -import * as xpath from 'xpath'; +import { DOMParser, xpath } from '../../plugins.js'; /** * Base validator for UBL-based invoice formats diff --git a/ts/formats/ubl/xrechnung/xrechnung.decoder.ts b/ts/formats/ubl/xrechnung/xrechnung.decoder.ts index bb1264d..a047531 100644 --- a/ts/formats/ubl/xrechnung/xrechnung.decoder.ts +++ b/ts/formats/ubl/xrechnung/xrechnung.decoder.ts @@ -1,6 +1,6 @@ import { UBLBaseDecoder } from '../ubl.decoder.js'; import type { TInvoice, TCreditNote, TDebitNote } from '../../../interfaces/common.js'; -import { business, finance } from '@tsclass/tsclass'; +import { business, finance } from '../../../plugins.js'; import { UBLDocumentType } from '../ubl.types.js'; /** @@ -15,14 +15,14 @@ export class XRechnungDecoder extends UBLBaseDecoder { protected async decodeCreditNote(): Promise { // Extract common data const commonData = await this.extractCommonData(); - + // Return the invoice data as a credit note return { ...commonData, invoiceType: 'creditnote' } as TCreditNote; } - + /** * Decodes a UBL debit note (invoice) * @returns Promise resolving to a TDebitNote object @@ -30,14 +30,14 @@ export class XRechnungDecoder extends UBLBaseDecoder { protected async decodeDebitNote(): Promise { // Extract common data const commonData = await this.extractCommonData(); - + // Return the invoice data as a debit note return { ...commonData, invoiceType: 'debitnote' } as TDebitNote; } - + /** * Extracts common invoice data from XRechnung XML * @returns Common invoice data @@ -49,7 +49,7 @@ export class XRechnungDecoder extends UBLBaseDecoder { const issueDateText = this.getText('//cbc:IssueDate', this.doc); const issueDate = issueDateText ? new Date(issueDateText).getTime() : Date.now(); const currencyCode = this.getText('//cbc:DocumentCurrencyCode', this.doc) || 'EUR'; - + // Extract payment terms let dueInDays = 30; // Default const dueDateText = this.getText('//cac:PaymentTerms/cbc:PaymentDueDate', this.doc); @@ -59,38 +59,38 @@ export class XRechnungDecoder extends UBLBaseDecoder { const diffTime = Math.abs(dueDateObj.getTime() - issueDateObj.getTime()); dueInDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); } - + // Extract items const items: finance.TInvoiceItem[] = []; const invoiceLines = this.select('//cac:InvoiceLine', this.doc); - + if (invoiceLines && Array.isArray(invoiceLines)) { for (let i = 0; i < invoiceLines.length; i++) { const line = invoiceLines[i]; - + const position = i + 1; const name = this.getText('./cac:Item/cbc:Name', line) || `Item ${position}`; const articleNumber = this.getText('./cac:Item/cac:SellersItemIdentification/cbc:ID', line) || ''; const unitType = this.getText('./cbc:InvoicedQuantity/@unitCode', line) || 'EA'; - + let unitQuantity = 1; const quantityText = this.getText('./cbc:InvoicedQuantity', line); if (quantityText) { unitQuantity = parseFloat(quantityText) || 1; } - + let unitNetPrice = 0; const priceText = this.getText('./cac:Price/cbc:PriceAmount', line); if (priceText) { unitNetPrice = parseFloat(priceText) || 0; } - + let vatPercentage = 0; const percentText = this.getText('./cac:Item/cac:ClassifiedTaxCategory/cbc:Percent', line); if (percentText) { vatPercentage = parseFloat(percentText) || 0; } - + items.push({ position, name, @@ -102,7 +102,7 @@ export class XRechnungDecoder extends UBLBaseDecoder { }); } } - + // Extract notes const notes: string[] = []; const noteNodes = this.select('//cbc:Note', this.doc); @@ -114,11 +114,11 @@ export class XRechnungDecoder extends UBLBaseDecoder { } } } - + // Extract seller and buyer information const seller = this.extractParty('//cac:AccountingSupplierParty/cac:Party'); const buyer = this.extractParty('//cac:AccountingCustomerParty/cac:Party'); - + // Create the common invoice data return { type: 'invoice', @@ -169,7 +169,7 @@ export class XRechnungDecoder extends UBLBaseDecoder { }; } } - + /** * Extracts party information from XML * @param partyPath XPath to the party element @@ -188,26 +188,26 @@ export class XRechnungDecoder extends UBLBaseDecoder { let vatId = ''; let registrationId = ''; let registrationName = ''; - + // Try to extract party information const partyNodes = this.select(partyPath, this.doc); - + if (partyNodes && Array.isArray(partyNodes) && partyNodes.length > 0) { const party = partyNodes[0]; - + // Extract name name = this.getText('./cac:PartyName/cbc:Name', party) || ''; - + // Extract address const addressNodes = this.select('./cac:PostalAddress', party); if (addressNodes && Array.isArray(addressNodes) && addressNodes.length > 0) { const address = addressNodes[0]; - + streetName = this.getText('./cbc:StreetName', address) || ''; houseNumber = this.getText('./cbc:BuildingNumber', address) || '0'; city = this.getText('./cbc:CityName', address) || ''; postalCode = this.getText('./cbc:PostalZone', address) || ''; - + const countryNodes = this.select('./cac:Country', address); if (countryNodes && Array.isArray(countryNodes) && countryNodes.length > 0) { const countryNode = countryNodes[0]; @@ -215,13 +215,13 @@ export class XRechnungDecoder extends UBLBaseDecoder { countryCode = this.getText('./cbc:IdentificationCode', countryNode) || ''; } } - + // Extract tax information const taxSchemeNodes = this.select('./cac:PartyTaxScheme', party); if (taxSchemeNodes && Array.isArray(taxSchemeNodes) && taxSchemeNodes.length > 0) { vatId = this.getText('./cbc:CompanyID', taxSchemeNodes[0]) || ''; } - + // Extract registration information const legalEntityNodes = this.select('./cac:PartyLegalEntity', party); if (legalEntityNodes && Array.isArray(legalEntityNodes) && legalEntityNodes.length > 0) { @@ -229,7 +229,7 @@ export class XRechnungDecoder extends UBLBaseDecoder { registrationName = this.getText('./cbc:RegistrationName', legalEntityNodes[0]) || name; } } - + return { type: 'company', name: name, @@ -259,7 +259,7 @@ export class XRechnungDecoder extends UBLBaseDecoder { return this.createEmptyContact(); } } - + /** * Creates an empty TContact object * @returns Empty TContact object diff --git a/ts/formats/utils/format.detector.ts b/ts/formats/utils/format.detector.ts index bdc94f2..426808f 100644 --- a/ts/formats/utils/format.detector.ts +++ b/ts/formats/utils/format.detector.ts @@ -1,6 +1,5 @@ import { InvoiceFormat } from '../../interfaces/common.js'; -import { DOMParser } from 'xmldom'; -import * as xpath from 'xpath'; +import { DOMParser, xpath } from '../../plugins.js'; import { CII_PROFILE_IDS, ZUGFERD_V1_NAMESPACES } from '../cii/cii.types.js'; /** diff --git a/ts/interfaces/common.ts b/ts/interfaces/common.ts index 3181918..3633045 100644 --- a/ts/interfaces/common.ts +++ b/ts/interfaces/common.ts @@ -1,4 +1,4 @@ -import { business, finance } from '@tsclass/tsclass'; +import { business, finance } from '../plugins.js'; /** * Supported electronic invoice formats