@fin.cx/einvoice 5.2.0 → 5.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.smartconfig.json +29 -11
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/einvoice.d.ts +2 -2
- package/dist_ts/einvoice.js +11 -9
- package/dist_ts/formats/cii/facturx/facturx.encoder.js +25 -16
- package/dist_ts/formats/pdf/extractors/associated.extractor.js +3 -2
- package/dist_ts/formats/pdf/extractors/base.extractor.d.ts +1 -1
- package/dist_ts/formats/pdf/extractors/base.extractor.js +2 -2
- package/dist_ts/formats/pdf/extractors/standard.extractor.js +3 -2
- package/dist_ts/formats/pdf/pdf.embedder.js +3 -2
- package/dist_ts/formats/pdf/pdf.extractor.d.ts +2 -1
- package/dist_ts/formats/pdf/pdf.extractor.js +20 -10
- package/dist_ts/formats/validation/schematron.downloader.d.ts +0 -1
- package/dist_ts/formats/validation/schematron.downloader.js +1 -3
- package/dist_ts/formats/validation/schematron.integration.js +5 -2
- package/dist_ts/formats/validation/schematron.validator.d.ts +9 -11
- package/dist_ts/formats/validation/schematron.validator.js +61 -67
- package/dist_ts/formats/validation/schematron.worker.d.ts +7 -6
- package/dist_ts/formats/validation/schematron.worker.js +44 -39
- package/dist_ts/plugins.d.ts +1 -3
- package/dist_ts/plugins.js +1 -9
- package/dist_ts/vendor/pdflib.d.ts +2 -0
- package/dist_ts/vendor/pdflib.js +9 -0
- package/dist_ts/vendor/saxonjs.d.ts +4 -3
- package/dist_ts/vendor/saxonjs.js +9 -3
- package/package.json +22 -18
- package/readme.md +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/einvoice.ts +12 -11
- package/ts/formats/cii/facturx/facturx.encoder.ts +25 -15
- package/ts/formats/pdf/extractors/associated.extractor.ts +2 -1
- package/ts/formats/pdf/extractors/base.extractor.ts +3 -2
- package/ts/formats/pdf/extractors/standard.extractor.ts +2 -1
- package/ts/formats/pdf/pdf.embedder.ts +6 -3
- package/ts/formats/pdf/pdf.extractor.ts +24 -16
- package/ts/formats/validation/schematron.downloader.ts +0 -2
- package/ts/formats/validation/schematron.integration.ts +4 -1
- package/ts/formats/validation/schematron.validator.ts +72 -73
- package/ts/formats/validation/schematron.worker.ts +59 -41
- package/ts/plugins.ts +0 -26
- package/ts/readme.md +21 -0
- package/ts/vendor/pdflib.ts +12 -0
- package/ts/vendor/saxonjs.ts +13 -5
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Worker } from 'worker_threads';
|
|
2
|
-
import * as path from 'path';
|
|
3
2
|
/**
|
|
4
3
|
* Worker pool for Schematron validation
|
|
5
4
|
* Provides non-blocking validation in worker threads
|
|
@@ -9,52 +8,76 @@ export class SchematronWorkerPool {
|
|
|
9
8
|
availableWorkers = [];
|
|
10
9
|
taskQueue = [];
|
|
11
10
|
maxWorkers;
|
|
12
|
-
|
|
11
|
+
stylesheetFileName;
|
|
12
|
+
stylesheetInternal;
|
|
13
13
|
constructor(maxWorkers = 4) {
|
|
14
14
|
this.maxWorkers = maxWorkers;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* Initialize worker pool
|
|
18
18
|
*/
|
|
19
|
-
async initialize(
|
|
20
|
-
this.
|
|
19
|
+
async initialize(stylesheetSource, options = {}) {
|
|
20
|
+
this.loadStylesheet(stylesheetSource, options);
|
|
21
21
|
// Create workers
|
|
22
22
|
for (let i = 0; i < this.maxWorkers; i++) {
|
|
23
23
|
await this.createWorker();
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
+
loadStylesheet(stylesheetSource, options) {
|
|
27
|
+
const source = stylesheetSource.trim();
|
|
28
|
+
const isFilePath = options.isFilePath ?? /\.sef\.json$/i.test(stylesheetSource);
|
|
29
|
+
this.stylesheetFileName = undefined;
|
|
30
|
+
this.stylesheetInternal = undefined;
|
|
31
|
+
if (isFilePath) {
|
|
32
|
+
if (!/\.sef\.json$/i.test(stylesheetSource)) {
|
|
33
|
+
throw new Error('Schematron worker validation requires a precompiled Saxon-JS SEF JSON stylesheet file.');
|
|
34
|
+
}
|
|
35
|
+
this.stylesheetFileName = stylesheetSource;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (!source.startsWith('{')) {
|
|
39
|
+
throw new Error('Schematron worker validation requires a precompiled Saxon-JS SEF JSON stylesheet. Raw .sch files are not executed.');
|
|
40
|
+
}
|
|
41
|
+
this.stylesheetInternal = JSON.parse(source);
|
|
42
|
+
}
|
|
26
43
|
/**
|
|
27
44
|
* Create a new worker
|
|
28
45
|
*/
|
|
29
46
|
async createWorker() {
|
|
30
|
-
const workerPath = path.join(import.meta.url, 'schematron.worker.impl.js');
|
|
31
47
|
const worker = new Worker(`
|
|
32
48
|
const { parentPort } = require('worker_threads');
|
|
33
49
|
const SaxonJS = require('saxon-js');
|
|
34
50
|
|
|
35
|
-
let
|
|
51
|
+
let stylesheetFileName = null;
|
|
52
|
+
let stylesheetInternal = null;
|
|
36
53
|
|
|
37
54
|
parentPort.on('message', async (msg) => {
|
|
38
55
|
try {
|
|
39
56
|
if (msg.type === 'init') {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
57
|
+
stylesheetFileName = msg.stylesheetFileName || null;
|
|
58
|
+
stylesheetInternal = msg.stylesheetInternal || null;
|
|
59
|
+
if (!stylesheetFileName && !stylesheetInternal) {
|
|
60
|
+
throw new Error('Worker not initialized with a SEF stylesheet');
|
|
61
|
+
}
|
|
45
62
|
parentPort.postMessage({ type: 'ready' });
|
|
46
63
|
} else if (msg.type === 'validate') {
|
|
47
|
-
if (!
|
|
64
|
+
if (!stylesheetFileName && !stylesheetInternal) {
|
|
48
65
|
throw new Error('Worker not initialized');
|
|
49
66
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const result = await SaxonJS.transform({
|
|
53
|
-
stylesheetInternal: compiledStylesheet,
|
|
67
|
+
|
|
68
|
+
const transformOptions = {
|
|
54
69
|
sourceText: msg.xmlContent,
|
|
55
70
|
destination: 'serialized',
|
|
56
71
|
stylesheetParams: msg.options.parameters || {}
|
|
57
|
-
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
if (stylesheetFileName) {
|
|
75
|
+
transformOptions.stylesheetFileName = stylesheetFileName;
|
|
76
|
+
} else {
|
|
77
|
+
transformOptions.stylesheetInternal = stylesheetInternal;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const result = await SaxonJS.transform(transformOptions, 'async');
|
|
58
81
|
|
|
59
82
|
parentPort.postMessage({
|
|
60
83
|
type: 'result',
|
|
@@ -69,7 +92,7 @@ export class SchematronWorkerPool {
|
|
|
69
92
|
}
|
|
70
93
|
});
|
|
71
94
|
`, { eval: true });
|
|
72
|
-
// Initialize worker with
|
|
95
|
+
// Initialize worker with a precompiled SEF stylesheet.
|
|
73
96
|
await new Promise((resolve, reject) => {
|
|
74
97
|
worker.once('message', (msg) => {
|
|
75
98
|
if (msg.type === 'ready') {
|
|
@@ -82,7 +105,8 @@ export class SchematronWorkerPool {
|
|
|
82
105
|
// Send initialization message
|
|
83
106
|
worker.postMessage({
|
|
84
107
|
type: 'init',
|
|
85
|
-
|
|
108
|
+
stylesheetFileName: this.stylesheetFileName,
|
|
109
|
+
stylesheetInternal: this.stylesheetInternal
|
|
86
110
|
});
|
|
87
111
|
});
|
|
88
112
|
this.workers.push(worker);
|
|
@@ -144,25 +168,6 @@ export class SchematronWorkerPool {
|
|
|
144
168
|
// Simplified for brevity
|
|
145
169
|
return results;
|
|
146
170
|
}
|
|
147
|
-
/**
|
|
148
|
-
* Generate XSLT from Schematron (simplified)
|
|
149
|
-
*/
|
|
150
|
-
generateXSLTFromSchematron(schematron) {
|
|
151
|
-
// Simplified - would use ISO Schematron skeleton in production
|
|
152
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
153
|
-
<xsl:stylesheet version="3.0"
|
|
154
|
-
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
|
155
|
-
xmlns:svrl="http://purl.oclc.org/dsdl/svrl">
|
|
156
|
-
|
|
157
|
-
<xsl:output method="xml" indent="yes"/>
|
|
158
|
-
|
|
159
|
-
<xsl:template match="/">
|
|
160
|
-
<svrl:schematron-output>
|
|
161
|
-
<svrl:active-pattern document="{base-uri(/)}"/>
|
|
162
|
-
</svrl:schematron-output>
|
|
163
|
-
</xsl:template>
|
|
164
|
-
</xsl:stylesheet>`;
|
|
165
|
-
}
|
|
166
171
|
/**
|
|
167
172
|
* Terminate all workers
|
|
168
173
|
*/
|
|
@@ -183,4 +188,4 @@ export class SchematronWorkerPool {
|
|
|
183
188
|
};
|
|
184
189
|
}
|
|
185
190
|
}
|
|
186
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
191
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hdHJvbi53b3JrZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9mb3JtYXRzL3ZhbGlkYXRpb24vc2NoZW1hdHJvbi53b3JrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBUXhDOzs7R0FHRztBQUNILE1BQU0sT0FBTyxvQkFBb0I7SUFDdkIsT0FBTyxHQUFhLEVBQUUsQ0FBQztJQUN2QixnQkFBZ0IsR0FBYSxFQUFFLENBQUM7SUFDaEMsU0FBUyxHQUtaLEVBQUUsQ0FBQztJQUNBLFVBQVUsQ0FBUztJQUNuQixrQkFBa0IsQ0FBVTtJQUM1QixrQkFBa0IsQ0FBVztJQUVyQyxZQUFZLGFBQXFCLENBQUM7UUFDaEMsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUM7SUFDL0IsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLFVBQVUsQ0FDckIsZ0JBQXdCLEVBQ3hCLFVBQWtELEVBQUU7UUFFcEQsSUFBSSxDQUFDLGNBQWMsQ0FBQyxnQkFBZ0IsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUUvQyxpQkFBaUI7UUFDakIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUN6QyxNQUFNLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUM1QixDQUFDO0lBQ0gsQ0FBQztJQUVPLGNBQWMsQ0FDcEIsZ0JBQXdCLEVBQ3hCLE9BQStDO1FBRS9DLE1BQU0sTUFBTSxHQUFHLGdCQUFnQixDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3ZDLE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxVQUFVLElBQUksZUFBZSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBRWhGLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxTQUFTLENBQUM7UUFDcEMs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
|
package/dist_ts/plugins.d.ts
CHANGED
|
@@ -6,10 +6,8 @@
|
|
|
6
6
|
import * as fs from 'fs/promises';
|
|
7
7
|
import * as path from 'path';
|
|
8
8
|
import * as crypto from 'crypto';
|
|
9
|
-
import { PDFDocument, PDFDict, PDFName, PDFRawStream, PDFArray, PDFString, AFRelationship } from 'pdf-lib';
|
|
10
9
|
import { DOMParser, XMLSerializer, xmldom } from './vendor/xmldom.js';
|
|
11
10
|
import * as xpath from 'xpath';
|
|
12
|
-
import { SaxonJS } from './vendor/saxonjs.js';
|
|
13
11
|
import { pako } from './vendor/pako.js';
|
|
14
12
|
import { business, finance, general } from '@tsclass/tsclass';
|
|
15
|
-
export { fs, path, crypto,
|
|
13
|
+
export { fs, path, crypto, DOMParser, XMLSerializer, xmldom, xpath, pako, business, finance, general };
|
package/dist_ts/plugins.js
CHANGED
|
@@ -7,13 +7,9 @@
|
|
|
7
7
|
import * as fs from 'fs/promises';
|
|
8
8
|
import * as path from 'path';
|
|
9
9
|
import * as crypto from 'crypto';
|
|
10
|
-
// PDF-related imports
|
|
11
|
-
import { PDFDocument, PDFDict, PDFName, PDFRawStream, PDFArray, PDFString, AFRelationship } from 'pdf-lib';
|
|
12
10
|
// XML-related imports
|
|
13
11
|
import { DOMParser, XMLSerializer, xmldom } from './vendor/xmldom.js';
|
|
14
12
|
import * as xpath from 'xpath';
|
|
15
|
-
// XSLT/Schematron imports
|
|
16
|
-
import { SaxonJS } from './vendor/saxonjs.js';
|
|
17
13
|
// Compression-related imports
|
|
18
14
|
import { pako } from './vendor/pako.js';
|
|
19
15
|
// Business model imports
|
|
@@ -22,14 +18,10 @@ import { business, finance, general } from '@tsclass/tsclass';
|
|
|
22
18
|
export {
|
|
23
19
|
// Node.js built-in modules
|
|
24
20
|
fs, path, crypto,
|
|
25
|
-
// PDF-lib exports
|
|
26
|
-
PDFDocument, PDFDict, PDFName, PDFRawStream, PDFArray, PDFString, AFRelationship,
|
|
27
21
|
// XML-related exports
|
|
28
22
|
DOMParser, XMLSerializer, xmldom, xpath,
|
|
29
|
-
// XSLT/Schematron exports
|
|
30
|
-
SaxonJS,
|
|
31
23
|
// Compression-related exports
|
|
32
24
|
pako,
|
|
33
25
|
// Business model exports
|
|
34
26
|
business, finance, general };
|
|
35
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
27
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7R0FJRztBQUVILDJCQUEyQjtBQUMzQixPQUFPLEtBQUssRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNsQyxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUM3QixPQUFPLEtBQUssTUFBTSxNQUFNLFFBQVEsQ0FBQztBQUVqQyxzQkFBc0I7QUFDdEIsT0FBTyxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDdEUsT0FBTyxLQUFLLEtBQUssTUFBTSxPQUFPLENBQUM7QUFFL0IsOEJBQThCO0FBQzlCLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUV4Qyx5QkFBeUI7QUFDekIsT0FBTyxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFOUQsd0JBQXdCO0FBQ3hCLE9BQU87QUFDTCwyQkFBMkI7QUFDM0IsRUFBRSxFQUNGLElBQUksRUFDSixNQUFNO0FBRU4sc0JBQXNCO0FBQ3RCLFNBQVMsRUFDVCxhQUFhLEVBQ2IsTUFBTSxFQUNOLEtBQUs7QUFFTCw4QkFBOEI7QUFDOUIsSUFBSTtBQUVKLHlCQUF5QjtBQUN6QixRQUFRLEVBQ1IsT0FBTyxFQUNQLE9BQU8sRUFDUixDQUFDIn0=
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
let cachedPdfLib;
|
|
2
|
+
// Use pdf-lib's bundled UMD build; the package entry requires JSON font assets,
|
|
3
|
+
// which some TS runners corrupt with require hooks.
|
|
4
|
+
const nativeImport = Function('specifier', 'return import(specifier)');
|
|
5
|
+
export const loadPdfLib = async () => {
|
|
6
|
+
cachedPdfLib ||= nativeImport('pdf-lib/dist/pdf-lib.js');
|
|
7
|
+
return cachedPdfLib;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGRmbGliLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvdmVuZG9yL3BkZmxpYi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxJQUFJLFlBQWdELENBQUM7QUFFckQsZ0ZBQWdGO0FBQ2hGLG9EQUFvRDtBQUNwRCxNQUFNLFlBQVksR0FBRyxRQUFRLENBQUMsV0FBVyxFQUFFLDBCQUEwQixDQUFrRCxDQUFDO0FBRXhILE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRyxLQUFLLElBQTRCLEVBQUU7SUFDM0QsWUFBWSxLQUFLLFlBQVksQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO0lBQ3pELE9BQU8sWUFBWSxDQUFDO0FBQ3RCLENBQUMsQ0FBQyJ9
|
|
@@ -5,6 +5,7 @@ export interface ISaxonJSCompileOptions {
|
|
|
5
5
|
}
|
|
6
6
|
export interface ISaxonJSTransformOptions {
|
|
7
7
|
stylesheetInternal?: unknown;
|
|
8
|
+
stylesheetFileName?: string;
|
|
8
9
|
sourceText: string;
|
|
9
10
|
destination?: string;
|
|
10
11
|
stylesheetParams?: Record<string, unknown>;
|
|
@@ -15,8 +16,8 @@ export interface ISaxonJSTransformResult {
|
|
|
15
16
|
[key: string]: unknown;
|
|
16
17
|
}
|
|
17
18
|
export interface ISaxonJSModule {
|
|
18
|
-
compile(options: ISaxonJSCompileOptions)
|
|
19
|
-
transform(options: ISaxonJSTransformOptions): Promise<ISaxonJSTransformResult
|
|
19
|
+
compile?: (options: ISaxonJSCompileOptions) => Promise<unknown>;
|
|
20
|
+
transform(options: ISaxonJSTransformOptions, executionMode?: 'async' | 'sync'): Promise<ISaxonJSTransformResult> | ISaxonJSTransformResult;
|
|
20
21
|
[key: string]: unknown;
|
|
21
22
|
}
|
|
22
|
-
export declare const
|
|
23
|
+
export declare const loadSaxonJS: () => Promise<ISaxonJSModule>;
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
export const
|
|
3
|
-
|
|
1
|
+
let cachedSaxonJS;
|
|
2
|
+
export const loadSaxonJS = async () => {
|
|
3
|
+
cachedSaxonJS ||= import('saxon-js').then((saxonJSRuntime) => {
|
|
4
|
+
const runtime = saxonJSRuntime.default ?? saxonJSRuntime;
|
|
5
|
+
return runtime;
|
|
6
|
+
});
|
|
7
|
+
return cachedSaxonJS;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2F4b25qcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3ZlbmRvci9zYXhvbmpzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQTBCQSxJQUFJLGFBQWtELENBQUM7QUFFdkQsTUFBTSxDQUFDLE1BQU0sV0FBVyxHQUFHLEtBQUssSUFBNkIsRUFBRTtJQUM3RCxhQUFhLEtBQUssTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLGNBQWMsRUFBRSxFQUFFO1FBQzNELE1BQU0sT0FBTyxHQUFJLGNBQXNCLENBQUMsT0FBTyxJQUFJLGNBQWMsQ0FBQztRQUNsRSxPQUFPLE9BQXlCLENBQUM7SUFDbkMsQ0FBQyxDQUFDLENBQUM7SUFFSCxPQUFPLGFBQWEsQ0FBQztBQUN2QixDQUFDLENBQUMifQ==
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fin.cx/einvoice",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -8,22 +8,12 @@
|
|
|
8
8
|
"type": "module",
|
|
9
9
|
"author": "Task Venture Capital GmbH",
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"scripts": {
|
|
12
|
-
"test": "(tstest test/ --verbose --logfile --timeout 60)",
|
|
13
|
-
"build": "(tsbuild tsfolders --allowimplicitany)",
|
|
14
|
-
"typecheck:published": "pnpm exec tsc -p test/strict-consumer/tsconfig.json",
|
|
15
|
-
"buildDocs": "(tsdoc)",
|
|
16
|
-
"postinstall": "node dist_ts_install/index.js 2>/dev/null || true",
|
|
17
|
-
"download-schematron": "tsx ts_install/download-schematron.ts",
|
|
18
|
-
"download-test-samples": "tsx ts_install/download-test-samples.ts",
|
|
19
|
-
"test:conformance": "tstest test/test.conformance-harness.ts"
|
|
20
|
-
},
|
|
21
11
|
"devDependencies": {
|
|
22
|
-
"@git.zone/tsbuild": "^4.4.
|
|
23
|
-
"@git.zone/tsbundle": "^2.10.
|
|
24
|
-
"@git.zone/tsrun": "^2.0.
|
|
25
|
-
"@git.zone/tstest": "^3.6.
|
|
26
|
-
"@types/node": "^25.
|
|
12
|
+
"@git.zone/tsbuild": "^4.4.2",
|
|
13
|
+
"@git.zone/tsbundle": "^2.10.4",
|
|
14
|
+
"@git.zone/tsrun": "^2.0.4",
|
|
15
|
+
"@git.zone/tstest": "^3.6.6",
|
|
16
|
+
"@types/node": "^25.9.1"
|
|
27
17
|
},
|
|
28
18
|
"dependencies": {
|
|
29
19
|
"@push.rocks/smartfile": "^13.1.2",
|
|
@@ -74,5 +64,19 @@
|
|
|
74
64
|
"esm",
|
|
75
65
|
"financial technology"
|
|
76
66
|
],
|
|
77
|
-
"
|
|
78
|
-
|
|
67
|
+
"scripts": {
|
|
68
|
+
"test": "pnpm run test:unit",
|
|
69
|
+
"test:unit": "tstest test/test.einvoice.ts --verbose --logfile --timeout 60 && tstest test/test.decimal.ts --verbose --logfile --timeout 60 && tstest test/test.decimal-currency-calculator.ts --verbose --logfile --timeout 60 && tstest test/test.currency-utils.ts --verbose --logfile --timeout 60 && tstest test/test.en16931-validators.ts --verbose --logfile --timeout 60 && tstest test/test.schematron-validator.ts --verbose --logfile --timeout 60",
|
|
70
|
+
"test:format": "tstest test/test.format-detection.ts --verbose --logfile --timeout 60",
|
|
71
|
+
"test:corpus": "tstest test/suite/einvoice_corpus-validation/ --verbose --logfile --timeout 60",
|
|
72
|
+
"test:performance": "NODE_OPTIONS=--expose-gc tstest test/suite/einvoice_performance/ --verbose --logfile --timeout 60",
|
|
73
|
+
"test:all": "NODE_OPTIONS=--expose-gc tstest test/ --verbose --logfile --timeout 60",
|
|
74
|
+
"build": "(tsbuild tsfolders --allowimplicitany)",
|
|
75
|
+
"typecheck:published": "pnpm exec tsc -p test/strict-consumer/tsconfig.json",
|
|
76
|
+
"buildDocs": "(tsdoc)",
|
|
77
|
+
"postinstall": "node dist_ts_install/index.js 2>/dev/null || true",
|
|
78
|
+
"download-schematron": "tsrun ts_install/download-schematron.ts",
|
|
79
|
+
"download-test-samples": "tsrun ts_install/download-test-samples.ts",
|
|
80
|
+
"test:conformance": "tstest test/test.conformance-harness.ts"
|
|
81
|
+
}
|
|
82
|
+
}
|
package/readme.md
CHANGED
|
@@ -290,7 +290,7 @@ const xmlForGermanB2G = await invoice.exportXml('xrechnung');
|
|
|
290
290
|
|
|
291
291
|
## License and Legal Information
|
|
292
292
|
|
|
293
|
-
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [
|
|
293
|
+
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](./license) file.
|
|
294
294
|
|
|
295
295
|
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
296
296
|
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@fin.cx/einvoice',
|
|
6
|
-
version: '5.2.
|
|
6
|
+
version: '5.2.1',
|
|
7
7
|
description: 'A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.'
|
|
8
8
|
}
|
package/ts/einvoice.ts
CHANGED
|
@@ -20,10 +20,6 @@ import { DecoderFactory } from './formats/factories/decoder.factory.js';
|
|
|
20
20
|
import { EncoderFactory } from './formats/factories/encoder.factory.js';
|
|
21
21
|
import { ValidatorFactory } from './formats/factories/validator.factory.js';
|
|
22
22
|
|
|
23
|
-
// Import PDF utilities
|
|
24
|
-
import { PDFEmbedder } from './formats/pdf/pdf.embedder.js';
|
|
25
|
-
import { PDFExtractor } from './formats/pdf/pdf.extractor.js';
|
|
26
|
-
|
|
27
23
|
// Import format detector
|
|
28
24
|
import { FormatDetector } from './formats/utils/format.detector.js';
|
|
29
25
|
|
|
@@ -42,8 +38,8 @@ import type { IEInvoiceMetadata } from './interfaces/en16931-metadata.js';
|
|
|
42
38
|
* Extends the TInvoice interface for seamless integration with existing systems
|
|
43
39
|
*/
|
|
44
40
|
export class EInvoice implements TInvoice {
|
|
45
|
-
private static sharedPdfEmbedder?: PDFEmbedder;
|
|
46
|
-
private static sharedPdfExtractor?: PDFExtractor;
|
|
41
|
+
private static sharedPdfEmbedder?: import('./formats/pdf/pdf.embedder.js').PDFEmbedder;
|
|
42
|
+
private static sharedPdfExtractor?: import('./formats/pdf/pdf.extractor.js').PDFExtractor;
|
|
47
43
|
|
|
48
44
|
/**
|
|
49
45
|
* Creates an EInvoice instance from XML string
|
|
@@ -79,7 +75,8 @@ export class EInvoice implements TInvoice {
|
|
|
79
75
|
await invoice.fromPdfFile(pdfBuffer);
|
|
80
76
|
} else {
|
|
81
77
|
// If given a buffer, extract XML and parse it
|
|
82
|
-
const
|
|
78
|
+
const pdfExtractor = await invoice.getPdfExtractor();
|
|
79
|
+
const extractResult = await pdfExtractor.extractXml(pdfBuffer);
|
|
83
80
|
if (!extractResult.success || !extractResult.xml) {
|
|
84
81
|
throw new EInvoicePDFError('No invoice XML found in PDF', 'extract');
|
|
85
82
|
}
|
|
@@ -200,15 +197,17 @@ export class EInvoice implements TInvoice {
|
|
|
200
197
|
};
|
|
201
198
|
|
|
202
199
|
// PDF utilities are created lazily because most invoice workflows never touch PDF I/O.
|
|
203
|
-
private
|
|
200
|
+
private async getPdfEmbedder(): Promise<import('./formats/pdf/pdf.embedder.js').PDFEmbedder> {
|
|
204
201
|
if (!EInvoice.sharedPdfEmbedder) {
|
|
202
|
+
const { PDFEmbedder } = await import('./formats/pdf/pdf.embedder.js');
|
|
205
203
|
EInvoice.sharedPdfEmbedder = new PDFEmbedder();
|
|
206
204
|
}
|
|
207
205
|
return EInvoice.sharedPdfEmbedder;
|
|
208
206
|
}
|
|
209
207
|
|
|
210
|
-
private
|
|
208
|
+
private async getPdfExtractor(): Promise<import('./formats/pdf/pdf.extractor.js').PDFExtractor> {
|
|
211
209
|
if (!EInvoice.sharedPdfExtractor) {
|
|
210
|
+
const { PDFExtractor } = await import('./formats/pdf/pdf.extractor.js');
|
|
212
211
|
EInvoice.sharedPdfExtractor = new PDFExtractor();
|
|
213
212
|
}
|
|
214
213
|
return EInvoice.sharedPdfExtractor;
|
|
@@ -350,7 +349,8 @@ export class EInvoice implements TInvoice {
|
|
|
350
349
|
public async fromPdfFile(filePath: string): Promise<EInvoice> {
|
|
351
350
|
try {
|
|
352
351
|
const pdfBuffer = await plugins.fs.readFile(filePath);
|
|
353
|
-
const
|
|
352
|
+
const pdfExtractor = await this.getPdfExtractor();
|
|
353
|
+
const extractResult = await pdfExtractor.extractXml(pdfBuffer);
|
|
354
354
|
const extractedXml = extractResult.success ? extractResult.xml : null;
|
|
355
355
|
|
|
356
356
|
if (!extractedXml) {
|
|
@@ -612,7 +612,8 @@ export class EInvoice implements TInvoice {
|
|
|
612
612
|
public async embedInPdf(pdfBuffer: Buffer, format: ExportFormat = 'facturx'): Promise<Buffer> {
|
|
613
613
|
try {
|
|
614
614
|
const xmlString = await this.toXmlString(format);
|
|
615
|
-
const
|
|
615
|
+
const pdfEmbedder = await this.getPdfEmbedder();
|
|
616
|
+
const embedResult = await pdfEmbedder.embedXml(pdfBuffer, xmlString, 'invoice.xml', `${format} Invoice`);
|
|
616
617
|
if (!embedResult.success) {
|
|
617
618
|
throw new EInvoicePDFError('Failed to embed XML in PDF', 'embed', { format });
|
|
618
619
|
}
|
|
@@ -2,6 +2,9 @@ import { CIIBaseEncoder } from '../cii.encoder.js';
|
|
|
2
2
|
import type { TInvoice, TCreditNote, TDebitNote } from '../../../interfaces/common.js';
|
|
3
3
|
import { FACTURX_PROFILE_IDS } from './facturx.types.js';
|
|
4
4
|
import { DOMParser, XMLSerializer } from '../../../plugins.js';
|
|
5
|
+
import { Decimal } from '../../utils/decimal.js';
|
|
6
|
+
import { DecimalCurrencyCalculator } from '../../utils/currency.calculator.decimal.js';
|
|
7
|
+
import { getCurrencyMinorUnits } from '../../utils/currency.utils.js';
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* Encoder for Factur-X invoice format
|
|
@@ -329,42 +332,46 @@ export class FacturXEncoder extends CIIBaseEncoder {
|
|
|
329
332
|
// Add totals
|
|
330
333
|
const monetarySummationElement = doc.createElement('ram:SpecifiedTradeSettlementHeaderMonetarySummation');
|
|
331
334
|
|
|
332
|
-
// Calculate totals
|
|
333
|
-
|
|
334
|
-
|
|
335
|
+
// Calculate totals with decimal arithmetic to avoid floating point drift.
|
|
336
|
+
const calculator = new DecimalCurrencyCalculator(invoice.currency);
|
|
337
|
+
const minorUnits = getCurrencyMinorUnits(invoice.currency);
|
|
338
|
+
let totalNetAmount = Decimal.ZERO;
|
|
339
|
+
let totalTaxAmount = Decimal.ZERO;
|
|
335
340
|
|
|
336
341
|
// Calculate from items
|
|
337
342
|
if (invoice.items) {
|
|
338
343
|
for (const item of invoice.items) {
|
|
339
|
-
const itemNetAmount = item.
|
|
340
|
-
const itemTaxAmount = itemNetAmount
|
|
344
|
+
const itemNetAmount = calculator.calculateLineNet(item.unitQuantity, item.unitNetPrice);
|
|
345
|
+
const itemTaxAmount = calculator.calculateVAT(itemNetAmount, item.vatPercentage);
|
|
341
346
|
|
|
342
|
-
totalNetAmount
|
|
343
|
-
totalTaxAmount
|
|
347
|
+
totalNetAmount = totalNetAmount.add(itemNetAmount);
|
|
348
|
+
totalTaxAmount = totalTaxAmount.add(itemTaxAmount);
|
|
344
349
|
}
|
|
345
350
|
}
|
|
346
351
|
|
|
347
|
-
|
|
352
|
+
totalNetAmount = calculator.round(totalNetAmount);
|
|
353
|
+
totalTaxAmount = calculator.round(totalTaxAmount);
|
|
354
|
+
const totalGrossAmount = calculator.calculateGrossAmount(totalNetAmount, totalTaxAmount);
|
|
348
355
|
|
|
349
356
|
// Add line total amount
|
|
350
357
|
const lineTotalElement = doc.createElement('ram:LineTotalAmount');
|
|
351
|
-
lineTotalElement.textContent = totalNetAmount.toFixed(
|
|
358
|
+
lineTotalElement.textContent = totalNetAmount.toFixed(minorUnits);
|
|
352
359
|
monetarySummationElement.appendChild(lineTotalElement);
|
|
353
360
|
|
|
354
361
|
// Add tax total amount
|
|
355
362
|
const taxTotalElement = doc.createElement('ram:TaxTotalAmount');
|
|
356
|
-
taxTotalElement.textContent = totalTaxAmount.toFixed(
|
|
363
|
+
taxTotalElement.textContent = totalTaxAmount.toFixed(minorUnits);
|
|
357
364
|
taxTotalElement.setAttribute('currencyID', invoice.currency);
|
|
358
365
|
monetarySummationElement.appendChild(taxTotalElement);
|
|
359
366
|
|
|
360
367
|
// Add grand total amount
|
|
361
368
|
const grandTotalElement = doc.createElement('ram:GrandTotalAmount');
|
|
362
|
-
grandTotalElement.textContent = totalGrossAmount.toFixed(
|
|
369
|
+
grandTotalElement.textContent = totalGrossAmount.toFixed(minorUnits);
|
|
363
370
|
monetarySummationElement.appendChild(grandTotalElement);
|
|
364
371
|
|
|
365
372
|
// Add due payable amount
|
|
366
373
|
const duePayableElement = doc.createElement('ram:DuePayableAmount');
|
|
367
|
-
duePayableElement.textContent = totalGrossAmount.toFixed(
|
|
374
|
+
duePayableElement.textContent = totalGrossAmount.toFixed(minorUnits);
|
|
368
375
|
monetarySummationElement.appendChild(duePayableElement);
|
|
369
376
|
|
|
370
377
|
settlementElement.appendChild(monetarySummationElement);
|
|
@@ -377,6 +384,9 @@ export class FacturXEncoder extends CIIBaseEncoder {
|
|
|
377
384
|
* @param invoice Invoice data
|
|
378
385
|
*/
|
|
379
386
|
private addLineItems(doc: Document, transactionElement: Element, invoice: TInvoice): void {
|
|
387
|
+
const calculator = new DecimalCurrencyCalculator(invoice.currency);
|
|
388
|
+
const minorUnits = getCurrencyMinorUnits(invoice.currency);
|
|
389
|
+
|
|
380
390
|
// Add each line item
|
|
381
391
|
if (invoice.items) {
|
|
382
392
|
for (const item of invoice.items) {
|
|
@@ -450,12 +460,12 @@ export class FacturXEncoder extends CIIBaseEncoder {
|
|
|
450
460
|
// Add monetary summation
|
|
451
461
|
const monetarySummationElement = doc.createElement('ram:SpecifiedLineTradeSettlementMonetarySummation');
|
|
452
462
|
|
|
453
|
-
// Calculate item total
|
|
454
|
-
const itemNetAmount = item.
|
|
463
|
+
// Calculate item total with decimal arithmetic and currency rounding.
|
|
464
|
+
const itemNetAmount = calculator.calculateLineNet(item.unitQuantity, item.unitNetPrice);
|
|
455
465
|
|
|
456
466
|
// Add line total amount
|
|
457
467
|
const lineTotalElement = doc.createElement('ram:LineTotalAmount');
|
|
458
|
-
lineTotalElement.textContent = itemNetAmount.toFixed(
|
|
468
|
+
lineTotalElement.textContent = itemNetAmount.toFixed(minorUnits);
|
|
459
469
|
monetarySummationElement.appendChild(lineTotalElement);
|
|
460
470
|
|
|
461
471
|
settlementElement.appendChild(monetarySummationElement);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PDFDocument, PDFDict, PDFName, PDFRawStream, PDFArray, PDFString } from '../../../plugins.js';
|
|
2
1
|
import { BaseXMLExtractor } from './base.extractor.js';
|
|
2
|
+
import { loadPdfLib } from '../../../vendor/pdflib.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Associated files extractor for PDF/A-3 documents
|
|
@@ -14,6 +14,7 @@ export class AssociatedFilesExtractor extends BaseXMLExtractor {
|
|
|
14
14
|
*/
|
|
15
15
|
public async extractXml(pdfBuffer: Uint8Array | Buffer): Promise<string | null> {
|
|
16
16
|
try {
|
|
17
|
+
const { PDFDocument, PDFDict, PDFName, PDFRawStream, PDFArray, PDFString } = await loadPdfLib();
|
|
17
18
|
const pdfDoc = await PDFDocument.load(pdfBuffer);
|
|
18
19
|
|
|
19
20
|
// Try to find associated files via the AF entry in the catalog
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { PDFRawStream } from 'pdf-lib';
|
|
2
|
+
import { pako } from '../../../plugins.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Base class for PDF XML extractors with common functionality
|
|
@@ -352,4 +353,4 @@ export abstract class BaseXMLExtractor {
|
|
|
352
353
|
(content.includes('<?xml') ||
|
|
353
354
|
this.knownFormats.some(format => content.includes(format)));
|
|
354
355
|
}
|
|
355
|
-
}
|
|
356
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PDFDocument, PDFDict, PDFName, PDFRawStream, PDFArray, PDFString } from '../../../plugins.js';
|
|
2
1
|
import { BaseXMLExtractor } from './base.extractor.js';
|
|
2
|
+
import { loadPdfLib } from '../../../vendor/pdflib.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Standard PDF XML extractor that extracts XML from embedded files
|
|
@@ -13,6 +13,7 @@ export class StandardXMLExtractor extends BaseXMLExtractor {
|
|
|
13
13
|
*/
|
|
14
14
|
public async extractXml(pdfBuffer: Uint8Array | Buffer): Promise<string | null> {
|
|
15
15
|
try {
|
|
16
|
+
const { PDFDocument, PDFDict, PDFName, PDFRawStream, PDFArray, PDFString } = await loadPdfLib();
|
|
16
17
|
const pdfDoc = await PDFDocument.load(pdfBuffer);
|
|
17
18
|
|
|
18
19
|
// Get the document's metadata dictionary
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { PDFDocument
|
|
1
|
+
import type { PDFDocument as TPdfDocument } from 'pdf-lib';
|
|
2
2
|
import type { IPdf } from '../../interfaces/common.js';
|
|
3
|
+
import { loadPdfLib } from '../../vendor/pdflib.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Error types for PDF embedding operations
|
|
@@ -57,8 +58,10 @@ export class PDFEmbedder {
|
|
|
57
58
|
// Ensure buffer is Uint8Array
|
|
58
59
|
const pdfBufferArray = Buffer.isBuffer(pdfBuffer) ? new Uint8Array(pdfBuffer) : pdfBuffer;
|
|
59
60
|
|
|
61
|
+
const { PDFDocument, AFRelationship } = await loadPdfLib();
|
|
62
|
+
|
|
60
63
|
// Load the PDF
|
|
61
|
-
let pdfDoc:
|
|
64
|
+
let pdfDoc: TPdfDocument;
|
|
62
65
|
try {
|
|
63
66
|
pdfDoc = await PDFDocument.load(pdfBufferArray, {
|
|
64
67
|
ignoreEncryption: true, // Try to load encrypted PDFs
|
|
@@ -239,4 +242,4 @@ export class PDFEmbedder {
|
|
|
239
242
|
}
|
|
240
243
|
};
|
|
241
244
|
}
|
|
242
|
-
}
|
|
245
|
+
}
|