@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.
- 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/base/base.decoder.d.ts +9 -1
- package/dist_ts/formats/base/base.decoder.js +24 -12
- 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/date.value.d.ts +24 -0
- package/dist_ts/formats/utils/date.value.js +77 -12
- 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 +5 -5
- package/readme.md +61 -20
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/einvoice.ts +8 -14
- package/ts/formats/base/base.decoder.ts +24 -17
- 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/date.value.ts +84 -11
- 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
|
@@ -4,7 +4,14 @@ import { DecimalCurrencyCalculator } from './currency.calculator.decimal.js';
|
|
|
4
4
|
import { toPlainDecimalString } from './number.text.js';
|
|
5
5
|
import { EInvoiceFormatError } from '../../errors.js';
|
|
6
6
|
import type { ValidationResult } from '../validation/validation.types.js';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
getDefaultVatExemption,
|
|
9
|
+
getItemVatCategory,
|
|
10
|
+
getItemVatExemption,
|
|
11
|
+
mergeVatExemption,
|
|
12
|
+
type IVatExemption,
|
|
13
|
+
type TVatCategoryCode,
|
|
14
|
+
} from './vat.category.js';
|
|
8
15
|
|
|
9
16
|
/** An item amount that is no finite number, so no total can be computed from it */
|
|
10
17
|
export interface IInvalidItemAmount {
|
|
@@ -49,6 +56,14 @@ export const getTotalsSkippedResult = (invalid: IInvalidItemAmount[], source: st
|
|
|
49
56
|
field: invalid.map((entry) => `items[${entry.index}].${entry.field}`).join(', '),
|
|
50
57
|
});
|
|
51
58
|
|
|
59
|
+
/**
|
|
60
|
+
* The VAT categories whose tax amount (BT-117) is the taxable amount × rate:
|
|
61
|
+
* standard rated (BR-S-09), IGIC (BR-AF-09) and IPSI (BR-AG-09). Every other
|
|
62
|
+
* category states the tax 0 (BR-Z-09, BR-E-09, BR-AE-09, BR-IC-09, BR-G-09,
|
|
63
|
+
* BR-O-09).
|
|
64
|
+
*/
|
|
65
|
+
const TAXED_CATEGORIES: readonly TVatCategoryCode[] = ['S', 'L', 'M'];
|
|
66
|
+
|
|
52
67
|
/** The most decimals an amount may have in EN 16931 (BR-DEC-09 to BR-DEC-23) */
|
|
53
68
|
export const EN16931_MAX_AMOUNT_DECIMALS = 2;
|
|
54
69
|
|
|
@@ -65,8 +80,8 @@ export const getDocumentCalculator = (currency: TAccountingDoc['currency']): Dec
|
|
|
65
80
|
/**
|
|
66
81
|
* The VAT breakdown of one VAT category and rate (BG-23): the category
|
|
67
82
|
* (BT-118), the rate (BT-119), the taxable amount (BT-116), the tax on it
|
|
68
|
-
* (BT-117) and
|
|
69
|
-
* (
|
|
83
|
+
* (BT-117) and the exemption reason (BT-121, BT-120): the one its items state,
|
|
84
|
+
* else the one its category states by default (`getDefaultVatExemption`).
|
|
70
85
|
*/
|
|
71
86
|
export interface IDocumentVatGroup {
|
|
72
87
|
category: TVatCategoryCode;
|
|
@@ -86,7 +101,7 @@ export interface IDocumentTotals {
|
|
|
86
101
|
minorUnits: number;
|
|
87
102
|
/** the Invoice line net amount (BT-131) of each item, in item order: quantity × net price, rounded */
|
|
88
103
|
lineNetAmounts: Decimal[];
|
|
89
|
-
/** the VAT category (BT-151) of each item, in item order */
|
|
104
|
+
/** the VAT category (BT-151) of each item, in item order: the one it states, else AE or S (`getItemVatCategory`) */
|
|
90
105
|
lineVatCategories: TVatCategoryCode[];
|
|
91
106
|
/** Sum of Invoice line net amount (BT-106) */
|
|
92
107
|
lineTotal: Decimal;
|
|
@@ -107,7 +122,8 @@ export interface IDocumentTotals {
|
|
|
107
122
|
* rounded; the sum of line net amounts (BT-106) is their sum (BR-CO-10);
|
|
108
123
|
* each VAT category taxable amount (BT-116) is the sum of the line net amounts
|
|
109
124
|
* of that category and rate, and its tax amount (BT-117) is BT-116 × rate / 100
|
|
110
|
-
* rounded (BR-CO-17), 0 for
|
|
125
|
+
* rounded (BR-CO-17), 0 for the categories whose rate is 0 or that state no tax
|
|
126
|
+
* (BR-Z-09, BR-E-09, BR-AE-09, BR-IC-09, BR-G-09, BR-O-09); the total VAT (BT-110) is the sum of the category tax amounts
|
|
111
127
|
* (BR-CO-14); the total with VAT (BT-112) is BT-109 + BT-110 (BR-CO-15).
|
|
112
128
|
* @param accountingDoc The document
|
|
113
129
|
*/
|
|
@@ -116,10 +132,12 @@ export const computeDocumentTotals = (
|
|
|
116
132
|
): IDocumentTotals => {
|
|
117
133
|
const calculator = getDocumentCalculator(accountingDoc.currency);
|
|
118
134
|
const minorUnits = calculator.getCurrencyInfo().minorUnits;
|
|
119
|
-
const category = getVatCategory(accountingDoc);
|
|
120
135
|
const lineNetAmounts: Decimal[] = [];
|
|
121
136
|
const lineVatCategories: TVatCategoryCode[] = [];
|
|
122
|
-
const taxableByGroup = new Map<
|
|
137
|
+
const taxableByGroup = new Map<
|
|
138
|
+
string,
|
|
139
|
+
{ category: TVatCategoryCode; rate: number; taxable: Decimal; exemptions: IVatExemption[] }
|
|
140
|
+
>();
|
|
123
141
|
// an amount that is no number cannot be computed with; it is refused, not treated as 0
|
|
124
142
|
const [firstInvalid] = findInvalidItemAmounts(accountingDoc.items);
|
|
125
143
|
if (firstInvalid) {
|
|
@@ -136,21 +154,30 @@ export const computeDocumentTotals = (
|
|
|
136
154
|
toPlainDecimalString(item.unitNetPrice),
|
|
137
155
|
);
|
|
138
156
|
lineNetAmounts.push(lineNet);
|
|
157
|
+
const category = getItemVatCategory(accountingDoc, item);
|
|
139
158
|
lineVatCategories.push(category);
|
|
140
159
|
// grouped by VAT category and rate: the same rate under two categories is two groups
|
|
141
160
|
const key = `${category}|${item.vatPercentage}`;
|
|
142
|
-
const group = taxableByGroup.get(key) ?? { category, rate: item.vatPercentage, taxable: Decimal.ZERO };
|
|
161
|
+
const group = taxableByGroup.get(key) ?? { category, rate: item.vatPercentage, taxable: Decimal.ZERO, exemptions: [] };
|
|
143
162
|
group.taxable = group.taxable.add(lineNet);
|
|
163
|
+
const exemption = getItemVatExemption(item);
|
|
164
|
+
if (exemption) {
|
|
165
|
+
group.exemptions.push(exemption);
|
|
166
|
+
}
|
|
144
167
|
taxableByGroup.set(key, group);
|
|
145
168
|
}
|
|
146
169
|
const vatGroups: IDocumentVatGroup[] = [...taxableByGroup.values()].map((group) => {
|
|
147
|
-
|
|
170
|
+
// the reason the items state; items of one breakdown that state different ones are refused
|
|
171
|
+
// on export and reported by the validators (findVatCategoryViolations)
|
|
172
|
+
const exemption = mergeVatExemption(group.exemptions[0], getDefaultVatExemption(group.category, accountingDoc.language));
|
|
148
173
|
return {
|
|
149
174
|
category: group.category,
|
|
150
175
|
rate: group.rate,
|
|
151
176
|
taxableAmount: calculator.round(group.taxable),
|
|
152
|
-
// reverse charge states
|
|
153
|
-
taxAmount:
|
|
177
|
+
// only a taxed category states tax; reverse charge states none, the recipient owes it (BR-AE-09)
|
|
178
|
+
taxAmount: TAXED_CATEGORIES.includes(group.category)
|
|
179
|
+
? calculator.calculateVAT(group.taxable, group.rate)
|
|
180
|
+
: calculator.round(Decimal.ZERO),
|
|
154
181
|
...(exemption ? { exemption } : {}),
|
|
155
182
|
};
|
|
156
183
|
});
|
|
@@ -485,8 +485,11 @@ export const findEnvelopeDifferences = (
|
|
|
485
485
|
}
|
|
486
486
|
});
|
|
487
487
|
values.vatBreakdown.forEach((group, index) => {
|
|
488
|
+
// a breakdown not subject to VAT (O) may state no rate; the envelope's has the rate 0
|
|
488
489
|
const computed = totals.vatGroups.find(
|
|
489
|
-
(candidate) =>
|
|
490
|
+
(candidate) =>
|
|
491
|
+
candidate.category === group.categoryCode &&
|
|
492
|
+
(group.rate === undefined ? group.categoryCode === 'O' : decimal(candidate.rate).equals(decimal(group.rate))),
|
|
490
493
|
);
|
|
491
494
|
if (!computed) {
|
|
492
495
|
differs('BG-23', `vatBreakdown[${index}]`, `${group.categoryCode ?? '?'} ${group.rate ?? ''}`.trim(), 'no breakdown of that category and rate');
|