@endevops/peppol-schema 0.1.2 → 0.1.4
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/README.md +5 -5
- package/dist/constants.d.ts +131 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +114 -1
- package/dist/constants.js.map +1 -1
- package/dist/{index-BBzPjq7o.d.ts → index-CILtZytZ.d.ts} +1710 -669
- package/dist/index-CILtZytZ.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1234 -187
- package/dist/index.js.map +1 -1
- package/dist/invoice-response-code-not-needs-schema-DNrfKYLh.js +36 -0
- package/dist/invoice-response-code-not-needs-schema-DNrfKYLh.js.map +1 -0
- package/dist/invoice-response-codes.d.ts +28 -0
- package/dist/invoice-response-codes.d.ts.map +1 -1
- package/dist/invoice-response-codes.js +11 -1
- package/dist/invoice-response-codes.js.map +1 -1
- package/dist/{is-valid-abn-160i38PG.js → is-valid-abn-D7eSygBG.js} +115 -2
- package/dist/is-valid-abn-D7eSygBG.js.map +1 -0
- package/dist/{is-valid-mod97-0208-CzMOXMO-.js → is-valid-mod97-0208-CjR324eG.js} +11 -1
- package/dist/is-valid-mod97-0208-CjR324eG.js.map +1 -0
- package/dist/message-level-response-profile-id-B9c0A7S_.js +27 -0
- package/dist/message-level-response-profile-id-B9c0A7S_.js.map +1 -0
- package/dist/{nodable-parser-CyDZtLCK.js → nodable-parser-CupS2H55.js} +31 -1
- package/dist/nodable-parser-CupS2H55.js.map +1 -0
- package/dist/schematron.d.ts +57 -1
- package/dist/schematron.d.ts.map +1 -1
- package/dist/schematron.js +453 -2
- package/dist/schematron.js.map +1 -1
- package/dist/validations.d.ts +132 -0
- package/dist/validations.d.ts.map +1 -1
- package/dist/validations.js +11 -2
- package/dist/validations.js.map +1 -1
- package/dist/values.d.ts +67 -2
- package/dist/values.d.ts.map +1 -1
- package/dist/xml.d.ts +30 -0
- package/dist/xml.d.ts.map +1 -1
- package/dist/xml.js +10 -1
- package/dist/xml.js.map +1 -1
- package/package.json +3 -2
- package/src/constants/credit-note-doctype-id.ts +9 -0
- package/src/constants/credit-note-process-id.ts +9 -0
- package/src/constants/document-scheme.ts +9 -0
- package/src/constants/invoice-doctype-id.ts +9 -0
- package/src/constants/invoice-process-id.ts +10 -0
- package/src/constants/invoice-response-doctype-id.ts +10 -0
- package/src/constants/invoice-response-process-id.ts +9 -0
- package/src/constants/invoice-response-profile-id.ts +9 -0
- package/src/constants/message-level-response-doctype-id.ts +10 -0
- package/src/constants/message-level-response-process-id.ts +9 -0
- package/src/constants/message-level-response-profile-id.ts +9 -0
- package/src/constants/participant-scheme.ts +10 -0
- package/src/constants/process-scheme.ts +9 -0
- package/src/constants/transport-profile.ts +10 -0
- package/src/invoice-response-codes/invoice-response-code-needs-schema.ts +9 -0
- package/src/invoice-response-codes/invoice-response-code-not-needs-schema.ts +9 -0
- package/src/invoice-response-codes/invoice-response-codes-order.ts +10 -0
- package/src/peppol-validations/add-piva.ts +12 -0
- package/src/peppol-validations/check-cf.ts +11 -0
- package/src/peppol-validations/check-cf16.ts +12 -0
- package/src/peppol-validations/check-piva-se-it.ts +11 -0
- package/src/peppol-validations/check-piva.ts +11 -0
- package/src/peppol-validations/check-se-orgnr.ts +9 -0
- package/src/peppol-validations/greek-tin-verification.ts +9 -0
- package/src/peppol-validations/is-valid-abn.ts +9 -0
- package/src/peppol-validations/is-valid-gln.ts +10 -0
- package/src/peppol-validations/is-valid-iban.ts +10 -0
- package/src/peppol-validations/is-valid-mod11.ts +9 -0
- package/src/peppol-validations/is-valid-mod97-0208.ts +10 -0
- package/src/peppol-validations/luhn-check.ts +9 -0
- package/src/schemas/fields/default-customization-id.ts +9 -0
- package/src/schemas/fields/default-profile-id.ts +9 -0
- package/src/schemas/fields/peppol-additional-document-reference-schema.ts +41 -2
- package/src/schemas/fields/peppol-address-schema.ts +25 -2
- package/src/schemas/fields/peppol-allowance-charge-schema.ts +65 -9
- package/src/schemas/fields/peppol-amount-schema.ts +15 -2
- package/src/schemas/fields/peppol-base-line-schema.ts +12 -2
- package/src/schemas/fields/peppol-billing-reference-schema.ts +13 -0
- package/src/schemas/fields/peppol-binary-object-schema.ts +12 -4
- package/src/schemas/fields/peppol-contact-schema.ts +11 -0
- package/src/schemas/fields/peppol-credit-note-line-schema.ts +7 -2
- package/src/schemas/fields/peppol-delivery-schema.ts +44 -2
- package/src/schemas/fields/peppol-identifier-schema.ts +8 -0
- package/src/schemas/fields/peppol-invoice-line-period-schema.ts +11 -0
- package/src/schemas/fields/peppol-invoice-line-schema.ts +9 -2
- package/src/schemas/fields/peppol-invoice-period-schema.ts +2 -2
- package/src/schemas/fields/peppol-item-classification-schema.ts +2 -2
- package/src/schemas/fields/peppol-line-allowance-charge-schema.ts +57 -9
- package/src/schemas/fields/peppol-line-item-schema.ts +69 -4
- package/src/schemas/fields/peppol-line-price-allowance-charge-schema.ts +20 -0
- package/src/schemas/fields/peppol-line-price-schema.ts +2 -0
- package/src/schemas/fields/peppol-order-reference-schema.ts +2 -0
- package/src/schemas/fields/peppol-party-base-schema.ts +36 -4
- package/src/schemas/fields/peppol-party-legal-entity-schema.ts +13 -0
- package/src/schemas/fields/peppol-party-tax-scheme-schema.ts +5 -0
- package/src/schemas/fields/peppol-payee-party-schema.ts +2 -2
- package/src/schemas/fields/peppol-payment-means-schema.ts +62 -2
- package/src/schemas/fields/peppol-payment-terms-schema.ts +2 -0
- package/src/schemas/fields/peppol-quantity-schema.ts +4 -2
- package/src/schemas/fields/peppol-reference-schema.ts +20 -1
- package/src/schemas/fields/peppol-tax-category-schema.ts +12 -2
- package/src/schemas/fields/peppol-tax-representative-schema.ts +5 -0
- package/src/schemas/fields/peppol-tax-subtotal-category-schema.ts +3 -0
- package/src/schemas/fields/peppol-tax-subtotal-schema.ts +2 -0
- package/src/schemas/fields/peppol-tax-totals-base-schema.ts +5 -0
- package/src/schemas/peppol-billing-base-schema.ts +47 -7
- package/src/schemas/peppol-credit-note-schema.ts +2 -2
- package/src/schemas/peppol-document-response-document-reference-schema.ts +15 -2
- package/src/schemas/peppol-document-response-document-schema.ts +8 -3
- package/src/schemas/peppol-document-response-line-response-content-schema.ts +22 -2
- package/src/schemas/peppol-document-response-line-response-schema.ts +26 -0
- package/src/schemas/peppol-document-schema.ts +74 -9
- package/src/schemas/peppol-invoice-response-document-actual-response-schema.ts +58 -5
- package/src/schemas/peppol-invoice-response-document-actual-response-status-schema.ts +25 -2
- package/src/schemas/peppol-invoice-response-schema.ts +57 -4
- package/src/schemas/peppol-invoice-response-status-reason-code-schema.ts +37 -10
- package/src/schemas/peppol-invoice-schema.ts +30 -2
- package/src/schemas/peppol-message-level-response-document-response-schema.ts +10 -0
- package/src/schemas/peppol-message-level-response-party-schema.ts +10 -0
- package/src/schemas/peppol-message-level-response-schema.ts +18 -0
- package/src/schemas/utils/format-quantity-with-unit.ts +19 -0
- package/src/schemas/utils/get-credit-note-type-code-description.ts +12 -0
- package/src/schemas/utils/get-invoice-type-code-description.ts +12 -0
- package/src/schemas/utils/is-valid-credit-note-type-code.ts +13 -0
- package/src/schemas/utils/is-valid-invoice-type-code.ts +13 -0
- package/src/schemas/utils/is-valid-process.ts +13 -0
- package/src/schemas/utils/peppol-base-64-schema.ts +14 -4
- package/src/schemas/utils/peppol-vat-regex-schema.ts +38 -30
- package/src/schemas/utils/peppol-xsd-time-schema.ts +12 -2
- package/src/schemas/utils/quantity-unit-code-to-intl-unit.ts +34 -0
- package/src/schemas/values/additional-document-reference-code-schema.ts +9 -15
- package/src/schemas/values/allowance-charge-reason-code-schema.ts +12 -14
- package/src/schemas/values/application-response-type-code-schema.ts +10 -14
- package/src/schemas/values/charge-reason-code-schema.ts +12 -14
- package/src/schemas/values/credit-note-type-code-schema.ts +8 -14
- package/src/schemas/values/currency-code-schema.ts +13 -16
- package/src/schemas/values/document-type-schemes-schema.ts +9 -5
- package/src/schemas/values/document-type-values-schema.ts +5 -6
- package/src/schemas/values/duty-tax-fee-category-schema.ts +15 -13
- package/src/schemas/values/electronic-codes-schema.ts +10 -12
- package/src/schemas/values/icd-codes-schema.ts +8 -12
- package/src/schemas/values/invoice-status-code-schema.ts +10 -12
- package/src/schemas/values/invoice-type-code-schema.ts +10 -12
- package/src/schemas/values/item-classification-codes-schema.ts +10 -12
- package/src/schemas/values/mime-codes-schema.ts +12 -14
- package/src/schemas/values/op-status-action-schema.ts +10 -12
- package/src/schemas/values/op-status-reason-schema.ts +10 -12
- package/src/schemas/values/participant-identifier-code-schema.ts +10 -13
- package/src/schemas/values/payment-means-code-schema.ts +10 -12
- package/src/schemas/values/peppol-country-code-schema.ts +10 -9
- package/src/schemas/values/peppol-document-type-code-schema.ts +10 -12
- package/src/schemas/values/peppol-document-type-schema.ts +12 -21
- package/src/schemas/values/peppol-process-schema.ts +19 -15
- package/src/schemas/values/process-from-document-type-schema.ts +3 -3
- package/src/schemas/values/quantity-unit-codes-schema.ts +10 -12
- package/src/schemas/values/vat-date-code-schema.ts +12 -5
- package/src/schemas/values/vatex-code-schema.ts +8 -12
- package/src/schematron/errors.ts +14 -0
- package/src/schematron/helpers.ts +451 -0
- package/src/schematron/run-all-rules.ts +10 -0
- package/src/schematron/schematron.ts +25 -0
- package/src/xml/builder-options.ts +9 -0
- package/src/xml/common-xml-options.ts +9 -0
- package/src/xml/nodable-parser.ts +12 -0
- package/src/xml/parser-options.ts +9 -0
- package/dist/index-BBzPjq7o.d.ts.map +0 -1
- package/dist/invoice-response-code-not-needs-schema-p_RKKNLc.js +0 -18
- package/dist/invoice-response-code-not-needs-schema-p_RKKNLc.js.map +0 -1
- package/dist/is-valid-abn-160i38PG.js.map +0 -1
- package/dist/is-valid-mod97-0208-CzMOXMO-.js.map +0 -1
- package/dist/message-level-response-profile-id-DBpOW91B.js +0 -9
- package/dist/message-level-response-profile-id-DBpOW91B.js.map +0 -1
- package/dist/nodable-parser-CyDZtLCK.js.map +0 -1
- package/dist/vat-dates.generated-CxFvt7m3.d.ts +0 -68
- package/dist/vat-dates.generated-CxFvt7m3.d.ts.map +0 -1
package/dist/schematron.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { t as PeppolIsoDateString } from "./peppol-iso-date-string-BXLxzxdz.js";
|
|
2
2
|
import { c as chargeReasonCodesKeys, f as currencyCodesKeys, i as vatDateCodesKeys, m as mimeCodesKeys, n as electronicAddressCodesKeys, o as countryCodesKeys, u as allowanceChargeReasonCodesKeys } from "./eas-codes.generated-D8jjHFij.js";
|
|
3
|
-
import { n as normalizeSpace, t as isValidMod97_0208 } from "./is-valid-mod97-0208-
|
|
4
|
-
import { c as isValidGLN, d as isValidIBAN, l as isValidMod11, n as checkSEOrgnr, o as checkCF, r as checkPIVAseIT, t as isValidABN, u as greekTinVerification } from "./is-valid-abn-
|
|
3
|
+
import { n as normalizeSpace, t as isValidMod97_0208 } from "./is-valid-mod97-0208-CjR324eG.js";
|
|
4
|
+
import { c as isValidGLN, d as isValidIBAN, l as isValidMod11, n as checkSEOrgnr, o as checkCF, r as checkPIVAseIT, t as isValidABN, u as greekTinVerification } from "./is-valid-abn-D7eSygBG.js";
|
|
5
5
|
import { Context, DateTime, Effect, Layer, Predicate, Schema } from "effect";
|
|
6
6
|
//#region src/schematron/errors.ts
|
|
7
7
|
/**
|
|
8
8
|
* @description Failure of a single schematron rule against a document.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* new SchematronRuleError({ id: 'PEPPOL-EN16931-R001', level: 'fatal', message: 'The document must carry a number.' });
|
|
13
|
+
* ```;
|
|
14
|
+
*
|
|
15
|
+
* @see {@link SchematronValidationError}
|
|
9
16
|
*/
|
|
10
17
|
var SchematronRuleError = class extends Schema.TaggedError()("SchematronRuleError", {
|
|
11
18
|
id: Schema.String,
|
|
@@ -14,12 +21,32 @@ var SchematronRuleError = class extends Schema.TaggedError()("SchematronRuleErro
|
|
|
14
21
|
}) {};
|
|
15
22
|
/**
|
|
16
23
|
* @description Failure of a schematron validation run, wrapping every rule that failed.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* new SchematronValidationError({ errors: [ruleError] });
|
|
28
|
+
* ```;
|
|
29
|
+
*
|
|
30
|
+
* @see {@link SchematronRuleError}
|
|
17
31
|
*/
|
|
18
32
|
var SchematronValidationError = class extends Schema.TaggedError()("SchematronValidationError", { errors: Schema.Array(SchematronRuleError) }) {};
|
|
19
33
|
//#endregion
|
|
20
34
|
//#region src/schematron/helpers.ts
|
|
21
35
|
/**
|
|
22
36
|
* @description Builds an effectful validator from rule metadata and a pure predicate.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const metadata: SchematronRule = { id: 'PEPPOL-EN16931-R001', level: 'fatal', message: 'The document must carry a number.' };
|
|
41
|
+
* const validate = schematronRule(metadata, document => document.taxTotals.length > 0);
|
|
42
|
+
* ```;
|
|
43
|
+
*
|
|
44
|
+
* @param rule - The rule identifier, severity and message reported on failure.
|
|
45
|
+
* @param predicate - The pure check applied to the document; returning `false` fails the rule.
|
|
46
|
+
*
|
|
47
|
+
* @returns A {@link SchematronDocumentValidator} that succeeds for a passing document and fails with a `SchematronRuleError` otherwise.
|
|
48
|
+
*
|
|
49
|
+
* @see {@link SchematronRule}
|
|
23
50
|
*/
|
|
24
51
|
function schematronRule(rule, predicate) {
|
|
25
52
|
return Effect.fn(`schematron.${rule.id}`)(function* (document) {
|
|
@@ -32,12 +59,32 @@ function schematronRule(rule, predicate) {
|
|
|
32
59
|
}
|
|
33
60
|
/**
|
|
34
61
|
* @description Rounds a number to 2 decimals, mirroring the schematron `round(x * 10 * 10) div 100`.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* round2(1.2345); // 1.23
|
|
66
|
+
* ```;
|
|
67
|
+
*
|
|
68
|
+
* @param value - The number to round.
|
|
69
|
+
*
|
|
70
|
+
* @returns The value rounded to 2 decimals.
|
|
35
71
|
*/
|
|
36
72
|
function round2(value) {
|
|
37
73
|
return Math.round(value * 100) / 100;
|
|
38
74
|
}
|
|
39
75
|
/**
|
|
40
76
|
* @description Checks that `val` is within `slack` of `exp`, mirroring the schematron `u:slack` function.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* slack(100, 100.5, 1); // true
|
|
81
|
+
* ```;
|
|
82
|
+
*
|
|
83
|
+
* @param exp - The expected value, the centre of the accepted range.
|
|
84
|
+
* @param val - The actual value to test.
|
|
85
|
+
* @param tolerance - The absolute slack allowed on either side of `exp`.
|
|
86
|
+
*
|
|
87
|
+
* @returns `true` when `val` is within `tolerance` of `exp`, otherwise `false`.
|
|
41
88
|
*/
|
|
42
89
|
function slack(exp, val, tolerance) {
|
|
43
90
|
return exp + tolerance >= val && exp - tolerance <= val;
|
|
@@ -45,6 +92,15 @@ function slack(exp, val, tolerance) {
|
|
|
45
92
|
/**
|
|
46
93
|
* @description Extracts the process number from the business process identifier, mirroring the schematron `$profile` variable. Returns `'Unknown'` when the
|
|
47
94
|
* identifier does not match the expected format.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* getProfile(document); // '01'
|
|
99
|
+
* ```;
|
|
100
|
+
*
|
|
101
|
+
* @param document - The document whose `profileId` is read.
|
|
102
|
+
*
|
|
103
|
+
* @returns The two digit process number, for example `'01'`, or `'Unknown'` when the identifier does not match the expected billing profile format.
|
|
48
104
|
*/
|
|
49
105
|
function getProfile(document) {
|
|
50
106
|
const profileId = document.profileId;
|
|
@@ -53,6 +109,15 @@ function getProfile(document) {
|
|
|
53
109
|
}
|
|
54
110
|
/**
|
|
55
111
|
* @description Resolves the supplier country from the VAT identifier prefix or the supplier postal address, mirroring the schematron `$supplierCountry` variable.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* getSupplierCountry(document); // 'IT'
|
|
116
|
+
* ```;
|
|
117
|
+
*
|
|
118
|
+
* @param document - The document whose supplier VAT identifiers and postal address are read.
|
|
119
|
+
*
|
|
120
|
+
* @returns The upper case ISO 3166-1 alpha-2 country code, defaulting to `'XX'` when neither the VAT prefix nor the postal address provides one.
|
|
56
121
|
*/
|
|
57
122
|
function getSupplierCountry(document) {
|
|
58
123
|
const prefix = (document.accountingSupplierParty.partyTaxSchemes?.find((s) => s.taxSchemeId.id === "VAT"))?.companyId?.slice(0, 2);
|
|
@@ -67,6 +132,15 @@ function getSupplierCountry(document) {
|
|
|
67
132
|
}
|
|
68
133
|
/**
|
|
69
134
|
* @description Resolves the customer country from the VAT identifier prefix or the customer postal address, mirroring the schematron `$customerCountry` variable.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* getCustomerCountry(document); // 'DE'
|
|
139
|
+
* ```;
|
|
140
|
+
*
|
|
141
|
+
* @param document - The document whose customer VAT identifiers and postal address are read.
|
|
142
|
+
*
|
|
143
|
+
* @returns The upper case ISO 3166-1 alpha-2 country code, defaulting to `'XX'` when neither the VAT prefix nor the postal address provides one.
|
|
70
144
|
*/
|
|
71
145
|
function getCustomerCountry(document) {
|
|
72
146
|
const prefix = (document.accountingCustomerParty.partyTaxSchemes?.find((s) => s.taxSchemeId.id === "VAT"))?.companyId?.slice(0, 2);
|
|
@@ -76,18 +150,45 @@ function getCustomerCountry(document) {
|
|
|
76
150
|
}
|
|
77
151
|
/**
|
|
78
152
|
* @description Whether the supplier postal address country is Germany, mirroring the schematron `$supplierCountryIsDE` variable.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* isSupplierGermany(document); // false
|
|
157
|
+
* ```;
|
|
158
|
+
*
|
|
159
|
+
* @param document - The document whose supplier postal address is read.
|
|
160
|
+
*
|
|
161
|
+
* @returns `true` when the supplier postal address country code is `DE`, otherwise `false`.
|
|
79
162
|
*/
|
|
80
163
|
function isSupplierGermany(document) {
|
|
81
164
|
return document.accountingSupplierParty.postalAddress.countryCode.identificationCode.toUpperCase() === "DE";
|
|
82
165
|
}
|
|
83
166
|
/**
|
|
84
167
|
* @description Whether the customer postal address country is Germany, mirroring the schematron `$customerCountryIsDE` variable.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* isCustomerGermany(document); // false
|
|
172
|
+
* ```;
|
|
173
|
+
*
|
|
174
|
+
* @param document - The document whose customer postal address is read.
|
|
175
|
+
*
|
|
176
|
+
* @returns `true` when the customer postal address country code is `DE`, otherwise `false`.
|
|
85
177
|
*/
|
|
86
178
|
function isCustomerGermany(document) {
|
|
87
179
|
return document.accountingCustomerParty.postalAddress.countryCode.identificationCode.toUpperCase() === "DE";
|
|
88
180
|
}
|
|
89
181
|
/**
|
|
90
182
|
* @description Whether both the supplier and customer countries are Denmark, mirroring the schematron `$supplierCountryIsDK` and `$customerCountryIsDK` variables.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* isDanishSupplierAndCustomer(document); // true
|
|
187
|
+
* ```;
|
|
188
|
+
*
|
|
189
|
+
* @param document - The document whose supplier and customer countries are read.
|
|
190
|
+
*
|
|
191
|
+
* @returns `true` when both {@link getSupplierCountry} and {@link getCustomerCountry} return `DK`, otherwise `false`.
|
|
91
192
|
*/
|
|
92
193
|
function isDanishSupplierAndCustomer(document) {
|
|
93
194
|
return getSupplierCountry(document) === "DK" && getCustomerCountry(document) === "DK";
|
|
@@ -95,12 +196,30 @@ function isDanishSupplierAndCustomer(document) {
|
|
|
95
196
|
/**
|
|
96
197
|
* @description Whether both the supplier and customer postal address countries are Germany, mirroring the schematron `$supplierCountryIsDE` and
|
|
97
198
|
* `$customerCountryIsDE` variables.
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```ts
|
|
202
|
+
* isGermanSupplierAndCustomer(document); // true
|
|
203
|
+
* ```;
|
|
204
|
+
*
|
|
205
|
+
* @param document - The document whose supplier and customer postal addresses are read.
|
|
206
|
+
*
|
|
207
|
+
* @returns `true` when both postal address country codes are `DE`, otherwise `false`.
|
|
98
208
|
*/
|
|
99
209
|
function isGermanSupplierAndCustomer(document) {
|
|
100
210
|
return isSupplierGermany(document) && isCustomerGermany(document);
|
|
101
211
|
}
|
|
102
212
|
/**
|
|
103
213
|
* @description Returns the invoice lines or credit note lines of a document.
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```ts
|
|
217
|
+
* getLines(document).length; // 3
|
|
218
|
+
* ```;
|
|
219
|
+
*
|
|
220
|
+
* @param document - The invoice or credit note whose lines are read.
|
|
221
|
+
*
|
|
222
|
+
* @returns The `invoiceLines` or `creditNoteLines` array, or an empty array when the document carries neither.
|
|
104
223
|
*/
|
|
105
224
|
function getLines(document) {
|
|
106
225
|
if ("invoiceLines" in document && Array.isArray(document.invoiceLines)) return document.invoiceLines;
|
|
@@ -109,6 +228,15 @@ function getLines(document) {
|
|
|
109
228
|
}
|
|
110
229
|
/**
|
|
111
230
|
* @description Returns the line quantity (invoiced or credited quantity) of a line, defaulting to 1.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```ts
|
|
234
|
+
* getLineQuantity(line); // 2
|
|
235
|
+
* ```;
|
|
236
|
+
*
|
|
237
|
+
* @param line - The invoice or credit note line whose quantity is read.
|
|
238
|
+
*
|
|
239
|
+
* @returns The `invoicedQuantity` or `creditedQuantity` value, or `1` when the line carries neither.
|
|
112
240
|
*/
|
|
113
241
|
function getLineQuantity(line) {
|
|
114
242
|
const quantity = "invoicedQuantity" in line ? line.invoicedQuantity : "creditedQuantity" in line ? line.creditedQuantity : void 0;
|
|
@@ -116,6 +244,15 @@ function getLineQuantity(line) {
|
|
|
116
244
|
}
|
|
117
245
|
/**
|
|
118
246
|
* @description Returns all document level and line level allowance/charges of a document.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```ts
|
|
250
|
+
* getAllAllowanceCharges(document).length; // 2
|
|
251
|
+
* ```;
|
|
252
|
+
*
|
|
253
|
+
* @param document - The document whose document level and line level allowance/charges are collected.
|
|
254
|
+
*
|
|
255
|
+
* @returns Every allowance and charge as a flat array of normalised records, document level entries first.
|
|
119
256
|
*/
|
|
120
257
|
function getAllAllowanceCharges(document) {
|
|
121
258
|
const documentLevel = (document.allowanceCharges ?? []).map((ac) => ({
|
|
@@ -140,12 +277,32 @@ function getAllAllowanceCharges(document) {
|
|
|
140
277
|
}
|
|
141
278
|
/**
|
|
142
279
|
* @description Whether a VAT breakdown group (BG-23) contains a VAT category code equal to `code`.
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```ts
|
|
283
|
+
* hasVatBreakdownCode(document, 'S'); // true
|
|
284
|
+
* ```;
|
|
285
|
+
*
|
|
286
|
+
* @param document - The document whose VAT breakdown groups (BG-23) are read.
|
|
287
|
+
* @param code - The VAT category code (BT-118) to look for, for example `'S'`.
|
|
288
|
+
*
|
|
289
|
+
* @returns `true` when any VAT breakdown group carries `code`, otherwise `false`.
|
|
143
290
|
*/
|
|
144
291
|
function hasVatBreakdownCode(document, code) {
|
|
145
292
|
return document.taxTotals.some((t) => t.taxSubtotals?.some((s) => s.taxCategory.id === code) ?? false);
|
|
146
293
|
}
|
|
147
294
|
/**
|
|
148
295
|
* @description Whether the document contains a VAT category code (BT-151, BT-95 or BT-102) equal to `code`.
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* ```ts
|
|
299
|
+
* hasVatCategoryCode(document, 'S'); // true
|
|
300
|
+
* ```;
|
|
301
|
+
*
|
|
302
|
+
* @param document - The document whose lines and document level allowance/charges are read.
|
|
303
|
+
* @param code - The VAT category code (BT-151, BT-95 or BT-102) to look for.
|
|
304
|
+
*
|
|
305
|
+
* @returns `true` when any line or document level allowance/charge carries `code`, otherwise `false`.
|
|
149
306
|
*/
|
|
150
307
|
function hasVatCategoryCode(document, code) {
|
|
151
308
|
const lineCategories = getLines(document).some((line) => line.item.classifiedTaxCategory.id === code);
|
|
@@ -154,24 +311,63 @@ function hasVatCategoryCode(document, code) {
|
|
|
154
311
|
}
|
|
155
312
|
/**
|
|
156
313
|
* @description Whether the supplier has a VAT identifier (BT-31), a seller tax registration identifier (BT-32) or a tax representative VAT identifier (BT-63).
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```ts
|
|
317
|
+
* hasSellerTaxIdentifier(document); // true
|
|
318
|
+
* ```;
|
|
319
|
+
*
|
|
320
|
+
* @param document - The document whose supplier party tax schemes are read.
|
|
321
|
+
*
|
|
322
|
+
* @returns `true` when the supplier carries at least one non-empty company identifier, otherwise `false`.
|
|
157
323
|
*/
|
|
158
324
|
function hasSellerTaxIdentifier(document) {
|
|
159
325
|
return document.accountingSupplierParty.partyTaxSchemes?.some((scheme) => scheme.companyId.trim() !== "") ?? false;
|
|
160
326
|
}
|
|
161
327
|
/**
|
|
162
328
|
* @description Whether a number has at most two fraction digits, mirroring the schematron `string-length(substring-after(.,'.'))<=2` checks.
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
* ```ts
|
|
332
|
+
* hasMaxTwoDecimals(1.25); // true
|
|
333
|
+
* hasMaxTwoDecimals(1.255); // false
|
|
334
|
+
* ```;
|
|
335
|
+
*
|
|
336
|
+
* @param value - The number to inspect.
|
|
337
|
+
*
|
|
338
|
+
* @returns `true` when the value has at most two fraction digits, otherwise `false`.
|
|
163
339
|
*/
|
|
164
340
|
function hasMaxTwoDecimals(value) {
|
|
165
341
|
return Math.abs(Math.round(value * 100) / 100 - value) < 1e-9;
|
|
166
342
|
}
|
|
167
343
|
/**
|
|
168
344
|
* @description Whether two monetary values are equal, tolerating floating point drift of parsed decimals.
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ```ts
|
|
348
|
+
* amountsEqual(100, 100.0000000001); // true
|
|
349
|
+
* ```;
|
|
350
|
+
*
|
|
351
|
+
* @param a - The first monetary value.
|
|
352
|
+
* @param b - The second monetary value.
|
|
353
|
+
*
|
|
354
|
+
* @returns `true` when the two values differ by less than `1e-9`, otherwise `false`.
|
|
169
355
|
*/
|
|
170
356
|
function amountsEqual(a, b) {
|
|
171
357
|
return Math.abs(a - b) < 1e-9;
|
|
172
358
|
}
|
|
173
359
|
/**
|
|
174
360
|
* @description Counts the VAT breakdown groups (BG-23) whose VAT category code (BT-118) equals `code`.
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```ts
|
|
364
|
+
* countVatBreakdownCode(document, 'S'); // 1
|
|
365
|
+
* ```;
|
|
366
|
+
*
|
|
367
|
+
* @param document - The document whose VAT breakdown groups (BG-23) are counted.
|
|
368
|
+
* @param code - The VAT category code (BT-118) to count.
|
|
369
|
+
*
|
|
370
|
+
* @returns The number of VAT breakdown groups carrying `code`.
|
|
175
371
|
*/
|
|
176
372
|
function countVatBreakdownCode(document, code) {
|
|
177
373
|
return document.taxTotals.reduce((count, total) => count + (total.taxSubtotals?.filter((subtotal) => subtotal.taxCategory.id === code).length ?? 0), 0);
|
|
@@ -184,36 +380,93 @@ function countVatCategoryCode(document, code) {
|
|
|
184
380
|
}
|
|
185
381
|
/**
|
|
186
382
|
* @description Whether an invoice line (BG-25) carries a VAT category code (BT-151) equal to `code`.
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```ts
|
|
386
|
+
* hasLineVatCategoryCode(document, 'S'); // true
|
|
387
|
+
* ```;
|
|
388
|
+
*
|
|
389
|
+
* @param document - The document whose invoice lines (BG-25) are read.
|
|
390
|
+
* @param code - The VAT category code (BT-151) to look for.
|
|
391
|
+
*
|
|
392
|
+
* @returns `true` when any invoice line carries `code`, otherwise `false`.
|
|
187
393
|
*/
|
|
188
394
|
function hasLineVatCategoryCode(document, code) {
|
|
189
395
|
return getLines(document).some((line) => line.item.classifiedTaxCategory.id === code);
|
|
190
396
|
}
|
|
191
397
|
/**
|
|
192
398
|
* @description Whether a document level allowance (BG-20) carries a VAT category code (BT-95) equal to `code`.
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```ts
|
|
402
|
+
* hasDocumentAllowanceVatCategoryCode(document, 'S'); // false
|
|
403
|
+
* ```;
|
|
404
|
+
*
|
|
405
|
+
* @param document - The document whose document level allowances (BG-20) are read.
|
|
406
|
+
* @param code - The VAT category code (BT-95) to look for.
|
|
407
|
+
*
|
|
408
|
+
* @returns `true` when any document level allowance carries `code`, otherwise `false`.
|
|
193
409
|
*/
|
|
194
410
|
function hasDocumentAllowanceVatCategoryCode(document, code) {
|
|
195
411
|
return (document.allowanceCharges ?? []).some((ac) => !ac.chargeIndicator && ac.taxCategory?.id === code);
|
|
196
412
|
}
|
|
197
413
|
/**
|
|
198
414
|
* @description Whether a document level charge (BG-21) carries a VAT category code (BT-102) equal to `code`.
|
|
415
|
+
*
|
|
416
|
+
* @example
|
|
417
|
+
* ```ts
|
|
418
|
+
* hasDocumentChargeVatCategoryCode(document, 'S'); // false
|
|
419
|
+
* ```;
|
|
420
|
+
*
|
|
421
|
+
* @param document - The document whose document level charges (BG-21) are read.
|
|
422
|
+
* @param code - The VAT category code (BT-102) to look for.
|
|
423
|
+
*
|
|
424
|
+
* @returns `true` when any document level charge carries `code`, otherwise `false`.
|
|
199
425
|
*/
|
|
200
426
|
function hasDocumentChargeVatCategoryCode(document, code) {
|
|
201
427
|
return (document.allowanceCharges ?? []).some((ac) => ac.chargeIndicator && ac.taxCategory?.id === code);
|
|
202
428
|
}
|
|
203
429
|
/**
|
|
204
430
|
* @description Whether the supplier has a VAT identifier (BT-31) on a party tax scheme whose tax scheme is "VAT".
|
|
431
|
+
*
|
|
432
|
+
* @example
|
|
433
|
+
* ```ts
|
|
434
|
+
* hasSellerVatCompanyId(document); // true
|
|
435
|
+
* ```;
|
|
436
|
+
*
|
|
437
|
+
* @param document - The document whose supplier party tax schemes are read.
|
|
438
|
+
*
|
|
439
|
+
* @returns `true` when the supplier has a non-empty VAT identifier on a `VAT` tax scheme, otherwise `false`.
|
|
205
440
|
*/
|
|
206
441
|
function hasSellerVatCompanyId(document) {
|
|
207
442
|
return document.accountingSupplierParty.partyTaxSchemes?.some((scheme) => scheme.taxSchemeId.id.toUpperCase() === "VAT" && scheme.companyId.trim() !== "") ?? false;
|
|
208
443
|
}
|
|
209
444
|
/**
|
|
210
445
|
* @description Whether the buyer has a VAT identifier (BT-48) on a party tax scheme whose tax scheme is "VAT".
|
|
446
|
+
*
|
|
447
|
+
* @example
|
|
448
|
+
* ```ts
|
|
449
|
+
* hasBuyerVatCompanyId(document); // false
|
|
450
|
+
* ```;
|
|
451
|
+
*
|
|
452
|
+
* @param document - The document whose buyer party tax schemes are read.
|
|
453
|
+
*
|
|
454
|
+
* @returns `true` when the buyer has a non-empty VAT identifier on a `VAT` tax scheme, otherwise `false`.
|
|
211
455
|
*/
|
|
212
456
|
function hasBuyerVatCompanyId(document) {
|
|
213
457
|
return document.accountingCustomerParty.partyTaxSchemes?.some((scheme) => scheme.taxSchemeId.id.toUpperCase() === "VAT" && scheme.companyId.trim() !== "") ?? false;
|
|
214
458
|
}
|
|
215
459
|
/**
|
|
216
460
|
* @description Whether the seller tax representative has a VAT identifier (BT-63) on a "VAT" tax scheme.
|
|
461
|
+
*
|
|
462
|
+
* @example
|
|
463
|
+
* ```ts
|
|
464
|
+
* hasTaxRepresentativeVatCompanyId(document); // false
|
|
465
|
+
* ```;
|
|
466
|
+
*
|
|
467
|
+
* @param document - The document whose seller tax representative party is read.
|
|
468
|
+
*
|
|
469
|
+
* @returns `true` when a tax representative exists with a non-empty VAT identifier on a `VAT` tax scheme, otherwise `false`.
|
|
217
470
|
*/
|
|
218
471
|
function hasTaxRepresentativeVatCompanyId(document) {
|
|
219
472
|
const taxRepresentative = document.taxRepresentativeParty;
|
|
@@ -222,6 +475,15 @@ function hasTaxRepresentativeVatCompanyId(document) {
|
|
|
222
475
|
}
|
|
223
476
|
/**
|
|
224
477
|
* @description Whether the buyer has a legal registration identifier (BT-47).
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* ```ts
|
|
481
|
+
* hasBuyerLegalCompanyId(document); // false
|
|
482
|
+
* ```;
|
|
483
|
+
*
|
|
484
|
+
* @param document - The document whose buyer legal entity is read.
|
|
485
|
+
*
|
|
486
|
+
* @returns `true` when the buyer has a non-empty legal registration identifier (BT-47), otherwise `false`.
|
|
225
487
|
*/
|
|
226
488
|
function hasBuyerLegalCompanyId(document) {
|
|
227
489
|
const id = document.accountingCustomerParty.partyLegalEntity.companyId?.id;
|
|
@@ -229,18 +491,51 @@ function hasBuyerLegalCompanyId(document) {
|
|
|
229
491
|
}
|
|
230
492
|
/**
|
|
231
493
|
* @description Whether every invoice line carrying a VAT category code (BT-151) equal to `code` satisfies `predicate` on its VAT rate (BT-152).
|
|
494
|
+
*
|
|
495
|
+
* @example
|
|
496
|
+
* ```ts
|
|
497
|
+
* everyLineCategoryPercent(document, 'S', percent => percent !== undefined && percent > 0); // true
|
|
498
|
+
* ```;
|
|
499
|
+
*
|
|
500
|
+
* @param document - The document whose invoice lines are read.
|
|
501
|
+
* @param code - The VAT category code (BT-151) to match.
|
|
502
|
+
* @param predicate - The check applied to the VAT rate (BT-152) of matching lines.
|
|
503
|
+
*
|
|
504
|
+
* @returns `true` when every matching line satisfies `predicate`, otherwise `false`.
|
|
232
505
|
*/
|
|
233
506
|
function everyLineCategoryPercent(document, code, predicate) {
|
|
234
507
|
return getLines(document).every((line) => line.item.classifiedTaxCategory.id !== code || predicate(line.item.classifiedTaxCategory.percent));
|
|
235
508
|
}
|
|
236
509
|
/**
|
|
237
510
|
* @description Whether every document level allowance carrying a VAT category code (BT-95) equal to `code` satisfies `predicate` on its VAT rate (BT-96).
|
|
511
|
+
*
|
|
512
|
+
* @example
|
|
513
|
+
* ```ts
|
|
514
|
+
* everyDocumentAllowanceCategoryPercent(document, 'S', percent => percent === 0); // false
|
|
515
|
+
* ```;
|
|
516
|
+
*
|
|
517
|
+
* @param document - The document whose document level allowances are read.
|
|
518
|
+
* @param code - The VAT category code (BT-95) to match.
|
|
519
|
+
* @param predicate - The check applied to the VAT rate (BT-96) of matching allowances.
|
|
520
|
+
*
|
|
521
|
+
* @returns `true` when every matching allowance satisfies `predicate`, otherwise `false`.
|
|
238
522
|
*/
|
|
239
523
|
function everyDocumentAllowanceCategoryPercent(document, code, predicate) {
|
|
240
524
|
return (document.allowanceCharges ?? []).every((ac) => ac.chargeIndicator || ac.taxCategory?.id !== code || predicate(ac.taxCategory.percent));
|
|
241
525
|
}
|
|
242
526
|
/**
|
|
243
527
|
* @description Whether every document level charge carrying a VAT category code (BT-102) equal to `code` satisfies `predicate` on its VAT rate (BT-103).
|
|
528
|
+
*
|
|
529
|
+
* @example
|
|
530
|
+
* ```ts
|
|
531
|
+
* everyDocumentChargeCategoryPercent(document, 'S', percent => percent === 0); // false
|
|
532
|
+
* ```;
|
|
533
|
+
*
|
|
534
|
+
* @param document - The document whose document level charges are read.
|
|
535
|
+
* @param code - The VAT category code (BT-102) to match.
|
|
536
|
+
* @param predicate - The check applied to the VAT rate (BT-103) of matching charges.
|
|
537
|
+
*
|
|
538
|
+
* @returns `true` when every matching charge satisfies `predicate`, otherwise `false`.
|
|
244
539
|
*/
|
|
245
540
|
function everyDocumentChargeCategoryPercent(document, code, predicate) {
|
|
246
541
|
return (document.allowanceCharges ?? []).every((ac) => !ac.chargeIndicator || ac.taxCategory?.id !== code || predicate(ac.taxCategory.percent));
|
|
@@ -248,6 +543,16 @@ function everyDocumentChargeCategoryPercent(document, code, predicate) {
|
|
|
248
543
|
/**
|
|
249
544
|
* @description Sum of invoice line net amounts (BT-131) plus document level charge amounts (BT-99) minus document level allowance amounts (BT-92) where the VAT
|
|
250
545
|
* category codes (BT-151, BT-102, BT-95) equal `code`.
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
* ```ts
|
|
549
|
+
* categoryTaxableSum(document, 'S'); // 1000
|
|
550
|
+
* ```;
|
|
551
|
+
*
|
|
552
|
+
* @param document - The document whose lines and document level allowance/charges are summed.
|
|
553
|
+
* @param code - The VAT category code (BT-151, BT-102 or BT-95) to sum.
|
|
554
|
+
*
|
|
555
|
+
* @returns The sum of matching invoice line net amounts plus charges minus allowances.
|
|
251
556
|
*/
|
|
252
557
|
function categoryTaxableSum(document, code) {
|
|
253
558
|
const lineSum = getLines(document).filter((line) => line.item.classifiedTaxCategory.id === code).reduce((sum, line) => sum + line.lineExtensionAmount.value, 0);
|
|
@@ -257,6 +562,16 @@ function categoryTaxableSum(document, code) {
|
|
|
257
562
|
}
|
|
258
563
|
/**
|
|
259
564
|
* @description Returns the VAT breakdown groups (BG-23) whose VAT category code (BT-118) equals `code`.
|
|
565
|
+
*
|
|
566
|
+
* @example
|
|
567
|
+
* ```ts
|
|
568
|
+
* getTaxSubtotalsWithCode(document, 'S').length; // 1
|
|
569
|
+
* ```;
|
|
570
|
+
*
|
|
571
|
+
* @param document - The document whose VAT breakdown groups are read.
|
|
572
|
+
* @param code - The VAT category code (BT-118) to collect.
|
|
573
|
+
*
|
|
574
|
+
* @returns The matching VAT breakdown groups with their amounts and category details.
|
|
260
575
|
*/
|
|
261
576
|
function getTaxSubtotalsWithCode(document, code) {
|
|
262
577
|
return document.taxTotals.flatMap((total) => (total.taxSubtotals ?? []).filter((subtotal) => subtotal.taxCategory.id === code).map((subtotal) => ({
|
|
@@ -282,6 +597,15 @@ function categoryTaxableSumAtRate(document, code, rate) {
|
|
|
282
597
|
}
|
|
283
598
|
/**
|
|
284
599
|
* @description Whether the seller has a legal registration identifier (BT-30), used by BR-CO-26.
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* ```ts
|
|
603
|
+
* hasSellerLegalCompanyId(document); // true
|
|
604
|
+
* ```;
|
|
605
|
+
*
|
|
606
|
+
* @param document - The document whose supplier legal entity is read.
|
|
607
|
+
*
|
|
608
|
+
* @returns `true` when the supplier has a non-empty legal registration identifier (BT-30), otherwise `false`.
|
|
285
609
|
*/
|
|
286
610
|
function hasSellerLegalCompanyId(document) {
|
|
287
611
|
const id = document.accountingSupplierParty.partyLegalEntity.companyId?.id;
|
|
@@ -289,6 +613,16 @@ function hasSellerLegalCompanyId(document) {
|
|
|
289
613
|
}
|
|
290
614
|
/**
|
|
291
615
|
* @description Whether every ISO 3166-1 alpha-2 country code present on the document equals `code`, used by BR-B-01.
|
|
616
|
+
*
|
|
617
|
+
* @example
|
|
618
|
+
* ```ts
|
|
619
|
+
* everyCountryCodeIs(document, 'DE'); // false
|
|
620
|
+
* ```;
|
|
621
|
+
*
|
|
622
|
+
* @param document - The document whose country codes are read.
|
|
623
|
+
* @param code - The upper case ISO 3166-1 alpha-2 code to match.
|
|
624
|
+
*
|
|
625
|
+
* @returns `true` when every non-empty country code on the document equals `code`, otherwise `false`.
|
|
292
626
|
*/
|
|
293
627
|
function everyCountryCodeIs(document, code) {
|
|
294
628
|
return [
|
|
@@ -301,6 +635,15 @@ function everyCountryCodeIs(document, code) {
|
|
|
301
635
|
}
|
|
302
636
|
/**
|
|
303
637
|
* @description Whether every VAT identifier (BT-31, BT-63, BT-48) carries a country prefix from the ISO 3166-1 alpha-2 list (including EL), used by BR-CO-09.
|
|
638
|
+
*
|
|
639
|
+
* @example
|
|
640
|
+
* ```ts
|
|
641
|
+
* allVatCompanyIdsHaveValidPrefix(document); // true
|
|
642
|
+
* ```;
|
|
643
|
+
*
|
|
644
|
+
* @param document - The document whose supplier, customer and tax representative VAT identifiers are read.
|
|
645
|
+
*
|
|
646
|
+
* @returns `true` when every VAT identifier starts with a known ISO 3166-1 alpha-2 country code (including `EL`), otherwise `false`.
|
|
304
647
|
*/
|
|
305
648
|
function allVatCompanyIdsHaveValidPrefix(document) {
|
|
306
649
|
const companyIds = [];
|
|
@@ -314,6 +657,15 @@ function allVatCompanyIdsHaveValidPrefix(document) {
|
|
|
314
657
|
}
|
|
315
658
|
/**
|
|
316
659
|
* @description Whether every invoice period / line period has an end date after or equal to its start date, used by BR-29 and BR-30.
|
|
660
|
+
*
|
|
661
|
+
* @example
|
|
662
|
+
* ```ts
|
|
663
|
+
* everyPeriodEndAfterStart(document); // true
|
|
664
|
+
* ```;
|
|
665
|
+
*
|
|
666
|
+
* @param document - The document whose invoice periods and line invoice periods are read.
|
|
667
|
+
*
|
|
668
|
+
* @returns `true` when every period with both dates has an end date on or after its start date, otherwise `false`.
|
|
317
669
|
*/
|
|
318
670
|
function everyPeriodEndAfterStart(document) {
|
|
319
671
|
const ok = (period) => {
|
|
@@ -325,6 +677,15 @@ function everyPeriodEndAfterStart(document) {
|
|
|
325
677
|
}
|
|
326
678
|
/**
|
|
327
679
|
* @description Whether every invoice period has a start/end date or a description code, used by BR-CO-19 and BR-CO-20.
|
|
680
|
+
*
|
|
681
|
+
* @example
|
|
682
|
+
* ```ts
|
|
683
|
+
* everyPeriodHasDateOrDescriptionCode(document); // true
|
|
684
|
+
* ```;
|
|
685
|
+
*
|
|
686
|
+
* @param document - The document whose invoice periods and line invoice periods are read.
|
|
687
|
+
*
|
|
688
|
+
* @returns `true` when every period has a start date, an end date or a description code, otherwise `false`.
|
|
328
689
|
*/
|
|
329
690
|
function everyPeriodHasDateOrDescriptionCode(document) {
|
|
330
691
|
const ok = (period) => {
|
|
@@ -336,6 +697,15 @@ function everyPeriodHasDateOrDescriptionCode(document) {
|
|
|
336
697
|
}
|
|
337
698
|
/**
|
|
338
699
|
* @description Whether the note subject code, when present, is coded using UNTDID 4451, used by BR-CL-08.
|
|
700
|
+
*
|
|
701
|
+
* @example
|
|
702
|
+
* ```ts
|
|
703
|
+
* noteSubjectCodeIsUncl4451(document); // true
|
|
704
|
+
* ```;
|
|
705
|
+
*
|
|
706
|
+
* @param document - The document whose note is read.
|
|
707
|
+
*
|
|
708
|
+
* @returns `true` when the note has no subject code, or the subject code is a UNTDID 4451 charge reason code, otherwise `false`.
|
|
339
709
|
*/
|
|
340
710
|
function noteSubjectCodeIsUncl4451(document) {
|
|
341
711
|
const note = document.note;
|
|
@@ -346,18 +716,47 @@ function noteSubjectCodeIsUncl4451(document) {
|
|
|
346
716
|
}
|
|
347
717
|
/**
|
|
348
718
|
* @description Total number of VAT breakdown groups (BG-23) present on the document.
|
|
719
|
+
*
|
|
720
|
+
* @example
|
|
721
|
+
* ```ts
|
|
722
|
+
* countAllVatBreakdowns(document); // 1
|
|
723
|
+
* ```;
|
|
724
|
+
*
|
|
725
|
+
* @param document - The document whose VAT breakdown groups (BG-23) are counted.
|
|
726
|
+
*
|
|
727
|
+
* @returns The total number of VAT breakdown groups.
|
|
349
728
|
*/
|
|
350
729
|
function countAllVatBreakdowns(document) {
|
|
351
730
|
return document.taxTotals.reduce((count, total) => count + (total.taxSubtotals?.length ?? 0), 0);
|
|
352
731
|
}
|
|
353
732
|
/**
|
|
354
733
|
* @description Whether the document contains a VAT category code (BT-151, BT-95, BT-102) or a VAT breakdown category code (BT-118) equal to `code`.
|
|
734
|
+
*
|
|
735
|
+
* @example
|
|
736
|
+
* ```ts
|
|
737
|
+
* hasAnyVatCategoryCode(document, 'S'); // true
|
|
738
|
+
* ```;
|
|
739
|
+
*
|
|
740
|
+
* @param document - The document whose VAT categories and breakdowns are read.
|
|
741
|
+
* @param code - The VAT category code to look for.
|
|
742
|
+
*
|
|
743
|
+
* @returns `true` when the code appears on a line, an allowance/charge or a VAT breakdown group, otherwise `false`.
|
|
355
744
|
*/
|
|
356
745
|
function hasAnyVatCategoryCode(document, code) {
|
|
357
746
|
return countVatCategoryCode(document, code) > 0 || countVatBreakdownCode(document, code) > 0;
|
|
358
747
|
}
|
|
359
748
|
/**
|
|
360
749
|
* @description Whether `a` and `b` are within 1 of each other, mirroring the schematron slack used by BR-S-08, BR-AF-08 and BR-AG-08.
|
|
750
|
+
*
|
|
751
|
+
* @example
|
|
752
|
+
* ```ts
|
|
753
|
+
* withinSlackOne(100, 100.5); // true
|
|
754
|
+
* ```;
|
|
755
|
+
*
|
|
756
|
+
* @param a - The first value.
|
|
757
|
+
* @param b - The second value.
|
|
758
|
+
*
|
|
759
|
+
* @returns `true` when the two values differ by less than `1`, otherwise `false`.
|
|
361
760
|
*/
|
|
362
761
|
function withinSlackOne(a, b) {
|
|
363
762
|
return Math.abs(a - b) < 1;
|
|
@@ -365,6 +764,16 @@ function withinSlackOne(a, b) {
|
|
|
365
764
|
/**
|
|
366
765
|
* @description Whether the VAT category tax amount (BT-117) equals the VAT category taxable amount (BT-116) multiplied by the VAT category rate (BT-119), allowing
|
|
367
766
|
* for a slack of 1, mirroring BR-CO-17.
|
|
767
|
+
*
|
|
768
|
+
* @example
|
|
769
|
+
* ```ts
|
|
770
|
+
* vatCategoryTaxAmountMatchesRate(subtotal); // true
|
|
771
|
+
* ```;
|
|
772
|
+
*
|
|
773
|
+
* @param subtotal - The VAT breakdown group (BG-23) whose tax amount and rate are compared.
|
|
774
|
+
*
|
|
775
|
+
* @returns `true` when the tax amount (BT-117) matches the taxable amount (BT-116) times the rate (BT-119), or when both are zero for a missing or
|
|
776
|
+
* zero rate; otherwise `false`.
|
|
368
777
|
*/
|
|
369
778
|
function vatCategoryTaxAmountMatchesRate(subtotal) {
|
|
370
779
|
const percent = subtotal.taxCategory.percent;
|
|
@@ -377,6 +786,16 @@ function vatCategoryTaxAmountMatchesRate(subtotal) {
|
|
|
377
786
|
* @description Whether every VAT breakdown (BG-23) whose VAT category code (BT-118) is "Standard rated", "IGIC" or "IPSI" has a VAT category taxable amount
|
|
378
787
|
* (BT-116) that equals the sum of invoice line net amounts (BT-131) plus document level charge amounts (BT-99) minus document level allowance amounts
|
|
379
788
|
* (BT-92) at the same VAT rate, allowing a slack of 1, mirroring BR-S-08, BR-AF-08 and BR-AG-08.
|
|
789
|
+
*
|
|
790
|
+
* @example
|
|
791
|
+
* ```ts
|
|
792
|
+
* everyVatBreakdownTaxableMatchesRateSum(document); // true
|
|
793
|
+
* ```;
|
|
794
|
+
*
|
|
795
|
+
* @param document - The document whose VAT breakdown groups are checked.
|
|
796
|
+
*
|
|
797
|
+
* @returns `true` when every standard rated, IGIC or IPSI breakdown matches the summed line and allowance/charge amounts at its rate, otherwise
|
|
798
|
+
* `false`.
|
|
380
799
|
*/
|
|
381
800
|
function everyVatBreakdownTaxableMatchesRateSum(document) {
|
|
382
801
|
for (const total of document.taxTotals) for (const subtotal of total.taxSubtotals ?? []) {
|
|
@@ -390,6 +809,15 @@ function everyVatBreakdownTaxableMatchesRateSum(document) {
|
|
|
390
809
|
}
|
|
391
810
|
/**
|
|
392
811
|
* @description Returns all identifiers carrying a scheme identifier, used by the PEPPOL-COMMON-R* rules.
|
|
812
|
+
*
|
|
813
|
+
* @example
|
|
814
|
+
* ```ts
|
|
815
|
+
* getIdentifiersWithSchemeId(document); // [{ id: '00743110157', schemeId: '0088' }]
|
|
816
|
+
* ```;
|
|
817
|
+
*
|
|
818
|
+
* @param document - The document whose supplier, customer and payee identifiers are read.
|
|
819
|
+
*
|
|
820
|
+
* @returns Every identifier that carries both an `id` and a `schemeId`, in supplier, customer then payee order.
|
|
393
821
|
*/
|
|
394
822
|
function getIdentifiersWithSchemeId(document) {
|
|
395
823
|
const candidates = [];
|
|
@@ -5001,6 +5429,16 @@ function evaluateSeR013(document) {
|
|
|
5001
5429
|
const validateSeR013 = schematronRule(rule, evaluateSeR013);
|
|
5002
5430
|
//#endregion
|
|
5003
5431
|
//#region src/schematron/run-all-rules.ts
|
|
5432
|
+
/**
|
|
5433
|
+
* @description Every schematron rule validator of this library, in evaluation order.
|
|
5434
|
+
*
|
|
5435
|
+
* @example
|
|
5436
|
+
* ```ts
|
|
5437
|
+
* ruleValidators.length; // 360
|
|
5438
|
+
* ```;
|
|
5439
|
+
*
|
|
5440
|
+
* @see {@link SchematronDocumentValidator}
|
|
5441
|
+
*/
|
|
5004
5442
|
const ruleValidators = [
|
|
5005
5443
|
validateCenEn16931BrAe01,
|
|
5006
5444
|
validateCenEn16931BrAe02,
|
|
@@ -5367,8 +5805,21 @@ const ruleValidators = [
|
|
|
5367
5805
|
//#region src/schematron/schematron.ts
|
|
5368
5806
|
/**
|
|
5369
5807
|
* @description Runs the schematron rules of this library against a PEPPOL document.
|
|
5808
|
+
*
|
|
5809
|
+
* @example
|
|
5810
|
+
* ```ts
|
|
5811
|
+
* const program = Effect.gen(function* () {
|
|
5812
|
+
* const schematron = yield* Schematron;
|
|
5813
|
+
* return yield* schematron.run(document);
|
|
5814
|
+
* });
|
|
5815
|
+
* ```;
|
|
5816
|
+
*
|
|
5817
|
+
* @see {@link SchematronShape}
|
|
5370
5818
|
*/
|
|
5371
5819
|
var Schematron = class Schematron extends Context.Service()("@endevops/peppol-schema/schematron/Schematron") {
|
|
5820
|
+
/**
|
|
5821
|
+
* @description Layer that builds the {@link Schematron} service from the rule validators in `ruleValidators`.
|
|
5822
|
+
*/
|
|
5372
5823
|
static layer = Layer.effect(Schematron, Effect.gen(function* () {
|
|
5373
5824
|
const run = Effect.fn("Schematron.run")(function* (document) {
|
|
5374
5825
|
const [errors] = yield* Effect.partition(ruleValidators, (validator) => validator(document));
|