@fin.cx/einvoice 8.3.1 → 10.0.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 +2 -2
- package/dist_ts/einvoice.d.ts +26 -0
- package/dist_ts/einvoice.js +61 -3
- package/dist_ts/formats/base/base.decoder.d.ts +18 -1
- package/dist_ts/formats/base/base.decoder.js +30 -17
- package/dist_ts/formats/cii/cii.decoder.d.ts +89 -6
- package/dist_ts/formats/cii/cii.decoder.js +125 -30
- package/dist_ts/formats/cii/cii.encoder.d.ts +71 -1
- package/dist_ts/formats/cii/cii.encoder.js +195 -12
- package/dist_ts/formats/cii/cii.types.d.ts +2 -5
- package/dist_ts/formats/cii/cii.types.js +7 -9
- package/dist_ts/formats/cii/cii.validator.d.ts +13 -6
- package/dist_ts/formats/cii/cii.validator.js +29 -30
- package/dist_ts/formats/cii/facturx/facturx.decoder.js +19 -3
- package/dist_ts/formats/cii/facturx/facturx.encoder.d.ts +0 -6
- package/dist_ts/formats/cii/facturx/facturx.encoder.js +17 -26
- package/dist_ts/formats/cii/facturx/facturx.types.d.ts +0 -5
- package/dist_ts/formats/cii/facturx/facturx.types.js +1 -8
- package/dist_ts/formats/cii/facturx/facturx.validator.d.ts +0 -5
- package/dist_ts/formats/cii/facturx/facturx.validator.js +5 -29
- package/dist_ts/formats/cii/zugferd/zugferd.decoder.js +19 -2
- package/dist_ts/formats/cii/zugferd/zugferd.encoder.d.ts +0 -7
- package/dist_ts/formats/cii/zugferd/zugferd.encoder.js +15 -68
- package/dist_ts/formats/cii/zugferd/zugferd.types.d.ts +0 -5
- package/dist_ts/formats/cii/zugferd/zugferd.types.js +1 -8
- package/dist_ts/formats/cii/zugferd/zugferd.v1.decoder.d.ts +26 -0
- package/dist_ts/formats/cii/zugferd/zugferd.v1.decoder.js +47 -1
- package/dist_ts/formats/cii/zugferd/zugferd.validator.js +6 -2
- package/dist_ts/formats/semantic/semantic.adapter.js +29 -20
- package/dist_ts/formats/semantic/semantic.validator.js +17 -2
- package/dist_ts/formats/ubl/en16931.ubl.validator.d.ts +0 -4
- package/dist_ts/formats/ubl/en16931.ubl.validator.js +5 -42
- package/dist_ts/formats/ubl/generic/ubl.encoder.js +29 -26
- package/dist_ts/formats/ubl/ubl.decoder.d.ts +4 -0
- package/dist_ts/formats/ubl/ubl.decoder.js +9 -1
- package/dist_ts/formats/ubl/ubl.encoder.js +13 -5
- package/dist_ts/formats/ubl/ubl.validator.d.ts +13 -0
- package/dist_ts/formats/ubl/ubl.validator.js +28 -1
- package/dist_ts/formats/ubl/xrechnung/xrechnung.decoder.js +14 -1
- package/dist_ts/formats/ubl/xrechnung/xrechnung.encoder.d.ts +0 -7
- package/dist_ts/formats/ubl/xrechnung/xrechnung.encoder.js +30 -45
- package/dist_ts/formats/ubl/xrechnung.validator.d.ts +5 -4
- package/dist_ts/formats/ubl/xrechnung.validator.js +71 -68
- package/dist_ts/formats/utils/date.value.d.ts +11 -0
- package/dist_ts/formats/utils/date.value.js +33 -1
- package/dist_ts/formats/utils/document.totals.d.ts +1 -3
- package/dist_ts/formats/utils/document.totals.js +1 -2
- package/dist_ts/formats/utils/format.detector.js +16 -10
- package/dist_ts/formats/utils/paid.amount.d.ts +44 -0
- package/dist_ts/formats/utils/paid.amount.js +124 -0
- package/dist_ts/formats/utils/party.contact.d.ts +16 -0
- package/dist_ts/formats/utils/party.contact.js +16 -0
- package/dist_ts/formats/utils/party.identifier.d.ts +28 -0
- package/dist_ts/formats/utils/party.identifier.js +49 -0
- package/dist_ts/formats/utils/peppol.profile.d.ts +10 -0
- package/dist_ts/formats/utils/peppol.profile.js +12 -0
- package/dist_ts/formats/utils/preceding.invoice.d.ts +12 -2
- package/dist_ts/formats/utils/preceding.invoice.js +22 -3
- package/dist_ts/formats/utils/seller.identifier.d.ts +46 -0
- package/dist_ts/formats/utils/seller.identifier.js +78 -0
- package/dist_ts/formats/utils/stated.values.d.ts +80 -0
- package/dist_ts/formats/utils/stated.values.js +418 -0
- package/dist_ts/formats/utils/vat.category.d.ts +30 -2
- package/dist_ts/formats/utils/vat.category.js +36 -6
- package/dist_ts/formats/utils/vat.id.d.ts +18 -0
- package/dist_ts/formats/utils/vat.id.js +22 -0
- package/dist_ts/formats/validation/conformance.harness.js +6 -6
- package/dist_ts/formats/validation/en16931.business-rules.validator.js +4 -19
- package/dist_ts/formats/validation/facturx.validator.js +6 -6
- package/dist_ts/formats/validation/integrated.validator.js +4 -13
- package/dist_ts/formats/validation/peppol.validator.js +6 -13
- package/dist_ts/formats/validation/validation.types.d.ts +5 -0
- package/dist_ts/formats/validation/validation.types.js +6 -1
- package/dist_ts/formats/validation/vat-categories.validator.d.ts +21 -42
- package/dist_ts/formats/validation/vat-categories.validator.js +137 -431
- package/dist_ts/formats/validation/xrechnung.validator.d.ts +11 -58
- package/dist_ts/formats/validation/xrechnung.validator.js +58 -324
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +1 -1
- package/dist_ts/interfaces/common.d.ts +1 -0
- package/dist_ts/interfaces/en16931-metadata.d.ts +0 -5
- package/dist_ts/interfaces/stated.values.d.ts +93 -0
- package/dist_ts/interfaces/stated.values.js +2 -0
- package/package.json +2 -2
- package/readme.md +182 -6
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/einvoice.ts +70 -2
- package/ts/formats/base/base.decoder.ts +32 -29
- package/ts/formats/cii/cii.decoder.ts +159 -32
- package/ts/formats/cii/cii.encoder.ts +210 -14
- package/ts/formats/cii/cii.types.ts +7 -9
- package/ts/formats/cii/cii.validator.ts +30 -32
- package/ts/formats/cii/facturx/facturx.decoder.ts +21 -2
- package/ts/formats/cii/facturx/facturx.encoder.ts +19 -26
- package/ts/formats/cii/facturx/facturx.types.ts +0 -9
- package/ts/formats/cii/facturx/facturx.validator.ts +5 -43
- package/ts/formats/cii/zugferd/zugferd.decoder.ts +21 -1
- package/ts/formats/cii/zugferd/zugferd.encoder.ts +16 -73
- package/ts/formats/cii/zugferd/zugferd.types.ts +0 -9
- package/ts/formats/cii/zugferd/zugferd.v1.decoder.ts +66 -0
- package/ts/formats/cii/zugferd/zugferd.validator.ts +5 -1
- package/ts/formats/semantic/semantic.adapter.ts +28 -19
- package/ts/formats/semantic/semantic.validator.ts +17 -1
- package/ts/formats/ubl/en16931.ubl.validator.ts +5 -64
- package/ts/formats/ubl/generic/ubl.encoder.ts +31 -25
- package/ts/formats/ubl/ubl.decoder.ts +12 -0
- package/ts/formats/ubl/ubl.encoder.ts +12 -4
- package/ts/formats/ubl/ubl.validator.ts +29 -0
- package/ts/formats/ubl/xrechnung/xrechnung.decoder.ts +13 -0
- package/ts/formats/ubl/xrechnung/xrechnung.encoder.ts +33 -45
- package/ts/formats/ubl/xrechnung.validator.ts +75 -127
- package/ts/formats/utils/date.value.ts +33 -0
- package/ts/formats/utils/document.totals.ts +1 -4
- package/ts/formats/utils/format.detector.ts +15 -9
- package/ts/formats/utils/paid.amount.ts +160 -0
- package/ts/formats/utils/party.contact.ts +30 -0
- package/ts/formats/utils/party.identifier.ts +61 -0
- package/ts/formats/utils/peppol.profile.ts +13 -0
- package/ts/formats/utils/preceding.invoice.ts +23 -2
- package/ts/formats/utils/seller.identifier.ts +102 -0
- package/ts/formats/utils/stated.values.ts +499 -0
- package/ts/formats/utils/vat.category.ts +47 -5
- package/ts/formats/utils/vat.id.ts +24 -0
- package/ts/formats/validation/conformance.harness.ts +5 -5
- package/ts/formats/validation/en16931.business-rules.validator.ts +3 -26
- package/ts/formats/validation/facturx.validator.ts +5 -5
- package/ts/formats/validation/integrated.validator.ts +3 -16
- package/ts/formats/validation/peppol.validator.ts +5 -16
- package/ts/formats/validation/validation.types.ts +7 -1
- package/ts/formats/validation/vat-categories.validator.ts +179 -761
- package/ts/formats/validation/xrechnung.validator.ts +61 -382
- package/ts/index.ts +9 -0
- package/ts/interfaces/common.ts +1 -0
- package/ts/interfaces/en16931-metadata.ts +3 -8
- package/ts/interfaces/stated.values.ts +94 -0
- package/ts/readme.md +1 -1
- package/dist_ts/formats/utils/eu.memberstates.d.ts +0 -11
- package/dist_ts/formats/utils/eu.memberstates.js +0 -16
- package/ts/formats/utils/eu.memberstates.ts +0 -16
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The amounts, VAT breakdown, line amounts and document level allowances and
|
|
3
|
+
* charges a received document states, read as they are written, with their
|
|
4
|
+
* EN 16931 business terms. They are not computed and not updated when the
|
|
5
|
+
* envelope changes, and no encoder writes them: the envelope computes its
|
|
6
|
+
* totals from quantity × net price (`computeDocumentTotals`), and cannot yet
|
|
7
|
+
* express document or line allowances and charges or a price base quantity.
|
|
8
|
+
* Every field is undefined when the document does not state it.
|
|
9
|
+
*/
|
|
10
|
+
export interface IEInvoiceStatedValues {
|
|
11
|
+
/** Document totals (BG-22) */
|
|
12
|
+
totals: IStatedDocumentTotals;
|
|
13
|
+
/** VAT breakdown (BG-23), in document order */
|
|
14
|
+
vatBreakdown: IStatedVatBreakdown[];
|
|
15
|
+
/** Invoice lines (BG-25), in document order */
|
|
16
|
+
lines: IStatedLine[];
|
|
17
|
+
/** Document level allowances (BG-20), in document order */
|
|
18
|
+
allowances: IStatedAllowanceCharge[];
|
|
19
|
+
/** Document level charges (BG-21), in document order */
|
|
20
|
+
charges: IStatedAllowanceCharge[];
|
|
21
|
+
}
|
|
22
|
+
/** Document totals (BG-22) as a document states them */
|
|
23
|
+
export interface IStatedDocumentTotals {
|
|
24
|
+
/** Sum of Invoice line net amount (BT-106) */
|
|
25
|
+
lineTotal?: number;
|
|
26
|
+
/** Sum of allowances on document level (BT-107) */
|
|
27
|
+
allowanceTotal?: number;
|
|
28
|
+
/** Sum of charges on document level (BT-108) */
|
|
29
|
+
chargeTotal?: number;
|
|
30
|
+
/** Invoice total amount without VAT (BT-109) */
|
|
31
|
+
taxExclusiveTotal?: number;
|
|
32
|
+
/** Invoice total VAT amount (BT-110), in the document currency */
|
|
33
|
+
taxTotal?: number;
|
|
34
|
+
/** Invoice total VAT amount in accounting currency (BT-111), with the VAT accounting currency code (BT-6) */
|
|
35
|
+
taxTotalInAccountingCurrency?: {
|
|
36
|
+
amount: number;
|
|
37
|
+
currency: string;
|
|
38
|
+
};
|
|
39
|
+
/** Invoice total amount with VAT (BT-112) */
|
|
40
|
+
taxInclusiveTotal?: number;
|
|
41
|
+
/** Paid amount (BT-113) */
|
|
42
|
+
paidAmount?: number;
|
|
43
|
+
/** Rounding amount (BT-114) */
|
|
44
|
+
roundingAmount?: number;
|
|
45
|
+
/** Amount due for payment (BT-115) */
|
|
46
|
+
amountDue?: number;
|
|
47
|
+
}
|
|
48
|
+
/** One VAT breakdown (BG-23) as a document states it */
|
|
49
|
+
export interface IStatedVatBreakdown {
|
|
50
|
+
/** VAT category taxable amount (BT-116) */
|
|
51
|
+
taxableAmount?: number;
|
|
52
|
+
/** VAT category tax amount (BT-117) */
|
|
53
|
+
taxAmount?: number;
|
|
54
|
+
/** VAT category code (BT-118), UNTDID 5305 */
|
|
55
|
+
categoryCode?: string;
|
|
56
|
+
/** VAT category rate (BT-119); a category not subject to VAT states none */
|
|
57
|
+
rate?: number;
|
|
58
|
+
/** VAT exemption reason text (BT-120) */
|
|
59
|
+
exemptionReason?: string;
|
|
60
|
+
/** VAT exemption reason code (BT-121) */
|
|
61
|
+
exemptionReasonCode?: string;
|
|
62
|
+
}
|
|
63
|
+
/** The amounts and the VAT category of one invoice line (BG-25) as a document states them */
|
|
64
|
+
export interface IStatedLine {
|
|
65
|
+
/** Invoice line identifier (BT-126) */
|
|
66
|
+
lineId?: string;
|
|
67
|
+
/** Invoice line net amount (BT-131) */
|
|
68
|
+
netAmount?: number;
|
|
69
|
+
/** Invoiced item VAT category code (BT-151) */
|
|
70
|
+
vatCategoryCode?: string;
|
|
71
|
+
/** Invoiced item VAT rate (BT-152) */
|
|
72
|
+
vatRate?: number;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* One document level allowance (BG-20: BT-92 to BT-98) or charge (BG-21:
|
|
76
|
+
* BT-99 to BT-105) as a document states it
|
|
77
|
+
*/
|
|
78
|
+
export interface IStatedAllowanceCharge {
|
|
79
|
+
/** Amount (BT-92, BT-99) */
|
|
80
|
+
amount?: number;
|
|
81
|
+
/** Base amount (BT-93, BT-100) */
|
|
82
|
+
baseAmount?: number;
|
|
83
|
+
/** Percentage (BT-94, BT-101) */
|
|
84
|
+
percentage?: number;
|
|
85
|
+
/** VAT category code (BT-95, BT-102) */
|
|
86
|
+
vatCategoryCode?: string;
|
|
87
|
+
/** VAT rate (BT-96, BT-103) */
|
|
88
|
+
vatRate?: number;
|
|
89
|
+
/** Reason (BT-97, BT-104) */
|
|
90
|
+
reason?: string;
|
|
91
|
+
/** Reason code (BT-98, BT-105) */
|
|
92
|
+
reasonCode?: string;
|
|
93
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fin.cx/einvoice",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.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",
|
|
@@ -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.corrected-invoice.node.ts --verbose --logfile --timeout 60 && tstest test/test.xrechnung-3-due-date.node.ts --verbose --logfile --timeout 60 && tstest test/test.calendar-dates.node.ts --verbose --logfile --timeout 60 && tstest test/test.document-totals.node.ts --verbose --logfile --timeout 60 && tstest test/test.reverse-charge.node.ts --verbose --logfile --timeout 60 && tstest test/test.cii-schema-order.node.ts --verbose --logfile --timeout 60 && tstest test/test.tax-number.node.ts --verbose --logfile --timeout 60 && tstest test/test.country-codes.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.xrechnung-3-due-date.node.ts --verbose --logfile --timeout 60 && tstest test/test.calendar-dates.node.ts --verbose --logfile --timeout 60 && tstest test/test.document-totals.node.ts --verbose --logfile --timeout 60 && tstest test/test.reverse-charge.node.ts --verbose --logfile --timeout 60 && tstest test/test.cii-schema-order.node.ts --verbose --logfile --timeout 60 && tstest test/test.tax-number.node.ts --verbose --logfile --timeout 60 && tstest test/test.country-codes.node.ts --verbose --logfile --timeout 60 && tstest test/test.paid-amount.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 && tstest test/test.cii-profile.node.ts --verbose --logfile --timeout 60 && tstest test/test.validator-rule-ids.node.ts --verbose --logfile --timeout 60 && tstest test/test.cii-period-address.node.ts --verbose --logfile --timeout 60 && tstest test/test.amount-due.node.ts --verbose --logfile --timeout 60 && tstest test/test.ubl-zoned-dates.node.ts --verbose --logfile --timeout 60 && tstest test/test.vat-categories.node.ts --verbose --logfile --timeout 60 && tstest test/test.xrechnung-buyer-reference.node.ts --verbose --logfile --timeout 60 && tstest test/test.specification-identifier.node.ts --verbose --logfile --timeout 60 && tstest test/test.seller-identifier.node.ts --verbose --logfile --timeout 60 && tstest test/test.stated-values.node.ts --verbose --logfile --timeout 60 && tstest test/test.vat-category-limits.node.ts --verbose --logfile --timeout 60 && tstest test/test.received-conformance.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
|
@@ -222,8 +222,8 @@ The encoders write only what the document states:
|
|
|
222
222
|
to enforce.
|
|
223
223
|
- `corrections` has no element in EN 16931 and is not written. State what is
|
|
224
224
|
corrected as a note (`notes`, BT-22); the encoders add no text of their own.
|
|
225
|
-
-
|
|
226
|
-
|
|
225
|
+
- Factur-X and ZUGFeRD write BG-3 in the EN 16931 profile. The MINIMUM profile,
|
|
226
|
+
which has no BG-3, is not written at all (see [CII profiles](#cii-profiles)).
|
|
227
227
|
|
|
228
228
|
With the reference stated, the XRechnung output of a corrected invoice passes
|
|
229
229
|
the official EN 16931 1.3.16 and KoSIT XRechnung Schematron 2.6.0 rules without
|
|
@@ -267,6 +267,35 @@ console.log(business.errors);
|
|
|
267
267
|
console.log(business.warnings ?? []);
|
|
268
268
|
```
|
|
269
269
|
|
|
270
|
+
At the business level, an XRechnung document in UBL syntax that was loaded from
|
|
271
|
+
XML is checked by the package's XRechnung validator. Its findings carry the IDs
|
|
272
|
+
of the official rule sets, the KoSIT XRechnung Schematron 2.6.0 and the CEN
|
|
273
|
+
EN 16931 validation artefacts 1.3.16, and check what those rules check, in their
|
|
274
|
+
context:
|
|
275
|
+
|
|
276
|
+
| Rule | What is missing |
|
|
277
|
+
| --- | --- |
|
|
278
|
+
| BR-DE-1 | Payment instructions (BG-16) |
|
|
279
|
+
| BR-49 | The payment means type code (BT-81) of a payment instruction |
|
|
280
|
+
| BR-DE-15 | The buyer reference (BT-10) |
|
|
281
|
+
| BR-DE-2 | The seller contact (BG-6) |
|
|
282
|
+
| BR-DE-6, BR-DE-7 | The telephone number (BT-42) or email address (BT-43) of the seller contact |
|
|
283
|
+
| BR-DE-3, BR-DE-4 | The seller city (BT-37) or post code (BT-38) |
|
|
284
|
+
| BR-DE-8, BR-DE-9 | The buyer city (BT-52) or post code (BT-53) |
|
|
285
|
+
| BR-DE-16 | The seller VAT identifier (BT-31), tax registration identifier (BT-32) or tax representative (BG-11), when a VAT category S, Z, E, AE, K, G, L or M is used |
|
|
286
|
+
| PEPPOL-EN16931-R010 | The buyer electronic address (BT-49), a rule the XRechnung Schematron includes |
|
|
287
|
+
| BR-21 | The identifier (BT-126) of an invoice line |
|
|
288
|
+
| BR-CO-25 | The payment due date (BT-9) or payment terms (BT-20), while an amount is due (BT-115). EN 16931 states this rule; the CEN artefacts for UBL and CII do not check it |
|
|
289
|
+
|
|
290
|
+
One check of its own has a code no official rule has: `EINVOICE-DE-VATID-FORMAT`,
|
|
291
|
+
a seller VAT identifier (BT-31) that begins with DE but is not DE followed by nine
|
|
292
|
+
digits, the format the Bundeszentralamt für Steuern issues. The official rules
|
|
293
|
+
do not check that format, and its finding has the source `EINVOICE`, not
|
|
294
|
+
`XRECHNUNG`.
|
|
295
|
+
|
|
296
|
+
These checks are a subset. The official Schematron remains the reference for
|
|
297
|
+
whether a document passes.
|
|
298
|
+
|
|
270
299
|
### Unit codes (BR-CL-23)
|
|
271
300
|
|
|
272
301
|
A line's `unitType` is written as the unit code of the invoiced quantity (BT-130), so it has to
|
|
@@ -375,6 +404,100 @@ The envelope states reverse charge for the whole document; an item has no VAT
|
|
|
375
404
|
category of its own. A document mixing reverse charge lines with standard rated
|
|
376
405
|
ones cannot be expressed until `@tsclass/tsclass` gives items a category.
|
|
377
406
|
|
|
407
|
+
Outside reverse charge every line is written as standard rated (`S`), whose
|
|
408
|
+
rate BR-S-05 asks to be greater than zero. Export refuses a document with a
|
|
409
|
+
line at the rate 0 outside reverse charge with an `EInvoiceFormatError` naming
|
|
410
|
+
BR-S-05 and the line (`unsupportedFeatures` `['BR-S-05']`), and `validate()`
|
|
411
|
+
reports it at the business level for a document built in code. The envelope cannot state
|
|
412
|
+
the other categories: zero rated (`Z`), exempt (`E`), intra-community supply
|
|
413
|
+
(`K`), export (`G`) or not subject to VAT (`O`), until `@tsclass/tsclass`
|
|
414
|
+
gives an item a VAT category, so a received document with such lines, which is
|
|
415
|
+
read as 0 % lines, cannot be written back or converted. The VAT category rules the
|
|
416
|
+
conformance harness checks on an envelope (BR-S-01, -05, -08 to -10, BR-AE-01,
|
|
417
|
+
-02, -05, -08 to -10 and BR-CO-17) take the categories and the VAT breakdown
|
|
418
|
+
from the same computation the encoders write, and report them under their
|
|
419
|
+
EN 16931 IDs and texts. A decoded line keeps the category its document states
|
|
420
|
+
(`statedValues.lines[].vatCategoryCode`); one that is not S is not reported as
|
|
421
|
+
BR-S-05, since the envelope reads it as S.
|
|
422
|
+
|
|
423
|
+
## Paid amount and advance payments
|
|
424
|
+
|
|
425
|
+
`paidAmount` is the gross sum received before the document was issued, the
|
|
426
|
+
paid amount (BT-113). Every format writes it (`cbc:PrepaidAmount`,
|
|
427
|
+
`ram:TotalPrepaidAmount`) and the amount due for payment (BT-115) as the gross
|
|
428
|
+
total less it (BR-CO-16), zero when paid in full and negative when more was
|
|
429
|
+
paid; a paid amount of zero is nothing paid and is not written. The decoders
|
|
430
|
+
read it back. A paid amount with more decimals than the currency has (two at
|
|
431
|
+
most, BR-DEC-16) is refused, not rounded. The amount due is always derived, so
|
|
432
|
+
the envelope has no field for it and the business rule check of the `EInvoice`
|
|
433
|
+
has nothing to compare it with; the official rules check BR-CO-16 on the
|
|
434
|
+
written XML.
|
|
435
|
+
|
|
436
|
+
A final invoice (an invoice, a corrected invoice or a self-billed invoice) may
|
|
437
|
+
list the `advancePayments` it deducts (`@tsclass/tsclass` 9.9.0 on).
|
|
438
|
+
`paidAmount` then equals their gross sum, the `net` plus the `vat` of every
|
|
439
|
+
group of every payment, to the last decimal; a document that breaks this, or
|
|
440
|
+
lists advance payments without a paid amount, is refused, and neither value is
|
|
441
|
+
recomputed from the other. The advance invoice of each payment that states one
|
|
442
|
+
is written as a preceding invoice reference (BG-3), after the invoice a
|
|
443
|
+
corrected invoice corrects. Another document type with advance payments is
|
|
444
|
+
refused.
|
|
445
|
+
|
|
446
|
+
The deduction a final invoice owes under § 14 Abs. 5 Satz 2 UStG, the
|
|
447
|
+
considerations received before the supply and the tax on them per rate
|
|
448
|
+
(Abschnitt 14.8 Abs. 7 UStAE), has no element in EN 16931: "Derzeit sind die
|
|
449
|
+
Anforderungen an eine Endrechnung noch nicht im strukturierten Teil einer
|
|
450
|
+
E-Rechnung darstellbar" (BMF letter of 15 October 2024, Rn. 48). The encoders
|
|
451
|
+
write the paid amount and the references and add no text of their own. The
|
|
452
|
+
issuer either adds the deduction as an annex (Abschnitt 14.8 Abs. 8 Nr. 2
|
|
453
|
+
UStAE), which Rn. 48 accepts as an unstructured file in an e-invoice issued
|
|
454
|
+
until 31 December 2027 and which `@design.estate/dees-document` 3.4 prints, or
|
|
455
|
+
issues an invoice over the remaining consideration only (Restrechnung,
|
|
456
|
+
Abschnitt 14.8 Abs. 11 UStAE), which Rn. 48 recommends. Reading an e-invoice
|
|
457
|
+
back yields its paid amount but no `advancePayments`: the XML does not carry the
|
|
458
|
+
net and the tax of every payment. A corrected invoice's advance invoices read
|
|
459
|
+
back as related documents it corrects, since BG-3 does not tell them apart,
|
|
460
|
+
and an invoice's are not read.
|
|
461
|
+
|
|
462
|
+
## Received documents: what they state
|
|
463
|
+
|
|
464
|
+
A document read from XML carries what it states in `statedValues`, with the
|
|
465
|
+
EN 16931 business terms, as written: the document totals (BG-22: BT-106 to
|
|
466
|
+
BT-115, and BT-111 with its currency BT-6), the VAT breakdown (BG-23: BT-116
|
|
467
|
+
to BT-121), each line's net amount, VAT category and rate (BT-126, BT-131,
|
|
468
|
+
BT-151, BT-152) and the document level allowances and charges (BG-20, BG-21:
|
|
469
|
+
BT-92 to BT-105). A field is undefined when the document does not state it.
|
|
470
|
+
UBL, XRechnung, Factur-X and ZUGFeRD 2.x documents are read; ZUGFeRD 1.0
|
|
471
|
+
predates EN 16931 and has none, nor has a document built in code.
|
|
472
|
+
|
|
473
|
+
```ts
|
|
474
|
+
const invoice = await EInvoice.fromXml(receivedXml);
|
|
475
|
+
const { totals, vatBreakdown } = invoice.statedValues!;
|
|
476
|
+
console.log(totals.taxExclusiveTotal, totals.taxTotal, totals.amountDue);
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
Book what the document states. The envelope's totals (`totalNet`, `totalVat`,
|
|
480
|
+
`totalGross`, `taxBreakdown`) are computed from quantity × net price, and the
|
|
481
|
+
envelope cannot express document or line allowances and charges, a price base
|
|
482
|
+
quantity (BT-149) or a VAT category other than standard rated and reverse
|
|
483
|
+
charge, so for such a document they differ from what it states. Neither value
|
|
484
|
+
is changed: `validate()` reports each difference at the business level as a
|
|
485
|
+
warning `EINVOICE-STATED-DIFFERS`, the package's own code, naming the
|
|
486
|
+
business term and both values. `statedValues` is not updated when the envelope
|
|
487
|
+
changes, and no encoder writes it: an export writes the envelope's
|
|
488
|
+
computation, so a received document with allowances, charges or base
|
|
489
|
+
quantities is not written back with the amounts it states, and a document with
|
|
490
|
+
lines of a category other than S and AE is refused on export (BR-S-05). The
|
|
491
|
+
`EINVOICE-STATED-DIFFERS` warnings are the signal that an export would not
|
|
492
|
+
state what was received; a faithful round trip needs `@tsclass/tsclass` to
|
|
493
|
+
express allowances, charges, base quantities and line categories.
|
|
494
|
+
|
|
495
|
+
The stated values are checked against each other at the business level as the
|
|
496
|
+
CEN artefacts 1.3.16 check them, under the official rule IDs BR-CO-10 to
|
|
497
|
+
BR-CO-17, in UBL and CII; these checks replace the approximate ones of earlier
|
|
498
|
+
releases. A stated amount that is no decimal number is refused on read with an
|
|
499
|
+
`EInvoiceParsingError` that names the business term.
|
|
500
|
+
|
|
378
501
|
## Calendar dates
|
|
379
502
|
|
|
380
503
|
A calendar date of the invoice (issue date, due date, delivery date, invoicing
|
|
@@ -383,7 +506,10 @@ zone: UBL writes it as `xsd:date` (`2026-09-20`), CII in format 102
|
|
|
383
506
|
(`20260920`). In the accounting document envelope such a day is a timestamp:
|
|
384
507
|
the UTC midnight of that day, `Date.UTC(2026, 8, 20)`.
|
|
385
508
|
|
|
386
|
-
- The decoders return that timestamp.
|
|
509
|
+
- The decoders return that timestamp. A UBL date may carry a time zone
|
|
510
|
+
(`2026-09-20+02:00`, `2026-09-20Z`); the zone does not move the day, so the
|
|
511
|
+
decoders return the UTC midnight of the stated day, and every format writes
|
|
512
|
+
that day.
|
|
387
513
|
- The encoders write the day of that timestamp in UTC and count the payment
|
|
388
514
|
term in UTC days, so what they write does not depend on the time zone of the
|
|
389
515
|
machine that writes it.
|
|
@@ -400,7 +526,7 @@ the UTC midnight of that day, `Date.UTC(2026, 8, 20)`.
|
|
|
400
526
|
| `xrechnung` | Yes | Yes | Yes | Yes | UBL export; UBL and CII import |
|
|
401
527
|
| `cii` | Yes | Yes | Yes | Partial | Generic CII export currently uses the Factur-X encoder path |
|
|
402
528
|
| `facturx` | Yes | Yes | Yes | Yes | Main CII-based generation path |
|
|
403
|
-
| `zugferd` | Yes | Yes | Yes | Partial | Input supports v1 and v2
|
|
529
|
+
| `zugferd` | Yes | Yes | Yes | Partial | Input supports v1 and v2+; export writes the EN 16931 profile, and v2 reads back as `facturx`, as XML and from a PDF (see [CII profiles](#cii-profiles)) |
|
|
404
530
|
| `fatturapa` | Yes | No | No | Basic placeholder | Detection exists, decoder/encoder do not |
|
|
405
531
|
|
|
406
532
|
### Important format notes
|
|
@@ -408,13 +534,63 @@ the UTC midnight of that day, `Date.UTC(2026, 8, 20)`.
|
|
|
408
534
|
- There is no dedicated root-level `peppol` export format. PEPPOL-specific checks exist, but the XML export targets are still `ubl` / `xrechnung`.
|
|
409
535
|
- XRechnung import routes UBL and CII by the namespace-qualified XML root.
|
|
410
536
|
Missing or invalid document-level invoice IDs and issue dates are rejected.
|
|
537
|
+
- The buyer reference (BT-10): every format writes `buyerReference`
|
|
538
|
+
(`cbc:BuyerReference`, `ram:BuyerReference`) and reads it back, ZUGFeRD 1.0
|
|
539
|
+
input as well. XRechnung requires it (BR-DE-15), and it is the
|
|
540
|
+
buyer's: the identifier the buyer gives the seller for routing the invoice, a
|
|
541
|
+
Leitweg-ID for a public buyer, any identifier of the buyer's choice in B2B.
|
|
542
|
+
`exportXml('xrechnung')` writes `buyerReference` (or the reference a decoded
|
|
543
|
+
UBL document keeps) and refuses a document without one with an
|
|
544
|
+
`EInvoiceFormatError` whose `unsupportedFeatures` is `['BR-DE-15']`; nothing,
|
|
545
|
+
not the invoice number, is put in its place. A seller whose buyer gave no
|
|
546
|
+
reference exports `ubl` (EN 16931), where BT-10 is optional. The metadata has
|
|
547
|
+
no buyer reference of its own: `buyerReference` is the one field.
|
|
411
548
|
- `FatturaPA` should be documented as detection-only right now.
|
|
412
|
-
- 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
|
|
413
|
-
-
|
|
549
|
+
- 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 `periodOfPerformance` as `ram:BillingSpecifiedPeriod` of the header settlement, and read each back only when the document states it; ZUGFeRD 1.0 input has its period read as well. `periodOfPerformance` has a start and an end date, so a document that states only one of them (EN 16931 allows that, BR-CO-19) reads back without `periodOfPerformance`, in every format; the date it states is kept in `metadata.extensions.dateInformation.periodStart` or `.periodEnd` (YYYY-MM-DD), and every encoder writes it back as the one date of the period, so it survives a round trip and a conversion between UBL and CII.
|
|
550
|
+
- The seller electronic address (BT-34): Factur-X and ZUGFeRD write `electronicAddress` as `ram:URIUniversalCommunication/ram:URIID` of the seller, with its scheme (BR-62), for a company or a person, and read it back. A ZUGFeRD 1.0 trade party has no electronic address.
|
|
551
|
+
- Country codes: every format writes the seller and buyer country (BT-40, BT-55) as an ISO 3166-1 alpha-2 code EN 16931 accepts (BR-CL-14), taken from `address.countryCode`, or from `address.country` when that is already such a code (letter case does not matter). A code is never derived from a country name: a name without a `countryCode`, a code EN 16931 does not know, or a missing country is refused with an `EInvoiceFormatError` naming the field (BR-CL-14, BR-09, BR-11). The list of codes is the UBL one of the CEN EN 16931 validation artefacts 1.3.16, for every format. Their CII list differs in two codes: it has AN (Netherlands Antilles, withdrawn from ISO 3166-1) and lacks SS (South Sudan). So a Factur-X or ZUGFeRD document with a party in South Sudan is written, and the official CII rules report BR-CL-14 for it; AN is refused in every format.
|
|
414
552
|
- Party identifiers: `registrationDetails.vatId` is the VAT identifier (BT-31 seller, BT-48 buyer), `registrationId` the legal registration identifier such as the commercial register number (BT-30, BT-47), and `taxNumber` the seller's tax registration identifier, the Steuernummer (BT-32), which § 14 Abs. 4 Satz 1 Nr. 2 UStG allows in place of the VAT identifier. UBL and XRechnung write the tax number as a second `cac:PartyTaxScheme` with the tax scheme `FC`, after the VAT one; Factur-X and ZUGFeRD as `ram:SpecifiedTaxRegistration/ram:ID` with the scheme `FC`. The decoders read each by its scheme, whatever order the document lists them in. EN 16931 has no tax number for the buyer, so a buyer's `taxNumber` is not written.
|
|
553
|
+
- The seller's identification (BR-CO-26): EN 16931 asks the seller to state a seller identifier (BT-29), a legal registration identifier (BT-30, `registrationDetails.registrationId`) or a VAT identifier (BT-31, `registrationDetails.vatId`); a tax number (BT-32) alone does not identify it. Every export whose seller states none of them is refused with an `EInvoiceFormatError` whose `unsupportedFeatures` is `['BR-CO-26']`, and `validate()` reports BR-CO-26 at the business level, for a loaded document and for one built in code. The party identifiers, the seller identifier (BT-29) and the buyer identifier (BT-46), have no typed field in the envelope: the decoders keep them in the party's `additionalIdentifiers` (`{ value, scheme }`), and every format writes them from there, UBL as `cac:PartyIdentification/cbc:ID` with the scheme as `schemeID`, CII as `ram:GlobalID` with its scheme or `ram:ID` without one. A UBL seller identification with the scheme SEPA is the bank assigned creditor identifier (BT-90) and is not written to CII. A buyer identifier with the scheme SEPA is refused (BR-CL-10): UBL admits the scheme for the seller and the payee only, CII for no party.
|
|
554
|
+
- The specification identifier (BT-24): the decoders set `metadata.customizationId` to the identifier the document states (UBL `cbc:CustomizationID`, CII `ram:GuidelineSpecifiedDocumentContextParameter/ram:ID`), so `MainValidator` runs the XRechnung, PEPPOL or Factur-X checks on a decoded document of that kind. The PEPPOL checks run for the PEPPOL BIS Billing 3.0 identifier only, `urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0`, not for PINT, A-NZ or another derivative that names PEPPOL. `metadata.profileId` is the business process type (BT-23, UBL `cbc:ProfileID`). The encoders write the identifier of the format they write, not `customizationId`.
|
|
415
555
|
- 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.
|
|
416
556
|
- 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.
|
|
417
557
|
|
|
558
|
+
### CII profiles
|
|
559
|
+
|
|
560
|
+
The Factur-X and ZUGFeRD encoders, and `cii`, which uses the Factur-X one, write
|
|
561
|
+
the full EN 16931 content, so they state the EN 16931 profile as the
|
|
562
|
+
specification identifier (BT-24): `urn:cen.eu:en16931:2017`, the identifier the
|
|
563
|
+
CEN validation artefacts use and the EN 16931 profile of Factur-X 1.0 and
|
|
564
|
+
ZUGFeRD 2.x, which ZUGFeRD also calls COMFORT.
|
|
565
|
+
|
|
566
|
+
`FacturXEncoder.setProfile` and `ZUGFeRDEncoder.setProfile` accept `EN16931` and
|
|
567
|
+
`COMFORT`, both written as that identifier. The other choices are refused with an
|
|
568
|
+
`EInvoiceFormatError` whose message names the profile and why, and whose
|
|
569
|
+
`unsupportedFeatures` is `['profile MINIMUM']` and so on; nothing is downgraded
|
|
570
|
+
to EN 16931 silently:
|
|
571
|
+
|
|
572
|
+
| Choice | Why it is refused |
|
|
573
|
+
| --- | --- |
|
|
574
|
+
| `MINIMUM` | The content written is EN 16931, which MINIMUM does not allow (no lines, no VAT breakdown, no street, postcode or city). A MINIMUM document is not an e-invoice (BMF letter of 15 October 2024, Rn. 25 and 30). |
|
|
575
|
+
| `BASIC` | The content written is EN 16931, which neither BASIC nor BASIC WL allows; BASIC WL has no lines and is not an e-invoice either (same letter, Rn. 25 and 30). |
|
|
576
|
+
| `EXTENDED` | The output has not been checked against the EXTENDED schema and rules. |
|
|
577
|
+
|
|
578
|
+
Factur-X 1.0 and ZUGFeRD 2.x are one standard with one XML, and their EN 16931
|
|
579
|
+
profile has one identifier. A ZUGFeRD 2.x export therefore reads back as
|
|
580
|
+
Factur-X, as XML and from a PDF alike, because a PDF's format is detected from
|
|
581
|
+
its embedded XML: `FormatDetector.detectFormat` and `getFormat()` return
|
|
582
|
+
`facturx`, and `FacturXDecoder` reads it. A ZUGFeRD PDF is still written with the
|
|
583
|
+
`zugferd-invoice.xml` attachment.
|
|
584
|
+
|
|
585
|
+
Decoding does not check the profile. A document that states another profile's
|
|
586
|
+
identifier, such as Factur-X MINIMUM or BASIC WL, a ZUGFeRD 2.0 identifier, the
|
|
587
|
+
`urn:zugferd:` identifiers earlier releases of this library wrote, or ZUGFeRD v1,
|
|
588
|
+
is detected and read as before. An identifier that names Factur-X reads as
|
|
589
|
+
`facturx`, one that names ZUGFeRD as `zugferd`: ZUGFeRD 2.0 has identifiers of
|
|
590
|
+
its own (`urn:zugferd.de:2p0:minimum`, and for BASIC and EXTENDED the EN 16931
|
|
591
|
+
identifier extended by `urn:zugferd.de:2p0:basic` or `urn:zugferd.de:2p0:extended`),
|
|
592
|
+
where ZUGFeRD 2.1 and Factur-X 1.0 share theirs.
|
|
593
|
+
|
|
418
594
|
## Public API overview
|
|
419
595
|
|
|
420
596
|
Top-level exports from `@fin.cx/einvoice` include:
|
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: '
|
|
6
|
+
version: '10.0.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
|
@@ -46,7 +46,11 @@ import {
|
|
|
46
46
|
// Import enhanced validators
|
|
47
47
|
import { EN16931Validator } from './formats/validation/en16931.validator.js';
|
|
48
48
|
import { EN16931BusinessRulesValidator } from './formats/validation/en16931.business-rules.validator.js';
|
|
49
|
-
import {
|
|
49
|
+
import { isRateNotAboveZero, standardRatedLineWithoutRateMessage } from './formats/utils/vat.category.js';
|
|
50
|
+
import { BR_CO_26_TEXT, envelopeSellerMeetsBrCo26 } from './formats/utils/seller.identifier.js';
|
|
51
|
+
import { findEnvelopeDifferences } from './formats/utils/stated.values.js';
|
|
52
|
+
import type { IEInvoiceStatedValues } from './interfaces/stated.values.js';
|
|
53
|
+
import { computeDocumentTotals, findInvalidItemAmounts } from './formats/utils/document.totals.js';
|
|
50
54
|
import { CodeListValidator } from './formats/validation/codelist.validator.js';
|
|
51
55
|
import type { ValidationOptions } from './formats/validation/validation.types.js';
|
|
52
56
|
|
|
@@ -168,6 +172,34 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
|
|
|
168
172
|
* written to the XML; an issuer states them as a note in `notes`.
|
|
169
173
|
*/
|
|
170
174
|
public corrections?: finance.TCorrectedInvoice['corrections'];
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* The gross sum received before the document was issued, the paid amount
|
|
178
|
+
* (BT-113); the amount due (BT-115) is the gross total less it. Absent or
|
|
179
|
+
* zero: nothing was paid.
|
|
180
|
+
*/
|
|
181
|
+
public paidAmount?: number;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* What a loaded document states: its totals (BG-22), VAT breakdown (BG-23),
|
|
185
|
+
* line net amounts and VAT categories (BT-131, BT-151) and document level
|
|
186
|
+
* allowances and charges (BG-20, BG-21), with their EN 16931 business terms,
|
|
187
|
+
* as written. Undefined for a document built in code and for ZUGFeRD 1.0.
|
|
188
|
+
* The envelope's totals (`totalNet`, `totalVat`, `totalGross`) are computed
|
|
189
|
+
* from quantity × net price and may differ; `validate()` reports each
|
|
190
|
+
* difference at the business level as `EINVOICE-STATED-DIFFERS`. No encoder
|
|
191
|
+
* writes these values, and they are not updated when the envelope changes.
|
|
192
|
+
*/
|
|
193
|
+
public statedValues?: IEInvoiceStatedValues;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* On a final invoice (an invoice, a corrected invoice or a self-billed
|
|
197
|
+
* invoice): the advance payments it deducts; `paidAmount` equals their gross
|
|
198
|
+
* sum. Their advance invoices are written as preceding invoice references
|
|
199
|
+
* (BG-3); the deduction per rate has no element in EN 16931, and a document
|
|
200
|
+
* read from XML has none.
|
|
201
|
+
*/
|
|
202
|
+
public advancePayments?: finance.TInvoice['advancePayments'];
|
|
171
203
|
public printResult?: {
|
|
172
204
|
pdfBufferString: string;
|
|
173
205
|
totalNet: number;
|
|
@@ -338,6 +370,8 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
|
|
|
338
370
|
|
|
339
371
|
// Map the decoded invoice to our properties
|
|
340
372
|
this.mapFromTInvoice(invoice);
|
|
373
|
+
// what the document states, as it states it; undefined for a ZUGFeRD 1.0 document
|
|
374
|
+
this.statedValues = decoder.getStatedValues();
|
|
341
375
|
|
|
342
376
|
// Validate if requested
|
|
343
377
|
if (this.options.validateOnLoad) {
|
|
@@ -465,6 +499,8 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
|
|
|
465
499
|
relatedDocuments: this.relatedDocuments,
|
|
466
500
|
correctedInvoice: this.correctedInvoice,
|
|
467
501
|
corrections: this.corrections,
|
|
502
|
+
paidAmount: this.paidAmount,
|
|
503
|
+
advancePayments: this.advancePayments,
|
|
468
504
|
printResult: this.printResult
|
|
469
505
|
};
|
|
470
506
|
|
|
@@ -495,7 +531,14 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
|
|
|
495
531
|
return await encoder.encode(invoice);
|
|
496
532
|
} catch (error) {
|
|
497
533
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
498
|
-
|
|
534
|
+
// a refusal keeps the rules it names, so a caller can tell why without reading the message
|
|
535
|
+
throw new EInvoiceFormatError(
|
|
536
|
+
`Failed to encode to ${format}: ${errorMessage}`,
|
|
537
|
+
{
|
|
538
|
+
targetFormat: format,
|
|
539
|
+
...(error instanceof EInvoiceFormatError && error.unsupportedFeatures ? { unsupportedFeatures: error.unsupportedFeatures } : {}),
|
|
540
|
+
},
|
|
541
|
+
);
|
|
499
542
|
}
|
|
500
543
|
}
|
|
501
544
|
|
|
@@ -536,6 +579,15 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
|
|
|
536
579
|
result = this.validateDecodedInvoice(level);
|
|
537
580
|
}
|
|
538
581
|
|
|
582
|
+
// What a loaded document states and the envelope computes differently, reported and not
|
|
583
|
+
// changed: the envelope cannot express allowances, charges or a price base quantity
|
|
584
|
+
if (level === ValidationLevel.BUSINESS && this.statedValues && findInvalidItemAmounts(this.items).length === 0) {
|
|
585
|
+
const differences = findEnvelopeDifferences(this.statedValues, computeDocumentTotals(this), this.paidAmount);
|
|
586
|
+
result.warnings = (result.warnings ?? []).concat(
|
|
587
|
+
differences.map((difference) => ({ code: difference.ruleId, message: difference.message, location: difference.location })),
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
|
|
539
591
|
// At the business level the document check covers the unit rules (BR-23, BR-CL-23) on every
|
|
540
592
|
// line. The feature flag validators below check the lines again on the decoded model, which
|
|
541
593
|
// cannot tell an absent unit from an empty unitCode="" attribute, so there they would report
|
|
@@ -606,6 +658,22 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
|
|
|
606
658
|
);
|
|
607
659
|
|
|
608
660
|
if (level === ValidationLevel.BUSINESS) {
|
|
661
|
+
// BR-S-05: a line outside reverse charge is written as standard rated, whose rate is greater than zero
|
|
662
|
+
if (!invoice.reverseCharge) {
|
|
663
|
+
(invoice.items ?? []).forEach((item, index) => {
|
|
664
|
+
if (isRateNotAboveZero(item.vatPercentage)) {
|
|
665
|
+
errors.push({
|
|
666
|
+
code: 'BR-S-05',
|
|
667
|
+
message: standardRatedLineWithoutRateMessage({ index, vatPercentage: item.vatPercentage }),
|
|
668
|
+
location: `items[${index}].vatPercentage`,
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
// BR-CO-26: the seller states a seller, legal registration or VAT identifier; a tax number alone does not identify it
|
|
674
|
+
if (!envelopeSellerMeetsBrCo26(invoice)) {
|
|
675
|
+
errors.push({ code: 'BR-CO-26', message: BR_CO_26_TEXT, location: 'from.registrationDetails' });
|
|
676
|
+
}
|
|
609
677
|
// BR-23 and BR-CL-23: every line has a unit code, and it is a code of UN/ECE Recommendation 20
|
|
610
678
|
// with the Recommendation 21 extension, as the XML validators check it on an exported document
|
|
611
679
|
(invoice.items ?? []).forEach((item, index) => {
|
|
@@ -2,6 +2,9 @@ import type { TAccountingDoc } from '../../interfaces/common.js';
|
|
|
2
2
|
import { ValidationLevel } from '../../interfaces/common.js';
|
|
3
3
|
import type { ValidationResult } from '../../interfaces/common.js';
|
|
4
4
|
import { EInvoiceParsingError } from '../../errors.js';
|
|
5
|
+
import { parseXsdDateDay } from '../utils/date.value.js';
|
|
6
|
+
import type { IEInvoiceStatedValues } from '../../interfaces/stated.values.js';
|
|
7
|
+
import { statedValueProblemMessage, type IStatedValuesRead } from '../utils/stated.values.js';
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* Base decoder class that defines common decoding functionality
|
|
@@ -45,6 +48,29 @@ export abstract class BaseDecoder {
|
|
|
45
48
|
return undefined;
|
|
46
49
|
}
|
|
47
50
|
|
|
51
|
+
/**
|
|
52
|
+
* The amounts, VAT breakdown, line amounts and document level allowances
|
|
53
|
+
* and charges the decoded document states, as it states them; undefined for
|
|
54
|
+
* a format that has no EN 16931 business terms to read them by
|
|
55
|
+
*/
|
|
56
|
+
public getStatedValues(): IEInvoiceStatedValues | undefined {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Refuses a document with a stated amount that is no decimal number, naming
|
|
62
|
+
* the business term; nothing is put in its place
|
|
63
|
+
* @param read The stated values read
|
|
64
|
+
* @param format The format read
|
|
65
|
+
*/
|
|
66
|
+
protected assertStatedValuesReadable(read: IStatedValuesRead, format: string): IEInvoiceStatedValues {
|
|
67
|
+
const [problem] = read.problems;
|
|
68
|
+
if (problem) {
|
|
69
|
+
throw new EInvoiceParsingError(statedValueProblemMessage(problem), { format });
|
|
70
|
+
}
|
|
71
|
+
return read.values;
|
|
72
|
+
}
|
|
73
|
+
|
|
48
74
|
/**
|
|
49
75
|
* Parses a CII date string based on format code
|
|
50
76
|
* @param dateStr Date string
|
|
@@ -122,40 +148,17 @@ export abstract class BaseDecoder {
|
|
|
122
148
|
}
|
|
123
149
|
|
|
124
150
|
/**
|
|
125
|
-
* Parses a required UBL xsd:date value strictly
|
|
151
|
+
* Parses a required UBL xsd:date value strictly: the calendar day it states,
|
|
152
|
+
* as the UTC midnight of that day. A time zone does not move the day
|
|
153
|
+
* (`parseXsdDateDay`).
|
|
126
154
|
*/
|
|
127
155
|
protected parseRequiredUblDate(dateStr: string): number {
|
|
128
156
|
const value = dateStr.trim();
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
if (!match) {
|
|
157
|
+
const day = parseXsdDateDay(value);
|
|
158
|
+
if (day === undefined) {
|
|
132
159
|
throw this.createInvalidDateError(value, 'UBL xsd:date');
|
|
133
160
|
}
|
|
134
|
-
|
|
135
|
-
const timestamp = this.createStrictUtcTimestamp(
|
|
136
|
-
Number(match[1]),
|
|
137
|
-
Number(match[2]),
|
|
138
|
-
Number(match[3]),
|
|
139
|
-
value,
|
|
140
|
-
'UBL xsd:date',
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
if (!match[5]) {
|
|
144
|
-
return timestamp;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const offsetHours = Number(match[6]);
|
|
148
|
-
const offsetMinutes = Number(match[7]);
|
|
149
|
-
if (
|
|
150
|
-
offsetHours > 14 ||
|
|
151
|
-
offsetMinutes > 59 ||
|
|
152
|
-
(offsetHours === 14 && offsetMinutes !== 0)
|
|
153
|
-
) {
|
|
154
|
-
throw this.createInvalidDateError(value, 'UBL xsd:date');
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const direction = match[5] === '+' ? 1 : -1;
|
|
158
|
-
return timestamp - direction * (offsetHours * 60 + offsetMinutes) * 60_000;
|
|
161
|
+
return day;
|
|
159
162
|
}
|
|
160
163
|
|
|
161
164
|
private createStrictUtcTimestamp(
|