@fin.cx/einvoice 10.0.1 → 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.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/einvoice.d.ts +2 -0
- package/dist_ts/einvoice.js +7 -13
- package/dist_ts/formats/cii/cii.decoder.d.ts +11 -3
- package/dist_ts/formats/cii/cii.decoder.js +27 -8
- package/dist_ts/formats/cii/cii.encoder.d.ts +12 -0
- package/dist_ts/formats/cii/cii.encoder.js +36 -1
- package/dist_ts/formats/cii/cii.types.js +2 -1
- package/dist_ts/formats/cii/facturx/facturx.encoder.js +12 -8
- package/dist_ts/formats/cii/zugferd/zugferd.encoder.js +12 -8
- package/dist_ts/formats/semantic/semantic.adapter.d.ts +0 -4
- package/dist_ts/formats/semantic/semantic.adapter.js +7 -18
- package/dist_ts/formats/ubl/generic/ubl.encoder.d.ts +6 -1
- package/dist_ts/formats/ubl/generic/ubl.encoder.js +40 -8
- package/dist_ts/formats/ubl/ubl.decoder.d.ts +11 -0
- package/dist_ts/formats/ubl/ubl.decoder.js +30 -1
- package/dist_ts/formats/ubl/xrechnung/xrechnung.decoder.js +3 -3
- package/dist_ts/formats/utils/delivery.address.d.ts +18 -0
- package/dist_ts/formats/utils/delivery.address.js +30 -0
- package/dist_ts/formats/utils/document.totals.d.ts +5 -4
- package/dist_ts/formats/utils/document.totals.js +24 -8
- package/dist_ts/formats/utils/stated.values.js +4 -2
- package/dist_ts/formats/utils/vat.category.d.ts +146 -55
- package/dist_ts/formats/utils/vat.category.js +357 -69
- package/dist_ts/formats/validation/vat-categories.validator.d.ts +14 -15
- package/dist_ts/formats/validation/vat-categories.validator.js +123 -85
- package/package.json +3 -3
- package/readme.md +55 -19
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/einvoice.ts +8 -14
- package/ts/formats/cii/cii.decoder.ts +28 -7
- package/ts/formats/cii/cii.encoder.ts +36 -0
- package/ts/formats/cii/cii.types.ts +1 -0
- package/ts/formats/cii/facturx/facturx.encoder.ts +12 -7
- package/ts/formats/cii/zugferd/zugferd.encoder.ts +12 -7
- package/ts/formats/semantic/semantic.adapter.ts +6 -15
- package/ts/formats/ubl/generic/ubl.encoder.ts +40 -7
- package/ts/formats/ubl/ubl.decoder.ts +31 -0
- package/ts/formats/ubl/xrechnung/xrechnung.decoder.ts +2 -2
- package/ts/formats/utils/delivery.address.ts +36 -0
- package/ts/formats/utils/document.totals.ts +38 -11
- package/ts/formats/utils/stated.values.ts +4 -1
- package/ts/formats/utils/vat.category.ts +426 -95
- package/ts/formats/validation/vat-categories.validator.ts +169 -88
|
@@ -9,7 +9,12 @@ import type { TAccountingDocItem } from '@tsclass/tsclass/dist_ts/finance/index.
|
|
|
9
9
|
import type { EInvoice } from '../../einvoice.js';
|
|
10
10
|
import type { DecimalCurrencyCalculator } from '../utils/currency.calculator.decimal.js';
|
|
11
11
|
import { Decimal } from '../utils/decimal.js';
|
|
12
|
-
import
|
|
12
|
+
import {
|
|
13
|
+
findVatCategoryViolations,
|
|
14
|
+
VAT_EXEMPTION_DIFFERS,
|
|
15
|
+
type IVatCategoryViolation,
|
|
16
|
+
type TVatCategoryCode,
|
|
17
|
+
} from '../utils/vat.category.js';
|
|
13
18
|
import type { ValidationResult } from './validation.types.js';
|
|
14
19
|
|
|
15
20
|
/**
|
|
@@ -40,9 +45,11 @@ export interface VATBreakdown {
|
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
/**
|
|
43
|
-
* The EN 16931 rules this validator checks, with their official text (CEN/TC 434
|
|
44
|
-
*
|
|
45
|
-
*
|
|
48
|
+
* The EN 16931 rules this validator checks, with their official text (CEN/TC 434 EN 16931
|
|
49
|
+
* validation artefacts 1.3.16, the UBL model; the CII model differs only in the case of
|
|
50
|
+
* "Breakdown" in BR-AE-10 and BR-S-10 and "VAT breakdown" in BR-AE-01), BR-CL-22 with the
|
|
51
|
+
* text of the code list rules, and the package's own check that the items of one breakdown
|
|
52
|
+
* state one exemption reason
|
|
46
53
|
*/
|
|
47
54
|
const VAT_CATEGORY_RULES = {
|
|
48
55
|
'BR-S-01':
|
|
@@ -55,6 +62,26 @@ const VAT_CATEGORY_RULES = {
|
|
|
55
62
|
'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).',
|
|
56
63
|
'BR-S-10':
|
|
57
64
|
'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).',
|
|
65
|
+
'BR-Z-02':
|
|
66
|
+
'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).',
|
|
67
|
+
'BR-Z-05':
|
|
68
|
+
'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).',
|
|
69
|
+
'BR-Z-08':
|
|
70
|
+
'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".',
|
|
71
|
+
'BR-Z-09':
|
|
72
|
+
'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).',
|
|
73
|
+
'BR-Z-10':
|
|
74
|
+
'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).',
|
|
75
|
+
'BR-E-02':
|
|
76
|
+
'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).',
|
|
77
|
+
'BR-E-05':
|
|
78
|
+
'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).',
|
|
79
|
+
'BR-E-08':
|
|
80
|
+
'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".',
|
|
81
|
+
'BR-E-09':
|
|
82
|
+
'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).',
|
|
83
|
+
'BR-E-10':
|
|
84
|
+
'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).',
|
|
58
85
|
'BR-AE-01':
|
|
59
86
|
'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".',
|
|
60
87
|
'BR-AE-02':
|
|
@@ -67,8 +94,67 @@ const VAT_CATEGORY_RULES = {
|
|
|
67
94
|
'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).',
|
|
68
95
|
'BR-AE-10':
|
|
69
96
|
'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).',
|
|
97
|
+
'BR-IC-02':
|
|
98
|
+
'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).',
|
|
99
|
+
'BR-IC-05':
|
|
100
|
+
'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).',
|
|
101
|
+
'BR-IC-08':
|
|
102
|
+
'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".',
|
|
103
|
+
'BR-IC-09':
|
|
104
|
+
'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).',
|
|
105
|
+
'BR-IC-10':
|
|
106
|
+
'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).',
|
|
107
|
+
'BR-IC-11':
|
|
108
|
+
'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.',
|
|
109
|
+
'BR-IC-12':
|
|
110
|
+
'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.',
|
|
111
|
+
'BR-G-02':
|
|
112
|
+
'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).',
|
|
113
|
+
'BR-G-05':
|
|
114
|
+
'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).',
|
|
115
|
+
'BR-G-08':
|
|
116
|
+
'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".',
|
|
117
|
+
'BR-G-09':
|
|
118
|
+
'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).',
|
|
119
|
+
'BR-G-10':
|
|
120
|
+
'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).',
|
|
121
|
+
'BR-O-02':
|
|
122
|
+
'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).',
|
|
123
|
+
'BR-O-05':
|
|
124
|
+
'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).',
|
|
125
|
+
'BR-O-08':
|
|
126
|
+
'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".',
|
|
127
|
+
'BR-O-09':
|
|
128
|
+
'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).',
|
|
129
|
+
'BR-O-10':
|
|
130
|
+
'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).',
|
|
131
|
+
'BR-O-11':
|
|
132
|
+
'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).',
|
|
133
|
+
'BR-AF-02':
|
|
134
|
+
'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).',
|
|
135
|
+
'BR-AF-05':
|
|
136
|
+
'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.',
|
|
137
|
+
'BR-AF-08':
|
|
138
|
+
'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).',
|
|
139
|
+
'BR-AF-09':
|
|
140
|
+
'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).',
|
|
141
|
+
'BR-AF-10':
|
|
142
|
+
'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).',
|
|
143
|
+
'BR-AG-02':
|
|
144
|
+
'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).',
|
|
145
|
+
'BR-AG-05':
|
|
146
|
+
'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.',
|
|
147
|
+
'BR-AG-08':
|
|
148
|
+
'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).',
|
|
149
|
+
'BR-AG-09':
|
|
150
|
+
'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).',
|
|
151
|
+
'BR-AG-10':
|
|
152
|
+
'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).',
|
|
70
153
|
'BR-CO-17':
|
|
71
154
|
'VAT category tax amount (BT-117) = VAT category taxable amount (BT-116) x (VAT category rate (BT-119) / 100), rounded to two decimals.',
|
|
155
|
+
'BR-CL-22': 'Tax exemption reason code identifier scheme identifier MUST belong to the CEF VATEX code list',
|
|
156
|
+
[VAT_EXEMPTION_DIFFERS]:
|
|
157
|
+
'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.',
|
|
72
158
|
} as const;
|
|
73
159
|
|
|
74
160
|
type TVatCategoryRuleId = keyof typeof VAT_CATEGORY_RULES;
|
|
@@ -83,20 +169,66 @@ interface IVatCategoryFinding {
|
|
|
83
169
|
remediation?: string;
|
|
84
170
|
}
|
|
85
171
|
|
|
172
|
+
/** The rule IDs of the taxable amount (-08) and the tax amount (-09) of a VAT breakdown, per category */
|
|
173
|
+
const BREAKDOWN_RULES: Record<TVatCategoryCode, { taxable: TVatCategoryRuleId; tax: TVatCategoryRuleId }> = {
|
|
174
|
+
S: { taxable: 'BR-S-08', tax: 'BR-S-09' },
|
|
175
|
+
Z: { taxable: 'BR-Z-08', tax: 'BR-Z-09' },
|
|
176
|
+
E: { taxable: 'BR-E-08', tax: 'BR-E-09' },
|
|
177
|
+
AE: { taxable: 'BR-AE-08', tax: 'BR-AE-09' },
|
|
178
|
+
K: { taxable: 'BR-IC-08', tax: 'BR-IC-09' },
|
|
179
|
+
G: { taxable: 'BR-G-08', tax: 'BR-G-09' },
|
|
180
|
+
O: { taxable: 'BR-O-08', tax: 'BR-O-09' },
|
|
181
|
+
L: { taxable: 'BR-AF-08', tax: 'BR-AF-09' },
|
|
182
|
+
M: { taxable: 'BR-AG-08', tax: 'BR-AG-09' },
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/** The categories whose tax amount is the taxable amount × rate (BR-S-09, BR-AF-09, BR-AG-09); the others state 0 */
|
|
186
|
+
const TAXED_CATEGORIES: readonly TVatCategoryCode[] = ['S', 'L', 'M'];
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* The business terms and groups a finding of a rule on the items' VAT categories points at
|
|
190
|
+
* @param violation The violation
|
|
191
|
+
*/
|
|
192
|
+
const referencesOf = (violation: IVatCategoryViolation): { btReference: string; bgReference?: string } => {
|
|
193
|
+
const suffix = violation.ruleId.split('-').pop();
|
|
194
|
+
if (violation.ruleId === 'BR-CL-22') {
|
|
195
|
+
return { btReference: 'BT-121', bgReference: 'BG-23' };
|
|
196
|
+
}
|
|
197
|
+
if (violation.ruleId === VAT_EXEMPTION_DIFFERS) {
|
|
198
|
+
return { btReference: 'BT-120', bgReference: 'BG-23' };
|
|
199
|
+
}
|
|
200
|
+
if (violation.ruleId === 'BR-IC-11') {
|
|
201
|
+
return { btReference: 'BT-72', bgReference: 'BG-13' };
|
|
202
|
+
}
|
|
203
|
+
if (violation.ruleId === 'BR-IC-12') {
|
|
204
|
+
return { btReference: 'BT-80', bgReference: 'BG-15' };
|
|
205
|
+
}
|
|
206
|
+
if (violation.ruleId === 'BR-O-11') {
|
|
207
|
+
return { btReference: 'BT-151', bgReference: 'BG-25' };
|
|
208
|
+
}
|
|
209
|
+
switch (suffix) {
|
|
210
|
+
case '02':
|
|
211
|
+
return violation.field.startsWith('to.') ? { btReference: 'BT-48', bgReference: 'BG-7' } : { btReference: 'BT-31', bgReference: 'BG-4' };
|
|
212
|
+
case '05':
|
|
213
|
+
return { btReference: 'BT-152', bgReference: 'BG-25' };
|
|
214
|
+
default:
|
|
215
|
+
return { btReference: 'BT-121', bgReference: 'BG-23' };
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
86
219
|
/**
|
|
87
220
|
* The EN 16931 VAT category rules of a document, checked on the VAT categories
|
|
88
221
|
* and the VAT breakdown the encoders write.
|
|
89
222
|
*
|
|
90
|
-
* The category of every line is the one the encoders write
|
|
91
|
-
*
|
|
92
|
-
* (
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* and rate. The envelope has no document level allowances or charges, so the
|
|
223
|
+
* The category of every line is the one the encoders write
|
|
224
|
+
* (`getItemVatCategory`): the one the item states (`vatCategory`, BT-151),
|
|
225
|
+
* else reverse charge (AE) when the document states `reverseCharge` and
|
|
226
|
+
* standard rated (S) otherwise. A decoded line carries the category its
|
|
227
|
+
* document states. The rules on the items, their exemption reasons and the
|
|
228
|
+
* parties' identifiers are those export refuses a document for
|
|
229
|
+
* (`findVatCategoryViolations`). The VAT breakdown is the one the encoders
|
|
230
|
+
* write (`computeDocumentTotals().vatGroups`): one group per category and
|
|
231
|
+
* rate. The envelope has no document level allowances or charges, so the
|
|
100
232
|
* rules on them (BR-S-02 to -04 and -06 to -07 and their counterparts) do not
|
|
101
233
|
* apply, and a taxable amount is the sum of the line net amounts.
|
|
102
234
|
*/
|
|
@@ -121,42 +253,23 @@ export class VATCategoriesValidator {
|
|
|
121
253
|
const calculator = getDocumentCalculator(invoice.currency);
|
|
122
254
|
const items = invoice.items ?? [];
|
|
123
255
|
|
|
124
|
-
|
|
256
|
+
for (const violation of findVatCategoryViolations(invoice)) {
|
|
257
|
+
this.addError(violation.ruleId as TVatCategoryRuleId, {
|
|
258
|
+
field: violation.field,
|
|
259
|
+
...referencesOf(violation),
|
|
260
|
+
remediation: violation.message,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
this.validateBreakdownPresence(totals);
|
|
125
264
|
this.validateBreakdown(items, totals, calculator);
|
|
126
265
|
return this.results;
|
|
127
266
|
}
|
|
128
267
|
|
|
129
268
|
/**
|
|
130
|
-
* The
|
|
131
|
-
*
|
|
269
|
+
* The breakdown a category of the lines needs: at least one for S (BR-S-01), exactly one for
|
|
270
|
+
* reverse charge (BR-AE-01)
|
|
132
271
|
*/
|
|
133
|
-
private
|
|
134
|
-
items.forEach((item, index) => {
|
|
135
|
-
const category = totals.lineVatCategories[index];
|
|
136
|
-
// A decoded line keeps the category its document states (BT-151). One that is not S is a
|
|
137
|
-
// category the envelope cannot express (Z, E, K, G, O, or AE in a document that mixes S and
|
|
138
|
-
// AE lines, which the decoders do not read as reverse charge); its rate says nothing of BR-S-05.
|
|
139
|
-
const statedCategory = invoice.statedValues?.lines[index]?.vatCategoryCode;
|
|
140
|
-
if (category === 'S' && !(item.vatPercentage > 0) && (statedCategory === undefined || statedCategory === 'S')) {
|
|
141
|
-
this.addError('BR-S-05', {
|
|
142
|
-
field: `items[${index}].vatPercentage`,
|
|
143
|
-
btReference: 'BT-152',
|
|
144
|
-
bgReference: 'BG-25',
|
|
145
|
-
value: item.vatPercentage,
|
|
146
|
-
expected: '> 0',
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
if (category === 'AE' && item.vatPercentage !== 0) {
|
|
150
|
-
this.addError('BR-AE-05', {
|
|
151
|
-
field: `items[${index}].vatPercentage`,
|
|
152
|
-
btReference: 'BT-152',
|
|
153
|
-
bgReference: 'BG-25',
|
|
154
|
-
value: item.vatPercentage,
|
|
155
|
-
expected: 0,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
|
|
272
|
+
private validateBreakdownPresence(totals: IDocumentTotals): void {
|
|
160
273
|
const hasCategory = (category: TVatCategoryCode) => totals.lineVatCategories.includes(category);
|
|
161
274
|
const groupsOf = (category: TVatCategoryCode) => totals.vatGroups.filter((group) => group.category === category);
|
|
162
275
|
if (hasCategory('S') && groupsOf('S').length === 0) {
|
|
@@ -171,32 +284,11 @@ export class VATCategoriesValidator {
|
|
|
171
284
|
remediation: 'a reverse charge breakdown per rate: give every reverse charge line the VAT rate 0 (BR-AE-05)',
|
|
172
285
|
});
|
|
173
286
|
}
|
|
174
|
-
|
|
175
|
-
if (hasCategory('AE')) {
|
|
176
|
-
const seller = invoice.from?.registrationDetails;
|
|
177
|
-
if (!seller?.vatId?.trim() && !seller?.taxNumber?.trim()) {
|
|
178
|
-
this.addError('BR-AE-02', {
|
|
179
|
-
field: 'from.registrationDetails',
|
|
180
|
-
btReference: 'BT-31',
|
|
181
|
-
bgReference: 'BG-4',
|
|
182
|
-
remediation: 'state from.registrationDetails.vatId (BT-31) or .taxNumber (BT-32); the envelope cannot state a tax representative (BT-63)',
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
const buyer = invoice.to?.registrationDetails;
|
|
186
|
-
if (!buyer?.vatId?.trim() && !buyer?.registrationId?.trim()) {
|
|
187
|
-
this.addError('BR-AE-02', {
|
|
188
|
-
field: 'to.registrationDetails',
|
|
189
|
-
bgReference: 'BG-7',
|
|
190
|
-
btReference: 'BT-48',
|
|
191
|
-
remediation: 'state to.registrationDetails.vatId (BT-48) or .registrationId (BT-47)',
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
287
|
}
|
|
196
288
|
|
|
197
289
|
/**
|
|
198
|
-
* The rules on each VAT breakdown group: its taxable amount (BR-S-08
|
|
199
|
-
*
|
|
290
|
+
* The rules on each VAT breakdown group: its taxable amount (BR-S-08 and its counterparts of
|
|
291
|
+
* every category) and its tax amount (BR-S-09 and its counterparts, BR-CO-17)
|
|
200
292
|
*/
|
|
201
293
|
private validateBreakdown(items: TAccountingDocItem[], totals: IDocumentTotals, calculator: DecimalCurrencyCalculator): void {
|
|
202
294
|
for (const group of totals.vatGroups) {
|
|
@@ -206,29 +298,18 @@ export class VATCategoriesValidator {
|
|
|
206
298
|
const expectedTaxable = calculator.round(Decimal.sum(lineNetAmounts));
|
|
207
299
|
const expectedTax = calculator.calculateVAT(group.taxableAmount, group.rate);
|
|
208
300
|
const amounts = (value: Decimal, expected: Decimal) => ({ value: value.toNumber(), expected: expected.toNumber() });
|
|
209
|
-
const at =
|
|
301
|
+
const at = `${group.category} rate ${group.rate}`;
|
|
302
|
+
const rules = BREAKDOWN_RULES[group.category];
|
|
210
303
|
|
|
211
|
-
if (group.
|
|
212
|
-
|
|
213
|
-
this.addError('BR-S-08', { field: `taxBreakdown[${at}].netAmount`, btReference: 'BT-116', ...amounts(group.taxableAmount, expectedTaxable) });
|
|
214
|
-
}
|
|
215
|
-
if (!group.taxAmount.equals(expectedTax)) {
|
|
216
|
-
this.addError('BR-S-09', { field: `taxBreakdown[${at}].taxAmount`, btReference: 'BT-117', ...amounts(group.taxAmount, expectedTax) });
|
|
217
|
-
}
|
|
218
|
-
if (group.exemption) {
|
|
219
|
-
this.addError('BR-S-10', { field: `taxBreakdown[${at}]`, btReference: 'BT-121', value: group.exemption.code });
|
|
220
|
-
}
|
|
304
|
+
if (!group.taxableAmount.equals(expectedTaxable)) {
|
|
305
|
+
this.addError(rules.taxable, { field: `taxBreakdown[${at}].netAmount`, btReference: 'BT-116', ...amounts(group.taxableAmount, expectedTaxable) });
|
|
221
306
|
}
|
|
222
|
-
if (group.category
|
|
223
|
-
if (!group.
|
|
224
|
-
this.addError(
|
|
225
|
-
}
|
|
226
|
-
if (!group.taxAmount.isZero()) {
|
|
227
|
-
this.addError('BR-AE-09', { field: `taxBreakdown[${at}].taxAmount`, btReference: 'BT-117', value: group.taxAmount.toNumber(), expected: 0 });
|
|
228
|
-
}
|
|
229
|
-
if (group.exemption?.code !== 'VATEX-EU-AE' && !group.exemption?.reason.trim()) {
|
|
230
|
-
this.addError('BR-AE-10', { field: `taxBreakdown[${at}]`, btReference: 'BT-121' });
|
|
307
|
+
if (TAXED_CATEGORIES.includes(group.category)) {
|
|
308
|
+
if (!group.taxAmount.equals(expectedTax)) {
|
|
309
|
+
this.addError(rules.tax, { field: `taxBreakdown[${at}].taxAmount`, btReference: 'BT-117', ...amounts(group.taxAmount, expectedTax) });
|
|
231
310
|
}
|
|
311
|
+
} else if (!group.taxAmount.isZero()) {
|
|
312
|
+
this.addError(rules.tax, { field: `taxBreakdown[${at}].taxAmount`, btReference: 'BT-117', value: group.taxAmount.toNumber(), expected: 0 });
|
|
232
313
|
}
|
|
233
314
|
if (!group.taxAmount.equals(expectedTax)) {
|
|
234
315
|
this.addError('BR-CO-17', { field: `taxBreakdown[${at}].taxAmount`, btReference: 'BT-117', ...amounts(group.taxAmount, expectedTax) });
|
|
@@ -239,7 +320,7 @@ export class VATCategoriesValidator {
|
|
|
239
320
|
private addError(ruleId: TVatCategoryRuleId, finding: IVatCategoryFinding): void {
|
|
240
321
|
this.results.push({
|
|
241
322
|
ruleId,
|
|
242
|
-
source: 'EN16931',
|
|
323
|
+
source: ruleId === VAT_EXEMPTION_DIFFERS ? 'EINVOICE' : 'EN16931',
|
|
243
324
|
severity: 'error',
|
|
244
325
|
message: VAT_CATEGORY_RULES[ruleId],
|
|
245
326
|
bgReference: 'BG-23',
|