@fin.cx/einvoice 8.1.0 → 8.2.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.d.ts +14 -1
- package/dist_ts/einvoice.js +17 -2
- package/dist_ts/formats/base/base.encoder.d.ts +1 -1
- package/dist_ts/formats/cii/cii.decoder.d.ts +12 -1
- package/dist_ts/formats/cii/cii.decoder.js +34 -1
- package/dist_ts/formats/cii/cii.encoder.d.ts +14 -3
- package/dist_ts/formats/cii/cii.encoder.js +58 -6
- package/dist_ts/formats/cii/cii.types.d.ts +1 -0
- package/dist_ts/formats/cii/cii.types.js +3 -2
- package/dist_ts/formats/cii/facturx/facturx.decoder.d.ts +1 -1
- package/dist_ts/formats/cii/facturx/facturx.decoder.js +9 -4
- package/dist_ts/formats/cii/facturx/facturx.encoder.d.ts +2 -2
- package/dist_ts/formats/cii/facturx/facturx.encoder.js +13 -10
- package/dist_ts/formats/cii/zugferd/zugferd.decoder.d.ts +1 -1
- package/dist_ts/formats/cii/zugferd/zugferd.decoder.js +9 -4
- package/dist_ts/formats/cii/zugferd/zugferd.encoder.d.ts +1 -1
- package/dist_ts/formats/cii/zugferd/zugferd.encoder.js +16 -13
- package/dist_ts/formats/cii/zugferd/zugferd.v1.decoder.d.ts +7 -1
- package/dist_ts/formats/cii/zugferd/zugferd.v1.decoder.js +27 -4
- package/dist_ts/formats/ubl/generic/ubl.encoder.d.ts +23 -1
- package/dist_ts/formats/ubl/generic/ubl.encoder.js +67 -11
- package/dist_ts/formats/ubl/ubl.decoder.d.ts +1 -1
- package/dist_ts/formats/ubl/ubl.encoder.d.ts +16 -3
- package/dist_ts/formats/ubl/ubl.encoder.js +75 -13
- package/dist_ts/formats/ubl/xrechnung/xrechnung.decoder.d.ts +6 -1
- package/dist_ts/formats/ubl/xrechnung/xrechnung.decoder.js +37 -7
- package/dist_ts/formats/ubl/xrechnung/xrechnung.encoder.d.ts +1 -1
- package/dist_ts/formats/ubl/xrechnung/xrechnung.encoder.js +4 -2
- package/dist_ts/formats/utils/date.value.d.ts +10 -0
- package/dist_ts/formats/utils/date.value.js +21 -0
- package/dist_ts/formats/utils/document.typecode.d.ts +3 -3
- package/dist_ts/formats/utils/document.typecode.js +8 -4
- package/dist_ts/formats/utils/preceding.invoice.d.ts +26 -0
- package/dist_ts/formats/utils/preceding.invoice.js +52 -0
- package/dist_ts/index.d.ts +1 -1
- package/dist_ts/index.js +1 -1
- package/dist_ts/interfaces/common.d.ts +6 -2
- package/package.json +3 -3
- package/readme.md +60 -10
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/einvoice.ts +18 -1
- package/ts/formats/base/base.encoder.ts +1 -1
- package/ts/formats/cii/cii.decoder.ts +43 -1
- package/ts/formats/cii/cii.encoder.ts +62 -6
- package/ts/formats/cii/cii.types.ts +2 -1
- package/ts/formats/cii/facturx/facturx.decoder.ts +9 -3
- package/ts/formats/cii/facturx/facturx.encoder.ts +13 -9
- package/ts/formats/cii/zugferd/zugferd.decoder.ts +9 -3
- package/ts/formats/cii/zugferd/zugferd.encoder.ts +16 -12
- package/ts/formats/cii/zugferd/zugferd.v1.decoder.ts +30 -3
- package/ts/formats/ubl/generic/ubl.encoder.ts +74 -10
- package/ts/formats/ubl/ubl.decoder.ts +1 -1
- package/ts/formats/ubl/ubl.encoder.ts +77 -13
- package/ts/formats/ubl/xrechnung/xrechnung.decoder.ts +45 -6
- package/ts/formats/ubl/xrechnung/xrechnung.encoder.ts +4 -1
- package/ts/formats/utils/date.value.ts +21 -0
- package/ts/formats/utils/document.typecode.ts +7 -3
- package/ts/formats/utils/preceding.invoice.ts +74 -0
- package/ts/index.ts +3 -0
- package/ts/interfaces/common.ts +6 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The preceding invoice references (BG-3) an encoder writes for a document,
|
|
3
|
+
* taken only from what the document states:
|
|
4
|
+
* - a corrected invoice: the invoice it corrects (`correctedInvoice`);
|
|
5
|
+
* - a corrected invoice or a credit note: every related document with the
|
|
6
|
+
* relation `'corrects'`.
|
|
7
|
+
* Other documents and other relations write none.
|
|
8
|
+
*/
|
|
9
|
+
export const getPrecedingInvoiceReferences = (accountingDoc) => {
|
|
10
|
+
const references = [];
|
|
11
|
+
if (accountingDoc.accountingDocType === 'corrected-invoice' && accountingDoc.correctedInvoice) {
|
|
12
|
+
references.push(accountingDoc.correctedInvoice);
|
|
13
|
+
}
|
|
14
|
+
if (accountingDoc.accountingDocType === 'corrected-invoice' ||
|
|
15
|
+
accountingDoc.accountingDocType === 'creditnote') {
|
|
16
|
+
for (const relatedDocument of accountingDoc.relatedDocuments ?? []) {
|
|
17
|
+
if (relatedDocument.relationType === 'corrects') {
|
|
18
|
+
references.push(relatedDocument.issueDate === undefined
|
|
19
|
+
? { documentId: relatedDocument.documentId }
|
|
20
|
+
: { documentId: relatedDocument.documentId, issueDate: relatedDocument.issueDate });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return references;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The envelope fields for the preceding invoice references a decoder read, the
|
|
28
|
+
* inverse of {@link getPrecedingInvoiceReferences}: on a corrected invoice the
|
|
29
|
+
* first reference is the invoice it corrects and further ones are related
|
|
30
|
+
* documents it corrects; on a credit note every reference is a related
|
|
31
|
+
* document it corrects. Other documents keep no reference, as before.
|
|
32
|
+
*/
|
|
33
|
+
export const getPrecedingInvoiceFields = (accountingDocType, references) => {
|
|
34
|
+
const asRelated = (reference) => ({
|
|
35
|
+
relationType: 'corrects',
|
|
36
|
+
...reference,
|
|
37
|
+
});
|
|
38
|
+
if (references.length === 0) {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
if (accountingDocType === 'corrected-invoice') {
|
|
42
|
+
const [correctedInvoice, ...furtherReferences] = references;
|
|
43
|
+
return furtherReferences.length === 0
|
|
44
|
+
? { correctedInvoice }
|
|
45
|
+
: { correctedInvoice, relatedDocuments: furtherReferences.map(asRelated) };
|
|
46
|
+
}
|
|
47
|
+
if (accountingDocType === 'creditnote') {
|
|
48
|
+
return { relatedDocuments: references.map(asRelated) };
|
|
49
|
+
}
|
|
50
|
+
return {};
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJlY2VkaW5nLmludm9pY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9mb3JtYXRzL3V0aWxzL3ByZWNlZGluZy5pbnZvaWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQWFBOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLENBQUMsTUFBTSw2QkFBNkIsR0FBRyxDQUMzQyxhQUE2QixFQUNDLEVBQUU7SUFDaEMsTUFBTSxVQUFVLEdBQWlDLEVBQUUsQ0FBQztJQUNwRCxJQUFJLGFBQWEsQ0FBQyxpQkFBaUIsS0FBSyxtQkFBbUIsSUFBSSxhQUFhLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUM5RixVQUFVLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ2xELENBQUM7SUFDRCxJQUNFLGFBQWEsQ0FBQyxpQkFBaUIsS0FBSyxtQkFBbUI7UUFDdkQsYUFBYSxDQUFDLGlCQUFpQixLQUFLLFlBQVksRUFDaEQsQ0FBQztRQUNELEtBQUssTUFBTSxlQUFlLElBQUksYUFBYSxDQUFDLGdCQUFnQixJQUFJLEVBQUUsRUFBRSxDQUFDO1lBQ25FLElBQUksZUFBZSxDQUFDLFlBQVksS0FBSyxVQUFVLEVBQUUsQ0FBQztnQkFDaEQsVUFBVSxDQUFDLElBQUksQ0FDYixlQUFlLENBQUMsU0FBUyxLQUFLLFNBQVM7b0JBQ3JDLENBQUMsQ0FBQyxFQUFFLFVBQVUsRUFBRSxlQUFlLENBQUMsVUFBVSxFQUFFO29CQUM1QyxDQUFDLENBQUMsRUFBRSxVQUFVLEVBQUUsZUFBZSxDQUFDLFVBQVUsRUFBRSxTQUFTLEVBQUUsZUFBZSxDQUFDLFNBQVMsRUFBRSxDQUNyRixDQUFDO1lBQ0osQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxVQUFVLENBQUM7QUFDcEIsQ0FBQyxDQUFDO0FBRUY7Ozs7OztHQU1HO0FBQ0gsTUFBTSxDQUFDLE1BQU0seUJBQXlCLEdBQUcsQ0FDdkMsaUJBQXFDLEVBQ3JDLFVBQXdDLEVBQ2tELEVBQUU7SUFDNUYsTUFBTSxTQUFTLEdBQUcsQ0FBQyxTQUFxQyxFQUFvQixFQUFFLENBQUMsQ0FBQztRQUM5RSxZQUFZLEVBQUUsVUFBVTtRQUN4QixHQUFHLFNBQVM7S0FDYixDQUFDLENBQUM7SUFDSCxJQUFJLFVBQVUsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDNUIsT0FBTyxFQUFFLENBQUM7SUFDWixDQUFDO0lBQ0QsSUFBSSxpQkFBaUIsS0FBSyxtQkFBbUIsRUFBRSxDQUFDO1FBQzlDLE1BQU0sQ0FBQyxnQkFBZ0IsRUFBRSxHQUFHLGlCQUFpQixDQUFDLEdBQUcsVUFBVSxDQUFDO1FBQzVELE9BQU8saUJBQWlCLENBQUMsTUFBTSxLQUFLLENBQUM7WUFDbkMsQ0FBQyxDQUFDLEVBQUUsZ0JBQWdCLEVBQUU7WUFDdEIsQ0FBQyxDQUFDLEVBQUUsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQUUsaUJBQWlCLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7SUFDL0UsQ0FBQztJQUNELElBQUksaUJBQWlCLEtBQUssWUFBWSxFQUFFLENBQUM7UUFDdkMsT0FBTyxFQUFFLGdCQUFnQixFQUFFLFVBQVUsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztJQUN6RCxDQUFDO0lBQ0QsT0FBTyxFQUFFLENBQUM7QUFDWixDQUFDLENBQUMifQ==
|
package/dist_ts/index.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ import { ZUGFeRDDecoder } from './formats/cii/zugferd/zugferd.decoder.js';
|
|
|
27
27
|
import { ZUGFeRDEncoder } from './formats/cii/zugferd/zugferd.encoder.js';
|
|
28
28
|
import { ZUGFeRDValidator } from './formats/cii/zugferd/zugferd.validator.js';
|
|
29
29
|
import { ZUGFeRDV1Decoder } from './formats/cii/zugferd/zugferd.v1.decoder.js';
|
|
30
|
-
export type { TInvoice, TCreditNote, TDebitNote, TSelfBilledInvoice, TAccountingDoc, TAccountingDocType, TAccountingDocEnvelope, TInvoiceDocument, TContact, TLetterEnvelope, TDocumentEnvelope, IPdf, ValidationError, ValidationResult, IValidator, ExportFormat, EInvoiceOptions } from './interfaces/common.js';
|
|
30
|
+
export type { TInvoice, TCorrectedInvoice, TCorrectedInvoiceReference, TInvoiceCorrection, TCreditNote, TDebitNote, TSelfBilledInvoice, TAccountingDoc, TAccountingDocType, TAccountingDocEnvelope, TInvoiceDocument, TContact, TLetterEnvelope, TDocumentEnvelope, IPdf, ValidationError, ValidationResult, IValidator, ExportFormat, EInvoiceOptions } from './interfaces/common.js';
|
|
31
31
|
export { ValidationLevel, InvoiceFormat } from './interfaces/common.js';
|
|
32
32
|
export { common as interfaces };
|
|
33
33
|
export { EInvoice };
|
package/dist_ts/index.js
CHANGED
|
@@ -98,4 +98,4 @@ export function validateXml(xml, level = common.ValidationLevel.SYNTAX) {
|
|
|
98
98
|
export function createEInvoice() {
|
|
99
99
|
return new EInvoice();
|
|
100
100
|
}
|
|
101
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
101
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxvQkFBb0I7QUFDcEIsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUV6QyxvQkFBb0I7QUFDcEIsT0FBTyxLQUFLLE1BQU0sTUFBTSx3QkFBd0IsQ0FBQztBQUVqRCxtQkFBbUI7QUFDbkIsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHdDQUF3QyxDQUFDO0FBQ3hFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUN4RSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQztBQUU1RSxzQkFBc0I7QUFDdEIsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQzdELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUM3RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFFakUsMEJBQTBCO0FBQzFCLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUM5RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDOUQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFbEUsMEJBQTBCO0FBQzFCLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUM5RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDOUQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFbEUsdUJBQXVCO0FBQ3ZCLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUM1RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDOUQsT0FBTyxFQUNMLGdCQUFnQixF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
|
|
@@ -72,8 +72,12 @@ export interface IPdf {
|
|
|
72
72
|
};
|
|
73
73
|
buffer: Uint8Array;
|
|
74
74
|
}
|
|
75
|
-
import type { TInvoice, TDebitNote, TSelfBilledInvoice } from '@tsclass/tsclass/dist_ts/finance/index.js';
|
|
75
|
+
import type { TInvoice, TCorrectedInvoice, TDebitNote, TSelfBilledInvoice } from '@tsclass/tsclass/dist_ts/finance/index.js';
|
|
76
76
|
export type { TInvoice } from '@tsclass/tsclass/dist_ts/finance/index.js';
|
|
77
|
+
export type { TCorrectedInvoice } from '@tsclass/tsclass/dist_ts/finance/index.js';
|
|
78
|
+
export type { TCorrectedInvoiceReference } from '@tsclass/tsclass/dist_ts/finance/index.js';
|
|
79
|
+
export type { TInvoiceCorrection } from '@tsclass/tsclass/dist_ts/finance/index.js';
|
|
80
|
+
export type { TRelatedDocument } from '@tsclass/tsclass/dist_ts/finance/index.js';
|
|
77
81
|
export type { TCreditNote } from '@tsclass/tsclass/dist_ts/finance/index.js';
|
|
78
82
|
export type { TDebitNote } from '@tsclass/tsclass/dist_ts/finance/index.js';
|
|
79
83
|
export type { TSelfBilledInvoice } from '@tsclass/tsclass/dist_ts/finance/index.js';
|
|
@@ -85,7 +89,7 @@ export type { TAccountingDocEnvelope } from '@tsclass/tsclass/dist_ts/finance/in
|
|
|
85
89
|
* `Invoice` root and the CII type codes of the invoice family. Only a credit
|
|
86
90
|
* note has a document shape of its own.
|
|
87
91
|
*/
|
|
88
|
-
export type TInvoiceDocument = TInvoice | TDebitNote | TSelfBilledInvoice;
|
|
92
|
+
export type TInvoiceDocument = TInvoice | TCorrectedInvoice | TDebitNote | TSelfBilledInvoice;
|
|
89
93
|
export type { TContact } from '@tsclass/tsclass/dist_ts/business/index.js';
|
|
90
94
|
export type { TLetterEnvelope } from '@tsclass/tsclass/dist_ts/business/index.js';
|
|
91
95
|
export type { TDocumentEnvelope } from '@tsclass/tsclass/dist_ts/business/index.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fin.cx/einvoice",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.2.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",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@push.rocks/smartxml": "^2.1.0",
|
|
25
|
-
"@tsclass/tsclass": "^9.
|
|
25
|
+
"@tsclass/tsclass": "^9.8.0",
|
|
26
26
|
"@xmldom/xmldom": "^0.9.10",
|
|
27
27
|
"pako": "^2.1.0",
|
|
28
28
|
"pdf-lib": "^1.17.1",
|
|
@@ -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 && tstest test/test.en16931-unitcodes.node.ts --verbose --logfile --timeout 60 && tstest test/test.en16931-lineunit.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.corrected-invoice.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
|
@@ -147,7 +147,7 @@ invoice.items = [
|
|
|
147
147
|
const xml = await invoice.exportXml('facturx');
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
-
### Credit notes, debit notes and self-billed invoices
|
|
150
|
+
### Credit notes, debit notes, corrected invoices and self-billed invoices
|
|
151
151
|
|
|
152
152
|
One class holds every accounting document. `accountingDocType` is the tsclass
|
|
153
153
|
`TAccountingDocType` and defaults to `invoice`; `invoiceType` is an alias for it.
|
|
@@ -163,24 +163,72 @@ creditNote.relatedDocuments = [{ relationType: 'corrects', documentId: 'INV-2026
|
|
|
163
163
|
| `accountingDocType` | Type code (BT-3) | UBL / XRechnung | CII / Factur-X / ZUGFeRD |
|
|
164
164
|
| --- | --- | --- | --- |
|
|
165
165
|
| `invoice` | 380 | `Invoice` with `cbc:InvoiceTypeCode` | `ram:TypeCode` |
|
|
166
|
+
| `corrected-invoice` | 384 | `Invoice` with `cbc:InvoiceTypeCode` | `ram:TypeCode` |
|
|
166
167
|
| `creditnote` | 381 | `CreditNote` with `cbc:CreditNoteTypeCode`, `cac:CreditNoteLine`, no `cbc:DueDate` | `ram:TypeCode` |
|
|
167
168
|
| `debitnote` | 383 | `Invoice` with `cbc:InvoiceTypeCode` | `ram:TypeCode` |
|
|
168
169
|
| `self-billed-invoice` | 389 | `Invoice` with `cbc:InvoiceTypeCode` | `ram:TypeCode` |
|
|
169
170
|
|
|
170
171
|
Import reads the type back from the code in UBL, XRechnung, Factur-X and
|
|
171
|
-
ZUGFeRD v2: 383 is a debit note,
|
|
172
|
-
related codes of EN16931 rule BR-CL-01 (381, 396, 261,
|
|
173
|
-
every other code an invoice, which covers 380
|
|
174
|
-
the envelope has no name for (326, 386, 875, ...). A UBL
|
|
175
|
-
always a credit note.
|
|
176
|
-
is written as 380. A value outside `TAccountingDocType`,
|
|
177
|
-
is refused with an `EInvoiceFormatError` by the
|
|
172
|
+
ZUGFeRD v2: 383 is a debit note, 384 a corrected invoice, 389 a self-billed
|
|
173
|
+
invoice, the credit note related codes of EN16931 rule BR-CL-01 (381, 396, 261,
|
|
174
|
+
...) a credit note, and every other code an invoice, which covers 380 and the
|
|
175
|
+
invoice related codes the envelope has no name for (326, 386, 875, ...). A UBL
|
|
176
|
+
`CreditNote` root is always a credit note. Those other codes are not kept: a 326
|
|
177
|
+
that is exported again is written as 380. A value outside `TAccountingDocType`,
|
|
178
|
+
a missing one included, is refused with an `EInvoiceFormatError` by the
|
|
179
|
+
`invoiceType` setter and at export.
|
|
178
180
|
|
|
179
181
|
ZUGFeRD v1 is the one exception: a v1 document always reads as `invoice`,
|
|
180
182
|
whatever its type code says, because `ZUGFeRDV1Decoder` does not read the v1
|
|
181
183
|
header at all. This is a known defect and will be fixed separately; until then
|
|
182
184
|
do not rely on the type of an imported ZUGFeRD v1 document.
|
|
183
185
|
|
|
186
|
+
### Corrected invoices and preceding invoice references (BG-3)
|
|
187
|
+
|
|
188
|
+
A corrected invoice (`TCorrectedInvoice`, type code 384) corrects an issued
|
|
189
|
+
invoice that lacked a mandatory detail or stated one incorrectly (§ 31 Abs. 5
|
|
190
|
+
UStDV). It is a complete invoice, and `correctedInvoice` names the invoice it
|
|
191
|
+
corrects:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
const correction = new EInvoice();
|
|
195
|
+
correction.accountingDocType = 'corrected-invoice';
|
|
196
|
+
correction.correctedInvoice = { documentId: 'INV-2026-001', issueDate: Date.UTC(2026, 8, 1) };
|
|
197
|
+
correction.corrections = [{ detail: 'supply-date', defect: 'missing' }];
|
|
198
|
+
correction.deliveryDate = Date.UTC(2026, 7, 28);
|
|
199
|
+
correction.notes = ['Berichtigung der Rechnung INV-2026-001 vom 01.09.2026: Leistungsdatum ergänzt.'];
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The encoders write a preceding invoice reference (BG-3) with the number (BT-25)
|
|
203
|
+
and, when stated, the issue date (BT-26) for:
|
|
204
|
+
|
|
205
|
+
- the `correctedInvoice` of a corrected invoice;
|
|
206
|
+
- every entry of `relatedDocuments` with `relationType: 'corrects'` on a
|
|
207
|
+
corrected invoice or a credit note.
|
|
208
|
+
|
|
209
|
+
UBL and XRechnung write `cac:BillingReference/cac:InvoiceDocumentReference`
|
|
210
|
+
(`cbc:ID`, `cbc:IssueDate`); Factur-X and ZUGFeRD write
|
|
211
|
+
`ram:InvoiceReferencedDocument` (`ram:IssuerAssignedID`,
|
|
212
|
+
`ram:FormattedIssueDateTime`) after the header monetary summation. Import reads
|
|
213
|
+
them back the same way: on a corrected invoice the first reference is
|
|
214
|
+
`correctedInvoice` and further ones are `relatedDocuments` with `'corrects'`; on
|
|
215
|
+
a credit note every reference is such a related document.
|
|
216
|
+
|
|
217
|
+
The encoders write only what the document states:
|
|
218
|
+
|
|
219
|
+
- A corrected invoice without `correctedInvoice` is written without BG-3. The
|
|
220
|
+
XRechnung rules then report the warning BR-DE-26; stating the reference is the
|
|
221
|
+
issuer's duty (§ 31 Abs. 5 Satz 2 UStDV), which the issuing application has
|
|
222
|
+
to enforce.
|
|
223
|
+
- `corrections` has no element in EN 16931 and is not written. State what is
|
|
224
|
+
corrected as a note (`notes`, BT-22); the encoders add no text of their own.
|
|
225
|
+
- The Factur-X MINIMUM profile has no BG-3. A document with a preceding invoice
|
|
226
|
+
reference is refused there with an `EInvoiceFormatError`.
|
|
227
|
+
|
|
228
|
+
With the reference stated, the XRechnung output of a corrected invoice passes
|
|
229
|
+
the official EN 16931 1.3.16 and KoSIT XRechnung Schematron 2.6.0 rules without
|
|
230
|
+
BR-DE-17 or BR-DE-26.
|
|
231
|
+
|
|
184
232
|
An `EInvoice` is a `TAccountingDoc`, not a `TInvoice`: narrow on
|
|
185
233
|
`accountingDocType` where a function takes one document type only.
|
|
186
234
|
|
|
@@ -196,6 +244,7 @@ import {
|
|
|
196
244
|
|
|
197
245
|
getDocumentTypeCode('debitnote'); // '383'; throws EInvoiceFormatError for an unknown type
|
|
198
246
|
getAccountingDocType('396'); // 'creditnote'; an unlisted or empty code is 'invoice'
|
|
247
|
+
getAccountingDocType('384'); // 'corrected-invoice'
|
|
199
248
|
isAccountingDocType('proforma'); // false
|
|
200
249
|
```
|
|
201
250
|
|
|
@@ -311,7 +360,8 @@ required archival profile.
|
|
|
311
360
|
- XRechnung import routes UBL and CII by the namespace-qualified XML root.
|
|
312
361
|
Missing or invalid document-level invoice IDs and issue dates are rejected.
|
|
313
362
|
- `FatturaPA` should be documented as detection-only right now.
|
|
314
|
-
-
|
|
363
|
+
- The day of supply: UBL and XRechnung write `deliveryDate` as `cac:Delivery/cbc:ActualDeliveryDate` (BT-72) and `periodOfPerformance` as `cac:InvoicePeriod` (BG-14), and read both back. Factur-X and ZUGFeRD write `deliveryDate` as `ram:ActualDeliverySupplyChainEvent` and read it back only when the document states it.
|
|
364
|
+
- Credit notes, debit notes, corrected invoices and self-billed invoices share the `EInvoice` class; see the type code table above. Credit and debit amounts are written as given, the encoders do not change signs.
|
|
315
365
|
- ZUGFeRD v1 input is detected and routed, but the v1 decoder does not read the v1 header, so such a document always arrives as an `invoice`. Known defect, to be fixed separately.
|
|
316
366
|
|
|
317
367
|
## Public API overview
|
|
@@ -330,7 +380,7 @@ Top-level exports from `@fin.cx/einvoice` include:
|
|
|
330
380
|
- `UBLBase*` and `CIIBase*` extension classes
|
|
331
381
|
- `FacturX*` and `ZUGFeRD*` format-specific classes
|
|
332
382
|
- `ValidationLevel`, `InvoiceFormat`
|
|
333
|
-
- exported types like `TAccountingDoc`, `TAccountingDocType`, `TInvoice`, `TCreditNote`, `TDebitNote`, `TSelfBilledInvoice`, `IEn16931UnitCodeList`, `ValidationResult`, `ValidationError`, `ExportFormat`, `IPdf`, `EInvoiceOptions`
|
|
383
|
+
- exported types like `TAccountingDoc`, `TAccountingDocType`, `TInvoice`, `TCorrectedInvoice`, `TCorrectedInvoiceReference`, `TInvoiceCorrection`, `TCreditNote`, `TDebitNote`, `TSelfBilledInvoice`, `IEn16931UnitCodeList`, `ValidationResult`, `ValidationError`, `ExportFormat`, `IPdf`, `EInvoiceOptions`
|
|
334
384
|
|
|
335
385
|
## Validation resources and install-time behavior
|
|
336
386
|
|
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.2.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
|
@@ -56,7 +56,8 @@ import type { IEInvoiceMetadata } from './interfaces/en16931-metadata.js';
|
|
|
56
56
|
* Main class for working with electronic invoices.
|
|
57
57
|
* Supports various invoice formats including Factur-X, ZUGFeRD, UBL, and XRechnung
|
|
58
58
|
* Implements the tsclass accounting document envelope: one class holds an invoice,
|
|
59
|
-
* a credit note, a debit note or a self-billed invoice, told
|
|
59
|
+
* a corrected invoice, a credit note, a debit note or a self-billed invoice, told
|
|
60
|
+
* apart by `accountingDocType`
|
|
60
61
|
*/
|
|
61
62
|
export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}> {
|
|
62
63
|
private static sharedPdfEmbedder?: import('./formats/pdf/pdf.embedder.js').PDFEmbedder;
|
|
@@ -152,6 +153,20 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
|
|
|
152
153
|
documentId: string;
|
|
153
154
|
issueDate?: number;
|
|
154
155
|
}>;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* On a corrected invoice (type code 384): the invoice it corrects, written
|
|
159
|
+
* as the first preceding invoice reference (BG-3). A received corrected
|
|
160
|
+
* invoice may lack it.
|
|
161
|
+
*/
|
|
162
|
+
public correctedInvoice?: finance.TCorrectedInvoiceReference;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* On a corrected invoice: the details it supplies or corrects, as the
|
|
166
|
+
* issuer states them. EN 16931 has no element for them, so they are not
|
|
167
|
+
* written to the XML; an issuer states them as a note in `notes`.
|
|
168
|
+
*/
|
|
169
|
+
public corrections?: finance.TCorrectedInvoice['corrections'];
|
|
155
170
|
public printResult?: {
|
|
156
171
|
pdfBufferString: string;
|
|
157
172
|
totalNet: number;
|
|
@@ -447,6 +462,8 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
|
|
|
447
462
|
electronicAddress: this.electronicAddress,
|
|
448
463
|
paymentOptions: this.paymentOptions,
|
|
449
464
|
relatedDocuments: this.relatedDocuments,
|
|
465
|
+
correctedInvoice: this.correctedInvoice,
|
|
466
|
+
corrections: this.corrections,
|
|
450
467
|
printResult: this.printResult
|
|
451
468
|
};
|
|
452
469
|
|
|
@@ -6,7 +6,7 @@ import type { TAccountingDoc } from '../../interfaces/common.js';
|
|
|
6
6
|
*/
|
|
7
7
|
export abstract class BaseEncoder {
|
|
8
8
|
/**
|
|
9
|
-
* Encodes an accounting document (invoice, credit note, debit note or self-billed invoice) into XML
|
|
9
|
+
* Encodes an accounting document (invoice, corrected invoice, credit note, debit note or self-billed invoice) into XML
|
|
10
10
|
* @param invoice Accounting document to encode
|
|
11
11
|
* @returns XML string
|
|
12
12
|
*/
|
|
@@ -4,12 +4,14 @@ import { CII_NAMESPACES, CIIProfile } from './cii.types.js';
|
|
|
4
4
|
import { DOMParser, xpath } from '../../plugins.js';
|
|
5
5
|
import { EInvoiceParsingError } from '../../errors.js';
|
|
6
6
|
import { getAccountingDocType } from '../utils/document.typecode.js';
|
|
7
|
+
import type { TPrecedingInvoiceReference } from '../utils/preceding.invoice.js';
|
|
7
8
|
|
|
8
9
|
const ciiParser = new DOMParser();
|
|
9
10
|
const ciiNamespaces = {
|
|
10
11
|
rsm: CII_NAMESPACES.RSM,
|
|
11
12
|
ram: CII_NAMESPACES.RAM,
|
|
12
13
|
udt: CII_NAMESPACES.UDT,
|
|
14
|
+
qdt: CII_NAMESPACES.QDT,
|
|
13
15
|
};
|
|
14
16
|
const ciiSelect = xpath.useNamespaces(ciiNamespaces);
|
|
15
17
|
|
|
@@ -91,7 +93,7 @@ export abstract class CIIBaseDecoder extends BaseDecoder {
|
|
|
91
93
|
protected abstract decodeCreditNote(): Promise<TCreditNote>;
|
|
92
94
|
|
|
93
95
|
/**
|
|
94
|
-
* Decodes a CII invoice document: an invoice, a debit note or a self-billed invoice
|
|
96
|
+
* Decodes a CII invoice document: an invoice, a corrected invoice, a debit note or a self-billed invoice
|
|
95
97
|
* @param accountingDocType The type the document type code stands for
|
|
96
98
|
* @returns Promise resolving to the invoice document
|
|
97
99
|
*/
|
|
@@ -99,6 +101,46 @@ export abstract class CIIBaseDecoder extends BaseDecoder {
|
|
|
99
101
|
accountingDocType: TInvoiceDocument['accountingDocType'],
|
|
100
102
|
): Promise<TInvoiceDocument>;
|
|
101
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Reads the preceding invoice references (BG-3) of the header settlement:
|
|
106
|
+
* the number (BT-25) and, when stated, the issue date (BT-26) of each.
|
|
107
|
+
*/
|
|
108
|
+
protected extractPrecedingInvoiceReferences(): TPrecedingInvoiceReference[] {
|
|
109
|
+
const referenceNodes = this.select(
|
|
110
|
+
'/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument',
|
|
111
|
+
this.doc,
|
|
112
|
+
);
|
|
113
|
+
const references: TPrecedingInvoiceReference[] = [];
|
|
114
|
+
for (const referenceNode of Array.isArray(referenceNodes) ? referenceNodes : []) {
|
|
115
|
+
const documentId = this.getText('./ram:IssuerAssignedID', referenceNode as Node).trim();
|
|
116
|
+
if (!documentId) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const issueDate = this.getText('./ram:FormattedIssueDateTime/qdt:DateTimeString', referenceNode as Node).trim();
|
|
120
|
+
const issueDateFormat = this.getText('./ram:FormattedIssueDateTime/qdt:DateTimeString/@format', referenceNode as Node).trim();
|
|
121
|
+
references.push(
|
|
122
|
+
issueDate
|
|
123
|
+
? { documentId, issueDate: this.parseRequiredCIIDate(issueDate, issueDateFormat) }
|
|
124
|
+
: { documentId },
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return references;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Reads the actual delivery date (BT-72) of the header delivery; undefined
|
|
132
|
+
* when the document does not state one.
|
|
133
|
+
*/
|
|
134
|
+
protected extractDeliveryDate(): number | undefined {
|
|
135
|
+
const deliveryDatePath =
|
|
136
|
+
'/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:ActualDeliverySupplyChainEvent/ram:OccurrenceDateTime/udt:DateTimeString';
|
|
137
|
+
const deliveryDate = this.getText(deliveryDatePath).trim();
|
|
138
|
+
if (!deliveryDate) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
return this.parseRequiredCIIDate(deliveryDate, this.getText(`${deliveryDatePath}/@format`).trim());
|
|
142
|
+
}
|
|
143
|
+
|
|
102
144
|
/**
|
|
103
145
|
* Gets a text value from an XPath expression
|
|
104
146
|
* @param xpath XPath expression
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { BaseEncoder } from '../base/base.encoder.js';
|
|
2
2
|
import type { TAccountingDoc, TCreditNote, TInvoiceDocument } from '../../interfaces/common.js';
|
|
3
|
+
import { EInvoiceFormatError } from '../../errors.js';
|
|
4
|
+
import { getPrecedingInvoiceReferences } from '../utils/preceding.invoice.js';
|
|
5
|
+
import { getWritableDate } from '../utils/date.value.js';
|
|
3
6
|
import { CII_NAMESPACES, CIIProfile } from './cii.types.js';
|
|
4
7
|
|
|
5
8
|
/**
|
|
@@ -22,6 +25,13 @@ export abstract class CIIBaseEncoder extends BaseEncoder {
|
|
|
22
25
|
* @returns CII XML string
|
|
23
26
|
*/
|
|
24
27
|
public async encode(invoice: TAccountingDoc): Promise<string> {
|
|
28
|
+
// MINIMUM has no preceding invoice reference (BG-3): a reference the document states cannot be written there
|
|
29
|
+
if (this.profile === CIIProfile.MINIMUM && getPrecedingInvoiceReferences(invoice).length > 0) {
|
|
30
|
+
throw new EInvoiceFormatError(
|
|
31
|
+
'The MINIMUM profile has no preceding invoice reference (BG-3); a document that refers to a preceding invoice needs a profile that carries it',
|
|
32
|
+
{ targetFormat: 'cii', unsupportedFeatures: ['BG-3 in profile MINIMUM'] },
|
|
33
|
+
);
|
|
34
|
+
}
|
|
25
35
|
// CII has one root for every document; the two paths differ in the type code they write
|
|
26
36
|
if (invoice.accountingDocType === 'creditnote') {
|
|
27
37
|
return this.encodeCreditNote(invoice);
|
|
@@ -37,7 +47,7 @@ export abstract class CIIBaseEncoder extends BaseEncoder {
|
|
|
37
47
|
protected abstract encodeCreditNote(creditNote: TCreditNote): Promise<string>;
|
|
38
48
|
|
|
39
49
|
/**
|
|
40
|
-
* Encodes an invoice document (invoice, debit note or self-billed invoice) into CII XML
|
|
50
|
+
* Encodes an invoice document (invoice, corrected invoice, debit note or self-billed invoice) into CII XML
|
|
41
51
|
* @param invoice Invoice document to encode
|
|
42
52
|
* @returns CII XML string
|
|
43
53
|
*/
|
|
@@ -51,17 +61,63 @@ export abstract class CIIBaseEncoder extends BaseEncoder {
|
|
|
51
61
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
52
62
|
<rsm:CrossIndustryInvoice xmlns:rsm="${CII_NAMESPACES.RSM}"
|
|
53
63
|
xmlns:ram="${CII_NAMESPACES.RAM}"
|
|
54
|
-
xmlns:udt="${CII_NAMESPACES.UDT}"
|
|
64
|
+
xmlns:udt="${CII_NAMESPACES.UDT}"
|
|
65
|
+
xmlns:qdt="${CII_NAMESPACES.QDT}">
|
|
55
66
|
</rsm:CrossIndustryInvoice>`;
|
|
56
67
|
}
|
|
57
68
|
|
|
58
69
|
/**
|
|
59
|
-
*
|
|
70
|
+
* Writes one preceding invoice reference (BG-3) per invoice the document
|
|
71
|
+
* states it corrects: the number (BT-25) and, when stated, the issue date
|
|
72
|
+
* (BT-26). In the header settlement they follow the monetary summation, as
|
|
73
|
+
* the CII schema orders them.
|
|
74
|
+
* @param doc XML document
|
|
75
|
+
* @param invoice Accounting document
|
|
76
|
+
*/
|
|
77
|
+
protected addPrecedingInvoiceReferences(doc: Document, invoice: TAccountingDoc): void {
|
|
78
|
+
const references = getPrecedingInvoiceReferences(invoice);
|
|
79
|
+
if (references.length === 0) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const settlement = doc.getElementsByTagName('ram:ApplicableHeaderTradeSettlement')[0];
|
|
83
|
+
if (!settlement) {
|
|
84
|
+
throw new EInvoiceFormatError('The document has no header trade settlement to carry its preceding invoice reference', {
|
|
85
|
+
targetFormat: 'cii',
|
|
86
|
+
unsupportedFeatures: ['BG-3'],
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
let summation: Element | undefined;
|
|
90
|
+
for (let node = settlement.firstChild; node; node = node.nextSibling) {
|
|
91
|
+
if (node.nodeType === 1 && node.nodeName === 'ram:SpecifiedTradeSettlementHeaderMonetarySummation') {
|
|
92
|
+
summation = node as Element;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const anchor = summation ? summation.nextSibling : null;
|
|
96
|
+
for (const reference of references) {
|
|
97
|
+
const referencedDocument = doc.createElement('ram:InvoiceReferencedDocument');
|
|
98
|
+
const issuerAssignedId = doc.createElement('ram:IssuerAssignedID');
|
|
99
|
+
issuerAssignedId.textContent = reference.documentId;
|
|
100
|
+
referencedDocument.appendChild(issuerAssignedId);
|
|
101
|
+
if (reference.issueDate !== undefined) {
|
|
102
|
+
const formattedIssueDateTime = doc.createElement('ram:FormattedIssueDateTime');
|
|
103
|
+
const dateTimeString = doc.createElement('qdt:DateTimeString');
|
|
104
|
+
dateTimeString.setAttribute('format', '102');
|
|
105
|
+
dateTimeString.textContent = this.formatDate(reference.issueDate, 'BT-26 preceding invoice issue date').replace(/-/g, '');
|
|
106
|
+
formattedIssueDateTime.appendChild(dateTimeString);
|
|
107
|
+
referencedDocument.appendChild(formattedIssueDateTime);
|
|
108
|
+
}
|
|
109
|
+
settlement.insertBefore(referencedDocument, anchor);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Formats a date as an ISO string (YYYY-MM-DD, UTC). A value that is no
|
|
115
|
+
* valid timestamp is refused with an `EInvoiceFormatError` naming the field.
|
|
60
116
|
* @param timestamp Timestamp to format
|
|
117
|
+
* @param field The business term the date is written as
|
|
61
118
|
* @returns Formatted date string
|
|
62
119
|
*/
|
|
63
|
-
protected formatDate(timestamp: number): string {
|
|
64
|
-
|
|
65
|
-
return date.toISOString().split('T')[0];
|
|
120
|
+
protected formatDate(timestamp: number, field: string): string {
|
|
121
|
+
return getWritableDate(timestamp, field, 'cii').toISOString().split('T')[0];
|
|
66
122
|
}
|
|
67
123
|
}
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
export const CII_NAMESPACES = {
|
|
7
7
|
RSM: 'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100',
|
|
8
8
|
RAM: 'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100',
|
|
9
|
-
UDT: 'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100'
|
|
9
|
+
UDT: 'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100',
|
|
10
|
+
QDT: 'urn:un:unece:uncefact:data:standard:QualifiedDataType:100'
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
// ZUGFeRD v1 namespaces
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
import { FACTURX_PROFILE_IDS } from './facturx.types.js';
|
|
9
9
|
import { business, finance, general } from '../../../plugins.js';
|
|
10
10
|
import { EN16931Validator } from '../../validation/en16931.validator.js';
|
|
11
|
+
import { getPrecedingInvoiceFields } from '../../utils/preceding.invoice.js';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Decoder for Factur-X invoice format
|
|
@@ -22,7 +23,7 @@ export class FacturXDecoder extends CIIBaseDecoder {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
|
-
* Decodes a Factur-X invoice document: an invoice, a debit note or a self-billed invoice
|
|
26
|
+
* Decodes a Factur-X invoice document: an invoice, a corrected invoice, a debit note or a self-billed invoice
|
|
26
27
|
* @param accountingDocType The type the document type code stands for
|
|
27
28
|
* @returns Promise resolving to the invoice document
|
|
28
29
|
*/
|
|
@@ -92,6 +93,9 @@ export class FacturXDecoder extends CIIBaseDecoder {
|
|
|
92
93
|
notes = allNotes.slice(1); // Remove subject from notes
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
// Extract the actual delivery date, if stated
|
|
97
|
+
const deliveryDate = this.extractDeliveryDate();
|
|
98
|
+
|
|
95
99
|
// Check for reverse charge
|
|
96
100
|
const reverseCharge = this.exists('//ram:SpecifiedTradeAllowanceCharge/ram:ReasonCode[text()="62"]');
|
|
97
101
|
|
|
@@ -119,8 +123,10 @@ export class FacturXDecoder extends CIIBaseDecoder {
|
|
|
119
123
|
reverseCharge: reverseCharge,
|
|
120
124
|
currency: currencyCode as finance.TCurrency,
|
|
121
125
|
notes: notes,
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
// the day of supply as the document states it (BT-72)
|
|
127
|
+
...(deliveryDate === undefined ? {} : { deliveryDate }),
|
|
128
|
+
objectActions: [],
|
|
129
|
+
...getPrecedingInvoiceFields(accountingDocType, this.extractPrecedingInvoiceReferences()),
|
|
124
130
|
};
|
|
125
131
|
|
|
126
132
|
// Validate mandatory EN16931 fields unless validation is skipped
|
|
@@ -6,6 +6,7 @@ import { Decimal } from '../../utils/decimal.js';
|
|
|
6
6
|
import { DecimalCurrencyCalculator } from '../../utils/currency.calculator.decimal.js';
|
|
7
7
|
import { getCurrencyMinorUnits } from '../../utils/currency.utils.js';
|
|
8
8
|
import { getDocumentTypeCode } from '../../utils/document.typecode.js';
|
|
9
|
+
import { getWritableDate } from '../../utils/date.value.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Encoder for Factur-X invoice format
|
|
@@ -31,7 +32,7 @@ export class FacturXEncoder extends CIIBaseEncoder {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
|
-
* Encodes an invoice document (invoice, debit note or self-billed invoice) into Factur-X XML
|
|
35
|
+
* Encodes an invoice document (invoice, corrected invoice, debit note or self-billed invoice) into Factur-X XML
|
|
35
36
|
* @param invoice Invoice document to encode
|
|
36
37
|
* @returns Factur-X XML string
|
|
37
38
|
*/
|
|
@@ -39,7 +40,7 @@ export class FacturXEncoder extends CIIBaseEncoder {
|
|
|
39
40
|
// Create base XML
|
|
40
41
|
const xmlDoc = this.createBaseXml();
|
|
41
42
|
|
|
42
|
-
// Set document type code (380 invoice, 383 debit note, 389 self-billed invoice)
|
|
43
|
+
// Set document type code (380 invoice, 383 debit note, 384 corrected invoice, 389 self-billed invoice)
|
|
43
44
|
this.setDocumentTypeCode(xmlDoc, getDocumentTypeCode(invoice.accountingDocType));
|
|
44
45
|
|
|
45
46
|
// Add common invoice data
|
|
@@ -101,7 +102,7 @@ export class FacturXEncoder extends CIIBaseEncoder {
|
|
|
101
102
|
/**
|
|
102
103
|
* Sets the document type code in the XML document
|
|
103
104
|
* @param doc XML document
|
|
104
|
-
* @param typeCode Document type code (380 invoice, 381 credit note, 383 debit note, 389 self-billed invoice)
|
|
105
|
+
* @param typeCode Document type code (380 invoice, 381 credit note, 383 debit note, 384 corrected invoice, 389 self-billed invoice)
|
|
105
106
|
*/
|
|
106
107
|
private setDocumentTypeCode(doc: Document, typeCode: string): void {
|
|
107
108
|
// Get root element
|
|
@@ -145,7 +146,7 @@ export class FacturXEncoder extends CIIBaseEncoder {
|
|
|
145
146
|
const issueDateElement = doc.createElement('ram:IssueDateTime');
|
|
146
147
|
const dateStringElement = doc.createElement('udt:DateTimeString');
|
|
147
148
|
dateStringElement.setAttribute('format', '102'); // YYYYMMDD format
|
|
148
|
-
dateStringElement.textContent = this.formatDateYYYYMMDD(invoice.date);
|
|
149
|
+
dateStringElement.textContent = this.formatDateYYYYMMDD(invoice.date, 'BT-2 invoice issue date');
|
|
149
150
|
issueDateElement.appendChild(dateStringElement);
|
|
150
151
|
documentElement.appendChild(issueDateElement);
|
|
151
152
|
|
|
@@ -183,6 +184,9 @@ export class FacturXEncoder extends CIIBaseEncoder {
|
|
|
183
184
|
// Add settlement section with payment terms and totals
|
|
184
185
|
this.addSettlementSection(doc, transactionElement, invoice);
|
|
185
186
|
|
|
187
|
+
// Add the preceding invoice references (BG-3) the document states
|
|
188
|
+
this.addPrecedingInvoiceReferences(doc, invoice);
|
|
189
|
+
|
|
186
190
|
// Add line items
|
|
187
191
|
this.addLineItems(doc, transactionElement, invoice);
|
|
188
192
|
}
|
|
@@ -284,12 +288,12 @@ export class FacturXEncoder extends CIIBaseEncoder {
|
|
|
284
288
|
transactionElement.appendChild(deliveryElement);
|
|
285
289
|
|
|
286
290
|
// Add delivery date if available
|
|
287
|
-
if (invoice.deliveryDate) {
|
|
291
|
+
if (invoice.deliveryDate !== undefined) {
|
|
288
292
|
const deliveryDateElement = doc.createElement('ram:ActualDeliverySupplyChainEvent');
|
|
289
293
|
const occurrenceDateElement = doc.createElement('ram:OccurrenceDateTime');
|
|
290
294
|
const dateStringElement = doc.createElement('udt:DateTimeString');
|
|
291
295
|
dateStringElement.setAttribute('format', '102'); // YYYYMMDD format
|
|
292
|
-
dateStringElement.textContent = this.formatDateYYYYMMDD(invoice.deliveryDate);
|
|
296
|
+
dateStringElement.textContent = this.formatDateYYYYMMDD(invoice.deliveryDate, 'BT-72 actual delivery date');
|
|
293
297
|
occurrenceDateElement.appendChild(dateStringElement);
|
|
294
298
|
deliveryDateElement.appendChild(occurrenceDateElement);
|
|
295
299
|
deliveryElement.appendChild(deliveryDateElement);
|
|
@@ -324,7 +328,7 @@ export class FacturXEncoder extends CIIBaseEncoder {
|
|
|
324
328
|
const dueDate = new Date(invoice.date);
|
|
325
329
|
dueDate.setDate(dueDate.getDate() + invoice.dueInDays);
|
|
326
330
|
|
|
327
|
-
dateStringElement.textContent = this.formatDateYYYYMMDD(dueDate.getTime());
|
|
331
|
+
dateStringElement.textContent = this.formatDateYYYYMMDD(dueDate.getTime(), 'BT-9 payment due date');
|
|
328
332
|
dueDateElement.appendChild(dateStringElement);
|
|
329
333
|
paymentTermsElement.appendChild(dueDateElement);
|
|
330
334
|
|
|
@@ -487,8 +491,8 @@ export class FacturXEncoder extends CIIBaseEncoder {
|
|
|
487
491
|
* @param timestamp Timestamp to format
|
|
488
492
|
* @returns Formatted date string
|
|
489
493
|
*/
|
|
490
|
-
private formatDateYYYYMMDD(timestamp: number): string {
|
|
491
|
-
const date =
|
|
494
|
+
private formatDateYYYYMMDD(timestamp: number, field: string): string {
|
|
495
|
+
const date = getWritableDate(timestamp, field, 'cii');
|
|
492
496
|
const year = date.getFullYear();
|
|
493
497
|
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
494
498
|
const day = date.getDate().toString().padStart(2, '0');
|