@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
|
@@ -44,8 +44,46 @@ export const getVatExemption = (category: TVatCategoryCode, language: string | u
|
|
|
44
44
|
: undefined;
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
47
|
+
* Whether a VAT rate is a number not greater than zero; a rate that is no
|
|
48
|
+
* number is refused as such (`findInvalidItemAmounts`), not as BR-S-05
|
|
49
|
+
* @param rate The rate
|
|
50
|
+
*/
|
|
51
|
+
export const isRateNotAboveZero = (rate: unknown): boolean => typeof rate === 'number' && Number.isFinite(rate) && !(rate > 0);
|
|
52
|
+
|
|
53
|
+
/** A line written as standard rated (S) whose VAT rate is not greater than zero */
|
|
54
|
+
export interface IStandardRatedLineWithoutRate {
|
|
55
|
+
index: number;
|
|
56
|
+
vatPercentage: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The first line of a document that is not reverse charge whose VAT rate is not
|
|
61
|
+
* greater than zero. The encoders write every such line as standard rated (S),
|
|
62
|
+
* and EN 16931 BR-S-05 asks a standard rated line for a rate greater than zero;
|
|
63
|
+
* the category the line has (zero rated Z, exempt E, intra-community supply K,
|
|
64
|
+
* export G, not subject to VAT O) cannot be stated until an item carries a VAT
|
|
65
|
+
* category of its own.
|
|
66
|
+
* @param items The document's items
|
|
67
|
+
*/
|
|
68
|
+
export const findStandardRatedLineWithoutRate = (
|
|
69
|
+
items: TAccountingDoc['items'] | undefined,
|
|
70
|
+
): IStandardRatedLineWithoutRate | undefined => {
|
|
71
|
+
const index = (items ?? []).findIndex((item) => isRateNotAboveZero(item.vatPercentage));
|
|
72
|
+
return index < 0 ? undefined : { index, vatPercentage: items![index].vatPercentage };
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The refusal or finding for such a line
|
|
77
|
+
* @param line The line
|
|
78
|
+
*/
|
|
79
|
+
export const standardRatedLineWithoutRateMessage = (line: IStandardRatedLineWithoutRate): string =>
|
|
80
|
+
`In an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "Standard rated" the Invoiced item VAT rate (BT-152) shall be greater than zero; items[${line.index}].vatPercentage is ${String(line.vatPercentage)}. A line outside reverse charge is written as standard rated (S); its category, zero rated, exempt, intra-community supply or export (Z, E, K, G or O), cannot be stated until an item carries a VAT category of its own`;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Refuses a document whose lines cannot be written with a valid VAT category,
|
|
84
|
+
* naming the rule: a line outside reverse charge whose rate is not greater than
|
|
85
|
+
* zero (BR-S-05), and a reverse charge document that lacks what EN 16931
|
|
86
|
+
* requires of one; no value is put in place of a missing one.
|
|
49
87
|
* - BR-AE-05: every line of a reverse charge document has the VAT rate 0; the
|
|
50
88
|
* recipient owes the tax, and the invoice states none (§ 14a Abs. 5 Satz 2
|
|
51
89
|
* UStG; a stated amount would be owed under § 14c Abs. 1 UStG).
|
|
@@ -66,12 +104,16 @@ export const getVatExemption = (category: TVatCategoryCode, language: string | u
|
|
|
66
104
|
* @param targetFormat The format being written
|
|
67
105
|
*/
|
|
68
106
|
export const assertVatCategoryWritable = (accountingDoc: TAccountingDoc, targetFormat: string): void => {
|
|
69
|
-
if (!accountingDoc.reverseCharge) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
107
|
const refuse = (rule: string, message: string): never => {
|
|
73
108
|
throw new EInvoiceFormatError(`${rule}: ${message}`, { targetFormat, unsupportedFeatures: [rule] });
|
|
74
109
|
};
|
|
110
|
+
if (!accountingDoc.reverseCharge) {
|
|
111
|
+
const line = findStandardRatedLineWithoutRate(accountingDoc.items);
|
|
112
|
+
if (line) {
|
|
113
|
+
refuse('BR-S-05', standardRatedLineWithoutRateMessage(line));
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
75
117
|
for (const [index, item] of (accountingDoc.items ?? []).entries()) {
|
|
76
118
|
if (item.vatPercentage !== 0) {
|
|
77
119
|
refuse('BR-AE-05', `a reverse charge line has the VAT rate 0, items[${index}].vatPercentage is ${String(item.vatPercentage)}`);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The code of the package's own check of the German VAT identification number format. It is no
|
|
3
|
+
* rule of EN 16931 or XRechnung; the official rules do not check the format of BT-31.
|
|
4
|
+
*/
|
|
5
|
+
export const GERMAN_VAT_ID_FORMAT_CODE = 'EINVOICE-DE-VATID-FORMAT';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The message of a German VAT identification number whose format is not the one the
|
|
9
|
+
* Bundeszentralamt für Steuern issues (§ 27a Abs. 1 UStG names it as the issuer, not the format)
|
|
10
|
+
* @param vatId The VAT identification number as stated
|
|
11
|
+
*/
|
|
12
|
+
export const germanVatIdFormatMessage = (vatId: string): string =>
|
|
13
|
+
`The German VAT identification number (BT-31) is DE followed by nine digits, the format the Bundeszentralamt für Steuern issues; it is "${vatId}"`;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Whether a VAT identification number with the German prefix DE lacks the format the
|
|
17
|
+
* Bundeszentralamt für Steuern issues, DE followed by nine digits. A number with another prefix, or
|
|
18
|
+
* none, is not checked. The number is taken as it is written, without removing spaces.
|
|
19
|
+
* @param vatId The VAT identification number as stated
|
|
20
|
+
*/
|
|
21
|
+
export const isMalformedGermanVatId = (vatId: string | undefined): boolean => {
|
|
22
|
+
const value = (vatId ?? '').trim();
|
|
23
|
+
return value.startsWith('DE') && !/^DE[0-9]{9}$/.test(value);
|
|
24
|
+
};
|
|
@@ -488,9 +488,9 @@ export class ConformanceTestHarness {
|
|
|
488
488
|
// VAT rules - Reverse charge
|
|
489
489
|
'BR-AE-01', 'BR-AE-02', 'BR-AE-03', 'BR-AE-04', 'BR-AE-05', 'BR-AE-06', 'BR-AE-07', 'BR-AE-08',
|
|
490
490
|
|
|
491
|
-
// VAT rules - Intra-community
|
|
492
|
-
'BR-
|
|
493
|
-
'BR-
|
|
491
|
+
// VAT rules - Intra-community supply
|
|
492
|
+
'BR-IC-01', 'BR-IC-02', 'BR-IC-03', 'BR-IC-04', 'BR-IC-05', 'BR-IC-06', 'BR-IC-07', 'BR-IC-08',
|
|
493
|
+
'BR-IC-09', 'BR-IC-10', 'BR-IC-11', 'BR-IC-12',
|
|
494
494
|
|
|
495
495
|
// VAT rules - Export
|
|
496
496
|
'BR-G-01', 'BR-G-02', 'BR-G-03', 'BR-G-04', 'BR-G-05', 'BR-G-06', 'BR-G-07', 'BR-G-08',
|
|
@@ -511,7 +511,7 @@ export class ConformanceTestHarness {
|
|
|
511
511
|
*/
|
|
512
512
|
private getRuleCategory(ruleId: string): keyof CoverageReport['byCategory'] | null {
|
|
513
513
|
if (ruleId.startsWith('BR-CO-')) return 'calculation';
|
|
514
|
-
if (ruleId.match(/^BR-
|
|
514
|
+
if (ruleId.match(/^BR-(S|Z|E|AE|IC|G|O)-/)) return 'vat';
|
|
515
515
|
if (ruleId.startsWith('BR-CL-')) return 'codeLists';
|
|
516
516
|
if (ruleId.match(/^BR-2[0-9]/) || ruleId.match(/^BR-3[0-9]/)) return 'lineLevel';
|
|
517
517
|
if (ruleId.match(/^BR-[0-9]/) || ruleId.match(/^BR-1[0-9]/)) return 'document';
|
|
@@ -548,7 +548,7 @@ export class ConformanceTestHarness {
|
|
|
548
548
|
'vat-category-S': ['BR-S-01', 'BR-S-02', 'BR-S-03', 'BR-S-04', 'BR-S-05'],
|
|
549
549
|
'vat-category-Z': ['BR-Z-01', 'BR-Z-02', 'BR-Z-03', 'BR-Z-04', 'BR-Z-05'],
|
|
550
550
|
'vat-category-AE': ['BR-AE-01', 'BR-AE-02', 'BR-AE-03', 'BR-AE-04', 'BR-AE-05', 'BR-AE-06'],
|
|
551
|
-
'vat-category-K': ['BR-
|
|
551
|
+
'vat-category-K': ['BR-IC-01', 'BR-IC-02', 'BR-IC-03', 'BR-IC-04', 'BR-IC-05', 'BR-IC-06'],
|
|
552
552
|
'vat-category-G': ['BR-G-01', 'BR-G-02', 'BR-G-03', 'BR-G-04', 'BR-G-05', 'BR-G-06'],
|
|
553
553
|
'vat-category-O': ['BR-O-01', 'BR-O-02', 'BR-O-03', 'BR-O-04', 'BR-O-05', 'BR-O-06']
|
|
554
554
|
};
|
|
@@ -247,32 +247,9 @@ export class EN16931BusinessRulesValidator {
|
|
|
247
247
|
);
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
// BR-CO-16
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
: invoice.metadata?.paidAmount || 0;
|
|
254
|
-
const expectedDueAmount = useDecimal
|
|
255
|
-
? (expectedGrossTotal as Decimal).subtract(paidAmount)
|
|
256
|
-
: (expectedGrossTotal as number) - (paidAmount as number);
|
|
257
|
-
const declaredDueAmount = useDecimal
|
|
258
|
-
? new Decimal(invoice.metadata?.amountDue || (useDecimal ? (expectedGrossTotal as Decimal).toNumber() : expectedGrossTotal))
|
|
259
|
-
: invoice.metadata?.amountDue || expectedGrossTotal;
|
|
260
|
-
|
|
261
|
-
const isDueEqual = useDecimal
|
|
262
|
-
? this.decimalCalculator!.areEqual(expectedDueAmount, declaredDueAmount)
|
|
263
|
-
: this.currencyCalculator
|
|
264
|
-
? this.currencyCalculator.areEqual(expectedDueAmount as number, declaredDueAmount as number)
|
|
265
|
-
: Math.abs((expectedDueAmount as number) - (declaredDueAmount as number)) < 0.01;
|
|
266
|
-
|
|
267
|
-
if (!isDueEqual) {
|
|
268
|
-
this.addError(
|
|
269
|
-
'BR-CO-16',
|
|
270
|
-
`Amount due (${useDecimal ? (declaredDueAmount as Decimal).toFixed(2) : (declaredDueAmount as number).toFixed(2)}) does not match calculation (${useDecimal ? (expectedDueAmount as Decimal).toFixed(2) : (expectedDueAmount as number).toFixed(2)})`,
|
|
271
|
-
'amountDue',
|
|
272
|
-
useDecimal ? (declaredDueAmount as Decimal).toNumber() : declaredDueAmount as number,
|
|
273
|
-
useDecimal ? (expectedDueAmount as Decimal).toNumber() : expectedDueAmount as number
|
|
274
|
-
);
|
|
275
|
-
}
|
|
250
|
+
// BR-CO-16 (amount due for payment = invoice total with VAT - paid amount) is not checked
|
|
251
|
+
// here: the envelope has no amount due (BT-115); the encoders derive it from the gross total
|
|
252
|
+
// and the paid amount (BT-113), and the official rules check it on the written XML
|
|
276
253
|
}
|
|
277
254
|
|
|
278
255
|
/**
|
|
@@ -79,7 +79,7 @@ export class FacturXValidator {
|
|
|
79
79
|
'dueDate', // BT-9: Payment due date
|
|
80
80
|
],
|
|
81
81
|
optional: [
|
|
82
|
-
'
|
|
82
|
+
'buyerReference', // BT-10: Buyer reference
|
|
83
83
|
'metadata.purchaseOrderReference', // BT-13: Purchase order reference
|
|
84
84
|
'metadata.salesOrderReference', // BT-14: Sales order reference
|
|
85
85
|
'metadata.contractReference', // BT-12: Contract reference
|
|
@@ -108,7 +108,7 @@ export class FacturXValidator {
|
|
|
108
108
|
// No items required
|
|
109
109
|
],
|
|
110
110
|
optional: [
|
|
111
|
-
'
|
|
111
|
+
'buyerReference',
|
|
112
112
|
'metadata.purchaseOrderReference',
|
|
113
113
|
'metadata.contractReference',
|
|
114
114
|
]
|
|
@@ -122,7 +122,7 @@ export class FacturXValidator {
|
|
|
122
122
|
'issueDate',
|
|
123
123
|
'accountingDocType',
|
|
124
124
|
'currency',
|
|
125
|
-
'
|
|
125
|
+
'buyerReference',
|
|
126
126
|
|
|
127
127
|
// Seller information
|
|
128
128
|
'from.name',
|
|
@@ -411,12 +411,12 @@ export class FacturXValidator {
|
|
|
411
411
|
|
|
412
412
|
case FacturXProfile.EN16931:
|
|
413
413
|
// EN16931 requires full compliance - additional checks handled by EN16931 validator
|
|
414
|
-
if (!invoice.
|
|
414
|
+
if (!invoice.buyerReference && !invoice.metadata?.extensions?.purchaseOrderReference) {
|
|
415
415
|
results.push({
|
|
416
416
|
ruleId: 'FX-EN-01',
|
|
417
417
|
severity: 'error',
|
|
418
418
|
message: 'EN16931 profile requires either buyer reference or purchase order reference',
|
|
419
|
-
field: '
|
|
419
|
+
field: 'buyerReference',
|
|
420
420
|
source: 'FACTURX'
|
|
421
421
|
});
|
|
422
422
|
}
|
|
@@ -12,6 +12,7 @@ import { CodeListValidator } from './codelist.validator.js';
|
|
|
12
12
|
import type { ValidationResult, ValidationOptions, ValidationReport } from './validation.types.js';
|
|
13
13
|
import type { EInvoice } from '../../einvoice.js';
|
|
14
14
|
import { UBL_CUSTOMIZATION_IDS } from '../ubl/ubl.types.js';
|
|
15
|
+
import { isPeppolBisBilling3 } from '../utils/peppol.profile.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Main validator that combines all validation capabilities
|
|
@@ -172,8 +173,7 @@ export class MainValidator {
|
|
|
172
173
|
return 'XRECHNUNG_3.0';
|
|
173
174
|
}
|
|
174
175
|
|
|
175
|
-
if (
|
|
176
|
-
profileId.includes('urn:fdc:peppol.eu')) {
|
|
176
|
+
if (isPeppolBisBilling3(customizationId)) {
|
|
177
177
|
return 'PEPPOL_BIS_3.0';
|
|
178
178
|
}
|
|
179
179
|
|
|
@@ -215,20 +215,7 @@ export class MainValidator {
|
|
|
215
215
|
* Check if invoice is PEPPOL
|
|
216
216
|
*/
|
|
217
217
|
private isPeppolInvoice(invoice: EInvoice): boolean {
|
|
218
|
-
|
|
219
|
-
const customizationId = invoice.metadata?.customizationId || '';
|
|
220
|
-
|
|
221
|
-
const peppolProfiles = [
|
|
222
|
-
'urn:fdc:peppol.eu:2017:poacc:billing:3.0',
|
|
223
|
-
'urn:fdc:peppol.eu:2017:poacc:billing:01:1.0',
|
|
224
|
-
'peppol-bis-3',
|
|
225
|
-
'peppol'
|
|
226
|
-
];
|
|
227
|
-
|
|
228
|
-
return peppolProfiles.some(profile =>
|
|
229
|
-
profileId.toLowerCase().includes(profile.toLowerCase()) ||
|
|
230
|
-
customizationId.toLowerCase().includes(profile.toLowerCase())
|
|
231
|
-
);
|
|
218
|
+
return isPeppolBisBilling3(invoice.metadata?.customizationId);
|
|
232
219
|
}
|
|
233
220
|
|
|
234
221
|
/**
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { ValidationResult } from './validation.types.js';
|
|
7
7
|
import type { EInvoice } from '../../einvoice.js';
|
|
8
|
+
import { isPeppolBisBilling3 } from '../utils/peppol.profile.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* PEPPOL BIS 3.0 Validator
|
|
@@ -50,20 +51,8 @@ export class PeppolValidator {
|
|
|
50
51
|
* Check if invoice is PEPPOL
|
|
51
52
|
*/
|
|
52
53
|
private isPeppolInvoice(invoice: EInvoice): boolean {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const peppolProfiles = [
|
|
57
|
-
'urn:fdc:peppol.eu:2017:poacc:billing:3.0',
|
|
58
|
-
'urn:fdc:peppol.eu:2017:poacc:billing:01:1.0',
|
|
59
|
-
'peppol-bis-3',
|
|
60
|
-
'peppol'
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
return peppolProfiles.some(profile =>
|
|
64
|
-
profileId.toLowerCase().includes(profile.toLowerCase()) ||
|
|
65
|
-
customizationId.toLowerCase().includes(profile.toLowerCase())
|
|
66
|
-
);
|
|
54
|
+
// the PEPPOL BIS Billing 3.0 specification identifier (BT-24), not a derivative that names PEPPOL
|
|
55
|
+
return isPeppolBisBilling3(invoice.metadata?.customizationId);
|
|
67
56
|
}
|
|
68
57
|
|
|
69
58
|
/**
|
|
@@ -393,12 +382,12 @@ export class PeppolValidator {
|
|
|
393
382
|
|
|
394
383
|
// PEPPOL-B-01: Invoice must have a buyer reference or purchase order reference
|
|
395
384
|
const purchaseOrderRef = invoice.metadata?.extensions?.purchaseOrderReference;
|
|
396
|
-
if (!invoice.
|
|
385
|
+
if (!invoice.buyerReference && !purchaseOrderRef) {
|
|
397
386
|
results.push({
|
|
398
387
|
ruleId: 'PEPPOL-B-01',
|
|
399
388
|
severity: 'error',
|
|
400
389
|
message: 'Invoice must have either a buyer reference (BT-10) or purchase order reference (BT-13)',
|
|
401
|
-
field: '
|
|
390
|
+
field: 'buyerReference',
|
|
402
391
|
source: 'PEPPOL'
|
|
403
392
|
});
|
|
404
393
|
}
|
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
* Enhanced validation types for EN16931 compliance
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The source of a check of the package's own, one no official rule set states
|
|
7
|
+
* (such as `EINVOICE-DE-VATID-FORMAT`), so it is not attributed to a standard
|
|
8
|
+
*/
|
|
9
|
+
export const EINVOICE_RULE_SOURCE = 'EINVOICE';
|
|
10
|
+
|
|
5
11
|
export interface ValidationResult {
|
|
6
12
|
// Core identification
|
|
7
13
|
ruleId: string; // e.g., "BR-CO-14"
|
|
8
|
-
source: string; // e.g., "EN16931", "PEPPOL", "XRECHNUNG"
|
|
14
|
+
source: string; // e.g., "EN16931", "PEPPOL", "XRECHNUNG"; EINVOICE_RULE_SOURCE for a check of the package's own
|
|
9
15
|
severity: 'error' | 'warning' | 'info';
|
|
10
16
|
message: string;
|
|
11
17
|
|