@endevops/peppol-schema 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/README.md +5 -5
  2. package/dist/constants.d.ts +131 -0
  3. package/dist/constants.d.ts.map +1 -1
  4. package/dist/constants.js +114 -1
  5. package/dist/constants.js.map +1 -1
  6. package/dist/{index-BBzPjq7o.d.ts → index-XG-N6PcG.d.ts} +1702 -724
  7. package/dist/index-XG-N6PcG.d.ts.map +1 -0
  8. package/dist/index.d.ts +2 -2
  9. package/dist/index.js +1247 -200
  10. package/dist/index.js.map +1 -1
  11. package/dist/invoice-response-code-not-needs-schema-DNrfKYLh.js +36 -0
  12. package/dist/invoice-response-code-not-needs-schema-DNrfKYLh.js.map +1 -0
  13. package/dist/invoice-response-codes.d.ts +28 -0
  14. package/dist/invoice-response-codes.d.ts.map +1 -1
  15. package/dist/invoice-response-codes.js +11 -1
  16. package/dist/invoice-response-codes.js.map +1 -1
  17. package/dist/{is-valid-abn-160i38PG.js → is-valid-abn-D7eSygBG.js} +115 -2
  18. package/dist/is-valid-abn-D7eSygBG.js.map +1 -0
  19. package/dist/{is-valid-mod97-0208-CzMOXMO-.js → is-valid-mod97-0208-CjR324eG.js} +11 -1
  20. package/dist/is-valid-mod97-0208-CjR324eG.js.map +1 -0
  21. package/dist/message-level-response-profile-id-B9c0A7S_.js +27 -0
  22. package/dist/message-level-response-profile-id-B9c0A7S_.js.map +1 -0
  23. package/dist/{nodable-parser-CyDZtLCK.js → nodable-parser-CupS2H55.js} +31 -1
  24. package/dist/nodable-parser-CupS2H55.js.map +1 -0
  25. package/dist/schematron.d.ts +57 -1
  26. package/dist/schematron.d.ts.map +1 -1
  27. package/dist/schematron.js +458 -7
  28. package/dist/schematron.js.map +1 -1
  29. package/dist/validations.d.ts +132 -0
  30. package/dist/validations.d.ts.map +1 -1
  31. package/dist/validations.js +11 -2
  32. package/dist/validations.js.map +1 -1
  33. package/dist/values.d.ts +67 -2
  34. package/dist/values.d.ts.map +1 -1
  35. package/dist/xml.d.ts +30 -0
  36. package/dist/xml.d.ts.map +1 -1
  37. package/dist/xml.js +10 -1
  38. package/dist/xml.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/constants/credit-note-doctype-id.ts +9 -0
  41. package/src/constants/credit-note-process-id.ts +9 -0
  42. package/src/constants/document-scheme.ts +9 -0
  43. package/src/constants/invoice-doctype-id.ts +9 -0
  44. package/src/constants/invoice-process-id.ts +10 -0
  45. package/src/constants/invoice-response-doctype-id.ts +10 -0
  46. package/src/constants/invoice-response-process-id.ts +9 -0
  47. package/src/constants/invoice-response-profile-id.ts +9 -0
  48. package/src/constants/message-level-response-doctype-id.ts +10 -0
  49. package/src/constants/message-level-response-process-id.ts +9 -0
  50. package/src/constants/message-level-response-profile-id.ts +9 -0
  51. package/src/constants/participant-scheme.ts +10 -0
  52. package/src/constants/process-scheme.ts +9 -0
  53. package/src/constants/transport-profile.ts +10 -0
  54. package/src/decoders/fields/decode-invoice-message-document-party.ts +2 -2
  55. package/src/decoders/fields/decode-parties-tax-scheme.ts +2 -2
  56. package/src/decoders/fields/decode-party-legal-entity.ts +2 -2
  57. package/src/decoders/fields/decode-party-tax-scheme.ts +2 -2
  58. package/src/decoders/fields/decode-tax-category.ts +1 -1
  59. package/src/decoders/fields/decode-tax-representative-party.ts +2 -2
  60. package/src/decoders/fields/encode-message-party.ts +2 -2
  61. package/src/decoders/fields/encode-parties-tax-scheme.ts +3 -3
  62. package/src/decoders/fields/encode-party-legal-entity.ts +1 -1
  63. package/src/decoders/fields/encode-party-tax-scheme.ts +2 -2
  64. package/src/decoders/fields/encode-tax-representative-party.ts +2 -2
  65. package/src/invoice-response-codes/invoice-response-code-needs-schema.ts +9 -0
  66. package/src/invoice-response-codes/invoice-response-code-not-needs-schema.ts +9 -0
  67. package/src/invoice-response-codes/invoice-response-codes-order.ts +10 -0
  68. package/src/peppol-validations/add-piva.ts +12 -0
  69. package/src/peppol-validations/check-cf.ts +11 -0
  70. package/src/peppol-validations/check-cf16.ts +12 -0
  71. package/src/peppol-validations/check-piva-se-it.ts +11 -0
  72. package/src/peppol-validations/check-piva.ts +11 -0
  73. package/src/peppol-validations/check-se-orgnr.ts +9 -0
  74. package/src/peppol-validations/greek-tin-verification.ts +9 -0
  75. package/src/peppol-validations/is-valid-abn.ts +9 -0
  76. package/src/peppol-validations/is-valid-gln.ts +10 -0
  77. package/src/peppol-validations/is-valid-iban.ts +10 -0
  78. package/src/peppol-validations/is-valid-mod11.ts +9 -0
  79. package/src/peppol-validations/is-valid-mod97-0208.ts +10 -0
  80. package/src/peppol-validations/luhn-check.ts +9 -0
  81. package/src/schemas/fields/default-customization-id.ts +9 -0
  82. package/src/schemas/fields/default-profile-id.ts +9 -0
  83. package/src/schemas/fields/peppol-additional-document-reference-schema.ts +61 -20
  84. package/src/schemas/fields/peppol-address-schema.ts +25 -2
  85. package/src/schemas/fields/peppol-allowance-charge-schema.ts +63 -9
  86. package/src/schemas/fields/peppol-amount-schema.ts +15 -2
  87. package/src/schemas/fields/peppol-base-line-schema.ts +12 -2
  88. package/src/schemas/fields/peppol-billing-reference-schema.ts +13 -0
  89. package/src/schemas/fields/peppol-binary-object-schema.ts +12 -4
  90. package/src/schemas/fields/peppol-contact-schema.ts +11 -0
  91. package/src/schemas/fields/peppol-credit-note-line-schema.ts +7 -2
  92. package/src/schemas/fields/peppol-delivery-schema.ts +44 -2
  93. package/src/schemas/fields/peppol-identifier-schema.ts +8 -0
  94. package/src/schemas/fields/peppol-invoice-line-period-schema.ts +11 -0
  95. package/src/schemas/fields/peppol-invoice-line-schema.ts +9 -2
  96. package/src/schemas/fields/peppol-invoice-period-schema.ts +2 -2
  97. package/src/schemas/fields/peppol-item-classification-schema.ts +2 -2
  98. package/src/schemas/fields/peppol-line-allowance-charge-schema.ts +56 -8
  99. package/src/schemas/fields/peppol-line-item-schema.ts +66 -6
  100. package/src/schemas/fields/peppol-line-price-allowance-charge-schema.ts +20 -0
  101. package/src/schemas/fields/peppol-line-price-schema.ts +2 -0
  102. package/src/schemas/fields/peppol-order-reference-schema.ts +2 -0
  103. package/src/schemas/fields/peppol-party-base-schema.ts +36 -4
  104. package/src/schemas/fields/peppol-party-legal-entity-schema.ts +10 -2
  105. package/src/schemas/fields/peppol-party-tax-scheme-schema.ts +4 -4
  106. package/src/schemas/fields/peppol-payee-party-schema.ts +8 -8
  107. package/src/schemas/fields/peppol-payment-means-schema.ts +62 -2
  108. package/src/schemas/fields/peppol-payment-terms-schema.ts +2 -0
  109. package/src/schemas/fields/peppol-quantity-schema.ts +4 -2
  110. package/src/schemas/fields/peppol-reference-schema.ts +20 -1
  111. package/src/schemas/fields/peppol-tax-category-schema.ts +12 -2
  112. package/src/schemas/fields/peppol-tax-representative-schema.ts +2 -2
  113. package/src/schemas/fields/peppol-tax-subtotal-category-schema.ts +3 -0
  114. package/src/schemas/fields/peppol-tax-subtotal-schema.ts +2 -0
  115. package/src/schemas/fields/peppol-tax-totals-base-schema.ts +3 -3
  116. package/src/schemas/peppol-billing-base-schema.ts +49 -9
  117. package/src/schemas/peppol-credit-note-schema.ts +2 -2
  118. package/src/schemas/peppol-document-response-document-reference-schema.ts +12 -4
  119. package/src/schemas/peppol-document-response-document-schema.ts +5 -5
  120. package/src/schemas/peppol-document-response-line-response-content-schema.ts +22 -2
  121. package/src/schemas/peppol-document-response-line-response-schema.ts +23 -2
  122. package/src/schemas/peppol-document-schema.ts +69 -6
  123. package/src/schemas/peppol-invoice-response-document-actual-response-schema.ts +53 -4
  124. package/src/schemas/peppol-invoice-response-document-actual-response-status-schema.ts +22 -4
  125. package/src/schemas/peppol-invoice-response-schema.ts +54 -6
  126. package/src/schemas/peppol-invoice-response-status-reason-code-schema.ts +36 -8
  127. package/src/schemas/peppol-invoice-schema.ts +30 -2
  128. package/src/schemas/peppol-message-level-response-document-response-schema.ts +10 -0
  129. package/src/schemas/peppol-message-level-response-party-schema.ts +10 -0
  130. package/src/schemas/peppol-message-level-response-schema.ts +18 -0
  131. package/src/schemas/utils/format-quantity-with-unit.ts +19 -0
  132. package/src/schemas/utils/get-credit-note-type-code-description.ts +12 -0
  133. package/src/schemas/utils/get-invoice-type-code-description.ts +12 -0
  134. package/src/schemas/utils/is-valid-credit-note-type-code.ts +13 -0
  135. package/src/schemas/utils/is-valid-invoice-type-code.ts +13 -0
  136. package/src/schemas/utils/is-valid-process.ts +13 -0
  137. package/src/schemas/utils/peppol-base-64-schema.ts +7 -4
  138. package/src/schemas/utils/peppol-vat-regex-schema.ts +38 -30
  139. package/src/schemas/utils/peppol-xsd-time-schema.ts +12 -2
  140. package/src/schemas/utils/quantity-unit-code-to-intl-unit.ts +34 -0
  141. package/src/schemas/values/additional-document-reference-code-schema.ts +9 -15
  142. package/src/schemas/values/allowance-charge-reason-code-schema.ts +12 -14
  143. package/src/schemas/values/application-response-type-code-schema.ts +10 -14
  144. package/src/schemas/values/charge-reason-code-schema.ts +12 -14
  145. package/src/schemas/values/credit-note-type-code-schema.ts +8 -14
  146. package/src/schemas/values/currency-code-schema.ts +13 -16
  147. package/src/schemas/values/document-type-schemes-schema.ts +9 -5
  148. package/src/schemas/values/document-type-values-schema.ts +5 -6
  149. package/src/schemas/values/duty-tax-fee-category-schema.ts +15 -13
  150. package/src/schemas/values/electronic-codes-schema.ts +10 -12
  151. package/src/schemas/values/icd-codes-schema.ts +8 -12
  152. package/src/schemas/values/invoice-status-code-schema.ts +10 -12
  153. package/src/schemas/values/invoice-type-code-schema.ts +10 -12
  154. package/src/schemas/values/item-classification-codes-schema.ts +10 -12
  155. package/src/schemas/values/mime-codes-schema.ts +12 -14
  156. package/src/schemas/values/op-status-action-schema.ts +10 -12
  157. package/src/schemas/values/op-status-reason-schema.ts +10 -12
  158. package/src/schemas/values/participant-identifier-code-schema.ts +10 -13
  159. package/src/schemas/values/payment-means-code-schema.ts +10 -12
  160. package/src/schemas/values/peppol-country-code-schema.ts +7 -9
  161. package/src/schemas/values/peppol-document-type-code-schema.ts +10 -12
  162. package/src/schemas/values/peppol-document-type-schema.ts +12 -21
  163. package/src/schemas/values/peppol-process-schema.ts +19 -15
  164. package/src/schemas/values/process-from-document-type-schema.ts +3 -3
  165. package/src/schemas/values/quantity-unit-codes-schema.ts +10 -12
  166. package/src/schemas/values/vat-date-code-schema.ts +12 -5
  167. package/src/schemas/values/vatex-code-schema.ts +8 -12
  168. package/src/schematron/errors.ts +14 -0
  169. package/src/schematron/helpers.ts +457 -11
  170. package/src/schematron/run-all-rules.ts +10 -0
  171. package/src/schematron/schematron.ts +25 -0
  172. package/src/xml/builder-options.ts +9 -0
  173. package/src/xml/common-xml-options.ts +9 -0
  174. package/src/xml/nodable-parser.ts +12 -0
  175. package/src/xml/parser-options.ts +9 -0
  176. package/dist/index-BBzPjq7o.d.ts.map +0 -1
  177. package/dist/invoice-response-code-not-needs-schema-p_RKKNLc.js +0 -18
  178. package/dist/invoice-response-code-not-needs-schema-p_RKKNLc.js.map +0 -1
  179. package/dist/is-valid-abn-160i38PG.js.map +0 -1
  180. package/dist/is-valid-mod97-0208-CzMOXMO-.js.map +0 -1
  181. package/dist/message-level-response-profile-id-DBpOW91B.js +0 -9
  182. package/dist/message-level-response-profile-id-DBpOW91B.js.map +0 -1
  183. package/dist/nodable-parser-CyDZtLCK.js.map +0 -1
  184. package/dist/vat-dates.generated-CxFvt7m3.d.ts +0 -68
  185. package/dist/vat-dates.generated-CxFvt7m3.d.ts.map +0 -1
@@ -1,4 +1,4 @@
1
- import { Effect } from 'effect';
1
+ import { Effect, Predicate, String } from 'effect';
2
2
 
3
3
  import type { PeppolInvoicePeriod } from '#/schemas/fields/peppol-invoice-period-schema.ts';
4
4
  import type { PeppolTaxSubTotal } from '#/schemas/fields/peppol-tax-subtotal-schema.ts';
@@ -9,24 +9,51 @@ import { SchematronRuleError } from '#/schematron/errors.ts';
9
9
  import { chargeReasonCodesKeys } from '#/values/charge-reason-codes.generated';
10
10
  import { countryCodesKeys } from '#/values/country-code.generated';
11
11
 
12
- export type PeppolLine = PeppolDocumentLine;
13
-
14
12
  /**
15
13
  * @description Rule metadata shared by all schematron rules.
16
14
  */
17
15
  export interface SchematronRule {
16
+ /**
17
+ * @description The rule identifier, for example `PEPPOL-EN16931-R001`.
18
+ */
18
19
  id: string;
20
+ /**
21
+ * @description The severity of the rule, mirroring the schematron `flag` attribute.
22
+ */
19
23
  level: SchematronRuleLevel;
24
+ /**
25
+ * @description Human readable reason reported when the rule fails.
26
+ */
20
27
  message: string;
21
28
  }
22
29
 
23
30
  /**
24
31
  * @description An effectful schematron rule validator. Fails with a `SchematronRuleError` when the document does not satisfy the rule.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const validate: SchematronDocumentValidator = schematronRule(metadata, document => document.taxTotals.length > 0);
36
+ * ```;
37
+ *
38
+ * @see {@link schematronRule}
25
39
  */
26
40
  export type SchematronDocumentValidator = (document: PeppolDocument) => Effect.Effect<void, SchematronRuleError>;
27
41
 
28
42
  /**
29
43
  * @description Builds an effectful validator from rule metadata and a pure predicate.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const metadata: SchematronRule = { id: 'PEPPOL-EN16931-R001', level: 'fatal', message: 'The document must carry a number.' };
48
+ * const validate = schematronRule(metadata, document => document.taxTotals.length > 0);
49
+ * ```;
50
+ *
51
+ * @param rule - The rule identifier, severity and message reported on failure.
52
+ * @param predicate - The pure check applied to the document; returning `false` fails the rule.
53
+ *
54
+ * @returns A {@link SchematronDocumentValidator} that succeeds for a passing document and fails with a `SchematronRuleError` otherwise.
55
+ *
56
+ * @see {@link SchematronRule}
30
57
  */
31
58
  export function schematronRule(rule: SchematronRule, predicate: (document: PeppolDocument) => boolean): SchematronDocumentValidator {
32
59
  return Effect.fn(`schematron.${rule.id}`)(function* (document: PeppolDocument) {
@@ -38,6 +65,15 @@ export function schematronRule(rule: SchematronRule, predicate: (document: Peppo
38
65
 
39
66
  /**
40
67
  * @description Rounds a number to 2 decimals, mirroring the schematron `round(x * 10 * 10) div 100`.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * round2(1.2345); // 1.23
72
+ * ```;
73
+ *
74
+ * @param value - The number to round.
75
+ *
76
+ * @returns The value rounded to 2 decimals.
41
77
  */
42
78
  export function round2(value: number): number {
43
79
  return Math.round(value * 100) / 100;
@@ -45,6 +81,17 @@ export function round2(value: number): number {
45
81
 
46
82
  /**
47
83
  * @description Checks that `val` is within `slack` of `exp`, mirroring the schematron `u:slack` function.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * slack(100, 100.5, 1); // true
88
+ * ```;
89
+ *
90
+ * @param exp - The expected value, the centre of the accepted range.
91
+ * @param val - The actual value to test.
92
+ * @param tolerance - The absolute slack allowed on either side of `exp`.
93
+ *
94
+ * @returns `true` when `val` is within `tolerance` of `exp`, otherwise `false`.
48
95
  */
49
96
  export function slack(exp: number, val: number, tolerance: number): boolean {
50
97
  return exp + tolerance >= val && exp - tolerance <= val;
@@ -53,6 +100,15 @@ export function slack(exp: number, val: number, tolerance: number): boolean {
53
100
  /**
54
101
  * @description Extracts the process number from the business process identifier, mirroring the schematron `$profile` variable. Returns `'Unknown'` when the
55
102
  * identifier does not match the expected format.
103
+ *
104
+ * @example
105
+ * ```ts
106
+ * getProfile(document); // '01'
107
+ * ```;
108
+ *
109
+ * @param document - The document whose `profileId` is read.
110
+ *
111
+ * @returns The two digit process number, for example `'01'`, or `'Unknown'` when the identifier does not match the expected billing profile format.
56
112
  */
57
113
  export function getProfile(document: Pick<PeppolDocument, 'profileId'>): string {
58
114
  const profileId = document.profileId;
@@ -64,6 +120,15 @@ export function getProfile(document: Pick<PeppolDocument, 'profileId'>): string
64
120
 
65
121
  /**
66
122
  * @description Resolves the supplier country from the VAT identifier prefix or the supplier postal address, mirroring the schematron `$supplierCountry` variable.
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * getSupplierCountry(document); // 'IT'
127
+ * ```;
128
+ *
129
+ * @param document - The document whose supplier VAT identifiers and postal address are read.
130
+ *
131
+ * @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.
67
132
  */
68
133
  export function getSupplierCountry(document: PeppolDocument): string {
69
134
  const vatScheme = document.accountingSupplierParty.partyTaxSchemes?.find(s => s.taxSchemeId.id === 'VAT');
@@ -79,11 +144,20 @@ export function getSupplierCountry(document: PeppolDocument): string {
79
144
  }
80
145
  }
81
146
  const country = document.accountingSupplierParty.postalAddress.countryCode.identificationCode;
82
- return country ? country.toUpperCase() : 'XX';
147
+ return country ? String.toUpperCase(country) : 'XX';
83
148
  }
84
149
 
85
150
  /**
86
151
  * @description Resolves the customer country from the VAT identifier prefix or the customer postal address, mirroring the schematron `$customerCountry` variable.
152
+ *
153
+ * @example
154
+ * ```ts
155
+ * getCustomerCountry(document); // 'DE'
156
+ * ```;
157
+ *
158
+ * @param document - The document whose customer VAT identifiers and postal address are read.
159
+ *
160
+ * @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.
87
161
  */
88
162
  export function getCustomerCountry(document: PeppolDocument): string {
89
163
  const vatScheme = document.accountingCustomerParty.partyTaxSchemes?.find(s => s.taxSchemeId.id === 'VAT');
@@ -92,11 +166,20 @@ export function getCustomerCountry(document: PeppolDocument): string {
92
166
  return prefix.toUpperCase();
93
167
  }
94
168
  const country = document.accountingCustomerParty.postalAddress.countryCode.identificationCode;
95
- return country ? country.toUpperCase() : 'XX';
169
+ return country ? String.toUpperCase(country) : 'XX';
96
170
  }
97
171
 
98
172
  /**
99
173
  * @description Whether the supplier postal address country is Germany, mirroring the schematron `$supplierCountryIsDE` variable.
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * isSupplierGermany(document); // false
178
+ * ```;
179
+ *
180
+ * @param document - The document whose supplier postal address is read.
181
+ *
182
+ * @returns `true` when the supplier postal address country code is `DE`, otherwise `false`.
100
183
  */
101
184
  export function isSupplierGermany(document: PeppolDocument): boolean {
102
185
  return document.accountingSupplierParty.postalAddress.countryCode.identificationCode.toUpperCase() === 'DE';
@@ -104,6 +187,15 @@ export function isSupplierGermany(document: PeppolDocument): boolean {
104
187
 
105
188
  /**
106
189
  * @description Whether the customer postal address country is Germany, mirroring the schematron `$customerCountryIsDE` variable.
190
+ *
191
+ * @example
192
+ * ```ts
193
+ * isCustomerGermany(document); // false
194
+ * ```;
195
+ *
196
+ * @param document - The document whose customer postal address is read.
197
+ *
198
+ * @returns `true` when the customer postal address country code is `DE`, otherwise `false`.
107
199
  */
108
200
  export function isCustomerGermany(document: PeppolDocument): boolean {
109
201
  return document.accountingCustomerParty.postalAddress.countryCode.identificationCode.toUpperCase() === 'DE';
@@ -111,6 +203,15 @@ export function isCustomerGermany(document: PeppolDocument): boolean {
111
203
 
112
204
  /**
113
205
  * @description Whether both the supplier and customer countries are Denmark, mirroring the schematron `$supplierCountryIsDK` and `$customerCountryIsDK` variables.
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * isDanishSupplierAndCustomer(document); // true
210
+ * ```;
211
+ *
212
+ * @param document - The document whose supplier and customer countries are read.
213
+ *
214
+ * @returns `true` when both {@link getSupplierCountry} and {@link getCustomerCountry} return `DK`, otherwise `false`.
114
215
  */
115
216
  export function isDanishSupplierAndCustomer(document: PeppolDocument): boolean {
116
217
  return getSupplierCountry(document) === 'DK' && getCustomerCountry(document) === 'DK';
@@ -119,6 +220,15 @@ export function isDanishSupplierAndCustomer(document: PeppolDocument): boolean {
119
220
  /**
120
221
  * @description Whether both the supplier and customer postal address countries are Germany, mirroring the schematron `$supplierCountryIsDE` and
121
222
  * `$customerCountryIsDE` variables.
223
+ *
224
+ * @example
225
+ * ```ts
226
+ * isGermanSupplierAndCustomer(document); // true
227
+ * ```;
228
+ *
229
+ * @param document - The document whose supplier and customer postal addresses are read.
230
+ *
231
+ * @returns `true` when both postal address country codes are `DE`, otherwise `false`.
122
232
  */
123
233
  export function isGermanSupplierAndCustomer(document: PeppolDocument): boolean {
124
234
  return isSupplierGermany(document) && isCustomerGermany(document);
@@ -126,27 +236,54 @@ export function isGermanSupplierAndCustomer(document: PeppolDocument): boolean {
126
236
 
127
237
  /**
128
238
  * @description Returns the invoice lines or credit note lines of a document.
239
+ *
240
+ * @example
241
+ * ```ts
242
+ * getLines(document).length; // 3
243
+ * ```;
244
+ *
245
+ * @param document - The invoice or credit note whose lines are read.
246
+ *
247
+ * @returns The `invoiceLines` or `creditNoteLines` array, or an empty array when the document carries neither.
129
248
  */
130
- export function getLines(document: PeppolDocument): Array<PeppolLine> {
249
+ export function getLines(document: PeppolDocument): Array<PeppolDocumentLine> {
131
250
  if ('invoiceLines' in document && Array.isArray(document.invoiceLines)) {
132
- return document.invoiceLines as Array<PeppolLine>;
251
+ return document.invoiceLines as Array<PeppolDocumentLine>;
133
252
  }
134
253
  if ('creditNoteLines' in document && Array.isArray(document.creditNoteLines)) {
135
- return document.creditNoteLines as Array<PeppolLine>;
254
+ return document.creditNoteLines as Array<PeppolDocumentLine>;
136
255
  }
137
256
  return [];
138
257
  }
139
258
 
140
259
  /**
141
260
  * @description Returns the line quantity (invoiced or credited quantity) of a line, defaulting to 1.
261
+ *
262
+ * @example
263
+ * ```ts
264
+ * getLineQuantity(line); // 2
265
+ * ```;
266
+ *
267
+ * @param line - The invoice or credit note line whose quantity is read.
268
+ *
269
+ * @returns The `invoicedQuantity` or `creditedQuantity` value, or `1` when the line carries neither.
142
270
  */
143
- export function getLineQuantity(line: PeppolLine): number {
271
+ export function getLineQuantity(line: PeppolDocumentLine): number {
144
272
  const quantity = 'invoicedQuantity' in line ? line.invoicedQuantity : 'creditedQuantity' in line ? line.creditedQuantity : undefined;
145
273
  return quantity ? quantity.value : 1;
146
274
  }
147
275
 
148
276
  /**
149
277
  * @description Returns all document level and line level allowance/charges of a document.
278
+ *
279
+ * @example
280
+ * ```ts
281
+ * getAllAllowanceCharges(document).length; // 2
282
+ * ```;
283
+ *
284
+ * @param document - The document whose document level and line level allowance/charges are collected.
285
+ *
286
+ * @returns Every allowance and charge as a flat array of normalised records, document level entries first.
150
287
  */
151
288
  export function getAllAllowanceCharges(
152
289
  document: PeppolDocument
@@ -184,6 +321,16 @@ export function getAllAllowanceCharges(
184
321
 
185
322
  /**
186
323
  * @description Whether a VAT breakdown group (BG-23) contains a VAT category code equal to `code`.
324
+ *
325
+ * @example
326
+ * ```ts
327
+ * hasVatBreakdownCode(document, 'S'); // true
328
+ * ```;
329
+ *
330
+ * @param document - The document whose VAT breakdown groups (BG-23) are read.
331
+ * @param code - The VAT category code (BT-118) to look for, for example `'S'`.
332
+ *
333
+ * @returns `true` when any VAT breakdown group carries `code`, otherwise `false`.
187
334
  */
188
335
  export function hasVatBreakdownCode(document: PeppolDocument, code: string): boolean {
189
336
  return document.taxTotals.some(t => t.taxSubtotals?.some(s => s.taxCategory.id === code) ?? false);
@@ -191,6 +338,16 @@ export function hasVatBreakdownCode(document: PeppolDocument, code: string): boo
191
338
 
192
339
  /**
193
340
  * @description Whether the document contains a VAT category code (BT-151, BT-95 or BT-102) equal to `code`.
341
+ *
342
+ * @example
343
+ * ```ts
344
+ * hasVatCategoryCode(document, 'S'); // true
345
+ * ```;
346
+ *
347
+ * @param document - The document whose lines and document level allowance/charges are read.
348
+ * @param code - The VAT category code (BT-151, BT-95 or BT-102) to look for.
349
+ *
350
+ * @returns `true` when any line or document level allowance/charge carries `code`, otherwise `false`.
194
351
  */
195
352
  export function hasVatCategoryCode(document: PeppolDocument, code: string): boolean {
196
353
  const lineCategories = getLines(document).some(line => line.item.classifiedTaxCategory.id === code);
@@ -200,6 +357,15 @@ export function hasVatCategoryCode(document: PeppolDocument, code: string): bool
200
357
 
201
358
  /**
202
359
  * @description Resolves the supplier VAT identifier (BT-31) or the seller tax registration identifier (BT-32).
360
+ *
361
+ * @example
362
+ * ```ts
363
+ * getSupplierTaxIdentifiers(document); // 'IT00743110157'
364
+ * ```;
365
+ *
366
+ * @param document - The document whose supplier party tax schemes are read.
367
+ *
368
+ * @returns The comma separated supplier and tax representative identifiers, or an empty string when none is present.
203
369
  */
204
370
  export function getSupplierTaxIdentifiers(document: PeppolDocument): string {
205
371
  const companyIds = document.accountingSupplierParty.partyTaxSchemes?.map(scheme => scheme.companyId) ?? [];
@@ -209,6 +375,15 @@ export function getSupplierTaxIdentifiers(document: PeppolDocument): string {
209
375
 
210
376
  /**
211
377
  * @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).
378
+ *
379
+ * @example
380
+ * ```ts
381
+ * hasSellerTaxIdentifier(document); // true
382
+ * ```;
383
+ *
384
+ * @param document - The document whose supplier party tax schemes are read.
385
+ *
386
+ * @returns `true` when the supplier carries at least one non-empty company identifier, otherwise `false`.
212
387
  */
213
388
  export function hasSellerTaxIdentifier(document: PeppolDocument): boolean {
214
389
  return document.accountingSupplierParty.partyTaxSchemes?.some(scheme => scheme.companyId.trim() !== '') ?? false;
@@ -216,6 +391,15 @@ export function hasSellerTaxIdentifier(document: PeppolDocument): boolean {
216
391
 
217
392
  /**
218
393
  * @description Whether the buyer has a VAT identifier (BT-48) or a legal registration identifier (BT-47).
394
+ *
395
+ * @example
396
+ * ```ts
397
+ * hasBuyerTaxIdentifier(document); // false
398
+ * ```;
399
+ *
400
+ * @param document - The document whose buyer party tax schemes and legal entity are read.
401
+ *
402
+ * @returns `true` when the buyer carries a VAT identifier (BT-48) or a legal registration identifier (BT-47), otherwise `false`.
219
403
  */
220
404
  export function hasBuyerTaxIdentifier(document: PeppolDocument): boolean {
221
405
  const buyerVat = document.accountingCustomerParty.partyTaxSchemes?.some(scheme => scheme.companyId.trim() !== '') ?? false;
@@ -227,6 +411,16 @@ export function hasBuyerTaxIdentifier(document: PeppolDocument): boolean {
227
411
 
228
412
  /**
229
413
  * @description Whether a number has at most two fraction digits, mirroring the schematron `string-length(substring-after(.,'.'))<=2` checks.
414
+ *
415
+ * @example
416
+ * ```ts
417
+ * hasMaxTwoDecimals(1.25); // true
418
+ * hasMaxTwoDecimals(1.255); // false
419
+ * ```;
420
+ *
421
+ * @param value - The number to inspect.
422
+ *
423
+ * @returns `true` when the value has at most two fraction digits, otherwise `false`.
230
424
  */
231
425
  export function hasMaxTwoDecimals(value: number): boolean {
232
426
  return Math.abs(Math.round(value * 100) / 100 - value) < 1e-9;
@@ -234,6 +428,16 @@ export function hasMaxTwoDecimals(value: number): boolean {
234
428
 
235
429
  /**
236
430
  * @description Whether two monetary values are equal, tolerating floating point drift of parsed decimals.
431
+ *
432
+ * @example
433
+ * ```ts
434
+ * amountsEqual(100, 100.0000000001); // true
435
+ * ```;
436
+ *
437
+ * @param a - The first monetary value.
438
+ * @param b - The second monetary value.
439
+ *
440
+ * @returns `true` when the two values differ by less than `1e-9`, otherwise `false`.
237
441
  */
238
442
  export function amountsEqual(a: number, b: number): boolean {
239
443
  return Math.abs(a - b) < 1e-9;
@@ -241,6 +445,16 @@ export function amountsEqual(a: number, b: number): boolean {
241
445
 
242
446
  /**
243
447
  * @description Counts the VAT breakdown groups (BG-23) whose VAT category code (BT-118) equals `code`.
448
+ *
449
+ * @example
450
+ * ```ts
451
+ * countVatBreakdownCode(document, 'S'); // 1
452
+ * ```;
453
+ *
454
+ * @param document - The document whose VAT breakdown groups (BG-23) are counted.
455
+ * @param code - The VAT category code (BT-118) to count.
456
+ *
457
+ * @returns The number of VAT breakdown groups carrying `code`.
244
458
  */
245
459
  export function countVatBreakdownCode(document: PeppolDocument, code: string): number {
246
460
  return document.taxTotals.reduce(
@@ -260,6 +474,16 @@ function countVatCategoryCode(document: PeppolDocument, code: string): number {
260
474
 
261
475
  /**
262
476
  * @description Whether an invoice line (BG-25) carries a VAT category code (BT-151) equal to `code`.
477
+ *
478
+ * @example
479
+ * ```ts
480
+ * hasLineVatCategoryCode(document, 'S'); // true
481
+ * ```;
482
+ *
483
+ * @param document - The document whose invoice lines (BG-25) are read.
484
+ * @param code - The VAT category code (BT-151) to look for.
485
+ *
486
+ * @returns `true` when any invoice line carries `code`, otherwise `false`.
263
487
  */
264
488
  export function hasLineVatCategoryCode(document: PeppolDocument, code: string): boolean {
265
489
  return getLines(document).some(line => line.item.classifiedTaxCategory.id === code);
@@ -267,6 +491,16 @@ export function hasLineVatCategoryCode(document: PeppolDocument, code: string):
267
491
 
268
492
  /**
269
493
  * @description Whether a document level allowance (BG-20) carries a VAT category code (BT-95) equal to `code`.
494
+ *
495
+ * @example
496
+ * ```ts
497
+ * hasDocumentAllowanceVatCategoryCode(document, 'S'); // false
498
+ * ```;
499
+ *
500
+ * @param document - The document whose document level allowances (BG-20) are read.
501
+ * @param code - The VAT category code (BT-95) to look for.
502
+ *
503
+ * @returns `true` when any document level allowance carries `code`, otherwise `false`.
270
504
  */
271
505
  export function hasDocumentAllowanceVatCategoryCode(document: PeppolDocument, code: string): boolean {
272
506
  return (document.allowanceCharges ?? []).some(ac => !ac.chargeIndicator && ac.taxCategory?.id === code);
@@ -274,6 +508,16 @@ export function hasDocumentAllowanceVatCategoryCode(document: PeppolDocument, co
274
508
 
275
509
  /**
276
510
  * @description Whether a document level charge (BG-21) carries a VAT category code (BT-102) equal to `code`.
511
+ *
512
+ * @example
513
+ * ```ts
514
+ * hasDocumentChargeVatCategoryCode(document, 'S'); // false
515
+ * ```;
516
+ *
517
+ * @param document - The document whose document level charges (BG-21) are read.
518
+ * @param code - The VAT category code (BT-102) to look for.
519
+ *
520
+ * @returns `true` when any document level charge carries `code`, otherwise `false`.
277
521
  */
278
522
  export function hasDocumentChargeVatCategoryCode(document: PeppolDocument, code: string): boolean {
279
523
  return (document.allowanceCharges ?? []).some(ac => ac.chargeIndicator && ac.taxCategory?.id === code);
@@ -281,6 +525,15 @@ export function hasDocumentChargeVatCategoryCode(document: PeppolDocument, code:
281
525
 
282
526
  /**
283
527
  * @description Whether the supplier has a VAT identifier (BT-31) on a party tax scheme whose tax scheme is "VAT".
528
+ *
529
+ * @example
530
+ * ```ts
531
+ * hasSellerVatCompanyId(document); // true
532
+ * ```;
533
+ *
534
+ * @param document - The document whose supplier party tax schemes are read.
535
+ *
536
+ * @returns `true` when the supplier has a non-empty VAT identifier on a `VAT` tax scheme, otherwise `false`.
284
537
  */
285
538
  export function hasSellerVatCompanyId(document: PeppolDocument): boolean {
286
539
  return (
@@ -292,6 +545,15 @@ export function hasSellerVatCompanyId(document: PeppolDocument): boolean {
292
545
 
293
546
  /**
294
547
  * @description Whether the buyer has a VAT identifier (BT-48) on a party tax scheme whose tax scheme is "VAT".
548
+ *
549
+ * @example
550
+ * ```ts
551
+ * hasBuyerVatCompanyId(document); // false
552
+ * ```;
553
+ *
554
+ * @param document - The document whose buyer party tax schemes are read.
555
+ *
556
+ * @returns `true` when the buyer has a non-empty VAT identifier on a `VAT` tax scheme, otherwise `false`.
295
557
  */
296
558
  export function hasBuyerVatCompanyId(document: PeppolDocument): boolean {
297
559
  return (
@@ -303,6 +565,15 @@ export function hasBuyerVatCompanyId(document: PeppolDocument): boolean {
303
565
 
304
566
  /**
305
567
  * @description Whether the seller tax representative has a VAT identifier (BT-63) on a "VAT" tax scheme.
568
+ *
569
+ * @example
570
+ * ```ts
571
+ * hasTaxRepresentativeVatCompanyId(document); // false
572
+ * ```;
573
+ *
574
+ * @param document - The document whose seller tax representative party is read.
575
+ *
576
+ * @returns `true` when a tax representative exists with a non-empty VAT identifier on a `VAT` tax scheme, otherwise `false`.
306
577
  */
307
578
  export function hasTaxRepresentativeVatCompanyId(document: PeppolDocument): boolean {
308
579
  const taxRepresentative = document.taxRepresentativeParty;
@@ -314,6 +585,15 @@ export function hasTaxRepresentativeVatCompanyId(document: PeppolDocument): bool
314
585
 
315
586
  /**
316
587
  * @description Whether the buyer has a legal registration identifier (BT-47).
588
+ *
589
+ * @example
590
+ * ```ts
591
+ * hasBuyerLegalCompanyId(document); // false
592
+ * ```;
593
+ *
594
+ * @param document - The document whose buyer legal entity is read.
595
+ *
596
+ * @returns `true` when the buyer has a non-empty legal registration identifier (BT-47), otherwise `false`.
317
597
  */
318
598
  export function hasBuyerLegalCompanyId(document: PeppolDocument): boolean {
319
599
  const id = document.accountingCustomerParty.partyLegalEntity.companyId?.id;
@@ -322,6 +602,17 @@ export function hasBuyerLegalCompanyId(document: PeppolDocument): boolean {
322
602
 
323
603
  /**
324
604
  * @description Whether every invoice line carrying a VAT category code (BT-151) equal to `code` satisfies `predicate` on its VAT rate (BT-152).
605
+ *
606
+ * @example
607
+ * ```ts
608
+ * everyLineCategoryPercent(document, 'S', percent => percent !== undefined && percent > 0); // true
609
+ * ```;
610
+ *
611
+ * @param document - The document whose invoice lines are read.
612
+ * @param code - The VAT category code (BT-151) to match.
613
+ * @param predicate - The check applied to the VAT rate (BT-152) of matching lines.
614
+ *
615
+ * @returns `true` when every matching line satisfies `predicate`, otherwise `false`.
325
616
  */
326
617
  export function everyLineCategoryPercent(document: PeppolDocument, code: string, predicate: (percent: number | undefined) => boolean): boolean {
327
618
  return getLines(document).every(line => line.item.classifiedTaxCategory.id !== code || predicate(line.item.classifiedTaxCategory.percent));
@@ -329,6 +620,17 @@ export function everyLineCategoryPercent(document: PeppolDocument, code: string,
329
620
 
330
621
  /**
331
622
  * @description Whether every document level allowance carrying a VAT category code (BT-95) equal to `code` satisfies `predicate` on its VAT rate (BT-96).
623
+ *
624
+ * @example
625
+ * ```ts
626
+ * everyDocumentAllowanceCategoryPercent(document, 'S', percent => percent === 0); // false
627
+ * ```;
628
+ *
629
+ * @param document - The document whose document level allowances are read.
630
+ * @param code - The VAT category code (BT-95) to match.
631
+ * @param predicate - The check applied to the VAT rate (BT-96) of matching allowances.
632
+ *
633
+ * @returns `true` when every matching allowance satisfies `predicate`, otherwise `false`.
332
634
  */
333
635
  export function everyDocumentAllowanceCategoryPercent(
334
636
  document: PeppolDocument,
@@ -340,6 +642,17 @@ export function everyDocumentAllowanceCategoryPercent(
340
642
 
341
643
  /**
342
644
  * @description Whether every document level charge carrying a VAT category code (BT-102) equal to `code` satisfies `predicate` on its VAT rate (BT-103).
645
+ *
646
+ * @example
647
+ * ```ts
648
+ * everyDocumentChargeCategoryPercent(document, 'S', percent => percent === 0); // false
649
+ * ```;
650
+ *
651
+ * @param document - The document whose document level charges are read.
652
+ * @param code - The VAT category code (BT-102) to match.
653
+ * @param predicate - The check applied to the VAT rate (BT-103) of matching charges.
654
+ *
655
+ * @returns `true` when every matching charge satisfies `predicate`, otherwise `false`.
343
656
  */
344
657
  export function everyDocumentChargeCategoryPercent(
345
658
  document: PeppolDocument,
@@ -352,6 +665,16 @@ export function everyDocumentChargeCategoryPercent(
352
665
  /**
353
666
  * @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
354
667
  * category codes (BT-151, BT-102, BT-95) equal `code`.
668
+ *
669
+ * @example
670
+ * ```ts
671
+ * categoryTaxableSum(document, 'S'); // 1000
672
+ * ```;
673
+ *
674
+ * @param document - The document whose lines and document level allowance/charges are summed.
675
+ * @param code - The VAT category code (BT-151, BT-102 or BT-95) to sum.
676
+ *
677
+ * @returns The sum of matching invoice line net amounts plus charges minus allowances.
355
678
  */
356
679
  export function categoryTaxableSum(document: PeppolDocument, code: string): number {
357
680
  const lineSum = getLines(document)
@@ -368,6 +691,16 @@ export function categoryTaxableSum(document: PeppolDocument, code: string): numb
368
691
 
369
692
  /**
370
693
  * @description Returns the VAT breakdown groups (BG-23) whose VAT category code (BT-118) equals `code`.
694
+ *
695
+ * @example
696
+ * ```ts
697
+ * getTaxSubtotalsWithCode(document, 'S').length; // 1
698
+ * ```;
699
+ *
700
+ * @param document - The document whose VAT breakdown groups are read.
701
+ * @param code - The VAT category code (BT-118) to collect.
702
+ *
703
+ * @returns The matching VAT breakdown groups with their amounts and category details.
371
704
  */
372
705
  export function getTaxSubtotalsWithCode(
373
706
  document: PeppolDocument,
@@ -412,6 +745,15 @@ function categoryTaxableSumAtRate(document: PeppolDocument, code: string, rate:
412
745
 
413
746
  /**
414
747
  * @description Whether the seller has a legal registration identifier (BT-30), used by BR-CO-26.
748
+ *
749
+ * @example
750
+ * ```ts
751
+ * hasSellerLegalCompanyId(document); // true
752
+ * ```;
753
+ *
754
+ * @param document - The document whose supplier legal entity is read.
755
+ *
756
+ * @returns `true` when the supplier has a non-empty legal registration identifier (BT-30), otherwise `false`.
415
757
  */
416
758
  export function hasSellerLegalCompanyId(document: PeppolDocument): boolean {
417
759
  const id = document.accountingSupplierParty.partyLegalEntity.companyId?.id;
@@ -420,6 +762,16 @@ export function hasSellerLegalCompanyId(document: PeppolDocument): boolean {
420
762
 
421
763
  /**
422
764
  * @description Whether every ISO 3166-1 alpha-2 country code present on the document equals `code`, used by BR-B-01.
765
+ *
766
+ * @example
767
+ * ```ts
768
+ * everyCountryCodeIs(document, 'DE'); // false
769
+ * ```;
770
+ *
771
+ * @param document - The document whose country codes are read.
772
+ * @param code - The upper case ISO 3166-1 alpha-2 code to match.
773
+ *
774
+ * @returns `true` when every non-empty country code on the document equals `code`, otherwise `false`.
423
775
  */
424
776
  export function everyCountryCodeIs(document: PeppolDocument, code: string): boolean {
425
777
  const codes = [
@@ -429,13 +781,22 @@ export function everyCountryCodeIs(document: PeppolDocument, code: string): bool
429
781
  document.delivery?.deliveryLocation?.address?.countryCode.identificationCode,
430
782
  ...getLines(document)
431
783
  .map(line => line.item.originCountryCode?.identificationCode)
432
- .filter((v): v is string => typeof v === 'string'),
433
- ].filter((v): v is string => typeof v === 'string' && v.trim() !== '');
784
+ .filter(v => Predicate.isString(v)),
785
+ ].filter(<T>(v: T): v is NonNullable<T> => Predicate.isString(v) && String.isNonEmpty(String.trim(v)));
434
786
  return codes.every(value => value.toUpperCase() === code);
435
787
  }
436
788
 
437
789
  /**
438
790
  * @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.
791
+ *
792
+ * @example
793
+ * ```ts
794
+ * allVatCompanyIdsHaveValidPrefix(document); // true
795
+ * ```;
796
+ *
797
+ * @param document - The document whose supplier, customer and tax representative VAT identifiers are read.
798
+ *
799
+ * @returns `true` when every VAT identifier starts with a known ISO 3166-1 alpha-2 country code (including `EL`), otherwise `false`.
439
800
  */
440
801
  export function allVatCompanyIdsHaveValidPrefix(document: PeppolDocument): boolean {
441
802
  const companyIds: Array<string> = [];
@@ -459,6 +820,15 @@ export function allVatCompanyIdsHaveValidPrefix(document: PeppolDocument): boole
459
820
 
460
821
  /**
461
822
  * @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.
823
+ *
824
+ * @example
825
+ * ```ts
826
+ * everyPeriodEndAfterStart(document); // true
827
+ * ```;
828
+ *
829
+ * @param document - The document whose invoice periods and line invoice periods are read.
830
+ *
831
+ * @returns `true` when every period with both dates has an end date on or after its start date, otherwise `false`.
462
832
  */
463
833
  export function everyPeriodEndAfterStart(document: PeppolDocument): boolean {
464
834
  const ok = (period: PeppolInvoicePeriod | undefined): boolean => {
@@ -473,6 +843,15 @@ export function everyPeriodEndAfterStart(document: PeppolDocument): boolean {
473
843
 
474
844
  /**
475
845
  * @description Whether every invoice period has a start/end date or a description code, used by BR-CO-19 and BR-CO-20.
846
+ *
847
+ * @example
848
+ * ```ts
849
+ * everyPeriodHasDateOrDescriptionCode(document); // true
850
+ * ```;
851
+ *
852
+ * @param document - The document whose invoice periods and line invoice periods are read.
853
+ *
854
+ * @returns `true` when every period has a start date, an end date or a description code, otherwise `false`.
476
855
  */
477
856
  export function everyPeriodHasDateOrDescriptionCode(document: PeppolDocument): boolean {
478
857
  const ok = (period: PeppolInvoicePeriod | undefined): boolean => {
@@ -487,6 +866,15 @@ export function everyPeriodHasDateOrDescriptionCode(document: PeppolDocument): b
487
866
 
488
867
  /**
489
868
  * @description Whether the note subject code, when present, is coded using UNTDID 4451, used by BR-CL-08.
869
+ *
870
+ * @example
871
+ * ```ts
872
+ * noteSubjectCodeIsUncl4451(document); // true
873
+ * ```;
874
+ *
875
+ * @param document - The document whose note is read.
876
+ *
877
+ * @returns `true` when the note has no subject code, or the subject code is a UNTDID 4451 charge reason code, otherwise `false`.
490
878
  */
491
879
  export function noteSubjectCodeIsUncl4451(document: PeppolDocument): boolean {
492
880
  const note = document.note;
@@ -502,6 +890,15 @@ export function noteSubjectCodeIsUncl4451(document: PeppolDocument): boolean {
502
890
 
503
891
  /**
504
892
  * @description Total number of VAT breakdown groups (BG-23) present on the document.
893
+ *
894
+ * @example
895
+ * ```ts
896
+ * countAllVatBreakdowns(document); // 1
897
+ * ```;
898
+ *
899
+ * @param document - The document whose VAT breakdown groups (BG-23) are counted.
900
+ *
901
+ * @returns The total number of VAT breakdown groups.
505
902
  */
506
903
  export function countAllVatBreakdowns(document: PeppolDocument): number {
507
904
  return document.taxTotals.reduce((count, total) => count + (total.taxSubtotals?.length ?? 0), 0);
@@ -509,6 +906,16 @@ export function countAllVatBreakdowns(document: PeppolDocument): number {
509
906
 
510
907
  /**
511
908
  * @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`.
909
+ *
910
+ * @example
911
+ * ```ts
912
+ * hasAnyVatCategoryCode(document, 'S'); // true
913
+ * ```;
914
+ *
915
+ * @param document - The document whose VAT categories and breakdowns are read.
916
+ * @param code - The VAT category code to look for.
917
+ *
918
+ * @returns `true` when the code appears on a line, an allowance/charge or a VAT breakdown group, otherwise `false`.
512
919
  */
513
920
  export function hasAnyVatCategoryCode(document: PeppolDocument, code: string): boolean {
514
921
  return countVatCategoryCode(document, code) > 0 || countVatBreakdownCode(document, code) > 0;
@@ -516,6 +923,16 @@ export function hasAnyVatCategoryCode(document: PeppolDocument, code: string): b
516
923
 
517
924
  /**
518
925
  * @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.
926
+ *
927
+ * @example
928
+ * ```ts
929
+ * withinSlackOne(100, 100.5); // true
930
+ * ```;
931
+ *
932
+ * @param a - The first value.
933
+ * @param b - The second value.
934
+ *
935
+ * @returns `true` when the two values differ by less than `1`, otherwise `false`.
519
936
  */
520
937
  export function withinSlackOne(a: number, b: number): boolean {
521
938
  return Math.abs(a - b) < 1;
@@ -524,6 +941,16 @@ export function withinSlackOne(a: number, b: number): boolean {
524
941
  /**
525
942
  * @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
526
943
  * for a slack of 1, mirroring BR-CO-17.
944
+ *
945
+ * @example
946
+ * ```ts
947
+ * vatCategoryTaxAmountMatchesRate(subtotal); // true
948
+ * ```;
949
+ *
950
+ * @param subtotal - The VAT breakdown group (BG-23) whose tax amount and rate are compared.
951
+ *
952
+ * @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
953
+ * zero rate; otherwise `false`.
527
954
  */
528
955
  export function vatCategoryTaxAmountMatchesRate(subtotal: PeppolTaxSubTotal): boolean {
529
956
  const percent = subtotal.taxCategory.percent;
@@ -542,6 +969,16 @@ export function vatCategoryTaxAmountMatchesRate(subtotal: PeppolTaxSubTotal): bo
542
969
  * @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
543
970
  * (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
544
971
  * (BT-92) at the same VAT rate, allowing a slack of 1, mirroring BR-S-08, BR-AF-08 and BR-AG-08.
972
+ *
973
+ * @example
974
+ * ```ts
975
+ * everyVatBreakdownTaxableMatchesRateSum(document); // true
976
+ * ```;
977
+ *
978
+ * @param document - The document whose VAT breakdown groups are checked.
979
+ *
980
+ * @returns `true` when every standard rated, IGIC or IPSI breakdown matches the summed line and allowance/charge amounts at its rate, otherwise
981
+ * `false`.
545
982
  */
546
983
  export function everyVatBreakdownTaxableMatchesRateSum(document: PeppolDocument): boolean {
547
984
  for (const total of document.taxTotals) {
@@ -564,6 +1001,15 @@ export function everyVatBreakdownTaxableMatchesRateSum(document: PeppolDocument)
564
1001
 
565
1002
  /**
566
1003
  * @description Returns all identifiers carrying a scheme identifier, used by the PEPPOL-COMMON-R* rules.
1004
+ *
1005
+ * @example
1006
+ * ```ts
1007
+ * getIdentifiersWithSchemeId(document); // [{ id: '00743110157', schemeId: '0088' }]
1008
+ * ```;
1009
+ *
1010
+ * @param document - The document whose supplier, customer and payee identifiers are read.
1011
+ *
1012
+ * @returns Every identifier that carries both an `id` and a `schemeId`, in supplier, customer then payee order.
567
1013
  */
568
1014
  export function getIdentifiersWithSchemeId(document: PeppolDocument): Array<{ id: string; schemeId: string }> {
569
1015
  const candidates: Array<{ id: string | undefined; schemeId: string | undefined }> = [];