@fin.cx/einvoice 8.0.0 → 8.1.0
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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/einvoice.js +32 -2
- package/dist_ts/formats/base/base.validator.d.ts +5 -0
- package/dist_ts/formats/base/base.validator.js +28 -1
- package/dist_ts/formats/cii/cii.validator.d.ts +11 -0
- package/dist_ts/formats/cii/cii.validator.js +35 -1
- package/dist_ts/formats/cii/facturx/facturx.decoder.js +3 -2
- package/dist_ts/formats/cii/facturx/facturx.encoder.js +5 -2
- package/dist_ts/formats/cii/facturx/facturx.validator.js +8 -4
- package/dist_ts/formats/cii/zugferd/zugferd.decoder.js +3 -2
- package/dist_ts/formats/cii/zugferd/zugferd.encoder.js +5 -2
- package/dist_ts/formats/cii/zugferd/zugferd.v1.decoder.js +3 -2
- package/dist_ts/formats/cii/zugferd/zugferd.validator.js +5 -4
- package/dist_ts/formats/converters/xml-to-einvoice.converter.d.ts +5 -0
- package/dist_ts/formats/converters/xml-to-einvoice.converter.js +11 -2
- package/dist_ts/formats/semantic/semantic.adapter.js +2 -2
- package/dist_ts/formats/ubl/en16931.ubl.validator.js +5 -1
- package/dist_ts/formats/ubl/generic/ubl.encoder.js +5 -2
- package/dist_ts/formats/ubl/ubl.validator.d.ts +11 -0
- package/dist_ts/formats/ubl/ubl.validator.js +35 -1
- package/dist_ts/formats/ubl/xrechnung/xrechnung.decoder.js +3 -2
- package/dist_ts/formats/utils/unit.codes.d.ts +40 -0
- package/dist_ts/formats/utils/unit.codes.js +193 -0
- package/dist_ts/formats/validation/codelist.validator.d.ts +1 -1
- package/dist_ts/formats/validation/codelist.validator.js +7 -9
- package/dist_ts/formats/validation/en16931.business-rules.validator.js +13 -13
- package/dist_ts/formats/validation/validation.types.d.ts +0 -4
- package/dist_ts/formats/validation/validation.types.js +1 -28
- package/dist_ts/index.d.ts +4 -0
- package/dist_ts/index.js +6 -1
- package/dist_ts_install/download-schematron.js +3 -5
- package/package.json +7 -7
- package/readme.md +44 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/einvoice.ts +33 -1
- package/ts/formats/base/base.validator.ts +28 -0
- package/ts/formats/cii/cii.validator.ts +47 -0
- package/ts/formats/cii/facturx/facturx.decoder.ts +2 -1
- package/ts/formats/cii/facturx/facturx.encoder.ts +4 -1
- package/ts/formats/cii/facturx/facturx.validator.ts +9 -3
- package/ts/formats/cii/zugferd/zugferd.decoder.ts +2 -1
- package/ts/formats/cii/zugferd/zugferd.encoder.ts +4 -1
- package/ts/formats/cii/zugferd/zugferd.v1.decoder.ts +2 -1
- package/ts/formats/cii/zugferd/zugferd.validator.ts +4 -3
- package/ts/formats/converters/xml-to-einvoice.converter.ts +11 -1
- package/ts/formats/semantic/semantic.adapter.ts +1 -1
- package/ts/formats/ubl/en16931.ubl.validator.ts +6 -0
- package/ts/formats/ubl/generic/ubl.encoder.ts +4 -1
- package/ts/formats/ubl/ubl.validator.ts +47 -0
- package/ts/formats/ubl/xrechnung/xrechnung.decoder.ts +2 -1
- package/ts/formats/utils/unit.codes.ts +215 -0
- package/ts/formats/validation/codelist.validator.ts +8 -19
- package/ts/formats/validation/en16931.business-rules.validator.ts +13 -13
- package/ts/formats/validation/validation.types.ts +0 -28
- package/ts/index.ts +9 -0
|
@@ -62,33 +62,6 @@ export const CodeLists = {
|
|
|
62
62
|
['58', 'SEPA credit transfer'],
|
|
63
63
|
['59', 'SEPA direct debit']
|
|
64
64
|
])
|
|
65
|
-
},
|
|
66
|
-
// UNECE Rec 20 Unit codes (subset)
|
|
67
|
-
UNECERec20: {
|
|
68
|
-
version: '2021',
|
|
69
|
-
codes: new Map([
|
|
70
|
-
['C62', 'One (unit)'],
|
|
71
|
-
['DAY', 'Day'],
|
|
72
|
-
['HAR', 'Hectare'],
|
|
73
|
-
['HUR', 'Hour'],
|
|
74
|
-
['KGM', 'Kilogram'],
|
|
75
|
-
['KTM', 'Kilometre'],
|
|
76
|
-
['KWH', 'Kilowatt hour'],
|
|
77
|
-
['LS', 'Lump sum'],
|
|
78
|
-
['LTR', 'Litre'],
|
|
79
|
-
['MIN', 'Minute'],
|
|
80
|
-
['MMT', 'Millimetre'],
|
|
81
|
-
['MON', 'Month'],
|
|
82
|
-
['MTK', 'Square metre'],
|
|
83
|
-
['MTQ', 'Cubic metre'],
|
|
84
|
-
['MTR', 'Metre'],
|
|
85
|
-
['NAR', 'Number of articles'],
|
|
86
|
-
['NPR', 'Number of pairs'],
|
|
87
|
-
['P1', 'Percent'],
|
|
88
|
-
['SET', 'Set'],
|
|
89
|
-
['TNE', 'Tonne (metric ton)'],
|
|
90
|
-
['WEE', 'Week']
|
|
91
|
-
])
|
|
92
65
|
}
|
|
93
66
|
};
|
|
94
67
|
// Business Term (BT) and Business Group (BG) mappings
|
|
@@ -178,4 +151,4 @@ export const SemanticModel = {
|
|
|
178
151
|
BG31: 'Item information',
|
|
179
152
|
BG32: 'Item attributes'
|
|
180
153
|
};
|
|
181
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
154
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdGlvbi50eXBlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL2Zvcm1hdHMvdmFsaWRhdGlvbi92YWxpZGF0aW9uLnR5cGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBcUZILHdCQUF3QjtBQUN4QixNQUFNLENBQUMsTUFBTSxTQUFTLEdBQUc7SUFDdkIsMEJBQTBCO0lBQzFCLE9BQU8sRUFBRTtRQUNQLE9BQU8sRUFBRSxNQUFNO1FBQ2YsS0FBSyxFQUFFLElBQUksR0FBRyxDQUFDO1lBQ2IsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSztZQUNwRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLO1NBQ3JFLENBQUM7S0FDSDtJQUVELG1DQUFtQztJQUNuQyxPQUFPLEVBQUU7UUFDUCxPQUFPLEVBQUUsTUFBTTtRQUNmLEtBQUssRUFBRSxJQUFJLEdBQUcsQ0FBQztZQUNiLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSTtZQUN0RSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUk7WUFDdEUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFB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
|
package/dist_ts/index.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ import { PDFExtractor } from './formats/pdf/pdf.extractor.js';
|
|
|
17
17
|
import { BaseXMLExtractor, StandardXMLExtractor, AssociatedFilesExtractor, TextXMLExtractor } from './formats/pdf/extractors/index.js';
|
|
18
18
|
import { FormatDetector } from './formats/utils/format.detector.js';
|
|
19
19
|
import { getAccountingDocType, getDocumentTypeCode, isAccountingDocType } from './formats/utils/document.typecode.js';
|
|
20
|
+
import { en16931UnitCodeList, isEn16931UnitCode } from './formats/utils/unit.codes.js';
|
|
21
|
+
import type { IEn16931UnitCodeList } from './formats/utils/unit.codes.js';
|
|
20
22
|
import { EInvoiceError, EInvoiceParsingError, EInvoiceValidationError, EInvoicePDFError, EInvoiceFormatError, ErrorRecovery, ErrorContext } from './errors.js';
|
|
21
23
|
import { FacturXDecoder } from './formats/cii/facturx/facturx.decoder.js';
|
|
22
24
|
import { FacturXEncoder } from './formats/cii/facturx/facturx.encoder.js';
|
|
@@ -38,6 +40,8 @@ export { ZUGFeRDDecoder, ZUGFeRDEncoder, ZUGFeRDValidator, ZUGFeRDV1Decoder };
|
|
|
38
40
|
export { PDFEmbedder, PDFExtractor, BaseXMLExtractor, StandardXMLExtractor, AssociatedFilesExtractor, TextXMLExtractor };
|
|
39
41
|
export { FormatDetector };
|
|
40
42
|
export { getAccountingDocType, getDocumentTypeCode, isAccountingDocType };
|
|
43
|
+
export { en16931UnitCodeList, isEn16931UnitCode };
|
|
44
|
+
export type { IEn16931UnitCodeList };
|
|
41
45
|
export { EInvoiceError, EInvoiceParsingError, EInvoiceValidationError, EInvoicePDFError, EInvoiceFormatError, ErrorRecovery, ErrorContext };
|
|
42
46
|
/**
|
|
43
47
|
* Validates an XML string against the appropriate format rules
|
package/dist_ts/index.js
CHANGED
|
@@ -26,6 +26,8 @@ import { BaseXMLExtractor, StandardXMLExtractor, AssociatedFilesExtractor, TextX
|
|
|
26
26
|
import { FormatDetector } from './formats/utils/format.detector.js';
|
|
27
27
|
// Import the document type code table (BT-3)
|
|
28
28
|
import { getAccountingDocType, getDocumentTypeCode, isAccountingDocType } from './formats/utils/document.typecode.js';
|
|
29
|
+
// Import the EN 16931 unit code list (BR-CL-23)
|
|
30
|
+
import { en16931UnitCodeList, isEn16931UnitCode } from './formats/utils/unit.codes.js';
|
|
29
31
|
// Import error classes
|
|
30
32
|
import { EInvoiceError, EInvoiceParsingError, EInvoiceValidationError, EInvoicePDFError, EInvoiceFormatError, ErrorRecovery, ErrorContext } from './errors.js';
|
|
31
33
|
// Import Factur-X implementation
|
|
@@ -61,6 +63,9 @@ export { FormatDetector };
|
|
|
61
63
|
// Export the document type code table, so a consumer that reads or writes BT-3
|
|
62
64
|
// itself does not need a second copy of it
|
|
63
65
|
export { getAccountingDocType, getDocumentTypeCode, isAccountingDocType };
|
|
66
|
+
// Export the EN 16931 unit code list (BR-CL-23: UN/ECE Recommendation 20 with the
|
|
67
|
+
// Recommendation 21 extension), so a consumer can check a unit before it builds a document
|
|
68
|
+
export { en16931UnitCodeList, isEn16931UnitCode };
|
|
64
69
|
// Export error classes
|
|
65
70
|
export { EInvoiceError, EInvoiceParsingError, EInvoiceValidationError, EInvoicePDFError, EInvoiceFormatError, ErrorRecovery, ErrorContext };
|
|
66
71
|
/**
|
|
@@ -93,4 +98,4 @@ export function validateXml(xml, level = common.ValidationLevel.SYNTAX) {
|
|
|
93
98
|
export function createEInvoice() {
|
|
94
99
|
return new EInvoice();
|
|
95
100
|
}
|
|
96
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
101
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxvQkFBb0I7QUFDcEIsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUV6QyxvQkFBb0I7QUFDcEIsT0FBTyxLQUFLLE1BQU0sTUFBTSx3QkFBd0IsQ0FBQztBQUVqRCxtQkFBbUI7QUFDbkIsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHdDQUF3QyxDQUFDO0FBQ3hFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUN4RSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQztBQUU1RSxzQkFBc0I7QUFDdEIsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQzdELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUM3RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFFakUsMEJBQTBCO0FBQzFCLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUM5RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDOUQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFbEUsMEJBQTBCO0FBQzFCLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUM5RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDOUQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFbEUsdUJBQXVCO0FBQ3ZCLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUM1RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDOUQsT0FBTyxFQUNMLGdCQUFnQixFQUNoQixvQkFBb0IsRUFDcEIsd0JBQXdCLEVBQ3hCLGdCQUFnQixFQUNqQixNQUFNLG1DQUFtQyxDQUFDO0FBRTNDLHlCQUF5QjtBQUN6QixPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFFcEUsNkNBQTZDO0FBQzdDLE9BQU8sRUFDTCxvQkFBb0IsRUFDcEIsbUJBQW1CLEVBQ25CLG1CQUFtQixFQUNwQixNQUFNLHNDQUFzQyxDQUFDO0FBRTlDLGdEQUFnRDtBQUNoRCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUd2Rix1QkFBdUI7QUFDdkIsT0FBTyxFQUNMLGFBQWEsRUFDYixvQkFBb0IsRUFDcEIsdUJBQXVCLEVBQ3ZCLGdCQUFnQixFQUNoQixtQkFBbUIsRUFDbkIsYUFBYSxFQUNiLFlBQVksRUFDYixNQUFNLGFBQWEsQ0FBQztBQUVyQixpQ0FBaUM7QUFDakMsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLDBDQUEwQyxDQUFDO0FBQzFFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQztBQUMxRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUU5RSxnQ0FBZ0M7QUFDaEMsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLDBDQUEwQyxDQUFDO0FBQzFFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQztBQUMxRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUM5RSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw2Q0FBNkMsQ0FBQztBQTRCL0UsT0FBTyxFQUFFLGVBQWUsRUFBRSxhQUFhLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUV4RSxxQ0FBcUM7QUFDckMsT0FBTyxFQUFFLE1BQU0sSUFBSSxVQUFVLEVBQUUsQ0FBQztBQUVoQyxvQkFBb0I7QUFDcEIsT0FBTyxFQUFFLFFBQVEsRUFBRSxDQUFDO0FBRXBCLG1CQUFtQjtBQUNuQixPQUFPLEVBQUUsY0FBYyxFQUFFLGNBQWMsRUFBRSxnQkFBZ0IsRUFBRSxDQUFDO0FBRTVELHNCQUFzQjtBQUN0QixPQUFPLEVBQUUsV0FBVyxFQUFFLFdBQVcsRUFBRSxhQUFhLEVBQUUsQ0FBQztBQUVuRCwwQkFBMEI7QUFDMUIsT0FBTyxFQUFFLGNBQWMsRUFBRSxjQUFjLEVBQUUsZ0JBQWdCLEVBQUUsQ0FBQztBQUU1RCwwQkFBMEI7QUFDMUIsT0FBTyxFQUFFLGNBQWMsRUFBRSxjQUFjLEVBQUUsZ0JBQWdCLEVBQUUsQ0FBQztBQUU1RCxpQ0FBaUM7QUFDakMsT0FBTyxFQUFFLGNBQWMsRUFBRSxjQUFjLEVBQUUsZ0JBQWdCLEVBQUUsQ0FBQztBQUU1RCxnQ0FBZ0M7QUFDaEMsT0FBTyxFQUFFLGNBQWMsRUFBRSxjQUFjLEVBQUUsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQUUsQ0FBQztBQUU5RSx1QkFBdUI7QUFDdkIsT0FBTyxFQUNMLFdBQVcsRUFDWCxZQUFZLEVBQ1osZ0JBQWdCLEVBQ2hCLG9CQUFvQixFQUNwQix3QkFBd0IsRUFDeEIsZ0JBQWdCLEVBQ2pCLENBQUM7QUFFRix5QkFBeUI7QUFDekIsT0FBTyxFQUFFLGNBQWMsRUFBRSxDQUFDO0FBRTFCLCtFQUErRTtBQUMvRSwyQ0FBMkM7QUFDM0MsT0FBTyxFQUFFLG9CQUFvQixFQUFFLG1CQUFtQixFQUFFLG1CQUFtQixFQUFFLENBQUM7QUFFMUUsa0ZBQWtGO0FBQ2xGLDJGQUEyRjtBQUMzRixPQUFPLEVBQUUsbUJBQW1CLEVBQUUsaUJBQWlCLEVBQUUsQ0FBQztBQUdsRCx1QkFBdUI7QUFDdkIsT0FBTyxFQUNMLGFBQWEsRUFDYixvQkFBb0IsRUFDcEIsdUJBQXVCLEVBQ3ZCLGdCQUFnQixFQUNoQixtQkFBbUIsRUFDbkIsYUFBYSxFQUNiLFlBQVksRUFDYixDQUFDO0FBRUY7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsV0FBVyxDQUN6QixHQUFXLEVBQ1gsUUFBZ0MsTUFBTSxDQUFDLGVBQWUsQ0FBQyxNQUFNO0lBRTdELElBQUksQ0FBQztRQUNILE1BQU0sU0FBUyxHQUFHLGdCQUFnQixDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN4RCxPQUFPLFNBQVMsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDbkMsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixNQUFNLFlBQVksR0FBRyxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDNUUsT0FBTztZQUNMLEtBQUssRUFBRSxLQUFLO1lBQ1osTUFBTSxFQUFFLENBQUM7b0JBQ1AsSUFBSSxFQUFFLFdBQVc7b0JBQ2pCLE9BQU8sRUFBRSxxQkFBcUIsWUFBWSxFQUFFO2lCQUM3QyxDQUFDO1lBQ0YsS0FBSztTQUNOLENBQUM7SUFDSixDQUFDO0FBQ0gsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0sVUFBVSxjQUFjO0lBQzVCLE9BQU8sSUFBSSxRQUFRLEVBQUUsQ0FBQztBQUN4QixDQUFDIn0=
|
|
@@ -60,10 +60,8 @@ async function main() {
|
|
|
60
60
|
});
|
|
61
61
|
console.log('\n✅ Schematron download complete!');
|
|
62
62
|
}
|
|
63
|
-
// Run if executed directly
|
|
64
|
-
|
|
65
|
-
// would exit silently without downloading anything.
|
|
66
|
-
const isEntryPoint = !process.argv[1] || import.meta.url === `file://${process.argv[1]}`;
|
|
63
|
+
// Run if executed directly
|
|
64
|
+
const isEntryPoint = import.meta.url === `file://${process.argv[1]}`;
|
|
67
65
|
if (isEntryPoint) {
|
|
68
66
|
main().catch(error => {
|
|
69
67
|
console.error('❌ Script failed:', error);
|
|
@@ -71,4 +69,4 @@ if (isEntryPoint) {
|
|
|
71
69
|
});
|
|
72
70
|
}
|
|
73
71
|
export default main;
|
|
74
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
72
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZG93bmxvYWQtc2NoZW1hdHJvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX2luc3RhbGwvZG93bmxvYWQtc2NoZW1hdHJvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQ0EsOEJBQThCO0FBRTlCOztHQUVHO0FBRUgsTUFBTSw4QkFBOEIsR0FBRyx3REFBd0QsQ0FBQztBQUVoRyxLQUFLLFVBQVUsZ0JBQWdCO0lBQzdCLE1BQU0sRUFBRSxvQkFBb0IsRUFBRSxHQUFHLE1BQU0sTUFBTSxDQUFDLDhCQUE4QixDQUFDLENBQUM7SUFDOUUsTUFBTSxVQUFVLEdBQUcsSUFBSSxvQkFBb0IsQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0lBQzVFLE1BQU0sVUFBVSxDQUFDLFVBQVUsRUFBRSxDQUFDO0lBQzlCLE9BQU8sVUFBVSxDQUFDO0FBQ3BCLENBQUM7QUFFRCxLQUFLLFVBQVUsSUFBSTtJQUNqQixPQUFPLENBQUMsR0FBRyxDQUFDLHNDQUFzQyxDQUFDLENBQUM7SUFFcEQsTUFBTSxVQUFVLEdBQUcsTUFBTSxnQkFBZ0IsRUFBRSxDQUFDO0lBRTVDLG9DQUFvQztJQUNwQyxPQUFPLENBQUMsR0FBRyxDQUFDLDRDQUE0QyxDQUFDLENBQUM7SUFDMUQsSUFBSSxDQUFDO1FBQ0gsTUFBTSxZQUFZLEdBQUcsTUFBTSxVQUFVLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDbEUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsWUFBWSxDQUFDLE1BQU0sZ0JBQWdCLENBQUMsQ0FBQztRQUNqRSxZQUFZLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBUyxFQUFFLEVBQUUsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ2hFLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsTUFBTSxZQUFZLEdBQUcsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FB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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fin.cx/einvoice",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.1.0",
|
|
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",
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
"author": "Task Venture Capital GmbH",
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"devDependencies": {
|
|
16
|
-
"@git.zone/tsbuild": "^
|
|
17
|
-
"@git.zone/tsbundle": "^2.
|
|
18
|
-
"@git.zone/tsrun": "^
|
|
19
|
-
"@git.zone/tstest": "^6.
|
|
16
|
+
"@git.zone/tsbuild": "^5.0.0",
|
|
17
|
+
"@git.zone/tsbundle": "^2.15.0",
|
|
18
|
+
"@git.zone/tsrun": "^3.0.0",
|
|
19
|
+
"@git.zone/tstest": "^6.2.0",
|
|
20
20
|
"@push.rocks/smartfile": "^13.1.3",
|
|
21
|
-
"@types/node": "^26.
|
|
21
|
+
"@types/node": "^26.6.1"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@push.rocks/smartxml": "^2.1.0",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
],
|
|
68
68
|
"scripts": {
|
|
69
69
|
"test": "pnpm run test:unit",
|
|
70
|
-
"test:unit": "tstest test/test.cii-xrechnung.node.ts --verbose --logfile --timeout 60 && 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 && tstest test/test.xrechnung-creditnote.node.ts --verbose --logfile --timeout 60 && tstest test/test.accountingdoc-types.node.ts --verbose --logfile --timeout 60",
|
|
70
|
+
"test:unit": "tstest test/test.cii-xrechnung.node.ts --verbose --logfile --timeout 60 && 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 && tstest test/test.xrechnung-creditnote.node.ts --verbose --logfile --timeout 60 && tstest test/test.accountingdoc-types.node.ts --verbose --logfile --timeout 60 && tstest test/test.en16931-unitcodes.node.ts --verbose --logfile --timeout 60 && tstest test/test.en16931-lineunit.node.ts --verbose --logfile --timeout 60",
|
|
71
71
|
"test:format": "tstest test/test.format-detection.ts --verbose --logfile --timeout 60",
|
|
72
72
|
"test:corpus": "tstest test/suite/einvoice_corpus-validation/ --verbose --logfile --timeout 60",
|
|
73
73
|
"test:performance": "NODE_OPTIONS=--expose-gc tstest test/suite/einvoice_performance/ --verbose --logfile --timeout 60",
|
package/readme.md
CHANGED
|
@@ -218,6 +218,48 @@ console.log(business.errors);
|
|
|
218
218
|
console.log(business.warnings ?? []);
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
+
### Unit codes (BR-CL-23)
|
|
222
|
+
|
|
223
|
+
A line's `unitType` is written as the unit code of the invoiced quantity (BT-130), so it has to
|
|
224
|
+
be a code EN 16931 admits: UN/ECE Recommendation 20 with the Recommendation 21 extension, as
|
|
225
|
+
business rule BR-CL-23 lists them. Words such as `hours` or `HOURS` are not codes; `HUR` (hour),
|
|
226
|
+
`DAY` (day), `MON` (month), `H87` (piece), `C62` (one), `LS` (lump sum) and package codes such as
|
|
227
|
+
`XPP` are.
|
|
228
|
+
|
|
229
|
+
The business level checks the rule on every document, without a feature flag: the unit codes of
|
|
230
|
+
the invoiced, credited, billed and base quantities (BT-130, BT-150) of UBL, XRechnung, Factur-X and
|
|
231
|
+
ZUGFeRD documents, and the lines of an invoice built in code. A failure is an error with the code
|
|
232
|
+
`BR-CL-23` and the location of the attribute or line. Whitespace around a code in the XML is
|
|
233
|
+
ignored, as in the official rule; the list comparison itself is exact.
|
|
234
|
+
|
|
235
|
+
The list is exported, so an application can check a unit before it builds a document:
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
import { en16931UnitCodeList, isEn16931UnitCode } from '@fin.cx/einvoice';
|
|
239
|
+
|
|
240
|
+
isEn16931UnitCode('HUR'); // true
|
|
241
|
+
isEn16931UnitCode('HOURS'); // false
|
|
242
|
+
isEn16931UnitCode('hur'); // false: codes are compared exactly
|
|
243
|
+
en16931UnitCodeList.codes.length; // 2162, a frozen array in the order of the rule
|
|
244
|
+
en16931UnitCodeList.source; // 'EN 16931 validation artefacts (CEN/TC 434) validation-1.3.16'
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The 2162 codes are the BR-CL-23 list of the EN 16931 validation artefacts of CEN/TC 434, release
|
|
248
|
+
validation-1.3.16 (https://github.com/ConnectingEurope/eInvoicing-EN16931), reproduced unaltered.
|
|
249
|
+
The source files are licensed under EUPL-1.2; the codes are those of UN/ECE Recommendations 20 and
|
|
250
|
+
21.
|
|
251
|
+
`createItem()` gives a line without a unit the code `C62` (one).
|
|
252
|
+
|
|
253
|
+
A line needs a unit (BR-23). A document whose quantity carries no `unitCode` reads with an empty
|
|
254
|
+
`unitType`, never with a unit it did not state, and the business level reports BR-23 with the
|
|
255
|
+
location of the line. An empty `unitType` is written without the attribute, so the unit stays
|
|
256
|
+
absent when the document is exported again.
|
|
257
|
+
|
|
258
|
+
A present but empty attribute, `unitCode=""`, fails BR-CL-23, as in the official rule. It reads
|
|
259
|
+
with an empty `unitType` too, so after a round trip the document fails BR-23 instead. At the
|
|
260
|
+
business level the `EN16931_BUSINESS_RULES` and `CODE_LIST_VALIDATION` flags leave both unit rules
|
|
261
|
+
to the document check, so each line is reported once.
|
|
262
|
+
|
|
221
263
|
### Convert between supported XML targets
|
|
222
264
|
|
|
223
265
|
```ts
|
|
@@ -280,6 +322,7 @@ Top-level exports from `@fin.cx/einvoice` include:
|
|
|
280
322
|
- `createEInvoice()`
|
|
281
323
|
- `validateXml(xml, level?)`
|
|
282
324
|
- `getDocumentTypeCode(accountingDocType)`, `getAccountingDocType(typeCode)`, `isAccountingDocType(value)`
|
|
325
|
+
- `en16931UnitCodeList`, `isEn16931UnitCode(value)` (the BR-CL-23 unit code list)
|
|
283
326
|
- `FormatDetector`
|
|
284
327
|
- `PDFExtractor`, `PDFEmbedder`
|
|
285
328
|
- `DecoderFactory`, `EncoderFactory`, `ValidatorFactory`
|
|
@@ -287,7 +330,7 @@ Top-level exports from `@fin.cx/einvoice` include:
|
|
|
287
330
|
- `UBLBase*` and `CIIBase*` extension classes
|
|
288
331
|
- `FacturX*` and `ZUGFeRD*` format-specific classes
|
|
289
332
|
- `ValidationLevel`, `InvoiceFormat`
|
|
290
|
-
- exported types like `TAccountingDoc`, `TAccountingDocType`, `TInvoice`, `TCreditNote`, `TDebitNote`, `TSelfBilledInvoice`, `ValidationResult`, `ValidationError`, `ExportFormat`, `IPdf`, `EInvoiceOptions`
|
|
333
|
+
- exported types like `TAccountingDoc`, `TAccountingDocType`, `TInvoice`, `TCreditNote`, `TDebitNote`, `TSelfBilledInvoice`, `IEn16931UnitCodeList`, `ValidationResult`, `ValidationError`, `ExportFormat`, `IPdf`, `EInvoiceOptions`
|
|
291
334
|
|
|
292
335
|
## Validation resources and install-time behavior
|
|
293
336
|
|
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: '8.
|
|
6
|
+
version: '8.1.0',
|
|
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
|
@@ -32,6 +32,7 @@ import { ValidatorFactory } from './formats/factories/validator.factory.js';
|
|
|
32
32
|
// Import format detector
|
|
33
33
|
import { FormatDetector } from './formats/utils/format.detector.js';
|
|
34
34
|
import { isAccountingDocType } from './formats/utils/document.typecode.js';
|
|
35
|
+
import { meetsUnitCodeRule } from './formats/utils/unit.codes.js';
|
|
35
36
|
|
|
36
37
|
import {
|
|
37
38
|
FACTURX_ATTACHMENT_DESCRIPTION,
|
|
@@ -517,6 +518,14 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
|
|
|
517
518
|
result = this.validateDecodedInvoice(level);
|
|
518
519
|
}
|
|
519
520
|
|
|
521
|
+
// At the business level the document check covers the unit rules (BR-23, BR-CL-23) on every
|
|
522
|
+
// line. The feature flag validators below check the lines again on the decoded model, which
|
|
523
|
+
// cannot tell an absent unit from an empty unitCode="" attribute, so there they would report
|
|
524
|
+
// a second, possibly different rule for the same line. Below the business level the flags
|
|
525
|
+
// are the only unit checks and report them.
|
|
526
|
+
const isDocumentUnitRule = (ruleId: string): boolean =>
|
|
527
|
+
level === ValidationLevel.BUSINESS && (ruleId === 'BR-23' || ruleId === 'BR-CL-23');
|
|
528
|
+
|
|
520
529
|
// Enhanced validation with feature flags
|
|
521
530
|
if (options?.featureFlags?.includes('EN16931_BUSINESS_RULES')) {
|
|
522
531
|
const businessRulesValidator = new EN16931BusinessRulesValidator();
|
|
@@ -526,6 +535,7 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
|
|
|
526
535
|
result.errors = result.errors.concat(
|
|
527
536
|
businessResults
|
|
528
537
|
.filter(r => r.severity === 'error')
|
|
538
|
+
.filter(r => !isDocumentUnitRule(r.ruleId))
|
|
529
539
|
.map(r => ({ code: r.ruleId, message: r.message, field: r.field }))
|
|
530
540
|
);
|
|
531
541
|
|
|
@@ -548,6 +558,7 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
|
|
|
548
558
|
result.errors = result.errors.concat(
|
|
549
559
|
codeListResults
|
|
550
560
|
.filter(r => r.severity === 'error')
|
|
561
|
+
.filter(r => !isDocumentUnitRule(r.ruleId))
|
|
551
562
|
.map(r => ({ code: r.ruleId, message: r.message, field: r.field }))
|
|
552
563
|
);
|
|
553
564
|
}
|
|
@@ -576,6 +587,27 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
|
|
|
576
587
|
this.createValidationError(message)
|
|
577
588
|
);
|
|
578
589
|
|
|
590
|
+
if (level === ValidationLevel.BUSINESS) {
|
|
591
|
+
// BR-23 and BR-CL-23: every line has a unit code, and it is a code of UN/ECE Recommendation 20
|
|
592
|
+
// with the Recommendation 21 extension, as the XML validators check it on an exported document
|
|
593
|
+
(invoice.items ?? []).forEach((item, index) => {
|
|
594
|
+
if (!item.unitType) {
|
|
595
|
+
// BR-23: the line has no unit code at all
|
|
596
|
+
errors.push({
|
|
597
|
+
code: 'BR-23',
|
|
598
|
+
message: 'An Invoice line (BG-25) shall have an Invoiced quantity unit of measure code (BT-130)',
|
|
599
|
+
location: `items[${index}].unitType`
|
|
600
|
+
});
|
|
601
|
+
} else if (!meetsUnitCodeRule(item.unitType)) {
|
|
602
|
+
errors.push({
|
|
603
|
+
code: 'BR-CL-23',
|
|
604
|
+
message: `Unit code MUST be coded according to the UN/ECE Recommendation 20 with Rec 21 extension; found "${item.unitType}"`,
|
|
605
|
+
location: `items[${index}].unitType`
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
|
|
579
611
|
return {
|
|
580
612
|
valid: errors.length === 0,
|
|
581
613
|
errors,
|
|
@@ -793,7 +825,7 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
|
|
|
793
825
|
position: data.position || this.items.length + 1,
|
|
794
826
|
name: data.name || '',
|
|
795
827
|
articleNumber: data.articleNumber,
|
|
796
|
-
unitType: data.unitType || '
|
|
828
|
+
unitType: data.unitType || 'C62',
|
|
797
829
|
unitQuantity: data.unitQuantity || 1,
|
|
798
830
|
unitNetPrice: data.unitNetPrice || 0,
|
|
799
831
|
vatPercentage: data.vatPercentage || 0
|
|
@@ -61,4 +61,32 @@ export abstract class BaseValidator {
|
|
|
61
61
|
location
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The path of an element in the document, with the position of each step among its siblings
|
|
67
|
+
* of the same name, for example `/Invoice/cac:InvoiceLine[2]/cbc:InvoicedQuantity`.
|
|
68
|
+
*/
|
|
69
|
+
protected locationOf(element: Element): string {
|
|
70
|
+
const steps: string[] = [];
|
|
71
|
+
let current: Element | null = element;
|
|
72
|
+
while (current) {
|
|
73
|
+
const name = current.nodeName;
|
|
74
|
+
let position = 1;
|
|
75
|
+
let count = 0;
|
|
76
|
+
const parent: Node | null = current.parentNode;
|
|
77
|
+
if (parent) {
|
|
78
|
+
for (let child = parent.firstChild; child; child = child.nextSibling) {
|
|
79
|
+
if (child.nodeType === 1 && child.nodeName === name) {
|
|
80
|
+
count++;
|
|
81
|
+
if (child === current) {
|
|
82
|
+
position = count;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
steps.unshift(count > 1 ? `${name}[${position}]` : name);
|
|
88
|
+
current = parent && parent.nodeType === 1 ? parent as Element : null;
|
|
89
|
+
}
|
|
90
|
+
return `/${steps.join('/')}`;
|
|
91
|
+
}
|
|
64
92
|
}
|
|
@@ -3,6 +3,7 @@ import { ValidationLevel } from '../../interfaces/common.js';
|
|
|
3
3
|
import type { ValidationResult } from '../../interfaces/common.js';
|
|
4
4
|
import { CII_NAMESPACES, CIIProfile } from './cii.types.js';
|
|
5
5
|
import { DOMParser, xpath } from '../../plugins.js';
|
|
6
|
+
import { meetsUnitCodeRule } from '../utils/unit.codes.js';
|
|
6
7
|
|
|
7
8
|
const ciiValidatorParser = new DOMParser();
|
|
8
9
|
const ciiValidatorNamespaces = {
|
|
@@ -113,6 +114,52 @@ export abstract class CIIBaseValidator extends BaseValidator {
|
|
|
113
114
|
*/
|
|
114
115
|
protected abstract validateStructure(): boolean;
|
|
115
116
|
|
|
117
|
+
/**
|
|
118
|
+
* BR-23: every invoice line has a unit code on its billed quantity.
|
|
119
|
+
* @returns True if every line has one
|
|
120
|
+
*/
|
|
121
|
+
protected validateLineUnitCodePresence(): boolean {
|
|
122
|
+
// BR-23: every invoice line (BG-25) has an invoiced quantity unit of measure code (BT-130)
|
|
123
|
+
const lines = this.select('//ram:IncludedSupplyChainTradeLineItem', this.doc) as Element[];
|
|
124
|
+
let valid = true;
|
|
125
|
+
for (const line of lines) {
|
|
126
|
+
if (!this.exists('./ram:SpecifiedLineTradeDelivery/ram:BilledQuantity/@unitCode', line)) {
|
|
127
|
+
this.addError(
|
|
128
|
+
'BR-23',
|
|
129
|
+
'An Invoice line (BG-25) shall have an Invoiced quantity unit of measure code (BT-130)',
|
|
130
|
+
this.locationOf(line)
|
|
131
|
+
);
|
|
132
|
+
valid = false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return valid;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* BR-CL-23: every unit code of a billed or basis quantity (BT-130, BT-150) is a code of
|
|
140
|
+
* UN/ECE Recommendation 20 with the Recommendation 21 extension.
|
|
141
|
+
* @returns True if every unit code passed
|
|
142
|
+
*/
|
|
143
|
+
protected validateUnitCodes(): boolean {
|
|
144
|
+
const quantities = this.select(
|
|
145
|
+
'//ram:BilledQuantity[@unitCode] | //ram:BasisQuantity[@unitCode]',
|
|
146
|
+
this.doc
|
|
147
|
+
) as Element[];
|
|
148
|
+
let valid = true;
|
|
149
|
+
for (const quantity of quantities) {
|
|
150
|
+
const unitCode = quantity.getAttribute('unitCode') ?? '';
|
|
151
|
+
if (!meetsUnitCodeRule(unitCode)) {
|
|
152
|
+
this.addError(
|
|
153
|
+
'BR-CL-23',
|
|
154
|
+
`Unit code MUST be coded according to the UN/ECE Recommendation 20 with Rec 21 extension; found "${unitCode}"`,
|
|
155
|
+
`${this.locationOf(quantity)}/@unitCode`
|
|
156
|
+
);
|
|
157
|
+
valid = false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return valid;
|
|
161
|
+
}
|
|
162
|
+
|
|
116
163
|
/**
|
|
117
164
|
* Detects the CII profile from the XML
|
|
118
165
|
*/
|
|
@@ -191,7 +191,8 @@ export class FacturXDecoder extends CIIBaseDecoder {
|
|
|
191
191
|
const name = this.getText('ram:SpecifiedTradeProduct/ram:Name', itemNode);
|
|
192
192
|
const articleNumber = this.getText('ram:SpecifiedTradeProduct/ram:SellerAssignedID', itemNode);
|
|
193
193
|
const unitQuantity = this.getNumber('ram:SpecifiedLineTradeDelivery/ram:BilledQuantity', itemNode);
|
|
194
|
-
|
|
194
|
+
// an absent unit code (BT-130) stays absent, so that BR-23 reports it
|
|
195
|
+
const unitType = this.getText('ram:SpecifiedLineTradeDelivery/ram:BilledQuantity/@unitCode', itemNode);
|
|
195
196
|
const unitNetPrice = this.getNumber('ram:SpecifiedLineTradeAgreement/ram:NetPriceProductTradePrice/ram:ChargeAmount', itemNode);
|
|
196
197
|
const vatPercentage = this.getNumber('ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent', itemNode);
|
|
197
198
|
|
|
@@ -431,7 +431,10 @@ export class FacturXEncoder extends CIIBaseEncoder {
|
|
|
431
431
|
const deliveryElement = doc.createElement('ram:SpecifiedLineTradeDelivery');
|
|
432
432
|
const quantityElement = doc.createElement('ram:BilledQuantity');
|
|
433
433
|
quantityElement.textContent = item.unitQuantity.toString();
|
|
434
|
-
|
|
434
|
+
// a line without a unit is written without one, so that BR-23 reports it
|
|
435
|
+
if (item.unitType) {
|
|
436
|
+
quantityElement.setAttribute('unitCode', item.unitType);
|
|
437
|
+
}
|
|
435
438
|
deliveryElement.appendChild(quantityElement);
|
|
436
439
|
lineItemElement.appendChild(deliveryElement);
|
|
437
440
|
|
|
@@ -24,14 +24,14 @@ export class FacturXValidator extends CIIBaseValidator {
|
|
|
24
24
|
];
|
|
25
25
|
|
|
26
26
|
for (const section of sections) {
|
|
27
|
-
if (!this.exists(section)) {
|
|
27
|
+
if (!this.exists(`/rsm:CrossIndustryInvoice/${section}`)) {
|
|
28
28
|
this.addError('FX-STRUCT-1', `Required section ${section} is missing`, '/rsm:CrossIndustryInvoice');
|
|
29
29
|
valid = false;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
// Check for SupplyChainTradeTransaction sections
|
|
34
|
-
if (this.exists('rsm:SupplyChainTradeTransaction')) {
|
|
34
|
+
if (this.exists('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction')) {
|
|
35
35
|
const tradeSubsections = [
|
|
36
36
|
'ram:ApplicableHeaderTradeAgreement',
|
|
37
37
|
'ram:ApplicableHeaderTradeDelivery',
|
|
@@ -39,7 +39,7 @@ export class FacturXValidator extends CIIBaseValidator {
|
|
|
39
39
|
];
|
|
40
40
|
|
|
41
41
|
for (const subsection of tradeSubsections) {
|
|
42
|
-
if (!this.exists(
|
|
42
|
+
if (!this.exists(`/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/${subsection}`)) {
|
|
43
43
|
this.addError('FX-STRUCT-2', `Required subsection ${subsection} is missing`,
|
|
44
44
|
'/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction');
|
|
45
45
|
valid = false;
|
|
@@ -70,6 +70,12 @@ export class FacturXValidator extends CIIBaseValidator {
|
|
|
70
70
|
// and/or the Seller tax representative VAT identifier (BT-63).
|
|
71
71
|
valid = this.validateSellerVatIdentifier() && valid;
|
|
72
72
|
|
|
73
|
+
// BR-23: every line has a unit code
|
|
74
|
+
valid = this.validateLineUnitCodePresence() && valid;
|
|
75
|
+
|
|
76
|
+
// BR-CL-23: unit codes of UN/ECE Recommendation 20 with the Recommendation 21 extension
|
|
77
|
+
valid = this.validateUnitCodes() && valid;
|
|
78
|
+
|
|
73
79
|
return valid;
|
|
74
80
|
}
|
|
75
81
|
|
|
@@ -195,7 +195,8 @@ export class ZUGFeRDDecoder extends CIIBaseDecoder {
|
|
|
195
195
|
const name = this.getText('ram:SpecifiedTradeProduct/ram:Name', itemNode);
|
|
196
196
|
const articleNumber = this.getText('ram:SpecifiedTradeProduct/ram:SellerAssignedID', itemNode);
|
|
197
197
|
const unitQuantity = this.getNumber('ram:SpecifiedLineTradeDelivery/ram:BilledQuantity', itemNode);
|
|
198
|
-
|
|
198
|
+
// an absent unit code (BT-130) stays absent, so that BR-23 reports it
|
|
199
|
+
const unitType = this.getText('ram:SpecifiedLineTradeDelivery/ram:BilledQuantity/@unitCode', itemNode);
|
|
199
200
|
const unitNetPrice = this.getNumber('ram:SpecifiedLineTradeAgreement/ram:NetPriceProductTradePrice/ram:ChargeAmount', itemNode);
|
|
200
201
|
const vatPercentage = this.getNumber('ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent', itemNode);
|
|
201
202
|
|
|
@@ -599,7 +599,10 @@ export class ZUGFeRDEncoder extends CIIBaseEncoder {
|
|
|
599
599
|
const deliveryElement = doc.createElement('ram:SpecifiedLineTradeDelivery');
|
|
600
600
|
const quantityElement = doc.createElement('ram:BilledQuantity');
|
|
601
601
|
quantityElement.textContent = item.unitQuantity.toString();
|
|
602
|
-
|
|
602
|
+
// a line without a unit is written without one, so that BR-23 reports it
|
|
603
|
+
if (item.unitType) {
|
|
604
|
+
quantityElement.setAttribute('unitCode', item.unitType);
|
|
605
|
+
}
|
|
603
606
|
deliveryElement.appendChild(quantityElement);
|
|
604
607
|
lineItemElement.appendChild(deliveryElement);
|
|
605
608
|
|
|
@@ -206,7 +206,8 @@ export class ZUGFeRDV1Decoder extends CIIBaseDecoder {
|
|
|
206
206
|
const name = this.getText('ram:SpecifiedTradeProduct/ram:Name', itemNode);
|
|
207
207
|
const articleNumber = this.getText('ram:SpecifiedTradeProduct/ram:SellerAssignedID', itemNode);
|
|
208
208
|
const unitQuantity = this.getNumber('ram:SpecifiedLineTradeDelivery/ram:BilledQuantity', itemNode);
|
|
209
|
-
|
|
209
|
+
// an absent unit code (BT-130) stays absent, so that BR-23 reports it
|
|
210
|
+
const unitType = this.getText('ram:SpecifiedLineTradeDelivery/ram:BilledQuantity/@unitCode', itemNode);
|
|
210
211
|
const unitNetPrice = this.getNumber('ram:SpecifiedLineTradeAgreement/ram:NetPriceProductTradePrice/ram:ChargeAmount', itemNode);
|
|
211
212
|
const vatPercentage = this.getNumber('ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent', itemNode);
|
|
212
213
|
|
|
@@ -24,8 +24,9 @@ export class ZUGFeRDValidator extends CIIBaseValidator {
|
|
|
24
24
|
* @returns True if business validation passed
|
|
25
25
|
*/
|
|
26
26
|
protected validateBusinessRules(): boolean {
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
// BR-23: every line has a unit code
|
|
28
|
+
const linesHaveUnits = this.validateLineUnitCodePresence();
|
|
29
|
+
// BR-CL-23: unit codes of UN/ECE Recommendation 20 with the Recommendation 21 extension
|
|
30
|
+
return this.validateUnitCodes() && linesHaveUnits;
|
|
30
31
|
}
|
|
31
32
|
}
|
|
@@ -100,7 +100,8 @@ export class XMLToEInvoiceConverter {
|
|
|
100
100
|
position: i,
|
|
101
101
|
name: this.getElementTextFromNode(line, 'cbc:Name') || `Item ${i + 1}`,
|
|
102
102
|
unitQuantity: parseFloat(this.getElementTextFromNode(line, 'cbc:InvoicedQuantity') || '1'),
|
|
103
|
-
|
|
103
|
+
// the unit code (BT-130) the document states, or none, which BR-23 reports
|
|
104
|
+
unitType: this.getAttributeFromNode(line, 'cbc:InvoicedQuantity', 'unitCode'),
|
|
104
105
|
unitNetPrice: parseFloat(this.getElementTextFromNode(line, 'cbc:PriceAmount') || '100'),
|
|
105
106
|
vatPercentage: parseFloat(this.getElementTextFromNode(line, 'cbc:Percent') || '19')
|
|
106
107
|
};
|
|
@@ -118,6 +119,15 @@ export class XMLToEInvoiceConverter {
|
|
|
118
119
|
return mockInvoice as unknown as EInvoice;
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Helper to get an attribute of the first matching element below a node, or '' when the element
|
|
124
|
+
* or the attribute is absent
|
|
125
|
+
*/
|
|
126
|
+
private getAttributeFromNode(node: Element, tagName: string, attributeName: string): string {
|
|
127
|
+
const element = node.getElementsByTagName(tagName)[0];
|
|
128
|
+
return element?.getAttribute(attributeName) ?? '';
|
|
129
|
+
}
|
|
130
|
+
|
|
121
131
|
/**
|
|
122
132
|
* Helper to get element text from a node
|
|
123
133
|
*/
|
|
@@ -442,7 +442,7 @@ export class SemanticModelAdapter {
|
|
|
442
442
|
identifier: (index + 1).toString(),
|
|
443
443
|
note: (item as any).description || (item as any).text || '',
|
|
444
444
|
invoicedQuantity: item.unitQuantity,
|
|
445
|
-
invoicedQuantityUnitOfMeasureCode: item.unitType
|
|
445
|
+
invoicedQuantityUnitOfMeasureCode: item.unitType,
|
|
446
446
|
lineExtensionAmount: item.unitNetPrice * item.unitQuantity,
|
|
447
447
|
purchaseOrderLineReference: (item as any).purchaseOrderLineRef,
|
|
448
448
|
buyerAccountingReference: (item as any).buyerAccountingRef,
|
|
@@ -148,6 +148,12 @@ export class EN16931UBLValidator extends UBLBaseValidator {
|
|
|
148
148
|
valid = false;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
// BR-23: every line has a unit code
|
|
152
|
+
valid = this.validateLineUnitCodePresence() && valid;
|
|
153
|
+
|
|
154
|
+
// BR-CL-23: unit codes of UN/ECE Recommendation 20 with the Recommendation 21 extension
|
|
155
|
+
valid = this.validateUnitCodes() && valid;
|
|
156
|
+
|
|
151
157
|
// Validate calculation rules if we have the necessary data
|
|
152
158
|
if (this.exists('//cac:LegalMonetaryTotal/cbc:LineExtensionAmount')) {
|
|
153
159
|
valid = this.validateCalculationRules() && valid;
|
|
@@ -459,7 +459,10 @@ export class UBLEncoder extends UBLBaseEncoder {
|
|
|
459
459
|
|
|
460
460
|
// Invoiced quantity
|
|
461
461
|
const quantityElement = doc.createElement(creditNote ? 'cbc:CreditedQuantity' : 'cbc:InvoicedQuantity');
|
|
462
|
-
|
|
462
|
+
// a line without a unit is written without one, so that BR-23 reports it
|
|
463
|
+
if (item.unitType) {
|
|
464
|
+
quantityElement.setAttribute('unitCode', item.unitType);
|
|
465
|
+
}
|
|
463
466
|
quantityElement.textContent = item.unitQuantity.toString();
|
|
464
467
|
invoiceLineNode.appendChild(quantityElement);
|
|
465
468
|
|