@fin.cx/einvoice 10.0.0 → 10.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/einvoice.d.ts +2 -0
  3. package/dist_ts/einvoice.js +7 -13
  4. package/dist_ts/formats/base/base.decoder.d.ts +9 -1
  5. package/dist_ts/formats/base/base.decoder.js +24 -12
  6. package/dist_ts/formats/cii/cii.decoder.d.ts +11 -3
  7. package/dist_ts/formats/cii/cii.decoder.js +27 -8
  8. package/dist_ts/formats/cii/cii.encoder.d.ts +12 -0
  9. package/dist_ts/formats/cii/cii.encoder.js +36 -1
  10. package/dist_ts/formats/cii/cii.types.js +2 -1
  11. package/dist_ts/formats/cii/facturx/facturx.encoder.js +12 -8
  12. package/dist_ts/formats/cii/zugferd/zugferd.encoder.js +12 -8
  13. package/dist_ts/formats/semantic/semantic.adapter.d.ts +0 -4
  14. package/dist_ts/formats/semantic/semantic.adapter.js +7 -18
  15. package/dist_ts/formats/ubl/generic/ubl.encoder.d.ts +6 -1
  16. package/dist_ts/formats/ubl/generic/ubl.encoder.js +40 -8
  17. package/dist_ts/formats/ubl/ubl.decoder.d.ts +11 -0
  18. package/dist_ts/formats/ubl/ubl.decoder.js +30 -1
  19. package/dist_ts/formats/ubl/xrechnung/xrechnung.decoder.js +3 -3
  20. package/dist_ts/formats/utils/date.value.d.ts +24 -0
  21. package/dist_ts/formats/utils/date.value.js +77 -12
  22. package/dist_ts/formats/utils/delivery.address.d.ts +18 -0
  23. package/dist_ts/formats/utils/delivery.address.js +30 -0
  24. package/dist_ts/formats/utils/document.totals.d.ts +5 -4
  25. package/dist_ts/formats/utils/document.totals.js +24 -8
  26. package/dist_ts/formats/utils/stated.values.js +4 -2
  27. package/dist_ts/formats/utils/vat.category.d.ts +146 -55
  28. package/dist_ts/formats/utils/vat.category.js +357 -69
  29. package/dist_ts/formats/validation/vat-categories.validator.d.ts +14 -15
  30. package/dist_ts/formats/validation/vat-categories.validator.js +123 -85
  31. package/package.json +5 -5
  32. package/readme.md +61 -20
  33. package/ts/00_commitinfo_data.ts +1 -1
  34. package/ts/einvoice.ts +8 -14
  35. package/ts/formats/base/base.decoder.ts +24 -17
  36. package/ts/formats/cii/cii.decoder.ts +28 -7
  37. package/ts/formats/cii/cii.encoder.ts +36 -0
  38. package/ts/formats/cii/cii.types.ts +1 -0
  39. package/ts/formats/cii/facturx/facturx.encoder.ts +12 -7
  40. package/ts/formats/cii/zugferd/zugferd.encoder.ts +12 -7
  41. package/ts/formats/semantic/semantic.adapter.ts +6 -15
  42. package/ts/formats/ubl/generic/ubl.encoder.ts +40 -7
  43. package/ts/formats/ubl/ubl.decoder.ts +31 -0
  44. package/ts/formats/ubl/xrechnung/xrechnung.decoder.ts +2 -2
  45. package/ts/formats/utils/date.value.ts +84 -11
  46. package/ts/formats/utils/delivery.address.ts +36 -0
  47. package/ts/formats/utils/document.totals.ts +38 -11
  48. package/ts/formats/utils/stated.values.ts +4 -1
  49. package/ts/formats/utils/vat.category.ts +426 -95
  50. package/ts/formats/validation/vat-categories.validator.ts +169 -88
@@ -1,5 +1,6 @@
1
1
  import { computeDocumentTotals, findInvalidItemAmounts, getDocumentCalculator, getTotalsSkippedResult, } from '../utils/document.totals.js';
2
2
  import { Decimal } from '../utils/decimal.js';
3
+ import { findVatCategoryViolations, VAT_EXEMPTION_DIFFERS, } from '../utils/vat.category.js';
3
4
  /**
4
5
  * VAT Category codes according to UNCL5305
5
6
  */
@@ -16,9 +17,11 @@ export var VATCategory;
16
17
  VATCategory["M"] = "M"; // Tax for production, services and importation in Ceuta and Melilla
17
18
  })(VATCategory || (VATCategory = {}));
18
19
  /**
19
- * The EN 16931 rules this validator checks, with their official text (CEN/TC 434
20
- * EN 16931 validation artefacts 1.3.16, the UBL model; the CII model differs only
21
- * in the case of "Breakdown" in BR-AE-10 and BR-S-10 and "VAT breakdown" in BR-AE-01)
20
+ * The EN 16931 rules this validator checks, with their official text (CEN/TC 434 EN 16931
21
+ * validation artefacts 1.3.16, the UBL model; the CII model differs only in the case of
22
+ * "Breakdown" in BR-AE-10 and BR-S-10 and "VAT breakdown" in BR-AE-01), BR-CL-22 with the
23
+ * text of the code list rules, and the package's own check that the items of one breakdown
24
+ * state one exemption reason
22
25
  */
23
26
  const VAT_CATEGORY_RULES = {
24
27
  'BR-S-01': 'An Invoice that contains an Invoice line (BG-25), a Document level allowance (BG-20) or a Document level charge (BG-21) where the VAT category code (BT-151, BT-95 or BT-102) is "Standard rated" shall contain in the VAT breakdown (BG-23) at least one VAT category code (BT-118) equal with "Standard rated".',
@@ -26,28 +29,111 @@ const VAT_CATEGORY_RULES = {
26
29
  'BR-S-08': 'For each different value of VAT category rate (BT-119) where the VAT category code (BT-118) is "Standard rated", the VAT category taxable amount (BT-116) in a VAT breakdown (BG-23) shall equal the sum of Invoice line net amounts (BT-131) plus the sum of document level charge amounts (BT-99) minus the sum of document level allowance amounts (BT-92) where the VAT category code (BT-151, BT-102, BT-95) is "Standard rated" and the VAT rate (BT-152, BT-103, BT-96) equals the VAT category rate (BT-119).',
27
30
  'BR-S-09': 'The VAT category tax amount (BT-117) in a VAT breakdown (BG-23) where VAT category code (BT-118) is "Standard rated" shall equal the VAT category taxable amount (BT-116) multiplied by the VAT category rate (BT-119).',
28
31
  'BR-S-10': 'A VAT breakdown (BG-23) with VAT Category code (BT-118) "Standard rate" shall not have a VAT exemption reason code (BT-121) or VAT exemption reason text (BT-120).',
32
+ 'BR-Z-02': 'An Invoice that contains an Invoice line where the Invoiced item VAT category code (BT-151) is "Zero rated" shall contain the Seller VAT Identifier (BT-31), the Seller tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63).',
33
+ 'BR-Z-05': 'In an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "Zero rated" the Invoiced item VAT rate (BT-152) shall be 0 (zero).',
34
+ 'BR-Z-08': 'In a VAT breakdown (BG-23) where VAT category code (BT-118) is "Zero rated" the VAT category taxable amount (BT-116) shall equal the sum of Invoice line net amount (BT-131) minus the sum of Document level allowance amounts (BT-92) plus the sum of Document level charge amounts (BT-99) where the VAT category codes (BT-151, BT-95, BT-102) are "Zero rated".',
35
+ 'BR-Z-09': 'The VAT category tax amount (BT-117) in a VAT breakdown (BG-23) where VAT category code (BT-118) is "Zero rated" shall equal 0 (zero).',
36
+ 'BR-Z-10': 'A VAT breakdown (BG-23) with VAT Category code (BT-118) "Zero rated" shall not have a VAT exemption reason code (BT-121) or VAT exemption reason text (BT-120).',
37
+ 'BR-E-02': 'An Invoice that contains an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "Exempt from VAT" shall contain the Seller VAT Identifier (BT-31), the Seller tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63).',
38
+ 'BR-E-05': 'In an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "Exempt from VAT", the Invoiced item VAT rate (BT-152) shall be 0 (zero).',
39
+ 'BR-E-08': 'In a VAT breakdown (BG-23) where the VAT category code (BT-118) is "Exempt from VAT" the VAT category taxable amount (BT-116) shall equal the sum of Invoice line net amounts (BT-131) minus the sum of Document level allowance amounts (BT-92) plus the sum of Document level charge amounts (BT-99) where the VAT category codes (BT-151, BT-95, BT-102) are "Exempt from VAT".',
40
+ 'BR-E-09': 'The VAT category tax amount (BT-117) In a VAT breakdown (BG-23) where the VAT category code (BT-118) equals "Exempt from VAT" shall equal 0 (zero).',
41
+ 'BR-E-10': 'A VAT breakdown (BG-23) with VAT Category code (BT-118) "Exempt from VAT" shall have a VAT exemption reason code (BT-121) or a VAT exemption reason text (BT-120).',
29
42
  'BR-AE-01': 'An Invoice that contains an Invoice line (BG-25), a Document level allowance (BG-20) or a Document level charge (BG-21) where the VAT category code (BT-151, BT-95 or BT-102) is "Reverse charge" shall contain in the VAT Breakdown (BG-23) exactly one VAT category code (BT-118) equal with "VAT reverse charge".',
30
43
  'BR-AE-02': 'An Invoice that contains an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "Reverse charge" shall contain the Seller VAT Identifier (BT-31), the Seller Tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63) and the Buyer VAT identifier (BT-48) and/or the Buyer legal registration identifier (BT-47).',
31
44
  'BR-AE-05': 'In an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "Reverse charge" the Invoiced item VAT rate (BT-152) shall be 0 (zero).',
32
45
  'BR-AE-08': 'In a VAT breakdown (BG-23) where the VAT category code (BT-118) is "Reverse charge" the VAT category taxable amount (BT-116) shall equal the sum of Invoice line net amounts (BT-131) minus the sum of Document level allowance amounts (BT-92) plus the sum of Document level charge amounts (BT-99) where the VAT category codes (BT-151, BT-95, BT-102) are "Reverse charge".',
33
46
  'BR-AE-09': 'The VAT category tax amount (BT-117) in a VAT breakdown (BG-23) where the VAT category code (BT-118) is "Reverse charge" shall be 0 (zero).',
34
47
  'BR-AE-10': 'A VAT breakdown (BG-23) with VAT Category code (BT-118) "Reverse charge" shall have a VAT exemption reason code (BT-121), meaning "Reverse charge" or the VAT exemption reason text (BT-120) "Reverse charge" (or the equivalent standard text in another language).',
48
+ 'BR-IC-02': 'An Invoice that contains an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "Intra-community supply" shall contain the Seller VAT Identifier (BT-31) or the Seller tax representative VAT identifier (BT-63) and the Buyer VAT identifier (BT-48).',
49
+ 'BR-IC-05': 'In an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "Intracommunity supply" the Invoiced item VAT rate (BT-152) shall be 0 (zero).',
50
+ 'BR-IC-08': 'In a VAT breakdown (BG-23) where the VAT category code (BT-118) is "Intra-community supply" the VAT category taxable amount (BT-116) shall equal the sum of Invoice line net amounts (BT-131) minus the sum of Document level allowance amounts (BT-92) plus the sum of Document level charge amounts (BT-99) where the VAT category codes (BT-151, BT-95, BT-102) are "Intra-community supply".',
51
+ 'BR-IC-09': 'The VAT category tax amount (BT-117) in a VAT breakdown (BG-23) where the VAT category code (BT-118) is "Intra-community supply" shall be 0 (zero).',
52
+ 'BR-IC-10': 'A VAT breakdown (BG-23) with the VAT Category code (BT-118) "Intra-community supply" shall have a VAT exemption reason code (BT-121), meaning "Intra-community supply" or the VAT exemption reason text (BT-120) "Intra-community supply" (or the equivalent standard text in another language).',
53
+ 'BR-IC-11': 'In an Invoice with a VAT breakdown (BG-23) where the VAT category code (BT-118) is "Intra-community supply" the Actual delivery date (BT-72) or the Invoicing period (BG-14) shall not be blank.',
54
+ 'BR-IC-12': 'In an Invoice with a VAT breakdown (BG-23) where the VAT category code (BT-118) is "Intra-community supply" the Deliver to country code (BT-80) shall not be blank.',
55
+ 'BR-G-02': 'An Invoice that contains an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "Export outside the EU" shall contain the Seller VAT Identifier (BT-31) or the Seller tax representative VAT identifier (BT-63).',
56
+ 'BR-G-05': 'In an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "Export outside the EU" the Invoiced item VAT rate (BT-152) shall be 0 (zero).',
57
+ 'BR-G-08': 'In a VAT breakdown (BG-23) where the VAT category code (BT-118) is "Export outside the EU" the VAT category taxable amount (BT-116) shall equal the sum of Invoice line net amounts (BT-131) minus the sum of Document level allowance amounts (BT-92) plus the sum of Document level charge amounts (BT-99) where the VAT category codes (BT-151, BT-95, BT-102) are "Export outside the EU".',
58
+ 'BR-G-09': 'The VAT category tax amount (BT-117) in a VAT breakdown (BG-23) where the VAT category code (BT-118) is "Export outside the EU" shall be 0 (zero).',
59
+ 'BR-G-10': 'A VAT breakdown (BG-23) with the VAT Category code (BT-118) "Export outside the EU" shall have a VAT exemption reason code (BT-121), meaning "Export outside the EU" or the VAT exemption reason text (BT-120) "Export outside the EU" (or the equivalent standard text in another language).',
60
+ 'BR-O-02': 'An Invoice that contains an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "Not subject to VAT" shall not contain the Seller VAT identifier (BT-31), the Seller tax representative VAT identifier (BT-63) or the Buyer VAT identifier (BT-48).',
61
+ 'BR-O-05': 'An Invoice line (BG-25) where the VAT category code (BT-151) is "Not subject to VAT" shall not contain an Invoiced item VAT rate (BT-152).',
62
+ 'BR-O-08': 'In a VAT breakdown (BG-23) where the VAT category code (BT-118) is " Not subject to VAT" the VAT category taxable amount (BT-116) shall equal the sum of Invoice line net amounts (BT-131) minus the sum of Document level allowance amounts (BT-92) plus the sum of Document level charge amounts (BT-99) where the VAT category codes (BT-151, BT-95, BT-102) are "Not subject to VAT".',
63
+ 'BR-O-09': 'The VAT category tax amount (BT-117) in a VAT breakdown (BG-23) where the VAT category code (BT-118) is "Not subject to VAT" shall be 0 (zero).',
64
+ 'BR-O-10': 'A VAT breakdown (BG-23) with VAT Category code (BT-118) " Not subject to VAT" shall have a VAT exemption reason code (BT-121), meaning " Not subject to VAT" or a VAT exemption reason text (BT-120) " Not subject to VAT" (or the equivalent standard text in another language).',
65
+ 'BR-O-11': 'An Invoice that contains a VAT breakdown group (BG-23) with a VAT category code (BT-118) "Not subject to VAT" shall not contain other VAT breakdown groups (BG-23).',
66
+ 'BR-AF-02': 'An Invoice that contains an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "IGIC" shall contain the Seller VAT Identifier (BT-31), the Seller tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63).',
67
+ 'BR-AF-05': 'In an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "IGIC" the invoiced item VAT rate (BT-152) shall be 0 (zero) or greater than zero.',
68
+ 'BR-AF-08': 'For each different value of VAT category rate (BT-119) where the VAT category code (BT-118) is "IGIC", the VAT category taxable amount (BT-116) in a VAT breakdown (BG-23) shall equal the sum of Invoice line net amounts (BT-131) plus the sum of document level charge amounts (BT-99) minus the sum of document level allowance amounts (BT-92) where the VAT category code (BT-151, BT-102, BT-95) is "IGIC" and the VAT rate (BT-152, BT-103, BT-96) equals the VAT category rate (BT-119).',
69
+ 'BR-AF-09': 'The VAT category tax amount (BT-117) in a VAT breakdown (BG-23) where VAT category code (BT-118) is "IGIC" shall equal the VAT category taxable amount (BT-116) multiplied by the VAT category rate (BT-119).',
70
+ 'BR-AF-10': 'A VAT breakdown (BG-23) with VAT Category code (BT-118) "IGIC" shall not have a VAT exemption reason code (BT-121) or VAT exemption reason text (BT-120).',
71
+ 'BR-AG-02': 'An Invoice that contains an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "IPSI" shall contain the Seller VAT Identifier (BT-31), the Seller tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63).',
72
+ 'BR-AG-05': 'In an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "IPSI" the Invoiced item VAT rate (BT-152) shall be 0 (zero) or greater than zero.',
73
+ 'BR-AG-08': 'For each different value of VAT category rate (BT-119) where the VAT category code (BT-118) is "IPSI", the VAT category taxable amount (BT-116) in a VAT breakdown (BG-23) shall equal the sum of Invoice line net amounts (BT-131) plus the sum of document level charge amounts (BT-99) minus the sum of document level allowance amounts (BT-92) where the VAT category code (BT-151, BT-102, BT-95) is "IPSI" and the VAT rate (BT-152, BT-103, BT-96) equals the VAT category rate (BT-119).',
74
+ 'BR-AG-09': 'The VAT category tax amount (BT-117) in a VAT breakdown (BG-23) where VAT category code (BT-118) is "IPSI" shall equal the VAT category taxable amount (BT-116) multiplied by the VAT category rate (BT-119).',
75
+ 'BR-AG-10': 'A VAT breakdown (BG-23) with VAT Category code (BT-118) "IPSI" shall not have a VAT exemption reason code (BT-121) or VAT exemption reason text (BT-120).',
35
76
  'BR-CO-17': 'VAT category tax amount (BT-117) = VAT category taxable amount (BT-116) x (VAT category rate (BT-119) / 100), rounded to two decimals.',
77
+ 'BR-CL-22': 'Tax exemption reason code identifier scheme identifier MUST belong to the CEF VATEX code list',
78
+ [VAT_EXEMPTION_DIFFERS]: 'The Invoice lines (BG-25) of one VAT breakdown (BG-23) state one VAT exemption reason text (BT-120) and code (BT-121), as the breakdown states one.',
79
+ };
80
+ /** The rule IDs of the taxable amount (-08) and the tax amount (-09) of a VAT breakdown, per category */
81
+ const BREAKDOWN_RULES = {
82
+ S: { taxable: 'BR-S-08', tax: 'BR-S-09' },
83
+ Z: { taxable: 'BR-Z-08', tax: 'BR-Z-09' },
84
+ E: { taxable: 'BR-E-08', tax: 'BR-E-09' },
85
+ AE: { taxable: 'BR-AE-08', tax: 'BR-AE-09' },
86
+ K: { taxable: 'BR-IC-08', tax: 'BR-IC-09' },
87
+ G: { taxable: 'BR-G-08', tax: 'BR-G-09' },
88
+ O: { taxable: 'BR-O-08', tax: 'BR-O-09' },
89
+ L: { taxable: 'BR-AF-08', tax: 'BR-AF-09' },
90
+ M: { taxable: 'BR-AG-08', tax: 'BR-AG-09' },
91
+ };
92
+ /** The categories whose tax amount is the taxable amount × rate (BR-S-09, BR-AF-09, BR-AG-09); the others state 0 */
93
+ const TAXED_CATEGORIES = ['S', 'L', 'M'];
94
+ /**
95
+ * The business terms and groups a finding of a rule on the items' VAT categories points at
96
+ * @param violation The violation
97
+ */
98
+ const referencesOf = (violation) => {
99
+ const suffix = violation.ruleId.split('-').pop();
100
+ if (violation.ruleId === 'BR-CL-22') {
101
+ return { btReference: 'BT-121', bgReference: 'BG-23' };
102
+ }
103
+ if (violation.ruleId === VAT_EXEMPTION_DIFFERS) {
104
+ return { btReference: 'BT-120', bgReference: 'BG-23' };
105
+ }
106
+ if (violation.ruleId === 'BR-IC-11') {
107
+ return { btReference: 'BT-72', bgReference: 'BG-13' };
108
+ }
109
+ if (violation.ruleId === 'BR-IC-12') {
110
+ return { btReference: 'BT-80', bgReference: 'BG-15' };
111
+ }
112
+ if (violation.ruleId === 'BR-O-11') {
113
+ return { btReference: 'BT-151', bgReference: 'BG-25' };
114
+ }
115
+ switch (suffix) {
116
+ case '02':
117
+ return violation.field.startsWith('to.') ? { btReference: 'BT-48', bgReference: 'BG-7' } : { btReference: 'BT-31', bgReference: 'BG-4' };
118
+ case '05':
119
+ return { btReference: 'BT-152', bgReference: 'BG-25' };
120
+ default:
121
+ return { btReference: 'BT-121', bgReference: 'BG-23' };
122
+ }
36
123
  };
37
124
  /**
38
125
  * The EN 16931 VAT category rules of a document, checked on the VAT categories
39
126
  * and the VAT breakdown the encoders write.
40
127
  *
41
- * The category of every line is the one the encoders write (`getVatCategory`):
42
- * reverse charge (AE) when the document states `reverseCharge`, standard rated
43
- * (S) otherwise. The envelope states no category of its own for an item or a
44
- * breakdown, so the other categories (Z, E, K, G, O, L, M) cannot occur, and
45
- * an item's `metadata` is not read for one. A decoded document may state one
46
- * for a line (`statedValues.lines[].vatCategoryCode`); the envelope reads such
47
- * a line as S, so BR-S-05 is not reported for a line whose stated category is
48
- * not S. The VAT breakdown is the one the
49
- * encoders write (`computeDocumentTotals().vatGroups`): one group per category
50
- * and rate. The envelope has no document level allowances or charges, so the
128
+ * The category of every line is the one the encoders write
129
+ * (`getItemVatCategory`): the one the item states (`vatCategory`, BT-151),
130
+ * else reverse charge (AE) when the document states `reverseCharge` and
131
+ * standard rated (S) otherwise. A decoded line carries the category its
132
+ * document states. The rules on the items, their exemption reasons and the
133
+ * parties' identifiers are those export refuses a document for
134
+ * (`findVatCategoryViolations`). The VAT breakdown is the one the encoders
135
+ * write (`computeDocumentTotals().vatGroups`): one group per category and
136
+ * rate. The envelope has no document level allowances or charges, so the
51
137
  * rules on them (BR-S-02 to -04 and -06 to -07 and their counterparts) do not
52
138
  * apply, and a taxable amount is the sum of the line net amounts.
53
139
  */
@@ -68,40 +154,22 @@ export class VATCategoriesValidator {
68
154
  const totals = computeDocumentTotals(invoice);
69
155
  const calculator = getDocumentCalculator(invoice.currency);
70
156
  const items = invoice.items ?? [];
71
- this.validateLines(invoice, items, totals);
157
+ for (const violation of findVatCategoryViolations(invoice)) {
158
+ this.addError(violation.ruleId, {
159
+ field: violation.field,
160
+ ...referencesOf(violation),
161
+ remediation: violation.message,
162
+ });
163
+ }
164
+ this.validateBreakdownPresence(totals);
72
165
  this.validateBreakdown(items, totals, calculator);
73
166
  return this.results;
74
167
  }
75
168
  /**
76
- * The rules on the lines of each category: the rate (BR-S-05, BR-AE-05), the breakdown the
77
- * category needs (BR-S-01, BR-AE-01) and the parties' identifiers of reverse charge (BR-AE-02)
169
+ * The breakdown a category of the lines needs: at least one for S (BR-S-01), exactly one for
170
+ * reverse charge (BR-AE-01)
78
171
  */
79
- validateLines(invoice, items, totals) {
80
- items.forEach((item, index) => {
81
- const category = totals.lineVatCategories[index];
82
- // A decoded line keeps the category its document states (BT-151). One that is not S is a
83
- // category the envelope cannot express (Z, E, K, G, O, or AE in a document that mixes S and
84
- // AE lines, which the decoders do not read as reverse charge); its rate says nothing of BR-S-05.
85
- const statedCategory = invoice.statedValues?.lines[index]?.vatCategoryCode;
86
- if (category === 'S' && !(item.vatPercentage > 0) && (statedCategory === undefined || statedCategory === 'S')) {
87
- this.addError('BR-S-05', {
88
- field: `items[${index}].vatPercentage`,
89
- btReference: 'BT-152',
90
- bgReference: 'BG-25',
91
- value: item.vatPercentage,
92
- expected: '> 0',
93
- });
94
- }
95
- if (category === 'AE' && item.vatPercentage !== 0) {
96
- this.addError('BR-AE-05', {
97
- field: `items[${index}].vatPercentage`,
98
- btReference: 'BT-152',
99
- bgReference: 'BG-25',
100
- value: item.vatPercentage,
101
- expected: 0,
102
- });
103
- }
104
- });
172
+ validateBreakdownPresence(totals) {
105
173
  const hasCategory = (category) => totals.lineVatCategories.includes(category);
106
174
  const groupsOf = (category) => totals.vatGroups.filter((group) => group.category === category);
107
175
  if (hasCategory('S') && groupsOf('S').length === 0) {
@@ -116,30 +184,10 @@ export class VATCategoriesValidator {
116
184
  remediation: 'a reverse charge breakdown per rate: give every reverse charge line the VAT rate 0 (BR-AE-05)',
117
185
  });
118
186
  }
119
- if (hasCategory('AE')) {
120
- const seller = invoice.from?.registrationDetails;
121
- if (!seller?.vatId?.trim() && !seller?.taxNumber?.trim()) {
122
- this.addError('BR-AE-02', {
123
- field: 'from.registrationDetails',
124
- btReference: 'BT-31',
125
- bgReference: 'BG-4',
126
- remediation: 'state from.registrationDetails.vatId (BT-31) or .taxNumber (BT-32); the envelope cannot state a tax representative (BT-63)',
127
- });
128
- }
129
- const buyer = invoice.to?.registrationDetails;
130
- if (!buyer?.vatId?.trim() && !buyer?.registrationId?.trim()) {
131
- this.addError('BR-AE-02', {
132
- field: 'to.registrationDetails',
133
- bgReference: 'BG-7',
134
- btReference: 'BT-48',
135
- remediation: 'state to.registrationDetails.vatId (BT-48) or .registrationId (BT-47)',
136
- });
137
- }
138
- }
139
187
  }
140
188
  /**
141
- * The rules on each VAT breakdown group: its taxable amount (BR-S-08, BR-AE-08), its tax
142
- * amount (BR-S-09, BR-AE-09, BR-CO-17) and its exemption reason (BR-S-10, BR-AE-10)
189
+ * The rules on each VAT breakdown group: its taxable amount (BR-S-08 and its counterparts of
190
+ * every category) and its tax amount (BR-S-09 and its counterparts, BR-CO-17)
143
191
  */
144
192
  validateBreakdown(items, totals, calculator) {
145
193
  for (const group of totals.vatGroups) {
@@ -147,28 +195,18 @@ export class VATCategoriesValidator {
147
195
  const expectedTaxable = calculator.round(Decimal.sum(lineNetAmounts));
148
196
  const expectedTax = calculator.calculateVAT(group.taxableAmount, group.rate);
149
197
  const amounts = (value, expected) => ({ value: value.toNumber(), expected: expected.toNumber() });
150
- const at = `rate ${group.rate}`;
151
- if (group.category === 'S') {
152
- if (!group.taxableAmount.equals(expectedTaxable)) {
153
- this.addError('BR-S-08', { field: `taxBreakdown[${at}].netAmount`, btReference: 'BT-116', ...amounts(group.taxableAmount, expectedTaxable) });
154
- }
198
+ const at = `${group.category} rate ${group.rate}`;
199
+ const rules = BREAKDOWN_RULES[group.category];
200
+ if (!group.taxableAmount.equals(expectedTaxable)) {
201
+ this.addError(rules.taxable, { field: `taxBreakdown[${at}].netAmount`, btReference: 'BT-116', ...amounts(group.taxableAmount, expectedTaxable) });
202
+ }
203
+ if (TAXED_CATEGORIES.includes(group.category)) {
155
204
  if (!group.taxAmount.equals(expectedTax)) {
156
- this.addError('BR-S-09', { field: `taxBreakdown[${at}].taxAmount`, btReference: 'BT-117', ...amounts(group.taxAmount, expectedTax) });
157
- }
158
- if (group.exemption) {
159
- this.addError('BR-S-10', { field: `taxBreakdown[${at}]`, btReference: 'BT-121', value: group.exemption.code });
205
+ this.addError(rules.tax, { field: `taxBreakdown[${at}].taxAmount`, btReference: 'BT-117', ...amounts(group.taxAmount, expectedTax) });
160
206
  }
161
207
  }
162
- if (group.category === 'AE') {
163
- if (!group.taxableAmount.equals(expectedTaxable)) {
164
- this.addError('BR-AE-08', { field: `taxBreakdown[${at}].netAmount`, btReference: 'BT-116', ...amounts(group.taxableAmount, expectedTaxable) });
165
- }
166
- if (!group.taxAmount.isZero()) {
167
- this.addError('BR-AE-09', { field: `taxBreakdown[${at}].taxAmount`, btReference: 'BT-117', value: group.taxAmount.toNumber(), expected: 0 });
168
- }
169
- if (group.exemption?.code !== 'VATEX-EU-AE' && !group.exemption?.reason.trim()) {
170
- this.addError('BR-AE-10', { field: `taxBreakdown[${at}]`, btReference: 'BT-121' });
171
- }
208
+ else if (!group.taxAmount.isZero()) {
209
+ this.addError(rules.tax, { field: `taxBreakdown[${at}].taxAmount`, btReference: 'BT-117', value: group.taxAmount.toNumber(), expected: 0 });
172
210
  }
173
211
  if (!group.taxAmount.equals(expectedTax)) {
174
212
  this.addError('BR-CO-17', { field: `taxBreakdown[${at}].taxAmount`, btReference: 'BT-117', ...amounts(group.taxAmount, expectedTax) });
@@ -178,7 +216,7 @@ export class VATCategoriesValidator {
178
216
  addError(ruleId, finding) {
179
217
  this.results.push({
180
218
  ruleId,
181
- source: 'EN16931',
219
+ source: ruleId === VAT_EXEMPTION_DIFFERS ? 'EINVOICE' : 'EN16931',
182
220
  severity: 'error',
183
221
  message: VAT_CATEGORY_RULES[ruleId],
184
222
  bgReference: 'BG-23',
@@ -203,4 +241,4 @@ export function getVATCategoryName(category) {
203
241
  };
204
242
  return names[category] || 'Unknown';
205
243
  }
206
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmF0LWNhdGVnb3JpZXMudmFsaWRhdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvZm9ybWF0cy92YWxpZGF0aW9uL3ZhdC1jYXRlZ29yaWVzLnZhbGlkYXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wscUJBQXFCLEVBQ3JCLHNCQUFzQixFQUN0QixxQkFBcUIsRUFDckIsc0JBQXNCLEdBRXZCLE1BQU0sNkJBQTZCLENBQUM7QUFJckMsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBSTlDOztHQUVHO0FBQ0gsTUFBTSxDQUFOLElBQVksV0FVWDtBQVZELFdBQVksV0FBVztJQUNyQixzQkFBTyxDQUFBO0lBQ1Asc0JBQU8sQ0FBQTtJQUNQLHNCQUFPLENBQUE7SUFDUCx3QkFBUyxDQUFBO0lBQ1Qsc0JBQU8sQ0FBQTtJQUNQLHNCQUFPLENBQUE7SUFDUCxzQkFBTyxDQUFBO0lBQ1Asc0JBQU8sQ0FBQTtJQUNQLHNCQUFPLENBQUEsQ0FBSSxvRUFBb0U7QUFDakYsQ0FBQyxFQVZXLFdBQVcsS0FBWCxXQUFXLFFBVXRCO0FBY0Q7Ozs7R0FJRztBQUNILE1BQU0sa0JBQWtCLEdBQUc7SUFDekIsU0FBUyxFQUNQLG1UQUFtVDtJQUNyVCxTQUFTLEVBQ1AsbUtBQW1LO0lBQ3JLLFNBQVMsRUFDUCx1ZkFBdWY7SUFDemYsU0FBUyxFQUNQLHlOQUF5TjtJQUMzTixTQUFTLEVBQ1Asb0tBQW9LO0lBQ3RLLFVBQVUsRUFDUixzVEFBc1Q7SUFDeFQsVUFBVSxFQUNSLGtYQUFrWDtJQUNwWCxVQUFVLEVBQ1IsMEpBQTBKO0lBQzVKLFVBQVUsRUFDUixrWEFBa1g7SUFDcFgsVUFBVSxFQUNSLDZJQUE2STtJQUMvSSxVQUFVLEVBQ1Isc1FBQXNRO0lBQ3hRLFVBQVUsRUFDUix3SUFBd0k7Q0FDbEksQ0FBQztBQWNYOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JHO0FBQ0gsTUFBTSxPQUFPLHNC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
244
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmF0LWNhdGVnb3JpZXMudmFsaWRhdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvZm9ybWF0cy92YWxpZGF0aW9uL3ZhdC1jYXRlZ29yaWVzLnZhbGlkYXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wscUJBQXFCLEVBQ3JCLHNCQUFzQixFQUN0QixxQkFBcUIsRUFDckIsc0JBQXNCLEdBRXZCLE1BQU0sNkJBQTZCLENBQUM7QUFJckMsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQzlDLE9BQU8sRUFDTCx5QkFBeUIsRUFDekIscUJBQXFCLEdBR3RCLE1BQU0sMEJBQTBCLENBQUM7QUFHbEM7O0dBRUc7QUFDSCxNQUFNLENBQU4sSUFBWSxXQVVYO0FBVkQsV0FBWSxXQUFXO0lBQ3JCLHNCQUFPLENBQUE7SUFDUCxzQkFBTyxDQUFBO0lBQ1Asc0JBQU8sQ0FBQTtJQUNQLHdCQUFTLENBQUE7SUFDVCxzQkFBTyxDQUFBO0lBQ1Asc0JBQU8sQ0FBQTtJQUNQLHNCQUFPLENBQUE7SUFDUCxzQkFBTyxDQUFBO0lBQ1Asc0JBQU8sQ0FBQSxDQUFJLG9FQUFvRTtBQUNqRixDQUFDLEVBVlcsV0FBVyxLQUFYLFdBQVcsUUFVdEI7QUFjRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLGtCQUFrQixHQUFHO0lBQ3pCLFNBQVMsRUFDUCxtVEFBbVQ7SUFDclQsU0FBUyxFQUNQLG1LQUFtSztJQUNySyxTQUFTLEVBQ1AsdWZBQXVmO0lBQ3pmLFNBQVMsRUFDUCx5TkFBeU47SUFDM04sU0FBUyxFQUNQLG9LQUFvSztJQUN0SyxTQUFTLEVBQ1AsMFFBQTBRO0lBQzVRLFNBQVMsRUFDUCxzSkFBc0o7SUFDeEosU0FBUyxFQUNQLHFXQUFxVztJQUN2VyxTQUFTLEVBQ1Asd0lBQXdJO0lBQzFJLFNBQVMsRUFDUCxpS0FBaUs7SUFDbkssU0FBUyxFQUNQLHVSQUF1UjtJQUN6UixTQUFTLEVBQ1As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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fin.cx/einvoice",
3
- "version": "10.0.0",
3
+ "version": "10.1.0",
4
4
  "private": false,
5
5
  "description": "A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.",
6
6
  "main": "dist_ts/index.js",
@@ -16,13 +16,13 @@
16
16
  "@git.zone/tsbuild": "^5.0.0",
17
17
  "@git.zone/tsbundle": "^2.15.0",
18
18
  "@git.zone/tsrun": "^3.0.0",
19
- "@git.zone/tstest": "^6.2.0",
19
+ "@git.zone/tstest": "^6.3.1",
20
20
  "@push.rocks/smartfile": "^13.1.3",
21
- "@types/node": "^26.6.1"
21
+ "@types/node": "^26.6.2"
22
22
  },
23
23
  "dependencies": {
24
24
  "@push.rocks/smartxml": "^2.1.0",
25
- "@tsclass/tsclass": "^9.10.0",
25
+ "@tsclass/tsclass": "^9.11.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.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",
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 && tstest test/test.cii-stated-dates.node.ts --verbose --logfile --timeout 60 && tstest test/test.vat-category-items.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
@@ -400,25 +400,61 @@ Satz 3 UStG) is for the issuing application to enforce: the parties' countries
400
400
  do not tell it, since a domestic § 13b supply to a buyer based in another
401
401
  member state is taxed in Germany and may state a tax number.
402
402
 
403
- The envelope states reverse charge for the whole document; an item has no VAT
404
- category of its own. A document mixing reverse charge lines with standard rated
405
- ones cannot be expressed until `@tsclass/tsclass` gives items a category.
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.
403
+ An item can also state reverse charge on its own (`vatCategory: 'AE'`), so one
404
+ document can carry reverse charge items beside taxed ones; the same rules apply
405
+ to it. A decoded document states `reverseCharge: true` when every line is `AE`,
406
+ and each item carries the category its line states.
407
+
408
+ ## VAT categories
409
+
410
+ Every item has a VAT category (EN 16931 BT-151, `TAccountingDocItem.vatCategory`
411
+ of `@tsclass/tsclass`): the one it states, else reverse charge (`AE`) when the
412
+ document states `reverseCharge`, standard rated (`S`) otherwise. The encoders
413
+ write one VAT breakdown (BG-23) per category and rate, and the decoders give
414
+ every item the category its line states and the exemption reason of its
415
+ breakdown.
416
+
417
+ | Category | Meaning | Rate | Exemption reason (BT-120, BT-121) |
418
+ | --- | --- | --- | --- |
419
+ | `S` | standard rated | greater than 0 (BR-S-05) | none (BR-S-10) |
420
+ | `Z` | zero rated, e.g. photovoltaic systems (§ 12 Abs. 3 UStG) | 0 (BR-Z-05) | none (BR-Z-10) |
421
+ | `E` | exempt, e.g. § 4 UStG or a small business (§ 19 Abs. 1 UStG) | 0 (BR-E-05) | the item's reason or VATEX code, required (BR-E-10) |
422
+ | `AE` | reverse charge (§ 13b UStG) | 0 (BR-AE-05) | the item's, else `VATEX-EU-AE` and the § 14a Abs. 5 UStG wording |
423
+ | `K` | intra-community supply (§ 4 Nr. 1 Buchst. b, § 6a UStG) | 0 (BR-IC-05) | the item's, else `VATEX-EU-IC` |
424
+ | `G` | export outside the EU (§ 4 Nr. 1 Buchst. a, § 6 UStG) | 0 (BR-G-05) | the item's, else `VATEX-EU-G` |
425
+ | `O` | not subject to VAT | none written; the item states 0 (BR-O-05) | the item's, else `VATEX-EU-O` |
426
+ | `L` | IGIC (Canary Islands) | 0 or more (BR-AF-05); more than 0 in CII | none (BR-AF-10) |
427
+ | `M` | IPSI (Ceuta and Melilla) | 0 or more (BR-AG-05) | none (BR-AG-10) |
428
+
429
+ The tax of a breakdown is its taxable amount × rate for `S`, `L` and `M`, and 0
430
+ for every other category. An item states its exemption in
431
+ `vatExemptionReason` (BT-120) and `vatExemptionReasonCode` (BT-121, a code of
432
+ the CEF VATEX list, BR-CL-22); the items of one breakdown state the same one,
433
+ as a breakdown states one.
434
+
435
+ Export refuses a document that breaks a rule of its categories with an
436
+ `EInvoiceFormatError` naming the rule, and `validate()` reports it at the
437
+ business level for a document built in code: the rate and the exemption reason
438
+ above; for `Z`, `E`, `L` and `M` the seller VAT identifier or tax number
439
+ (BR-Z-02, BR-E-02, BR-AF-02, BR-AG-02); for `K` the seller and the buyer VAT
440
+ identifier (BR-IC-02), the delivery date or invoicing period (BR-IC-11) and the
441
+ deliver to country (BR-IC-12); for `G` the seller VAT identifier (BR-G-02); for
442
+ `O` neither VAT identifier (BR-O-02) and no item of another category (BR-O-11);
443
+ items of one breakdown with different exemption reasons
444
+ (`EINVOICE-VAT-EXEMPTION-DIFFERS`). The rules would also accept a seller tax
445
+ representative (BT-63), which the envelope cannot state.
446
+
447
+ The deliver to address (BG-15) is `metadata.deliveryAddress`; every format
448
+ writes it when it states a country (`countryCode`, BT-80) and reads it back.
449
+ An item at the rate 0 outside reverse charge that states no category is
450
+ standard rated and refused as BR-S-05: state its category.
451
+
452
+ The tests check documents of every category with the official CEN EN 16931
453
+ rules on all four exports and with the KoSIT XRechnung rules on the XRechnung
454
+ export only.
455
+
456
+ A ZUGFeRD 1.0 document states no values einvoice reads as stated
457
+ (`statedValues`), so its items carry no category; it is read as before.
422
458
 
423
459
  ## Paid amount and advance payments
424
460
 
@@ -509,7 +545,12 @@ the UTC midnight of that day, `Date.UTC(2026, 8, 20)`.
509
545
  - The decoders return that timestamp. A UBL date may carry a time zone
510
546
  (`2026-09-20+02:00`, `2026-09-20Z`); the zone does not move the day, so the
511
547
  decoders return the UTC midnight of the stated day, and every format writes
512
- that day.
548
+ that day. A CII date stated with a time of day, in format 203
549
+ (`202609202330`) or 204 (`20260920233000`), or without a format code as an
550
+ ISO 8601 date or date and time (`2026-09-20T00:30:00+02:00`), is read the
551
+ same way: the day it states, whatever the time of day and the zone. A CII
552
+ date in another form, or in a format that names no day, is refused with an
553
+ `EInvoiceParsingError`; no day is guessed.
513
554
  - The encoders write the day of that timestamp in UTC and count the payment
514
555
  term in UTC days, so what they write does not depend on the time zone of the
515
556
  machine that writes it.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@fin.cx/einvoice',
6
- version: '10.0.0',
6
+ version: '10.1.0',
7
7
  description: 'A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.'
8
8
  }
package/ts/einvoice.ts CHANGED
@@ -46,7 +46,7 @@ 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 { isRateNotAboveZero, standardRatedLineWithoutRateMessage } from './formats/utils/vat.category.js';
49
+ import { findVatCategoryViolations, type TVatCategoryCode } from './formats/utils/vat.category.js';
50
50
  import { BR_CO_26_TEXT, envelopeSellerMeetsBrCo26 } from './formats/utils/seller.identifier.js';
51
51
  import { findEnvelopeDifferences } from './formats/utils/stated.values.js';
52
52
  import type { IEInvoiceStatedValues } from './interfaces/stated.values.js';
@@ -247,7 +247,7 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
247
247
  return computeDocumentTotals(this).grandTotal.toNumber();
248
248
  }
249
249
 
250
- public get taxBreakdown(): Array<{ taxPercent: number; netAmount: number; taxAmount: number }> {
250
+ public get taxBreakdown(): Array<{ category: TVatCategoryCode; taxPercent: number; netAmount: number; taxAmount: number }> {
251
251
  return this.calculateTaxBreakdown();
252
252
  }
253
253
 
@@ -658,17 +658,10 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
658
658
  );
659
659
 
660
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
- });
661
+ // the rules of the items' VAT categories, as export refuses a document that breaks one: the
662
+ // rate of each category (BR-S-05, BR-Z-05, ...), the exemption reasons and the identifiers
663
+ for (const violation of findVatCategoryViolations({ ...invoice, metadata: this.metadata })) {
664
+ errors.push({ code: violation.ruleId, message: violation.message, location: violation.field });
672
665
  }
673
666
  // BR-CO-26: the seller states a seller, legal registration or VAT identifier; a tax number alone does not identify it
674
667
  if (!envelopeSellerMeetsBrCo26(invoice)) {
@@ -879,9 +872,10 @@ export class EInvoice implements TAccountingDocEnvelope<TAccountingDocType, {}>
879
872
  /**
880
873
  * Calculates tax breakdown by rate
881
874
  */
882
- private calculateTaxBreakdown(): Array<{ taxPercent: number; netAmount: number; taxAmount: number }> {
875
+ private calculateTaxBreakdown(): Array<{ category: TVatCategoryCode; taxPercent: number; netAmount: number; taxAmount: number }> {
883
876
  // the VAT breakdown (BG-23) the encoders write
884
877
  return computeDocumentTotals(this).vatGroups.map((group) => ({
878
+ category: group.category,
885
879
  taxPercent: group.rate,
886
880
  netAmount: group.taxableAmount.toNumber(),
887
881
  taxAmount: group.taxAmount.toNumber(),
@@ -2,7 +2,7 @@ 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';
5
+ import { parseCiiDateDay, parseIsoDateTimeDay, parseXsdDateDay } from '../utils/date.value.js';
6
6
  import type { IEInvoiceStatedValues } from '../../interfaces/stated.values.js';
7
7
  import { statedValueProblemMessage, type IStatedValuesRead } from '../utils/stated.values.js';
8
8
 
@@ -102,7 +102,15 @@ export abstract class BaseDecoder {
102
102
  }
103
103
 
104
104
  /**
105
- * Parses a required CII date without inventing a fallback value.
105
+ * Parses a required CII date without inventing a fallback value: the
106
+ * calendar day it states, as the UTC midnight of that day. Format 102
107
+ * (`YYYYMMDD`) is the one EN 16931 asks for; a date stated with a time of
108
+ * day in format 203 (`YYYYMMDDhhmm`) or 204 (`YYYYMMDDhhmmss`), or without a
109
+ * format code as an ISO 8601 date or date and time (`parseIsoDateTimeDay`),
110
+ * is read as the day it states, whatever the time of day and the zone.
111
+ * Format 610 (`YYYYMM`) is read as the 1st of the month. Any other value is
112
+ * refused with an `EInvoiceParsingError`: no day is guessed, and none is read
113
+ * in the zone of the machine.
106
114
  */
107
115
  protected parseRequiredCIIDate(dateStr: string, format?: string): number {
108
116
  const value = dateStr.trim();
@@ -111,22 +119,17 @@ export abstract class BaseDecoder {
111
119
  format: 'cii',
112
120
  });
113
121
  }
122
+ const formatCode = (format ?? '').trim();
114
123
 
115
- if (format === '102') {
116
- const match = /^(\d{4})(\d{2})(\d{2})$/.exec(value);
117
- if (!match) {
118
- throw this.createInvalidDateError(value, 'CII format 102');
124
+ if (formatCode === '102' || formatCode === '203' || formatCode === '204') {
125
+ const day = parseCiiDateDay(value, formatCode);
126
+ if (day === undefined) {
127
+ throw this.createInvalidDateError(value, `CII format ${formatCode}`);
119
128
  }
120
- return this.createStrictUtcTimestamp(
121
- Number(match[1]),
122
- Number(match[2]),
123
- Number(match[3]),
124
- value,
125
- 'CII format 102',
126
- );
129
+ return day;
127
130
  }
128
131
 
129
- if (format === '610') {
132
+ if (formatCode === '610') {
130
133
  const match = /^(\d{4})(\d{2})$/.exec(value);
131
134
  if (!match) {
132
135
  throw this.createInvalidDateError(value, 'CII format 610');
@@ -140,11 +143,15 @@ export abstract class BaseDecoder {
140
143
  );
141
144
  }
142
145
 
143
- const parsed = Date.parse(value);
144
- if (Number.isNaN(parsed)) {
146
+ if (formatCode) {
147
+ throw this.createInvalidDateError(value, `CII format ${formatCode}`);
148
+ }
149
+
150
+ const day = parseIsoDateTimeDay(value);
151
+ if (day === undefined) {
145
152
  throw this.createInvalidDateError(value, 'CII');
146
153
  }
147
- return parsed;
154
+ return day;
148
155
  }
149
156
 
150
157
  /**