@getpeppr/cli 0.8.1 → 0.8.3
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/CHANGELOG.md +32 -0
- package/dist/index.js +1557 -689
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -130,6 +130,518 @@ Validating: ${pc.bold(filename)}
|
|
|
130
130
|
return lines.join("\n");
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
// ../sdk/dist/core/canonical-schemes.js
|
|
134
|
+
var ALIAS_TO_EAS = /* @__PURE__ */ new Map([
|
|
135
|
+
["AD:VAT", "9922"],
|
|
136
|
+
["AE:TIN", "0235"],
|
|
137
|
+
["AL:VAT", "9923"],
|
|
138
|
+
["AT:CID", "9916"],
|
|
139
|
+
["AT:GOV", "9915"],
|
|
140
|
+
["AT:KUR", "9919"],
|
|
141
|
+
["AT:VAT", "9914"],
|
|
142
|
+
["AU:ABN", "0151"],
|
|
143
|
+
["BA:VAT", "9924"],
|
|
144
|
+
["BE:CBE", "9956"],
|
|
145
|
+
["BE:EN", "0208"],
|
|
146
|
+
["BE:VAT", "9925"],
|
|
147
|
+
["BG:VAT", "9926"],
|
|
148
|
+
["CH:UIDB", "0183"],
|
|
149
|
+
["CH:VAT", "9927"],
|
|
150
|
+
["CY:VAT", "9928"],
|
|
151
|
+
["CZ:VAT", "9929"],
|
|
152
|
+
["DE:GEBA", "0246"],
|
|
153
|
+
["DE:LID", "9958"],
|
|
154
|
+
["DE:LWID", "0204"],
|
|
155
|
+
["DE:VAT", "9930"],
|
|
156
|
+
["DK:CPR", "9901"],
|
|
157
|
+
["DK:CVR", "9902"],
|
|
158
|
+
["DK:DIGST", "0184"],
|
|
159
|
+
["DK:ERST", "0198"],
|
|
160
|
+
["DK:P", "0096"],
|
|
161
|
+
["DK:SE", "9904"],
|
|
162
|
+
["DK:VANS", "9905"],
|
|
163
|
+
["DUNS", "0060"],
|
|
164
|
+
["EE:CC", "0191"],
|
|
165
|
+
["EE:VAT", "9931"],
|
|
166
|
+
["ES:VAT", "9920"],
|
|
167
|
+
["EU:NAL", "0130"],
|
|
168
|
+
["EU:REID", "9913"],
|
|
169
|
+
["EU:VAT", "9912"],
|
|
170
|
+
["FI:NSI", "0215"],
|
|
171
|
+
["FI:ORG", "0212"],
|
|
172
|
+
["FI:OVT", "0037"],
|
|
173
|
+
["FI:OVT2", "0216"],
|
|
174
|
+
["FI:VAT", "0213"],
|
|
175
|
+
["FR:CTC", "0225"],
|
|
176
|
+
["FR:SIRENE", "0002"],
|
|
177
|
+
["FR:SIRET", "0009"],
|
|
178
|
+
["FR:VAT", "9957"],
|
|
179
|
+
["GB:VAT", "9932"],
|
|
180
|
+
["GLN", "0088"],
|
|
181
|
+
["GR:VAT", "9933"],
|
|
182
|
+
["GS1", "0209"],
|
|
183
|
+
["HR:VAT", "9934"],
|
|
184
|
+
["HU:VAT", "9910"],
|
|
185
|
+
["IBAN", "9918"],
|
|
186
|
+
["IE:VAT", "9935"],
|
|
187
|
+
["IS:KT", "9917"],
|
|
188
|
+
["IS:KTNR", "0196"],
|
|
189
|
+
["IT:CF", "9907"],
|
|
190
|
+
["IT:CFI", "0210"],
|
|
191
|
+
["IT:COD", "0205"],
|
|
192
|
+
["IT:CUUO", "0201"],
|
|
193
|
+
["IT:FTI", "0097"],
|
|
194
|
+
["IT:IPA", "9921"],
|
|
195
|
+
["IT:IVA", "0211"],
|
|
196
|
+
["IT:SECETI", "0142"],
|
|
197
|
+
["IT:SIA", "0135"],
|
|
198
|
+
["IT:VAT", "9906"],
|
|
199
|
+
["JP:IIN", "0221"],
|
|
200
|
+
["JP:SST", "0188"],
|
|
201
|
+
["LEI", "0199"],
|
|
202
|
+
["LI:VAT", "9936"],
|
|
203
|
+
["LT:LEC", "0200"],
|
|
204
|
+
["LT:VAT", "9937"],
|
|
205
|
+
["LU:MAT", "0240"],
|
|
206
|
+
["LU:VAT", "9938"],
|
|
207
|
+
["LV:URN", "0218"],
|
|
208
|
+
["LV:VAT", "9939"],
|
|
209
|
+
["MC:VAT", "9940"],
|
|
210
|
+
["ME:VAT", "9941"],
|
|
211
|
+
["MK:VAT", "9942"],
|
|
212
|
+
["MT:VAT", "9943"],
|
|
213
|
+
["MY:EIF", "0230"],
|
|
214
|
+
["NG:TID", "0244"],
|
|
215
|
+
["NL:KVK", "0106"],
|
|
216
|
+
["NL:OIN", "9954"],
|
|
217
|
+
["NL:OINO", "0190"],
|
|
218
|
+
["NL:VAT", "9944"],
|
|
219
|
+
["NO:ORG", "0192"],
|
|
220
|
+
["NO:ORGNR", "9908"],
|
|
221
|
+
["NO:VAT", "9909"],
|
|
222
|
+
["OM:VAT", "0248"],
|
|
223
|
+
["PL:VAT", "9945"],
|
|
224
|
+
["PT:VAT", "9946"],
|
|
225
|
+
["RO:VAT", "9947"],
|
|
226
|
+
["RS:VAT", "9948"],
|
|
227
|
+
["SE:ORGNR", "0007"],
|
|
228
|
+
["SE:VAT", "9955"],
|
|
229
|
+
["SG:UEN", "0195"],
|
|
230
|
+
["SI:VAT", "9949"],
|
|
231
|
+
["SK:DIC", "0245"],
|
|
232
|
+
["SK:ICO", "0158"],
|
|
233
|
+
["SK:VAT", "9950"],
|
|
234
|
+
["SM:VAT", "9951"],
|
|
235
|
+
["SPIS", "0242"],
|
|
236
|
+
["TR:VAT", "9952"],
|
|
237
|
+
["UBLBE", "0193"],
|
|
238
|
+
["US:EIN", "9959"],
|
|
239
|
+
["VA:VAT", "9953"]
|
|
240
|
+
]);
|
|
241
|
+
function asciiUpper(value) {
|
|
242
|
+
let out = "";
|
|
243
|
+
for (const char of value) {
|
|
244
|
+
const code = char.charCodeAt(0);
|
|
245
|
+
out += code >= 97 && code <= 122 ? String.fromCharCode(code - 32) : char;
|
|
246
|
+
}
|
|
247
|
+
return out;
|
|
248
|
+
}
|
|
249
|
+
function canonicalScheme(scheme) {
|
|
250
|
+
return lookupCanonicalScheme(scheme) ?? scheme.trim();
|
|
251
|
+
}
|
|
252
|
+
function lookupCanonicalScheme(scheme) {
|
|
253
|
+
return ALIAS_TO_EAS.get(asciiUpper(scheme.trim()));
|
|
254
|
+
}
|
|
255
|
+
var EAS_TO_COUNTRY = /* @__PURE__ */ new Map([
|
|
256
|
+
["0002", "FR"],
|
|
257
|
+
["0007", "SE"],
|
|
258
|
+
["0009", "FR"],
|
|
259
|
+
["0037", "FI"],
|
|
260
|
+
["0096", "DK"],
|
|
261
|
+
["0097", "IT"],
|
|
262
|
+
["0106", "NL"],
|
|
263
|
+
["0135", "IT"],
|
|
264
|
+
["0142", "IT"],
|
|
265
|
+
["0151", "AU"],
|
|
266
|
+
["0158", "SK"],
|
|
267
|
+
["0183", "CH"],
|
|
268
|
+
["0184", "DK"],
|
|
269
|
+
["0188", "JP"],
|
|
270
|
+
["0190", "NL"],
|
|
271
|
+
["0191", "EE"],
|
|
272
|
+
["0192", "NO"],
|
|
273
|
+
["0193", "BE"],
|
|
274
|
+
["0195", "SG"],
|
|
275
|
+
["0196", "IS"],
|
|
276
|
+
["0198", "DK"],
|
|
277
|
+
["0200", "LT"],
|
|
278
|
+
["0201", "IT"],
|
|
279
|
+
["0204", "DE"],
|
|
280
|
+
["0205", "IT"],
|
|
281
|
+
["0208", "BE"],
|
|
282
|
+
["0210", "IT"],
|
|
283
|
+
["0211", "IT"],
|
|
284
|
+
["0212", "FI"],
|
|
285
|
+
["0213", "FI"],
|
|
286
|
+
["0215", "FI"],
|
|
287
|
+
["0216", "FI"],
|
|
288
|
+
["0218", "LV"],
|
|
289
|
+
["0221", "JP"],
|
|
290
|
+
["0225", "FR"],
|
|
291
|
+
["0230", "MY"],
|
|
292
|
+
["0235", "AE"],
|
|
293
|
+
["0240", "LU"],
|
|
294
|
+
["0244", "NG"],
|
|
295
|
+
["0245", "SK"],
|
|
296
|
+
["0246", "DE"],
|
|
297
|
+
["0248", "OM"],
|
|
298
|
+
["9901", "DK"],
|
|
299
|
+
["9902", "DK"],
|
|
300
|
+
["9904", "DK"],
|
|
301
|
+
["9905", "DK"],
|
|
302
|
+
["9906", "IT"],
|
|
303
|
+
["9907", "IT"],
|
|
304
|
+
["9908", "NO"],
|
|
305
|
+
["9909", "NO"],
|
|
306
|
+
["9910", "HU"],
|
|
307
|
+
["9914", "AT"],
|
|
308
|
+
["9915", "AT"],
|
|
309
|
+
["9916", "AT"],
|
|
310
|
+
["9917", "IS"],
|
|
311
|
+
["9919", "AT"],
|
|
312
|
+
["9920", "ES"],
|
|
313
|
+
["9921", "IT"],
|
|
314
|
+
["9922", "AD"],
|
|
315
|
+
["9923", "AL"],
|
|
316
|
+
["9924", "BA"],
|
|
317
|
+
["9925", "BE"],
|
|
318
|
+
["9926", "BG"],
|
|
319
|
+
["9927", "CH"],
|
|
320
|
+
["9928", "CY"],
|
|
321
|
+
["9929", "CZ"],
|
|
322
|
+
["9930", "DE"],
|
|
323
|
+
["9931", "EE"],
|
|
324
|
+
["9932", "GB"],
|
|
325
|
+
["9933", "GR"],
|
|
326
|
+
["9934", "HR"],
|
|
327
|
+
["9935", "IE"],
|
|
328
|
+
["9936", "LI"],
|
|
329
|
+
["9937", "LT"],
|
|
330
|
+
["9938", "LU"],
|
|
331
|
+
["9939", "LV"],
|
|
332
|
+
["9940", "MC"],
|
|
333
|
+
["9941", "ME"],
|
|
334
|
+
["9942", "MK"],
|
|
335
|
+
["9943", "MT"],
|
|
336
|
+
["9944", "NL"],
|
|
337
|
+
["9945", "PL"],
|
|
338
|
+
["9946", "PT"],
|
|
339
|
+
["9947", "RO"],
|
|
340
|
+
["9948", "RS"],
|
|
341
|
+
["9949", "SI"],
|
|
342
|
+
["9950", "SK"],
|
|
343
|
+
["9951", "SM"],
|
|
344
|
+
["9952", "TR"],
|
|
345
|
+
["9953", "VA"],
|
|
346
|
+
["9954", "NL"],
|
|
347
|
+
["9955", "SE"],
|
|
348
|
+
["9956", "BE"],
|
|
349
|
+
["9957", "FR"],
|
|
350
|
+
["9958", "DE"],
|
|
351
|
+
["9959", "US"]
|
|
352
|
+
]);
|
|
353
|
+
function countryForScheme(scheme) {
|
|
354
|
+
return EAS_TO_COUNTRY.get(canonicalScheme(scheme));
|
|
355
|
+
}
|
|
356
|
+
var CANONICAL_SCHEME_COUNT = ALIAS_TO_EAS.size;
|
|
357
|
+
var SCHEME_COUNTRY_COUNT = EAS_TO_COUNTRY.size;
|
|
358
|
+
|
|
359
|
+
// ../sdk/dist/core/peppol-id.js
|
|
360
|
+
function isWellFormedPeppolId(peppolId) {
|
|
361
|
+
if (!peppolId.includes(":"))
|
|
362
|
+
return false;
|
|
363
|
+
const { scheme, id } = parsePeppolId(peppolId);
|
|
364
|
+
return id.trim().length > 0 && (/^\d{4}$/.test(scheme) || EAS_LITERAL_SCHEMES.has(scheme));
|
|
365
|
+
}
|
|
366
|
+
var EAS_LITERAL_SCHEMES = /* @__PURE__ */ new Set(["AN", "AQ", "AS", "AU", "EM", "SEPA"]);
|
|
367
|
+
function parsePeppolId(peppolId) {
|
|
368
|
+
const first = peppolId.indexOf(":");
|
|
369
|
+
if (first === -1) {
|
|
370
|
+
return { scheme: canonicalScheme(peppolId), id: "" };
|
|
371
|
+
}
|
|
372
|
+
const second = peppolId.indexOf(":", first + 1);
|
|
373
|
+
if (second !== -1) {
|
|
374
|
+
const resolved = lookupCanonicalScheme(peppolId.slice(0, second));
|
|
375
|
+
if (resolved !== void 0) {
|
|
376
|
+
return { scheme: resolved, id: peppolId.slice(second + 1) };
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
scheme: canonicalScheme(peppolId.slice(0, first)),
|
|
381
|
+
id: peppolId.slice(first + 1)
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// ../sdk/dist/core/iso6523-icd-codes.js
|
|
386
|
+
var ICD_CODES = /* @__PURE__ */ new Set([
|
|
387
|
+
"0002",
|
|
388
|
+
"0003",
|
|
389
|
+
"0004",
|
|
390
|
+
"0005",
|
|
391
|
+
"0006",
|
|
392
|
+
"0007",
|
|
393
|
+
"0008",
|
|
394
|
+
"0009",
|
|
395
|
+
"0010",
|
|
396
|
+
"0011",
|
|
397
|
+
"0012",
|
|
398
|
+
"0013",
|
|
399
|
+
"0014",
|
|
400
|
+
"0015",
|
|
401
|
+
"0016",
|
|
402
|
+
"0017",
|
|
403
|
+
"0018",
|
|
404
|
+
"0019",
|
|
405
|
+
"0020",
|
|
406
|
+
"0021",
|
|
407
|
+
"0022",
|
|
408
|
+
"0023",
|
|
409
|
+
"0024",
|
|
410
|
+
"0025",
|
|
411
|
+
"0026",
|
|
412
|
+
"0027",
|
|
413
|
+
"0028",
|
|
414
|
+
"0029",
|
|
415
|
+
"0030",
|
|
416
|
+
"0031",
|
|
417
|
+
"0032",
|
|
418
|
+
"0033",
|
|
419
|
+
"0034",
|
|
420
|
+
"0035",
|
|
421
|
+
"0036",
|
|
422
|
+
"0037",
|
|
423
|
+
"0038",
|
|
424
|
+
"0039",
|
|
425
|
+
"0040",
|
|
426
|
+
"0041",
|
|
427
|
+
"0042",
|
|
428
|
+
"0043",
|
|
429
|
+
"0044",
|
|
430
|
+
"0045",
|
|
431
|
+
"0046",
|
|
432
|
+
"0047",
|
|
433
|
+
"0048",
|
|
434
|
+
"0049",
|
|
435
|
+
"0050",
|
|
436
|
+
"0051",
|
|
437
|
+
"0052",
|
|
438
|
+
"0053",
|
|
439
|
+
"0054",
|
|
440
|
+
"0055",
|
|
441
|
+
"0056",
|
|
442
|
+
"0057",
|
|
443
|
+
"0058",
|
|
444
|
+
"0059",
|
|
445
|
+
"0060",
|
|
446
|
+
"0061",
|
|
447
|
+
"0062",
|
|
448
|
+
"0063",
|
|
449
|
+
"0064",
|
|
450
|
+
"0065",
|
|
451
|
+
"0066",
|
|
452
|
+
"0067",
|
|
453
|
+
"0068",
|
|
454
|
+
"0069",
|
|
455
|
+
"0070",
|
|
456
|
+
"0071",
|
|
457
|
+
"0072",
|
|
458
|
+
"0073",
|
|
459
|
+
"0074",
|
|
460
|
+
"0075",
|
|
461
|
+
"0076",
|
|
462
|
+
"0077",
|
|
463
|
+
"0078",
|
|
464
|
+
"0079",
|
|
465
|
+
"0080",
|
|
466
|
+
"0081",
|
|
467
|
+
"0082",
|
|
468
|
+
"0083",
|
|
469
|
+
"0084",
|
|
470
|
+
"0085",
|
|
471
|
+
"0086",
|
|
472
|
+
"0087",
|
|
473
|
+
"0088",
|
|
474
|
+
"0089",
|
|
475
|
+
"0090",
|
|
476
|
+
"0091",
|
|
477
|
+
"0093",
|
|
478
|
+
"0094",
|
|
479
|
+
"0095",
|
|
480
|
+
"0096",
|
|
481
|
+
"0097",
|
|
482
|
+
"0098",
|
|
483
|
+
"0099",
|
|
484
|
+
"0100",
|
|
485
|
+
"0101",
|
|
486
|
+
"0102",
|
|
487
|
+
"0104",
|
|
488
|
+
"0105",
|
|
489
|
+
"0106",
|
|
490
|
+
"0107",
|
|
491
|
+
"0108",
|
|
492
|
+
"0109",
|
|
493
|
+
"0110",
|
|
494
|
+
"0111",
|
|
495
|
+
"0112",
|
|
496
|
+
"0113",
|
|
497
|
+
"0114",
|
|
498
|
+
"0115",
|
|
499
|
+
"0116",
|
|
500
|
+
"0117",
|
|
501
|
+
"0118",
|
|
502
|
+
"0119",
|
|
503
|
+
"0120",
|
|
504
|
+
"0121",
|
|
505
|
+
"0122",
|
|
506
|
+
"0123",
|
|
507
|
+
"0124",
|
|
508
|
+
"0125",
|
|
509
|
+
"0126",
|
|
510
|
+
"0127",
|
|
511
|
+
"0128",
|
|
512
|
+
"0129",
|
|
513
|
+
"0130",
|
|
514
|
+
"0131",
|
|
515
|
+
"0132",
|
|
516
|
+
"0133",
|
|
517
|
+
"0134",
|
|
518
|
+
"0135",
|
|
519
|
+
"0136",
|
|
520
|
+
"0137",
|
|
521
|
+
"0138",
|
|
522
|
+
"0139",
|
|
523
|
+
"0140",
|
|
524
|
+
"0141",
|
|
525
|
+
"0142",
|
|
526
|
+
"0143",
|
|
527
|
+
"0144",
|
|
528
|
+
"0145",
|
|
529
|
+
"0146",
|
|
530
|
+
"0147",
|
|
531
|
+
"0148",
|
|
532
|
+
"0149",
|
|
533
|
+
"0150",
|
|
534
|
+
"0151",
|
|
535
|
+
"0152",
|
|
536
|
+
"0153",
|
|
537
|
+
"0154",
|
|
538
|
+
"0155",
|
|
539
|
+
"0156",
|
|
540
|
+
"0157",
|
|
541
|
+
"0158",
|
|
542
|
+
"0159",
|
|
543
|
+
"0160",
|
|
544
|
+
"0161",
|
|
545
|
+
"0162",
|
|
546
|
+
"0163",
|
|
547
|
+
"0164",
|
|
548
|
+
"0165",
|
|
549
|
+
"0166",
|
|
550
|
+
"0167",
|
|
551
|
+
"0168",
|
|
552
|
+
"0169",
|
|
553
|
+
"0170",
|
|
554
|
+
"0171",
|
|
555
|
+
"0172",
|
|
556
|
+
"0173",
|
|
557
|
+
"0174",
|
|
558
|
+
"0175",
|
|
559
|
+
"0176",
|
|
560
|
+
"0177",
|
|
561
|
+
"0178",
|
|
562
|
+
"0179",
|
|
563
|
+
"0180",
|
|
564
|
+
"0183",
|
|
565
|
+
"0184",
|
|
566
|
+
"0185",
|
|
567
|
+
"0186",
|
|
568
|
+
"0187",
|
|
569
|
+
"0188",
|
|
570
|
+
"0189",
|
|
571
|
+
"0190",
|
|
572
|
+
"0191",
|
|
573
|
+
"0192",
|
|
574
|
+
"0193",
|
|
575
|
+
"0194",
|
|
576
|
+
"0195",
|
|
577
|
+
"0196",
|
|
578
|
+
"0197",
|
|
579
|
+
"0198",
|
|
580
|
+
"0199",
|
|
581
|
+
"0200",
|
|
582
|
+
"0201",
|
|
583
|
+
"0202",
|
|
584
|
+
"0203",
|
|
585
|
+
"0204",
|
|
586
|
+
"0205",
|
|
587
|
+
"0206",
|
|
588
|
+
"0207",
|
|
589
|
+
"0208",
|
|
590
|
+
"0209",
|
|
591
|
+
"0210",
|
|
592
|
+
"0211",
|
|
593
|
+
"0212",
|
|
594
|
+
"0213",
|
|
595
|
+
"0214",
|
|
596
|
+
"0215",
|
|
597
|
+
"0216",
|
|
598
|
+
"0217",
|
|
599
|
+
"0218",
|
|
600
|
+
"0219",
|
|
601
|
+
"0220",
|
|
602
|
+
"0221",
|
|
603
|
+
"0222",
|
|
604
|
+
"0223",
|
|
605
|
+
"0224",
|
|
606
|
+
"0225",
|
|
607
|
+
"0226",
|
|
608
|
+
"0227",
|
|
609
|
+
"0228",
|
|
610
|
+
"0229",
|
|
611
|
+
"0230",
|
|
612
|
+
"0231",
|
|
613
|
+
"0232",
|
|
614
|
+
"0233",
|
|
615
|
+
"0234",
|
|
616
|
+
"0235",
|
|
617
|
+
"0236",
|
|
618
|
+
"0237",
|
|
619
|
+
"0238",
|
|
620
|
+
"0239",
|
|
621
|
+
"0240",
|
|
622
|
+
"0241",
|
|
623
|
+
"0242",
|
|
624
|
+
"0243",
|
|
625
|
+
"0244",
|
|
626
|
+
"0245"
|
|
627
|
+
]);
|
|
628
|
+
var ISO6523_ICD_CODES = Object.freeze({
|
|
629
|
+
has: (v) => ICD_CODES.has(v),
|
|
630
|
+
get size() {
|
|
631
|
+
return ICD_CODES.size;
|
|
632
|
+
},
|
|
633
|
+
keys: () => ICD_CODES.keys(),
|
|
634
|
+
values: () => ICD_CODES.values(),
|
|
635
|
+
entries: () => ICD_CODES.entries(),
|
|
636
|
+
forEach: (fn, thisArg) => ICD_CODES.forEach((v, v2) => fn.call(thisArg, v, v2, ISO6523_ICD_CODES)),
|
|
637
|
+
[Symbol.iterator]: () => ICD_CODES[Symbol.iterator]()
|
|
638
|
+
});
|
|
639
|
+
function canCarryPartyIdentification(scheme, context) {
|
|
640
|
+
if (ICD_CODES.has(scheme))
|
|
641
|
+
return true;
|
|
642
|
+
return scheme === "SEPA" && context !== "AccountingCustomerParty";
|
|
643
|
+
}
|
|
644
|
+
|
|
133
645
|
// ../sdk/dist/core/ubl-builder.js
|
|
134
646
|
var UBL_NS = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2";
|
|
135
647
|
var CAC_NS = "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2";
|
|
@@ -139,6 +651,24 @@ var PEPPOL_CUSTOMIZATION_ID = "urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.
|
|
|
139
651
|
var PEPPOL_PROFILE_ID = "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0";
|
|
140
652
|
var DEFAULT_UNIT = "EA";
|
|
141
653
|
var DEFAULT_PAYMENT_MEANS = 30;
|
|
654
|
+
var EXEMPTION_REASON_CATEGORIES = /* @__PURE__ */ new Set(["E", "AE", "K", "G", "O"]);
|
|
655
|
+
var EXEMPTION_REASON_RULES = {
|
|
656
|
+
E: "BR-E-10",
|
|
657
|
+
AE: "BR-AE-10",
|
|
658
|
+
K: "BR-IC-10",
|
|
659
|
+
G: "BR-G-10",
|
|
660
|
+
O: "BR-O-10"
|
|
661
|
+
};
|
|
662
|
+
var UblBuilderInputError = class extends Error {
|
|
663
|
+
field;
|
|
664
|
+
ruleId;
|
|
665
|
+
constructor(message, field, ruleId) {
|
|
666
|
+
super(message);
|
|
667
|
+
this.field = field;
|
|
668
|
+
this.ruleId = ruleId;
|
|
669
|
+
this.name = "UblBuilderInputError";
|
|
670
|
+
}
|
|
671
|
+
};
|
|
142
672
|
var UNIT_CODE_MAP = {
|
|
143
673
|
each: "EA",
|
|
144
674
|
piece: "EA",
|
|
@@ -181,13 +711,28 @@ function formatDate(dateStr) {
|
|
|
181
711
|
function formatAmount(amount) {
|
|
182
712
|
return amount.toFixed(2);
|
|
183
713
|
}
|
|
714
|
+
function formatVatRate(vatRate, field) {
|
|
715
|
+
if (typeof vatRate !== "number" || !Number.isFinite(vatRate)) {
|
|
716
|
+
throw new UblBuilderInputError("vatRate must be a finite number.", field);
|
|
717
|
+
}
|
|
718
|
+
return String(vatRate);
|
|
719
|
+
}
|
|
184
720
|
function round2(n) {
|
|
185
721
|
return Math.round(n * 100) / 100;
|
|
186
722
|
}
|
|
187
|
-
function
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
723
|
+
function normalizedTaxExemptReason(vatCategory, reason) {
|
|
724
|
+
if (!EXEMPTION_REASON_CATEGORIES.has(vatCategory) || typeof reason !== "string") {
|
|
725
|
+
return void 0;
|
|
726
|
+
}
|
|
727
|
+
const normalized = reason.trim();
|
|
728
|
+
for (const character of normalized) {
|
|
729
|
+
const codePoint = character.codePointAt(0);
|
|
730
|
+
const allowed = codePoint === 9 || codePoint === 10 || codePoint === 13 || codePoint >= 32 && codePoint <= 55295 || codePoint >= 57344 && codePoint <= 65533 || codePoint >= 65536 && codePoint <= 1114111;
|
|
731
|
+
if (!allowed) {
|
|
732
|
+
throw new UblBuilderInputError("taxExemptReason contains an invalid XML character.", "taxExemptReason");
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return normalized || void 0;
|
|
191
736
|
}
|
|
192
737
|
function buildPartyXml(party, role) {
|
|
193
738
|
const { scheme: endpointScheme, id: endpointId } = parsePeppolId(party.peppolId);
|
|
@@ -195,9 +740,28 @@ function buildPartyXml(party, role) {
|
|
|
195
740
|
<cac:${role}>
|
|
196
741
|
<cac:Party>
|
|
197
742
|
<cbc:EndpointID schemeID="${escapeXml(endpointScheme)}">${escapeXml(endpointId)}</cbc:EndpointID>
|
|
198
|
-
|
|
743
|
+
${// ⛔ DEUX listes, pas une. `BR-CL-25` juge l'EndpointID ci-dessus contre la
|
|
744
|
+
// liste EAS (101 valeurs, jusqu'à `9959`) ; `BR-CL-10` juge CE champ-ci
|
|
745
|
+
// contre la liste ISO 6523 ICD (240 valeurs, `0002`–`0245`). `9932` (GB),
|
|
746
|
+
// `9935` (IE) et `9930` (DE) sont légaux là-haut et FATALS ici.
|
|
747
|
+
//
|
|
748
|
+
// Le champ (BT-29) est optionnel : l'omettre est la seule écriture
|
|
749
|
+
// conforme pour ces schemes, et c'est ce que le tir réseau de GPR-1102
|
|
750
|
+
// avait déjà mesuré — `9932`/`9935` n'apparaissent ni en BT-29 ni en
|
|
751
|
+
// BT-30, seulement en BT-31. Émettre quand même rendait `BR-CL-10` fatale
|
|
752
|
+
// pour tout vendeur britannique, irlandais ou allemand en `9930`,
|
|
753
|
+
// y compris en écrivant la forme numérique (GPR-1110).
|
|
754
|
+
//
|
|
755
|
+
// ⚠️ BR-CO-26 exige alors qu'un autre identifiant porte l'expéditeur —
|
|
756
|
+
// `vatNumber` (BT-31) ou `companyId` (BT-30) ci-dessous. `validateInvoice`
|
|
757
|
+
// AVERTIT quand il n'y en a aucun ; il ne refuse pas, et `toXml()` rend
|
|
758
|
+
// donc bel et bien un document que le réseau rejettera. C'est délibéré :
|
|
759
|
+
// `from` est déclaré « deprecated and ignored » à l'envoi, où l'expéditeur
|
|
760
|
+
// vient de la clé API — bloquer ici casserait des appelants dont le
|
|
761
|
+
// document part très bien.
|
|
762
|
+
canCarryPartyIdentification(endpointScheme, role) ? `<cac:PartyIdentification>
|
|
199
763
|
<cbc:ID schemeID="${escapeXml(endpointScheme)}">${escapeXml(endpointId)}</cbc:ID>
|
|
200
|
-
</cac:PartyIdentification
|
|
764
|
+
</cac:PartyIdentification>` : ""}
|
|
201
765
|
<cac:PartyName>
|
|
202
766
|
<cbc:Name>${escapeXml(party.name)}</cbc:Name>
|
|
203
767
|
</cac:PartyName>
|
|
@@ -231,9 +795,12 @@ function buildPayeePartyXml(party) {
|
|
|
231
795
|
const { scheme, id } = parsePeppolId(party.peppolId);
|
|
232
796
|
return `
|
|
233
797
|
<cac:PayeeParty>
|
|
234
|
-
|
|
798
|
+
${// Même règle qu'au-dessus : `BR-CL-10` a pour contexte TOUT
|
|
799
|
+
// `cac:PartyIdentification/cbc:ID[@schemeID]`, PayeeParty compris. Le
|
|
800
|
+
// bénéficiaire (BT-60) reste identifié par son nom, toujours émis.
|
|
801
|
+
canCarryPartyIdentification(scheme, "PayeeParty") ? `<cac:PartyIdentification>
|
|
235
802
|
<cbc:ID schemeID="${escapeXml(scheme)}">${escapeXml(id)}</cbc:ID>
|
|
236
|
-
</cac:PartyIdentification
|
|
803
|
+
</cac:PartyIdentification>` : ""}
|
|
237
804
|
<cac:PartyName>
|
|
238
805
|
<cbc:Name>${escapeXml(party.name)}</cbc:Name>
|
|
239
806
|
</cac:PartyName>
|
|
@@ -347,8 +914,8 @@ function buildDocumentAllowanceChargeXml(item, isCharge, currency) {
|
|
|
347
914
|
<cbc:AllowanceChargeReason>${escapeXml(item.reason)}</cbc:AllowanceChargeReason>
|
|
348
915
|
<cbc:Amount currencyID="${escapeXml(currency)}">${formatAmount(item.amount)}</cbc:Amount>
|
|
349
916
|
<cac:TaxCategory>
|
|
350
|
-
<cbc:ID>${vatCategory}</cbc:ID>
|
|
351
|
-
|
|
917
|
+
<cbc:ID>${escapeXml(vatCategory)}</cbc:ID>
|
|
918
|
+
${vatCategory === "O" ? "" : `<cbc:Percent>${formatVatRate(item.vatRate, "vatRate")}</cbc:Percent>`}
|
|
352
919
|
<cac:TaxScheme>
|
|
353
920
|
<cbc:ID>VAT</cbc:ID>
|
|
354
921
|
</cac:TaxScheme>
|
|
@@ -388,8 +955,8 @@ function buildDocumentLineXml(line, index, currency, lineTag, qtyTag) {
|
|
|
388
955
|
<cbc:ID>${escapeXml(line.itemId)}</cbc:ID>
|
|
389
956
|
</cac:SellersItemIdentification>` : ""}
|
|
390
957
|
<cac:ClassifiedTaxCategory>
|
|
391
|
-
<cbc:ID>${vatCategory}</cbc:ID>
|
|
392
|
-
|
|
958
|
+
<cbc:ID>${escapeXml(vatCategory)}</cbc:ID>
|
|
959
|
+
${vatCategory === "O" ? "" : `<cbc:Percent>${formatVatRate(line.vatRate, "vatRate")}</cbc:Percent>`}
|
|
393
960
|
<cac:TaxScheme>
|
|
394
961
|
<cbc:ID>VAT</cbc:ID>
|
|
395
962
|
</cac:TaxScheme>
|
|
@@ -414,39 +981,60 @@ function buildDocumentLineXml(line, index, currency, lineTag, qtyTag) {
|
|
|
414
981
|
function buildInvoiceLineXml(line, index, currency) {
|
|
415
982
|
return buildDocumentLineXml(line, index, currency, "InvoiceLine", "InvoicedQuantity");
|
|
416
983
|
}
|
|
417
|
-
function calculateTaxSubtotals(lines, allowances, charges) {
|
|
984
|
+
function calculateTaxSubtotals(lines, allowances, charges, options = {}) {
|
|
418
985
|
const groups = /* @__PURE__ */ new Map();
|
|
419
|
-
function addToGroup(vatCategory, vatRate, amount) {
|
|
420
|
-
|
|
986
|
+
function addToGroup(vatCategory, vatRate, amount, taxExemptReason, field = "taxExemptReason") {
|
|
987
|
+
if (typeof vatCategory !== "string") {
|
|
988
|
+
throw new UblBuilderInputError("vatCategory must be a string.", `${field}.vatCategory`);
|
|
989
|
+
}
|
|
990
|
+
formatVatRate(vatRate, `${field}.vatRate`);
|
|
991
|
+
if (options.forUbl && vatCategory === "O" && vatRate !== 0) {
|
|
992
|
+
throw new UblBuilderInputError("Category O must use vatRate 0 in SDK input.", `${field}.vatRate`);
|
|
993
|
+
}
|
|
994
|
+
const effectiveVatRate = options.forUbl && vatCategory === "O" ? 0 : vatRate;
|
|
995
|
+
const reason = options.forUbl ? normalizedTaxExemptReason(vatCategory, taxExemptReason) : void 0;
|
|
996
|
+
const key = `${vatCategory}-${effectiveVatRate}`;
|
|
421
997
|
const existing = groups.get(key);
|
|
422
998
|
if (existing) {
|
|
999
|
+
if (reason && existing.taxExemptReason && reason !== existing.taxExemptReason) {
|
|
1000
|
+
throw new UblBuilderInputError(`Conflicting taxExemptReason values for VAT group ${vatCategory}/${effectiveVatRate}.`, "taxExemptReason");
|
|
1001
|
+
}
|
|
1002
|
+
existing.taxExemptReason ??= reason;
|
|
423
1003
|
existing.taxableAmount = round2(existing.taxableAmount + amount);
|
|
424
1004
|
} else {
|
|
425
1005
|
groups.set(key, {
|
|
426
|
-
vatRate,
|
|
1006
|
+
vatRate: effectiveVatRate,
|
|
427
1007
|
vatCategory,
|
|
1008
|
+
taxExemptReason: reason,
|
|
428
1009
|
taxableAmount: amount,
|
|
429
1010
|
taxAmount: 0
|
|
430
1011
|
// computed once per group below (BR-CO-17)
|
|
431
1012
|
});
|
|
432
1013
|
}
|
|
433
1014
|
}
|
|
434
|
-
for (const line of lines) {
|
|
435
|
-
addToGroup(line.vatCategory ?? "S", line.vatRate, calculateLineExtensionAmount(line));
|
|
1015
|
+
for (const [index, line] of lines.entries()) {
|
|
1016
|
+
addToGroup(line.vatCategory ?? "S", line.vatRate, calculateLineExtensionAmount(line), line.taxExemptReason, `lines[${index}]`);
|
|
436
1017
|
}
|
|
437
|
-
for (const a of allowances ?? []) {
|
|
438
|
-
addToGroup(a.vatCategory ?? "S", a.vatRate, -a.amount);
|
|
1018
|
+
for (const [index, a] of (allowances ?? []).entries()) {
|
|
1019
|
+
addToGroup(a.vatCategory ?? "S", a.vatRate, -a.amount, a.taxExemptReason, `allowances[${index}]`);
|
|
439
1020
|
}
|
|
440
|
-
for (const c of charges ?? []) {
|
|
441
|
-
addToGroup(c.vatCategory ?? "S", c.vatRate, c.amount);
|
|
1021
|
+
for (const [index, c] of (charges ?? []).entries()) {
|
|
1022
|
+
addToGroup(c.vatCategory ?? "S", c.vatRate, c.amount, c.taxExemptReason, `charges[${index}]`);
|
|
1023
|
+
}
|
|
1024
|
+
if (options.forUbl) {
|
|
1025
|
+
for (const subtotal of groups.values()) {
|
|
1026
|
+
if (EXEMPTION_REASON_CATEGORIES.has(subtotal.vatCategory) && !subtotal.taxExemptReason) {
|
|
1027
|
+
throw new UblBuilderInputError(`VAT category ${subtotal.vatCategory} requires a non-empty taxExemptReason.`, "taxExemptReason", EXEMPTION_REASON_RULES[subtotal.vatCategory]);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
442
1030
|
}
|
|
443
1031
|
return Array.from(groups.values()).map((subtotal) => ({
|
|
444
1032
|
...subtotal,
|
|
445
1033
|
taxAmount: round2(subtotal.taxableAmount * (subtotal.vatRate / 100))
|
|
446
1034
|
}));
|
|
447
1035
|
}
|
|
448
|
-
function calculateDocumentTotals(lines, allowances, charges) {
|
|
449
|
-
const taxSubtotals = calculateTaxSubtotals(lines, allowances, charges);
|
|
1036
|
+
function calculateDocumentTotals(lines, allowances, charges, options = {}) {
|
|
1037
|
+
const taxSubtotals = calculateTaxSubtotals(lines, allowances, charges, options);
|
|
450
1038
|
const lineExtensionAmount = lines.reduce((sum, line) => sum + calculateLineExtensionAmount(line), 0);
|
|
451
1039
|
const allowanceTotalAmount = (allowances ?? []).reduce((sum, a) => sum + a.amount, 0);
|
|
452
1040
|
const chargeTotalAmount = (charges ?? []).reduce((sum, c) => sum + c.amount, 0);
|
|
@@ -470,8 +1058,9 @@ function buildTaxTotalXml(taxSubtotals, totalTax, currency) {
|
|
|
470
1058
|
<cbc:TaxableAmount currencyID="${escapeXml(currency)}">${formatAmount(st.taxableAmount)}</cbc:TaxableAmount>
|
|
471
1059
|
<cbc:TaxAmount currencyID="${escapeXml(currency)}">${formatAmount(st.taxAmount)}</cbc:TaxAmount>
|
|
472
1060
|
<cac:TaxCategory>
|
|
473
|
-
<cbc:ID>${st.vatCategory}</cbc:ID>
|
|
474
|
-
|
|
1061
|
+
<cbc:ID>${escapeXml(st.vatCategory)}</cbc:ID>
|
|
1062
|
+
${st.vatCategory === "O" ? "" : `<cbc:Percent>${formatVatRate(st.vatRate, "vatRate")}</cbc:Percent>`}
|
|
1063
|
+
${st.taxExemptReason ? `<cbc:TaxExemptionReason>${escapeXml(st.taxExemptReason)}</cbc:TaxExemptionReason>` : ""}
|
|
475
1064
|
<cac:TaxScheme>
|
|
476
1065
|
<cbc:ID>VAT</cbc:ID>
|
|
477
1066
|
</cac:TaxScheme>
|
|
@@ -540,7 +1129,7 @@ function buildInvoiceXml(input) {
|
|
|
540
1129
|
const date = formatDate(input.date);
|
|
541
1130
|
const dueDate = input.dueDate ? formatDate(input.dueDate) : void 0;
|
|
542
1131
|
const hasTaxCurrency = input.taxCurrency && input.taxCurrency !== currency;
|
|
543
|
-
const totals = calculateDocumentTotals(input.lines, input.allowances, input.charges);
|
|
1132
|
+
const totals = calculateDocumentTotals(input.lines, input.allowances, input.charges, { forUbl: true });
|
|
544
1133
|
const linesXml = input.lines.map((line, i) => buildInvoiceLineXml(line, i, currency)).join("");
|
|
545
1134
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
546
1135
|
<Invoice xmlns="${UBL_NS}"
|
|
@@ -587,7 +1176,7 @@ function buildCreditNoteXml(input) {
|
|
|
587
1176
|
const date = formatDate(input.date);
|
|
588
1177
|
const dueDate = input.dueDate ? formatDate(input.dueDate) : void 0;
|
|
589
1178
|
const hasTaxCurrency = input.taxCurrency && input.taxCurrency !== currency;
|
|
590
|
-
const totals = calculateDocumentTotals(input.lines, input.allowances, input.charges);
|
|
1179
|
+
const totals = calculateDocumentTotals(input.lines, input.allowances, input.charges, { forUbl: true });
|
|
591
1180
|
const linesXml = input.lines.map((line, i) => buildCreditNoteLineXml(line, i, currency)).join("");
|
|
592
1181
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
593
1182
|
<CreditNote xmlns="${CREDIT_NOTE_NS}"
|
|
@@ -964,8 +1553,8 @@ function validateParty(party, path) {
|
|
|
964
1553
|
if (party.peppolId === void 0 || party.peppolId === null || party.peppolId === "") {
|
|
965
1554
|
errors.push(error(`${path}.peppolId`, "Peppol participant ID is required", void 0, 'Format: "scheme:id", e.g. "0208:0685660237" for Belgian companies'));
|
|
966
1555
|
} else if (!assertString(party.peppolId, `${path}.peppolId`, errors)) {
|
|
967
|
-
} else if (!party.peppolId
|
|
968
|
-
errors.push(error(`${path}.peppolId`, `Invalid Peppol ID format: "${party.peppolId}"`, void 0, 'Must be "scheme:id"
|
|
1556
|
+
} else if (!isWellFormedPeppolId(party.peppolId)) {
|
|
1557
|
+
errors.push(error(`${path}.peppolId`, `Invalid Peppol ID format: "${party.peppolId}"`, void 0, 'Must be "scheme:id" \u2014 e.g. "0208:0685660237" or "GB:VAT:123456789". The scheme alone ("GB:VAT") is not an identifier.'));
|
|
969
1558
|
}
|
|
970
1559
|
if (party.country === void 0 || party.country === null || party.country === "") {
|
|
971
1560
|
errors.push(error(`${path}.country`, "Country code is required", "BR-11"));
|
|
@@ -1047,6 +1636,14 @@ function validateInvoice(input) {
|
|
|
1047
1636
|
}
|
|
1048
1637
|
if (input.from) {
|
|
1049
1638
|
warnings.push(warning("from", "Seller info is determined by your API key. The 'from' field is deprecated and ignored."));
|
|
1639
|
+
const fromId = input.from.peppolId;
|
|
1640
|
+
if (typeof fromId === "string" && fromId.length > 0) {
|
|
1641
|
+
const { scheme } = parsePeppolId(fromId);
|
|
1642
|
+
const bt29Written = canCarryPartyIdentification(scheme, "AccountingSupplierParty");
|
|
1643
|
+
if (!bt29Written && !input.from.vatNumber && !input.from.companyId) {
|
|
1644
|
+
warnings.push(warning("from.peppolId", `Scheme "${scheme}" cannot carry a seller identifier (BT-29), so that field is omitted. Add a vatNumber (BT-31) or companyId (BT-30), or the network will reject the document under BR-CO-26.`, "BR-CO-26"));
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1050
1647
|
}
|
|
1051
1648
|
if (!input.to) {
|
|
1052
1649
|
errors.push(error("to", "Buyer (to) is required", "BR-07"));
|
|
@@ -1066,15 +1663,36 @@ function validateInvoice(input) {
|
|
|
1066
1663
|
if (ppId === void 0 || ppId === null || ppId === "") {
|
|
1067
1664
|
errors.push(error("payeeParty.peppolId", "Payee party Peppol ID is required", void 0, 'Format: "scheme:id", e.g. "0208:0685660237"'));
|
|
1068
1665
|
} else if (!assertString(ppId, "payeeParty.peppolId", errors)) {
|
|
1069
|
-
} else if (!ppId
|
|
1070
|
-
errors.push(error("payeeParty.peppolId", `Invalid Peppol ID format: "${ppId}"`, void 0, 'Must be "scheme:id"
|
|
1666
|
+
} else if (!isWellFormedPeppolId(ppId)) {
|
|
1667
|
+
errors.push(error("payeeParty.peppolId", `Invalid Peppol ID format: "${ppId}"`, void 0, 'Must be "scheme:id" \u2014 e.g. "0208:0685660237". The scheme alone ("GB:VAT") is not an identifier.'));
|
|
1071
1668
|
}
|
|
1072
1669
|
}
|
|
1073
|
-
|
|
1670
|
+
const linesValue = input.lines;
|
|
1671
|
+
if (!Array.isArray(linesValue)) {
|
|
1672
|
+
errors.push(error("lines", "Line items must be an array", void 0));
|
|
1673
|
+
} else if (linesValue.length === 0) {
|
|
1074
1674
|
errors.push(error("lines", "At least one line item is required", "BR-16", "Add items to the lines array"));
|
|
1075
1675
|
} else {
|
|
1076
|
-
for (
|
|
1077
|
-
|
|
1676
|
+
for (const [i, line] of linesValue.entries()) {
|
|
1677
|
+
if (line === null || typeof line !== "object") {
|
|
1678
|
+
errors.push(error(`lines[${i}]`, `Line item ${i} must be an object`, void 0));
|
|
1679
|
+
continue;
|
|
1680
|
+
}
|
|
1681
|
+
errors.push(...validateLine(line, i, input.isCreditNote));
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
for (const field of ["allowances", "charges"]) {
|
|
1685
|
+
const value = input[field];
|
|
1686
|
+
if (value === void 0)
|
|
1687
|
+
continue;
|
|
1688
|
+
if (!Array.isArray(value)) {
|
|
1689
|
+
errors.push(error(field, `${field} must be an array`, void 0));
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1692
|
+
for (const [index, item] of value.entries()) {
|
|
1693
|
+
if (item === null || typeof item !== "object") {
|
|
1694
|
+
errors.push(error(`${field}[${index}]`, `${field}[${index}] must be an object`, void 0));
|
|
1695
|
+
}
|
|
1078
1696
|
}
|
|
1079
1697
|
}
|
|
1080
1698
|
if (input.date) {
|
|
@@ -1136,154 +1754,679 @@ function validateInvoice(input) {
|
|
|
1136
1754
|
};
|
|
1137
1755
|
}
|
|
1138
1756
|
|
|
1139
|
-
// ../sdk/dist/core/
|
|
1140
|
-
|
|
1141
|
-
{
|
|
1142
|
-
{ status: "rejected", family: "terminal-failure" },
|
|
1143
|
-
{ status: "paid", family: "terminal-success" },
|
|
1144
|
-
{ status: "partially_paid", family: "progress" },
|
|
1145
|
-
{ status: "accepted", family: "progress" },
|
|
1146
|
-
{ status: "conditionally_accepted", family: "progress" },
|
|
1147
|
-
{ status: "under_query", family: "progress" },
|
|
1148
|
-
{ status: "in_process", family: "progress" },
|
|
1149
|
-
{ status: "cleared", family: "progress" },
|
|
1150
|
-
{ status: "delivered", family: "progress" },
|
|
1151
|
-
{ status: "acknowledged", family: "progress" },
|
|
1152
|
-
// Terminal for developer wait semantics only — stays rank 40 (non-terminal)
|
|
1153
|
-
// in the projection guard (§3.12 two-level terminality).
|
|
1154
|
-
{ status: "no_action", family: "terminal-failure" },
|
|
1155
|
-
{ status: "submitted", family: "progress" },
|
|
1156
|
-
{ status: "unknown", family: "fallback" }
|
|
1157
|
-
];
|
|
1158
|
-
function statusFamily(status) {
|
|
1159
|
-
return STATUS_PRECEDENCE.find((e) => e.status === status)?.family ?? "fallback";
|
|
1757
|
+
// ../sdk/dist/core/schematron.js
|
|
1758
|
+
function violation(ruleId, severity, message, field) {
|
|
1759
|
+
return { ruleId, severity, message, field };
|
|
1160
1760
|
}
|
|
1161
|
-
var
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
// ../sdk/dist/core/client.js
|
|
1167
|
-
function findHeaderCaseInsensitive(headers, name) {
|
|
1168
|
-
if (!headers)
|
|
1169
|
-
return void 0;
|
|
1170
|
-
const target = name.toLowerCase();
|
|
1171
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
1172
|
-
if (key.toLowerCase() === target)
|
|
1173
|
-
return value;
|
|
1761
|
+
var _knownUnitCodes;
|
|
1762
|
+
function getKnownUnitCodes() {
|
|
1763
|
+
if (!_knownUnitCodes) {
|
|
1764
|
+
_knownUnitCodes = new Set(getAllUnits().map((u) => u.code));
|
|
1174
1765
|
}
|
|
1175
|
-
return
|
|
1176
|
-
}
|
|
1177
|
-
var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
1178
|
-
function sleep(ms) {
|
|
1179
|
-
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
1766
|
+
return _knownUnitCodes;
|
|
1180
1767
|
}
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
const
|
|
1186
|
-
|
|
1768
|
+
var VALID_VAT_CATEGORIES = /* @__PURE__ */ new Set(["S", "Z", "E", "AE", "K", "G", "O", "L", "M", "B"]);
|
|
1769
|
+
var SENDABLE_VAT_CATEGORIES = ["S", "Z", "E", "AE", "K", "G", "O"];
|
|
1770
|
+
var UNROUTABLE_VAT_CATEGORIES = /* @__PURE__ */ new Set(["L", "M", "B"]);
|
|
1771
|
+
function computeLineNet(line) {
|
|
1772
|
+
const baseQty = line.baseQuantity ?? 1;
|
|
1773
|
+
if (baseQty === 0)
|
|
1774
|
+
return NaN;
|
|
1775
|
+
const baseAmount = line.quantity * line.unitPrice / baseQty;
|
|
1776
|
+
const chargeTotal = (line.charges ?? []).reduce((sum, c) => sum + c.amount, 0);
|
|
1777
|
+
const allowanceTotal = (line.allowances ?? []).reduce((sum, a) => sum + a.amount, 0);
|
|
1778
|
+
return baseAmount + chargeTotal - allowanceTotal;
|
|
1187
1779
|
}
|
|
1188
|
-
|
|
1189
|
-
if (!
|
|
1190
|
-
return
|
|
1191
|
-
const seconds = Number(headerValue);
|
|
1192
|
-
if (Number.isFinite(seconds) && seconds >= 0) {
|
|
1193
|
-
return seconds * 1e3;
|
|
1194
|
-
}
|
|
1195
|
-
const dateMs = Date.parse(headerValue);
|
|
1196
|
-
if (!Number.isNaN(dateMs)) {
|
|
1197
|
-
const delayMs = dateMs - Date.now();
|
|
1198
|
-
return delayMs > 0 ? delayMs : 0;
|
|
1780
|
+
var br02 = (input) => {
|
|
1781
|
+
if (!input.number?.trim()) {
|
|
1782
|
+
return [violation("BR-02", "error", "Invoice number is required.", "number")];
|
|
1199
1783
|
}
|
|
1200
|
-
return
|
|
1201
|
-
}
|
|
1202
|
-
var
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
return Object.hasOwn(source, key) ? source[key] : void 0;
|
|
1208
|
-
}
|
|
1209
|
-
function readSentence(source, key) {
|
|
1210
|
-
const value = readOwn(source, key);
|
|
1211
|
-
if (typeof value !== "string")
|
|
1212
|
-
return null;
|
|
1213
|
-
const cleaned = stripControls(value).trim();
|
|
1214
|
-
return cleaned === "" ? null : cleaned;
|
|
1215
|
-
}
|
|
1216
|
-
function safeDocsUrl(value) {
|
|
1217
|
-
if (typeof value !== "string")
|
|
1218
|
-
return null;
|
|
1219
|
-
let parsed;
|
|
1220
|
-
try {
|
|
1221
|
-
parsed = new URL(value);
|
|
1222
|
-
} catch {
|
|
1223
|
-
return null;
|
|
1784
|
+
return [];
|
|
1785
|
+
};
|
|
1786
|
+
var br03 = (input) => {
|
|
1787
|
+
if (!input.date) {
|
|
1788
|
+
return [
|
|
1789
|
+
violation("BR-03", "warning", "Invoice issue date is not set. The SDK will default to today's date.", "date")
|
|
1790
|
+
];
|
|
1224
1791
|
}
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
try {
|
|
1233
|
-
parsed = JSON.parse(rawBody);
|
|
1234
|
-
} catch {
|
|
1235
|
-
return verbatim;
|
|
1792
|
+
return [];
|
|
1793
|
+
};
|
|
1794
|
+
var br06 = (input) => {
|
|
1795
|
+
if (input.from && !input.from.vatNumber) {
|
|
1796
|
+
return [
|
|
1797
|
+
violation("BR-06", "warning", "Seller party has no VAT number. The gateway will use the account's VAT registration.", "from.vatNumber")
|
|
1798
|
+
];
|
|
1236
1799
|
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1800
|
+
return [];
|
|
1801
|
+
};
|
|
1802
|
+
var br07 = (input) => {
|
|
1803
|
+
if (!input.to?.name?.trim()) {
|
|
1804
|
+
return [violation("BR-07", "error", "Buyer name is required.", "to.name")];
|
|
1239
1805
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
}
|
|
1246
|
-
function isRetryableError(error2) {
|
|
1247
|
-
if (error2 instanceof PeppolApiError) {
|
|
1248
|
-
return RETRYABLE_STATUS_CODES.has(error2.statusCode);
|
|
1806
|
+
return [];
|
|
1807
|
+
};
|
|
1808
|
+
var br08 = (input) => {
|
|
1809
|
+
if (!input.lines || input.lines.length === 0) {
|
|
1810
|
+
return [violation("BR-08", "error", "Invoice must have at least one line item.", "lines")];
|
|
1249
1811
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1812
|
+
return [];
|
|
1813
|
+
};
|
|
1814
|
+
var br09 = (input) => {
|
|
1815
|
+
if (!input.dueDate && !input.paymentTerms) {
|
|
1816
|
+
return [
|
|
1817
|
+
violation("BR-09", "warning", "Neither due date nor payment terms specified. At least one is recommended.", "dueDate")
|
|
1818
|
+
];
|
|
1252
1819
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1820
|
+
return [];
|
|
1821
|
+
};
|
|
1822
|
+
var br10 = (input) => {
|
|
1823
|
+
if (!input.buyerReference && !input.orderReference) {
|
|
1824
|
+
return [
|
|
1825
|
+
violation("BR-10", "warning", "Neither buyerReference nor orderReference specified. Peppol BIS 3.0 requires at least one.", "buyerReference")
|
|
1826
|
+
];
|
|
1255
1827
|
}
|
|
1256
|
-
return
|
|
1257
|
-
}
|
|
1258
|
-
var
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
1271
|
-
this.retryConfig = {
|
|
1272
|
-
maxRetries: config.retry?.maxRetries ?? 3,
|
|
1273
|
-
initialDelayMs: config.retry?.initialDelayMs ?? 500,
|
|
1274
|
-
maxDelayMs: config.retry?.maxDelayMs ?? 3e4
|
|
1275
|
-
};
|
|
1276
|
-
this.onRequest = config.onRequest;
|
|
1277
|
-
this.onResponse = config.onResponse;
|
|
1828
|
+
return [];
|
|
1829
|
+
};
|
|
1830
|
+
var brCo10 = (input) => {
|
|
1831
|
+
const violations = [];
|
|
1832
|
+
if (!input.lines)
|
|
1833
|
+
return violations;
|
|
1834
|
+
for (let i = 0; i < input.lines.length; i++) {
|
|
1835
|
+
const line = input.lines[i];
|
|
1836
|
+
const net = computeLineNet(line);
|
|
1837
|
+
if (!Number.isFinite(net)) {
|
|
1838
|
+
const baseQty = line.baseQuantity ?? 1;
|
|
1839
|
+
const detail = baseQty === 0 ? "baseQuantity is 0, causing division by zero." : "Computed line amount is not a finite number.";
|
|
1840
|
+
violations.push(violation("BR-CO-10", "error", `Line ${i}: invalid net amount. ${detail}`, `lines[${i}]`));
|
|
1841
|
+
}
|
|
1278
1842
|
}
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1843
|
+
return violations;
|
|
1844
|
+
};
|
|
1845
|
+
var brCo13 = (input) => {
|
|
1846
|
+
if (!input.lines || input.lines.length === 0)
|
|
1847
|
+
return [];
|
|
1848
|
+
let totalVat = 0;
|
|
1849
|
+
for (let i = 0; i < input.lines.length; i++) {
|
|
1850
|
+
const line = input.lines[i];
|
|
1851
|
+
const net = computeLineNet(line);
|
|
1852
|
+
if (!Number.isFinite(net))
|
|
1853
|
+
continue;
|
|
1854
|
+
totalVat += net * (line.vatRate / 100);
|
|
1855
|
+
}
|
|
1856
|
+
for (const allowance of input.allowances ?? []) {
|
|
1857
|
+
totalVat -= allowance.amount * (allowance.vatRate / 100);
|
|
1858
|
+
}
|
|
1859
|
+
for (const charge of input.charges ?? []) {
|
|
1860
|
+
totalVat += charge.amount * (charge.vatRate / 100);
|
|
1861
|
+
}
|
|
1862
|
+
if (!Number.isFinite(totalVat)) {
|
|
1863
|
+
return [
|
|
1864
|
+
violation("BR-CO-13", "error", "Computed total VAT amount is not a finite number. Check line amounts and VAT rates.")
|
|
1865
|
+
];
|
|
1866
|
+
}
|
|
1867
|
+
if (totalVat < -0.01) {
|
|
1868
|
+
return [
|
|
1869
|
+
violation("BR-CO-13", "warning", `Computed total VAT is negative (${totalVat.toFixed(2)}). This is unusual for an invoice.`)
|
|
1870
|
+
];
|
|
1871
|
+
}
|
|
1872
|
+
return [];
|
|
1873
|
+
};
|
|
1874
|
+
var brCo15 = (input) => {
|
|
1875
|
+
if (!input.lines || input.lines.length === 0)
|
|
1876
|
+
return [];
|
|
1877
|
+
let lineTotal = 0;
|
|
1878
|
+
let vatTotal = 0;
|
|
1879
|
+
for (const line of input.lines) {
|
|
1880
|
+
const net = computeLineNet(line);
|
|
1881
|
+
if (!Number.isFinite(net))
|
|
1882
|
+
continue;
|
|
1883
|
+
lineTotal += net;
|
|
1884
|
+
vatTotal += net * (line.vatRate / 100);
|
|
1885
|
+
}
|
|
1886
|
+
for (const allowance of input.allowances ?? []) {
|
|
1887
|
+
lineTotal -= allowance.amount;
|
|
1888
|
+
vatTotal -= allowance.amount * (allowance.vatRate / 100);
|
|
1889
|
+
}
|
|
1890
|
+
for (const charge of input.charges ?? []) {
|
|
1891
|
+
lineTotal += charge.amount;
|
|
1892
|
+
vatTotal += charge.amount * (charge.vatRate / 100);
|
|
1893
|
+
}
|
|
1894
|
+
const taxInclusive = lineTotal + vatTotal;
|
|
1895
|
+
if (!Number.isFinite(taxInclusive)) {
|
|
1896
|
+
return [
|
|
1897
|
+
violation("BR-CO-15", "error", "Computed tax-inclusive amount is not a finite number.")
|
|
1898
|
+
];
|
|
1899
|
+
}
|
|
1900
|
+
if (taxInclusive < -0.01) {
|
|
1901
|
+
return [
|
|
1902
|
+
violation("BR-CO-15", "warning", `Computed tax-inclusive amount is negative (${taxInclusive.toFixed(2)}). Consider using a credit note instead.`)
|
|
1903
|
+
];
|
|
1904
|
+
}
|
|
1905
|
+
return [];
|
|
1906
|
+
};
|
|
1907
|
+
var brCo16 = (input) => {
|
|
1908
|
+
if (!input.lines || input.lines.length === 0)
|
|
1909
|
+
return [];
|
|
1910
|
+
let lineTotal = 0;
|
|
1911
|
+
let vatTotal = 0;
|
|
1912
|
+
for (const line of input.lines) {
|
|
1913
|
+
const net = computeLineNet(line);
|
|
1914
|
+
if (!Number.isFinite(net))
|
|
1915
|
+
continue;
|
|
1916
|
+
lineTotal += net;
|
|
1917
|
+
vatTotal += net * (line.vatRate / 100);
|
|
1918
|
+
}
|
|
1919
|
+
for (const allowance of input.allowances ?? []) {
|
|
1920
|
+
lineTotal -= allowance.amount;
|
|
1921
|
+
vatTotal -= allowance.amount * (allowance.vatRate / 100);
|
|
1922
|
+
}
|
|
1923
|
+
for (const charge of input.charges ?? []) {
|
|
1924
|
+
lineTotal += charge.amount;
|
|
1925
|
+
vatTotal += charge.amount * (charge.vatRate / 100);
|
|
1926
|
+
}
|
|
1927
|
+
const taxInclusive = lineTotal + vatTotal;
|
|
1928
|
+
const prepaid = input.prepaidAmount ?? 0;
|
|
1929
|
+
const rounding = input.roundingAmount ?? 0;
|
|
1930
|
+
const payable = taxInclusive - prepaid + rounding;
|
|
1931
|
+
if (!Number.isFinite(payable)) {
|
|
1932
|
+
return [
|
|
1933
|
+
violation("BR-CO-16", "error", "Computed payable amount is not a finite number.")
|
|
1934
|
+
];
|
|
1935
|
+
}
|
|
1936
|
+
if (payable < -0.01) {
|
|
1937
|
+
return [
|
|
1938
|
+
violation("BR-CO-16", "warning", `Computed payable amount is negative (${payable.toFixed(2)}). Prepaid amount (${prepaid}) exceeds the invoice total.`)
|
|
1939
|
+
];
|
|
1940
|
+
}
|
|
1941
|
+
return [];
|
|
1942
|
+
};
|
|
1943
|
+
var brS05 = (input) => {
|
|
1944
|
+
const violations = [];
|
|
1945
|
+
if (!input.lines)
|
|
1946
|
+
return violations;
|
|
1947
|
+
for (let i = 0; i < input.lines.length; i++) {
|
|
1948
|
+
const line = input.lines[i];
|
|
1949
|
+
const category = line.vatCategory ?? "S";
|
|
1950
|
+
if (category === "S" && (line.vatRate === void 0 || line.vatRate <= 0)) {
|
|
1951
|
+
violations.push(violation("BR-S-05", "error", `Line ${i}: standard rate (S) requires vatRate > 0, got ${line.vatRate ?? "undefined"}.`, `lines[${i}].vatRate`));
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
return violations;
|
|
1955
|
+
};
|
|
1956
|
+
var brZ05 = (input) => {
|
|
1957
|
+
const violations = [];
|
|
1958
|
+
if (!input.lines)
|
|
1959
|
+
return violations;
|
|
1960
|
+
for (let i = 0; i < input.lines.length; i++) {
|
|
1961
|
+
const line = input.lines[i];
|
|
1962
|
+
if (line.vatCategory === "Z" && line.vatRate !== 0) {
|
|
1963
|
+
violations.push(violation("BR-Z-05", "error", `Line ${i}: zero-rated (Z) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
return violations;
|
|
1967
|
+
};
|
|
1968
|
+
var brE05 = (input) => {
|
|
1969
|
+
const violations = [];
|
|
1970
|
+
if (!input.lines)
|
|
1971
|
+
return violations;
|
|
1972
|
+
for (let i = 0; i < input.lines.length; i++) {
|
|
1973
|
+
const line = input.lines[i];
|
|
1974
|
+
if (line.vatCategory === "E" && line.vatRate !== 0) {
|
|
1975
|
+
violations.push(violation("BR-E-05", "error", `Line ${i}: exempt (E) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
return violations;
|
|
1979
|
+
};
|
|
1980
|
+
var brAe05 = (input) => {
|
|
1981
|
+
const violations = [];
|
|
1982
|
+
if (!input.lines)
|
|
1983
|
+
return violations;
|
|
1984
|
+
for (let i = 0; i < input.lines.length; i++) {
|
|
1985
|
+
const line = input.lines[i];
|
|
1986
|
+
if (line.vatCategory === "AE" && line.vatRate !== 0) {
|
|
1987
|
+
violations.push(violation("BR-AE-05", "error", `Line ${i}: reverse charge (AE) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
return violations;
|
|
1991
|
+
};
|
|
1992
|
+
var brG05 = (input) => {
|
|
1993
|
+
const violations = [];
|
|
1994
|
+
for (let i = 0; i < (input.lines ?? []).length; i++) {
|
|
1995
|
+
const line = input.lines[i];
|
|
1996
|
+
if (line.vatCategory === "G" && line.vatRate !== 0) {
|
|
1997
|
+
violations.push(violation("BR-G-05", "error", `Line ${i}: export outside the EU (G) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
return violations;
|
|
2001
|
+
};
|
|
2002
|
+
var brIc05 = (input) => {
|
|
2003
|
+
const violations = [];
|
|
2004
|
+
for (let i = 0; i < (input.lines ?? []).length; i++) {
|
|
2005
|
+
const line = input.lines[i];
|
|
2006
|
+
if (line.vatCategory === "K" && line.vatRate !== 0) {
|
|
2007
|
+
violations.push(violation("BR-IC-05", "error", `Line ${i}: intra-community supply (K) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
return violations;
|
|
2011
|
+
};
|
|
2012
|
+
var documentAdjustmentVatRates = (input) => {
|
|
2013
|
+
const violations = [];
|
|
2014
|
+
const configs = /* @__PURE__ */ new Map([
|
|
2015
|
+
["S", { allowanceRule: "BR-S-06", chargeRule: "BR-S-07", valid: (rate) => rate > 0, label: "standard rate (S)" }],
|
|
2016
|
+
["Z", { allowanceRule: "BR-Z-06", chargeRule: "BR-Z-07", valid: (rate) => rate === 0, label: "zero-rated (Z)" }],
|
|
2017
|
+
["E", { allowanceRule: "BR-E-06", chargeRule: "BR-E-07", valid: (rate) => rate === 0, label: "exempt (E)" }],
|
|
2018
|
+
["AE", { allowanceRule: "BR-AE-06", chargeRule: "BR-AE-07", valid: (rate) => rate === 0, label: "reverse charge (AE)" }],
|
|
2019
|
+
["G", { allowanceRule: "BR-G-06", chargeRule: "BR-G-07", valid: (rate) => rate === 0, label: "export outside the EU (G)" }],
|
|
2020
|
+
["K", { allowanceRule: "BR-IC-06", chargeRule: "BR-IC-07", valid: (rate) => rate === 0, label: "intra-community supply (K)" }]
|
|
2021
|
+
]);
|
|
2022
|
+
const check = (items, kind) => {
|
|
2023
|
+
(items ?? []).forEach((item, index) => {
|
|
2024
|
+
const category = item.vatCategory ?? "S";
|
|
2025
|
+
const config = configs.get(category);
|
|
2026
|
+
if (!config || config.valid(item.vatRate))
|
|
2027
|
+
return;
|
|
2028
|
+
const ruleId = kind === "allowance" ? config.allowanceRule : config.chargeRule;
|
|
2029
|
+
violations.push(violation(ruleId, "error", `Document ${kind} ${index}: ${config.label} has an invalid vatRate (${item.vatRate}).`, `${kind === "allowance" ? "allowances" : "charges"}[${index}].vatRate`));
|
|
2030
|
+
});
|
|
2031
|
+
};
|
|
2032
|
+
check(input.allowances, "allowance");
|
|
2033
|
+
check(input.charges, "charge");
|
|
2034
|
+
return violations;
|
|
2035
|
+
};
|
|
2036
|
+
function exemptionReasonRule(vatCategory, ruleId, label) {
|
|
2037
|
+
return (input) => {
|
|
2038
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2039
|
+
const add = (category, rate, reason, field) => {
|
|
2040
|
+
if (category !== vatCategory)
|
|
2041
|
+
return;
|
|
2042
|
+
const effectiveRate = category === "O" ? 0 : rate;
|
|
2043
|
+
const key = `${category}-${effectiveRate}`;
|
|
2044
|
+
const hasReason = typeof reason === "string" && reason.trim().length > 0;
|
|
2045
|
+
const existing = groups.get(key);
|
|
2046
|
+
if (existing) {
|
|
2047
|
+
existing.hasReason ||= hasReason;
|
|
2048
|
+
} else {
|
|
2049
|
+
groups.set(key, { hasReason, field });
|
|
2050
|
+
}
|
|
2051
|
+
};
|
|
2052
|
+
(input.lines ?? []).forEach((line, index) => {
|
|
2053
|
+
add(line.vatCategory ?? "S", line.vatRate, line.taxExemptReason, `lines[${index}].taxExemptReason`);
|
|
2054
|
+
});
|
|
2055
|
+
(input.allowances ?? []).forEach((item, index) => {
|
|
2056
|
+
add(item.vatCategory ?? "S", item.vatRate, item.taxExemptReason, `allowances[${index}].taxExemptReason`);
|
|
2057
|
+
});
|
|
2058
|
+
(input.charges ?? []).forEach((item, index) => {
|
|
2059
|
+
add(item.vatCategory ?? "S", item.vatRate, item.taxExemptReason, `charges[${index}].taxExemptReason`);
|
|
2060
|
+
});
|
|
2061
|
+
return [...groups.values()].filter((group) => !group.hasReason).map((group) => violation(ruleId, "error", `${label} requires a non-empty taxExemptReason in its VAT breakdown.`, group.field));
|
|
2062
|
+
};
|
|
2063
|
+
}
|
|
2064
|
+
var brE10 = exemptionReasonRule("E", "BR-E-10", "Exempt from VAT (E)");
|
|
2065
|
+
var brAe10 = exemptionReasonRule("AE", "BR-AE-10", "Reverse charge (AE)");
|
|
2066
|
+
var brG10 = exemptionReasonRule("G", "BR-G-10", "Export outside the EU (G)");
|
|
2067
|
+
var brO10 = exemptionReasonRule("O", "BR-O-10", "Not subject to VAT (O)");
|
|
2068
|
+
var brIc10 = exemptionReasonRule("K", "BR-IC-10", "Intra-community supply (K)");
|
|
2069
|
+
var builderVatCategoryValidity = (input) => {
|
|
2070
|
+
const violations = [];
|
|
2071
|
+
const check = (category, field) => {
|
|
2072
|
+
if (category !== void 0 && !VALID_VAT_CATEGORIES.has(category)) {
|
|
2073
|
+
violations.push(violation("BR-CL-17", "error", "VAT category is not an EN 16931 code.", field));
|
|
2074
|
+
}
|
|
2075
|
+
};
|
|
2076
|
+
(input.lines ?? []).forEach((item, index) => check(item.vatCategory, `lines[${index}].vatCategory`));
|
|
2077
|
+
(input.allowances ?? []).forEach((item, index) => check(item.vatCategory, `allowances[${index}].vatCategory`));
|
|
2078
|
+
(input.charges ?? []).forEach((item, index) => check(item.vatCategory, `charges[${index}].vatCategory`));
|
|
2079
|
+
return violations;
|
|
2080
|
+
};
|
|
2081
|
+
var UBL_BUILDER_VAT_RULES = [
|
|
2082
|
+
builderVatCategoryValidity,
|
|
2083
|
+
brS05,
|
|
2084
|
+
brZ05,
|
|
2085
|
+
brE05,
|
|
2086
|
+
brAe05,
|
|
2087
|
+
brG05,
|
|
2088
|
+
brIc05,
|
|
2089
|
+
documentAdjustmentVatRates,
|
|
2090
|
+
brE10,
|
|
2091
|
+
brAe10,
|
|
2092
|
+
brG10,
|
|
2093
|
+
brO10,
|
|
2094
|
+
brIc10
|
|
2095
|
+
];
|
|
2096
|
+
function validateUblBuilderVat(input) {
|
|
2097
|
+
const shapeViolations = [];
|
|
2098
|
+
const checkCollection = (value, field) => {
|
|
2099
|
+
if (field === "lines" || value !== void 0) {
|
|
2100
|
+
if (!Array.isArray(value)) {
|
|
2101
|
+
shapeViolations.push(violation("SDK-INPUT", "error", `${field} must be an array.`, field));
|
|
2102
|
+
return;
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
if (!Array.isArray(value))
|
|
2106
|
+
return;
|
|
2107
|
+
for (const [index, item] of value.entries()) {
|
|
2108
|
+
const itemField = `${field}[${index}]`;
|
|
2109
|
+
if (item === null || typeof item !== "object") {
|
|
2110
|
+
shapeViolations.push(violation("SDK-INPUT", "error", `${itemField} must be an object.`, itemField));
|
|
2111
|
+
continue;
|
|
2112
|
+
}
|
|
2113
|
+
const candidate = item;
|
|
2114
|
+
if (typeof candidate.vatRate !== "number" || !Number.isFinite(candidate.vatRate)) {
|
|
2115
|
+
shapeViolations.push(violation("SDK-INPUT", "error", `${itemField}.vatRate must be a finite number.`, `${itemField}.vatRate`));
|
|
2116
|
+
}
|
|
2117
|
+
if (candidate.vatCategory !== void 0 && typeof candidate.vatCategory !== "string") {
|
|
2118
|
+
shapeViolations.push(violation("SDK-INPUT", "error", `${itemField}.vatCategory must be a string.`, `${itemField}.vatCategory`));
|
|
2119
|
+
}
|
|
2120
|
+
if (candidate.taxExemptReason !== void 0 && typeof candidate.taxExemptReason !== "string") {
|
|
2121
|
+
shapeViolations.push(violation("SDK-INPUT", "error", `${itemField}.taxExemptReason must be a string.`, `${itemField}.taxExemptReason`));
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
};
|
|
2125
|
+
checkCollection(input.lines, "lines");
|
|
2126
|
+
checkCollection(input.allowances, "allowances");
|
|
2127
|
+
checkCollection(input.charges, "charges");
|
|
2128
|
+
if (shapeViolations.length > 0)
|
|
2129
|
+
return shapeViolations;
|
|
2130
|
+
const oRateViolations = [];
|
|
2131
|
+
const checkORates = (items, field) => {
|
|
2132
|
+
(items ?? []).forEach((item, index) => {
|
|
2133
|
+
if (item.vatCategory === "O" && item.vatRate !== 0) {
|
|
2134
|
+
oRateViolations.push(violation("SDK-INPUT", "error", `Category O must use vatRate 0 in SDK input.`, `${field}[${index}].vatRate`));
|
|
2135
|
+
}
|
|
2136
|
+
});
|
|
2137
|
+
};
|
|
2138
|
+
checkORates(input.lines, "lines");
|
|
2139
|
+
checkORates(input.allowances, "allowances");
|
|
2140
|
+
checkORates(input.charges, "charges");
|
|
2141
|
+
return [
|
|
2142
|
+
...oRateViolations,
|
|
2143
|
+
...UBL_BUILDER_VAT_RULES.flatMap((rule) => rule(input))
|
|
2144
|
+
];
|
|
2145
|
+
}
|
|
2146
|
+
var peppolR004 = (input) => {
|
|
2147
|
+
if (!input.to?.peppolId) {
|
|
2148
|
+
return [
|
|
2149
|
+
violation("PEPPOL-EN16931-R004", "error", "Buyer electronic address (peppolId) is required for Peppol delivery.", "to.peppolId")
|
|
2150
|
+
];
|
|
2151
|
+
}
|
|
2152
|
+
return [];
|
|
2153
|
+
};
|
|
2154
|
+
var vatCategoryCodes = (input) => {
|
|
2155
|
+
const violations = [];
|
|
2156
|
+
const sendable = SENDABLE_VAT_CATEGORIES.join(", ");
|
|
2157
|
+
const echo = (v) => v.length <= 16 ? v : `${v.slice(0, 16)}\u2026`;
|
|
2158
|
+
const check = (cat, field, label) => {
|
|
2159
|
+
if (cat === void 0 || cat === null)
|
|
2160
|
+
return;
|
|
2161
|
+
if (!VALID_VAT_CATEGORIES.has(cat)) {
|
|
2162
|
+
violations.push(violation("BR-CL-17", "error", `${label}: "${echo(cat)}" is not a VAT category code. Use one of: ${sendable}. Codes are case-sensitive \u2014 "AE" is reverse charge, "ae" is not a category.`, field));
|
|
2163
|
+
return;
|
|
2164
|
+
}
|
|
2165
|
+
if (UNROUTABLE_VAT_CATEGORIES.has(cat)) {
|
|
2166
|
+
violations.push(violation("unsupported_vat_category", "error", `${label}: VAT category "${echo(cat)}" is valid under EN 16931 but getpeppr cannot route it \u2014 our provider has no vocabulary for it. Sendable categories: ${sendable}.`, field));
|
|
2167
|
+
}
|
|
2168
|
+
};
|
|
2169
|
+
(input.lines ?? []).forEach((line, i) => check(line.vatCategory, `lines[${i}].vatCategory`, `Line ${i}`));
|
|
2170
|
+
(input.allowances ?? []).forEach((a, i) => check(a.vatCategory, `allowances[${i}].vatCategory`, `Allowance ${i}`));
|
|
2171
|
+
(input.charges ?? []).forEach((c, i) => check(c.vatCategory, `charges[${i}].vatCategory`, `Charge ${i}`));
|
|
2172
|
+
return violations;
|
|
2173
|
+
};
|
|
2174
|
+
var peppolR080 = (input) => {
|
|
2175
|
+
const violations = [];
|
|
2176
|
+
if (!input.lines)
|
|
2177
|
+
return violations;
|
|
2178
|
+
const knownCodes = getKnownUnitCodes();
|
|
2179
|
+
for (let i = 0; i < input.lines.length; i++) {
|
|
2180
|
+
const line = input.lines[i];
|
|
2181
|
+
if (line.unit) {
|
|
2182
|
+
const resolved = resolveUnit(line.unit);
|
|
2183
|
+
if (!knownCodes.has(resolved)) {
|
|
2184
|
+
violations.push(violation("PEPPOL-EN16931-R080", "warning", `Line ${i}: unit "${line.unit}" (resolved: "${resolved}") is not a known UN/ECE Rec20 code. Common codes: EA, HUR, DAY, KGM.`, `lines[${i}].unit`));
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
return violations;
|
|
2189
|
+
};
|
|
2190
|
+
var ALL_RULES = [
|
|
2191
|
+
// Required fields (BR)
|
|
2192
|
+
br02,
|
|
2193
|
+
br03,
|
|
2194
|
+
br06,
|
|
2195
|
+
br07,
|
|
2196
|
+
br08,
|
|
2197
|
+
br09,
|
|
2198
|
+
br10,
|
|
2199
|
+
// Calculations (BR-CO)
|
|
2200
|
+
brCo10,
|
|
2201
|
+
brCo13,
|
|
2202
|
+
brCo15,
|
|
2203
|
+
brCo16,
|
|
2204
|
+
// Tax categories (one family per category)
|
|
2205
|
+
brS05,
|
|
2206
|
+
brZ05,
|
|
2207
|
+
brE05,
|
|
2208
|
+
brAe05,
|
|
2209
|
+
brG05,
|
|
2210
|
+
brIc05,
|
|
2211
|
+
documentAdjustmentVatRates,
|
|
2212
|
+
brE10,
|
|
2213
|
+
brAe10,
|
|
2214
|
+
brG10,
|
|
2215
|
+
brO10,
|
|
2216
|
+
brIc10,
|
|
2217
|
+
// Peppol-specific
|
|
2218
|
+
peppolR004,
|
|
2219
|
+
vatCategoryCodes,
|
|
2220
|
+
peppolR080
|
|
2221
|
+
];
|
|
2222
|
+
function validateSchematron(input) {
|
|
2223
|
+
const errors = [];
|
|
2224
|
+
const warnings = [];
|
|
2225
|
+
for (const rule of ALL_RULES) {
|
|
2226
|
+
const violations = rule(input);
|
|
2227
|
+
for (const v of violations) {
|
|
2228
|
+
if (v.severity === "error") {
|
|
2229
|
+
errors.push(v);
|
|
2230
|
+
} else {
|
|
2231
|
+
warnings.push(v);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
return {
|
|
2236
|
+
valid: errors.length === 0,
|
|
2237
|
+
coverage: { rulesChecked: SDK_SCHEMATRON_RULE_IDS.length, ofNetworkFatalRules: "partial" },
|
|
2238
|
+
errors,
|
|
2239
|
+
warnings
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
var SDK_SCHEMATRON_RULE_IDS = [
|
|
2243
|
+
"BR-02",
|
|
2244
|
+
"BR-03",
|
|
2245
|
+
"BR-06",
|
|
2246
|
+
"BR-07",
|
|
2247
|
+
"BR-08",
|
|
2248
|
+
"BR-09",
|
|
2249
|
+
"BR-10",
|
|
2250
|
+
"BR-CL-17",
|
|
2251
|
+
"BR-CO-10",
|
|
2252
|
+
"BR-CO-13",
|
|
2253
|
+
"BR-CO-15",
|
|
2254
|
+
"BR-CO-16",
|
|
2255
|
+
"BR-S-05",
|
|
2256
|
+
"BR-Z-05",
|
|
2257
|
+
"BR-E-05",
|
|
2258
|
+
"BR-AE-05",
|
|
2259
|
+
"BR-G-05",
|
|
2260
|
+
"BR-IC-05",
|
|
2261
|
+
"BR-S-06",
|
|
2262
|
+
"BR-S-07",
|
|
2263
|
+
"BR-Z-06",
|
|
2264
|
+
"BR-Z-07",
|
|
2265
|
+
"BR-E-06",
|
|
2266
|
+
"BR-E-07",
|
|
2267
|
+
"BR-AE-06",
|
|
2268
|
+
"BR-AE-07",
|
|
2269
|
+
"BR-G-06",
|
|
2270
|
+
"BR-G-07",
|
|
2271
|
+
"BR-IC-06",
|
|
2272
|
+
"BR-IC-07",
|
|
2273
|
+
"BR-E-10",
|
|
2274
|
+
"BR-AE-10",
|
|
2275
|
+
"BR-G-10",
|
|
2276
|
+
"BR-O-10",
|
|
2277
|
+
"BR-IC-10",
|
|
2278
|
+
"PEPPOL-EN16931-R004",
|
|
2279
|
+
"PEPPOL-EN16931-R080"
|
|
2280
|
+
];
|
|
2281
|
+
|
|
2282
|
+
// ../sdk/dist/core/status-precedence.js
|
|
2283
|
+
var STATUS_PRECEDENCE = [
|
|
2284
|
+
{ status: "failed", family: "terminal-failure" },
|
|
2285
|
+
{ status: "rejected", family: "terminal-failure" },
|
|
2286
|
+
{ status: "paid", family: "terminal-success" },
|
|
2287
|
+
{ status: "partially_paid", family: "progress" },
|
|
2288
|
+
{ status: "accepted", family: "progress" },
|
|
2289
|
+
{ status: "conditionally_accepted", family: "progress" },
|
|
2290
|
+
{ status: "under_query", family: "progress" },
|
|
2291
|
+
{ status: "in_process", family: "progress" },
|
|
2292
|
+
{ status: "cleared", family: "progress" },
|
|
2293
|
+
{ status: "delivered", family: "progress" },
|
|
2294
|
+
{ status: "acknowledged", family: "progress" },
|
|
2295
|
+
// Terminal for developer wait semantics only — stays rank 40 (non-terminal)
|
|
2296
|
+
// in the projection guard (§3.12 two-level terminality).
|
|
2297
|
+
{ status: "no_action", family: "terminal-failure" },
|
|
2298
|
+
{ status: "submitted", family: "progress" },
|
|
2299
|
+
{ status: "unknown", family: "fallback" }
|
|
2300
|
+
];
|
|
2301
|
+
function statusFamily(status) {
|
|
2302
|
+
return STATUS_PRECEDENCE.find((e) => e.status === status)?.family ?? "fallback";
|
|
2303
|
+
}
|
|
2304
|
+
var TERMINAL_FAILURE_STATUSES = STATUS_PRECEDENCE.filter((e) => e.family === "terminal-failure").map((e) => e.status);
|
|
2305
|
+
|
|
2306
|
+
// ../sdk/dist/version.js
|
|
2307
|
+
var SDK_VERSION = "4.6.0";
|
|
2308
|
+
|
|
2309
|
+
// ../sdk/dist/core/client.js
|
|
2310
|
+
function findHeaderCaseInsensitive(headers, name) {
|
|
2311
|
+
if (!headers)
|
|
2312
|
+
return void 0;
|
|
2313
|
+
const target = name.toLowerCase();
|
|
2314
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
2315
|
+
if (key.toLowerCase() === target)
|
|
2316
|
+
return value;
|
|
2317
|
+
}
|
|
2318
|
+
return void 0;
|
|
2319
|
+
}
|
|
2320
|
+
var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
2321
|
+
function sleep(ms) {
|
|
2322
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
2323
|
+
}
|
|
2324
|
+
function calculateRetryDelay(attempt, initialDelayMs, maxDelayMs, retryAfterMs) {
|
|
2325
|
+
if (retryAfterMs !== void 0)
|
|
2326
|
+
return Math.min(retryAfterMs, maxDelayMs);
|
|
2327
|
+
const exponentialDelay = initialDelayMs * Math.pow(2, attempt);
|
|
2328
|
+
const jitter = Math.random() * initialDelayMs;
|
|
2329
|
+
return Math.min(exponentialDelay + jitter, maxDelayMs);
|
|
2330
|
+
}
|
|
2331
|
+
function parseRetryAfter(headerValue) {
|
|
2332
|
+
if (!headerValue)
|
|
2333
|
+
return void 0;
|
|
2334
|
+
const seconds = Number(headerValue);
|
|
2335
|
+
if (Number.isFinite(seconds) && seconds >= 0) {
|
|
2336
|
+
return seconds * 1e3;
|
|
2337
|
+
}
|
|
2338
|
+
const dateMs = Date.parse(headerValue);
|
|
2339
|
+
if (!Number.isNaN(dateMs)) {
|
|
2340
|
+
const delayMs = dateMs - Date.now();
|
|
2341
|
+
return delayMs > 0 ? delayMs : 0;
|
|
2342
|
+
}
|
|
2343
|
+
return void 0;
|
|
2344
|
+
}
|
|
2345
|
+
var CONTROL_CHARACTERS = /[\u0000-\u001F\u007F-\u009F]/g;
|
|
2346
|
+
function stripControls(value) {
|
|
2347
|
+
return value.replace(CONTROL_CHARACTERS, " ");
|
|
2348
|
+
}
|
|
2349
|
+
function readOwn(source, key) {
|
|
2350
|
+
return Object.hasOwn(source, key) ? source[key] : void 0;
|
|
2351
|
+
}
|
|
2352
|
+
function readSentence(source, key) {
|
|
2353
|
+
const value = readOwn(source, key);
|
|
2354
|
+
if (typeof value !== "string")
|
|
2355
|
+
return null;
|
|
2356
|
+
const cleaned = stripControls(value).trim();
|
|
2357
|
+
return cleaned === "" ? null : cleaned;
|
|
2358
|
+
}
|
|
2359
|
+
function safeDocsUrl(value) {
|
|
2360
|
+
if (typeof value !== "string")
|
|
2361
|
+
return null;
|
|
2362
|
+
let parsed;
|
|
2363
|
+
try {
|
|
2364
|
+
parsed = new URL(value);
|
|
2365
|
+
} catch {
|
|
2366
|
+
return null;
|
|
2367
|
+
}
|
|
2368
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
|
|
2369
|
+
return null;
|
|
2370
|
+
return parsed.href;
|
|
2371
|
+
}
|
|
2372
|
+
function formatApiErrorMessage(status, rawBody) {
|
|
2373
|
+
const verbatim = `getpeppr API error (${status}): ${stripControls(rawBody)}`;
|
|
2374
|
+
let parsed;
|
|
2375
|
+
try {
|
|
2376
|
+
parsed = JSON.parse(rawBody);
|
|
2377
|
+
} catch {
|
|
2378
|
+
return verbatim;
|
|
2379
|
+
}
|
|
2380
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
2381
|
+
return verbatim;
|
|
2382
|
+
}
|
|
2383
|
+
const sentence = readSentence(parsed, "message") ?? readSentence(parsed, "error");
|
|
2384
|
+
if (sentence === null)
|
|
2385
|
+
return verbatim;
|
|
2386
|
+
const link = safeDocsUrl(readOwn(parsed, "docs"));
|
|
2387
|
+
return `getpeppr API error (${status}): ${sentence}${link ? ` See ${link}` : ""}`;
|
|
2388
|
+
}
|
|
2389
|
+
function isRetryableError(error2) {
|
|
2390
|
+
if (error2 instanceof PeppolApiError) {
|
|
2391
|
+
return RETRYABLE_STATUS_CODES.has(error2.statusCode);
|
|
2392
|
+
}
|
|
2393
|
+
if (error2 instanceof Error && error2.name === "AbortError") {
|
|
2394
|
+
return true;
|
|
2395
|
+
}
|
|
2396
|
+
if (error2 instanceof TypeError && /fetch failed|network|ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT/i.test(error2.message)) {
|
|
2397
|
+
return true;
|
|
2398
|
+
}
|
|
2399
|
+
return false;
|
|
2400
|
+
}
|
|
2401
|
+
var DEFAULT_BASE_URL = "https://api.getpeppr.dev/v1";
|
|
2402
|
+
var GetpepprAdapter = class {
|
|
2403
|
+
name = "getpeppr";
|
|
2404
|
+
baseUrl;
|
|
2405
|
+
apiKey;
|
|
2406
|
+
timeout;
|
|
2407
|
+
retryConfig;
|
|
2408
|
+
onRequest;
|
|
2409
|
+
onResponse;
|
|
2410
|
+
constructor(config) {
|
|
2411
|
+
this.apiKey = config.apiKey;
|
|
2412
|
+
this.timeout = config.timeout ?? 3e4;
|
|
2413
|
+
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
2414
|
+
this.retryConfig = {
|
|
2415
|
+
maxRetries: config.retry?.maxRetries ?? 3,
|
|
2416
|
+
initialDelayMs: config.retry?.initialDelayMs ?? 500,
|
|
2417
|
+
maxDelayMs: config.retry?.maxDelayMs ?? 3e4
|
|
2418
|
+
};
|
|
2419
|
+
this.onRequest = config.onRequest;
|
|
2420
|
+
this.onResponse = config.onResponse;
|
|
2421
|
+
}
|
|
2422
|
+
async request(method, path, body, extraHeaders) {
|
|
2423
|
+
const { maxRetries, initialDelayMs, maxDelayMs } = this.retryConfig;
|
|
2424
|
+
let lastError;
|
|
2425
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
2426
|
+
try {
|
|
2427
|
+
return await this.doRequest(method, path, body, extraHeaders);
|
|
2428
|
+
} catch (err) {
|
|
2429
|
+
lastError = err;
|
|
1287
2430
|
const is429 = err instanceof PeppolApiError && err.statusCode === 429;
|
|
1288
2431
|
const isSafeMethod = /^(GET|DELETE|HEAD)$/i.test(method);
|
|
1289
2432
|
const hasIdempotencyKey = !!findHeaderCaseInsensitive(extraHeaders, "Idempotency-Key");
|
|
@@ -1761,7 +2904,24 @@ var GetpepprAdapter = class {
|
|
|
1761
2904
|
// Declared, never derived from the document: routing decides delivery, and
|
|
1762
2905
|
// parsing a caller's XML for a destination would put a parse error on the
|
|
1763
2906
|
// "whose invoice goes where" path.
|
|
1764
|
-
to: options.to
|
|
2907
|
+
to: options.to,
|
|
2908
|
+
// ⛔ GPR-1129 — ce corps est une LISTE BLANCHE, exactement comme
|
|
2909
|
+
// `parseSendResult` l'est en sortie : tout champ non recopié ici est
|
|
2910
|
+
// supprimé en SILENCE, et la capacité correspondante devient
|
|
2911
|
+
// inatteignable pour quiconque passe par le SDK.
|
|
2912
|
+
//
|
|
2913
|
+
// Quatrième occurrence de cette classe, la première dans le sens REQUÊTE
|
|
2914
|
+
// (`rulebook` GPR-1069, `transmission` GPR-1089, `duplicateOf` GPR-1105
|
|
2915
|
+
// étaient des champs de RÉPONSE). Le préjudice n'est pas symétrique : un
|
|
2916
|
+
// champ de réponse jeté casse qui le lit, un champ de requête jeté
|
|
2917
|
+
// produit un refus que l'appelant ne peut relier à rien — il envoie
|
|
2918
|
+
// `sender`, la passerelle ne le voit jamais, et le 422 qu'il reçoit parle
|
|
2919
|
+
// d'une identité qu'il ne revendiquait pas.
|
|
2920
|
+
//
|
|
2921
|
+
// ⚠️ Conditionnel, jamais `sender: options.sender` : une clé présente à
|
|
2922
|
+
// `undefined` disparaît du JSON, donc l'écriture nue passerait les tests
|
|
2923
|
+
// tout en salissant le corps des envois standards.
|
|
2924
|
+
...options.sender ? { sender: options.sender } : {}
|
|
1765
2925
|
};
|
|
1766
2926
|
const result = await this.request("POST", "/invoices/import", body);
|
|
1767
2927
|
return parseSendResult(result);
|
|
@@ -1883,6 +3043,9 @@ function parseSendResult(body) {
|
|
|
1883
3043
|
sendResult.transmission = { mode, bytePreservation };
|
|
1884
3044
|
}
|
|
1885
3045
|
}
|
|
3046
|
+
const duplicateOf = optionalWireId(readOwn(result, "duplicateOf"));
|
|
3047
|
+
if (duplicateOf)
|
|
3048
|
+
sendResult.duplicateOf = duplicateOf;
|
|
1886
3049
|
const submissionId = optionalWireId(result.submissionId);
|
|
1887
3050
|
if (submissionId)
|
|
1888
3051
|
sendResult.submissionId = submissionId;
|
|
@@ -2253,9 +3416,11 @@ var Peppol = class {
|
|
|
2253
3416
|
* API with `peppol.identity.get()`, which works with any key. This surface is
|
|
2254
3417
|
* for platforms that onboard their customers as sub-tenants.
|
|
2255
3418
|
*
|
|
2256
|
-
* **Getting access:**
|
|
2257
|
-
*
|
|
2258
|
-
* master key
|
|
3419
|
+
* **Getting access:** in the sandbox, an organisation admin starts the
|
|
3420
|
+
* platform sandbox trial from the console overview (or chooses "A platform
|
|
3421
|
+
* for my customers" at signup), then creates the sandbox master key at
|
|
3422
|
+
* https://console.getpeppr.dev/api-keys. Production platform access is set up
|
|
3423
|
+
* with our team — email hello@getpeppr.dev to request it.
|
|
2259
3424
|
*
|
|
2260
3425
|
* @see https://getpeppr.dev/docs/platform/legal-entities/
|
|
2261
3426
|
*/
|
|
@@ -2276,8 +3441,9 @@ var Peppol = class {
|
|
|
2276
3441
|
this.legalEntities = new LegalEntityOperations(this.adapter);
|
|
2277
3442
|
}
|
|
2278
3443
|
/**
|
|
2279
|
-
* Validate
|
|
2280
|
-
* Useful for pre-flight checks in your UI
|
|
3444
|
+
* Validate the structured JSON send payload without sending it.
|
|
3445
|
+
* Useful for pre-flight checks in your UI; provider-side normalization still
|
|
3446
|
+
* applies on send. `toXml()` adds the stricter direct-UBL builder checks.
|
|
2281
3447
|
*/
|
|
2282
3448
|
validate(input) {
|
|
2283
3449
|
return validateInvoice(input);
|
|
@@ -2287,14 +3453,39 @@ var Peppol = class {
|
|
|
2287
3453
|
* Useful for debugging or manual submission.
|
|
2288
3454
|
*/
|
|
2289
3455
|
toXml(input) {
|
|
2290
|
-
const
|
|
3456
|
+
const baseValidation = validateInvoice(input);
|
|
3457
|
+
const vatViolations = baseValidation.valid ? validateUblBuilderVat(input) : [];
|
|
3458
|
+
const validation = {
|
|
3459
|
+
valid: baseValidation.valid && vatViolations.every((item) => item.severity !== "error"),
|
|
3460
|
+
errors: [
|
|
3461
|
+
...baseValidation.errors,
|
|
3462
|
+
...vatViolations.filter((item) => item.severity === "error").map(({ field, message, ruleId }) => ({
|
|
3463
|
+
field: field ?? "invoice",
|
|
3464
|
+
message,
|
|
3465
|
+
ruleId: ruleId === "SDK-INPUT" ? void 0 : ruleId
|
|
3466
|
+
}))
|
|
3467
|
+
],
|
|
3468
|
+
warnings: baseValidation.warnings
|
|
3469
|
+
};
|
|
2291
3470
|
if (!validation.valid) {
|
|
2292
3471
|
throw new PeppolValidationError(`Invoice validation failed: ${validation.errors.map((e) => e.message).join("; ")}`, validation);
|
|
2293
3472
|
}
|
|
2294
|
-
|
|
2295
|
-
|
|
3473
|
+
try {
|
|
3474
|
+
if (input.isCreditNote) {
|
|
3475
|
+
return buildCreditNoteXml(input);
|
|
3476
|
+
}
|
|
3477
|
+
return buildInvoiceXml(input);
|
|
3478
|
+
} catch (error2) {
|
|
3479
|
+
if (error2 instanceof UblBuilderInputError) {
|
|
3480
|
+
const builderValidation = {
|
|
3481
|
+
valid: false,
|
|
3482
|
+
errors: [{ field: error2.field, message: error2.message, ruleId: error2.ruleId }],
|
|
3483
|
+
warnings: validation.warnings
|
|
3484
|
+
};
|
|
3485
|
+
throw new PeppolValidationError(`Invoice validation failed: ${error2.message}`, builderValidation);
|
|
3486
|
+
}
|
|
3487
|
+
throw error2;
|
|
2296
3488
|
}
|
|
2297
|
-
return buildInvoiceXml(input);
|
|
2298
3489
|
}
|
|
2299
3490
|
};
|
|
2300
3491
|
async function* paginate(fetchPage, options) {
|
|
@@ -2312,6 +3503,18 @@ async function* paginate(fetchPage, options) {
|
|
|
2312
3503
|
offset += page.data.length;
|
|
2313
3504
|
}
|
|
2314
3505
|
}
|
|
3506
|
+
function toGatewayInvoiceInput(input) {
|
|
3507
|
+
const stripReason = (item) => {
|
|
3508
|
+
const { taxExemptReason: _builderOnly, ...gatewayItem } = item;
|
|
3509
|
+
return gatewayItem;
|
|
3510
|
+
};
|
|
3511
|
+
return {
|
|
3512
|
+
...input,
|
|
3513
|
+
lines: input.lines.map(stripReason),
|
|
3514
|
+
...input.allowances ? { allowances: input.allowances.map(stripReason) } : {},
|
|
3515
|
+
...input.charges ? { charges: input.charges.map(stripReason) } : {}
|
|
3516
|
+
};
|
|
3517
|
+
}
|
|
2315
3518
|
var InvoiceOperations = class {
|
|
2316
3519
|
adapter;
|
|
2317
3520
|
constructor(adapter) {
|
|
@@ -2335,7 +3538,7 @@ var InvoiceOperations = class {
|
|
|
2335
3538
|
throw new PeppolValidationError(`Invoice validation failed:
|
|
2336
3539
|
${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ""}`).join("\n")}`, validation);
|
|
2337
3540
|
}
|
|
2338
|
-
const result = await this.adapter.createInvoice(input, options);
|
|
3541
|
+
const result = await this.adapter.createInvoice(toGatewayInvoiceInput(input), options);
|
|
2339
3542
|
if (validation.warnings.length > 0) {
|
|
2340
3543
|
result.warnings = validation.warnings;
|
|
2341
3544
|
}
|
|
@@ -2375,7 +3578,7 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
|
|
|
2375
3578
|
throw new PeppolValidationError(`Invoice validation failed:
|
|
2376
3579
|
${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ""}`).join("\n")}`, validation);
|
|
2377
3580
|
}
|
|
2378
|
-
const result = await this.adapter.sendInvoice(input, options);
|
|
3581
|
+
const result = await this.adapter.sendInvoice(toGatewayInvoiceInput(input), options);
|
|
2379
3582
|
if (validation.warnings.length > 0) {
|
|
2380
3583
|
result.warnings = validation.warnings;
|
|
2381
3584
|
}
|
|
@@ -2684,7 +3887,7 @@ var CreditNoteOperations = class {
|
|
|
2684
3887
|
throw new PeppolValidationError(`Credit note validation failed:
|
|
2685
3888
|
${validation.errors.map((e) => ` - ${e.field}: ${e.message}`).join("\n")}`, validation);
|
|
2686
3889
|
}
|
|
2687
|
-
return this.adapter.sendInvoice(invoiceInput);
|
|
3890
|
+
return this.adapter.sendInvoice(toGatewayInvoiceInput(invoiceInput));
|
|
2688
3891
|
}
|
|
2689
3892
|
};
|
|
2690
3893
|
var DirectoryOperations = class {
|
|
@@ -2702,12 +3905,10 @@ var DirectoryOperations = class {
|
|
|
2702
3905
|
* ```
|
|
2703
3906
|
*/
|
|
2704
3907
|
async lookup(peppolId) {
|
|
2705
|
-
|
|
2706
|
-
if (colonIndex === -1) {
|
|
3908
|
+
if (!peppolId.includes(":")) {
|
|
2707
3909
|
throw new PeppolError('Invalid Peppol ID format. Expected "scheme:id" (e.g., "0208:0685660237")');
|
|
2708
3910
|
}
|
|
2709
|
-
const scheme = peppolId
|
|
2710
|
-
const id = peppolId.slice(colonIndex + 1);
|
|
3911
|
+
const { scheme, id } = parsePeppolId(peppolId);
|
|
2711
3912
|
return this.adapter.lookupDirectory(scheme, id);
|
|
2712
3913
|
}
|
|
2713
3914
|
/**
|
|
@@ -2907,9 +4108,11 @@ var LegalEntityOperations = class {
|
|
|
2907
4108
|
/**
|
|
2908
4109
|
* Create a sub-tenant Legal Entity for one of your customers.
|
|
2909
4110
|
*
|
|
2910
|
-
* **Platform accounts only — requires a master API key.**
|
|
2911
|
-
*
|
|
2912
|
-
*
|
|
4111
|
+
* **Platform accounts only — requires a master API key.** In the sandbox,
|
|
4112
|
+
* an organisation admin starts the platform sandbox trial from the console
|
|
4113
|
+
* overview and creates a sandbox master key at
|
|
4114
|
+
* https://console.getpeppr.dev/api-keys; production platform access is set
|
|
4115
|
+
* up with our team (hello@getpeppr.dev).
|
|
2913
4116
|
*
|
|
2914
4117
|
* Your own company's legal entity is managed in the console, on the Peppol
|
|
2915
4118
|
* identity page (and read from the API with `peppol.identity.get()`); this
|
|
@@ -2936,9 +4139,11 @@ var LegalEntityOperations = class {
|
|
|
2936
4139
|
/**
|
|
2937
4140
|
* Fetch a single sub-tenant Legal Entity by id.
|
|
2938
4141
|
*
|
|
2939
|
-
* **Platform accounts only — requires a master API key.**
|
|
2940
|
-
*
|
|
2941
|
-
*
|
|
4142
|
+
* **Platform accounts only — requires a master API key.** In the sandbox,
|
|
4143
|
+
* an organisation admin starts the platform sandbox trial from the console
|
|
4144
|
+
* overview and creates a sandbox master key at
|
|
4145
|
+
* https://console.getpeppr.dev/api-keys; production platform access is set
|
|
4146
|
+
* up with our team (hello@getpeppr.dev).
|
|
2942
4147
|
*
|
|
2943
4148
|
* For production entities the `status` reflects the attestation lifecycle
|
|
2944
4149
|
* (awaiting_authz → attested → active).
|
|
@@ -2949,9 +4154,11 @@ var LegalEntityOperations = class {
|
|
|
2949
4154
|
/**
|
|
2950
4155
|
* List your sub-tenant Legal Entities, newest first.
|
|
2951
4156
|
*
|
|
2952
|
-
* **Platform accounts only — requires a master API key.**
|
|
2953
|
-
*
|
|
2954
|
-
*
|
|
4157
|
+
* **Platform accounts only — requires a master API key.** In the sandbox,
|
|
4158
|
+
* an organisation admin starts the platform sandbox trial from the console
|
|
4159
|
+
* overview and creates a sandbox master key at
|
|
4160
|
+
* https://console.getpeppr.dev/api-keys; production platform access is set
|
|
4161
|
+
* up with our team (hello@getpeppr.dev).
|
|
2955
4162
|
*
|
|
2956
4163
|
* This lists the customers you have onboarded, never your own legal entity.
|
|
2957
4164
|
*/
|
|
@@ -2961,9 +4168,11 @@ var LegalEntityOperations = class {
|
|
|
2961
4168
|
/**
|
|
2962
4169
|
* Async iterator over all sub-tenant Legal Entities, handling pagination.
|
|
2963
4170
|
*
|
|
2964
|
-
* **Platform accounts only — requires a master API key.**
|
|
2965
|
-
*
|
|
2966
|
-
*
|
|
4171
|
+
* **Platform accounts only — requires a master API key.** In the sandbox,
|
|
4172
|
+
* an organisation admin starts the platform sandbox trial from the console
|
|
4173
|
+
* overview and creates a sandbox master key at
|
|
4174
|
+
* https://console.getpeppr.dev/api-keys; production platform access is set
|
|
4175
|
+
* up with our team (hello@getpeppr.dev).
|
|
2967
4176
|
*
|
|
2968
4177
|
* @example
|
|
2969
4178
|
* ```ts
|
|
@@ -2977,9 +4186,11 @@ var LegalEntityOperations = class {
|
|
|
2977
4186
|
* Archive (soft-delete) a sub-tenant Legal Entity. The id stays resolvable
|
|
2978
4187
|
* for audit.
|
|
2979
4188
|
*
|
|
2980
|
-
* **Platform accounts only — requires a master API key.**
|
|
2981
|
-
*
|
|
2982
|
-
*
|
|
4189
|
+
* **Platform accounts only — requires a master API key.** In the sandbox,
|
|
4190
|
+
* an organisation admin starts the platform sandbox trial from the console
|
|
4191
|
+
* overview and creates a sandbox master key at
|
|
4192
|
+
* https://console.getpeppr.dev/api-keys; production platform access is set
|
|
4193
|
+
* up with our team (hello@getpeppr.dev).
|
|
2983
4194
|
*/
|
|
2984
4195
|
async archive(id) {
|
|
2985
4196
|
return this.adapter.archiveLegalEntity(id);
|
|
@@ -2988,525 +4199,183 @@ var LegalEntityOperations = class {
|
|
|
2988
4199
|
* Request a sub-tenant attestation (production only). Emails the co-branded
|
|
2989
4200
|
* confirmation link to the sub-tenant contact and returns the pending status.
|
|
2990
4201
|
*
|
|
2991
|
-
* **Platform accounts only — requires a master API key.**
|
|
2992
|
-
*
|
|
2993
|
-
*
|
|
2994
|
-
*
|
|
2995
|
-
*
|
|
2996
|
-
*
|
|
2997
|
-
*
|
|
2998
|
-
*
|
|
2999
|
-
* ```ts
|
|
3000
|
-
* await peppol.legalEntities.requestAttestation(le.id, { contactEmail: "owner@acme.example" });
|
|
3001
|
-
* ```
|
|
3002
|
-
*/
|
|
3003
|
-
async requestAttestation(id, input, options) {
|
|
3004
|
-
return this.adapter.requestLegalEntityAttestation(id, input, options);
|
|
3005
|
-
}
|
|
3006
|
-
};
|
|
3007
|
-
var BankAccountOperations = class {
|
|
3008
|
-
adapter;
|
|
3009
|
-
constructor(adapter) {
|
|
3010
|
-
this.adapter = adapter;
|
|
3011
|
-
}
|
|
3012
|
-
/**
|
|
3013
|
-
* List bank accounts with optional pagination.
|
|
3014
|
-
*
|
|
3015
|
-
* @example
|
|
3016
|
-
* ```ts
|
|
3017
|
-
* const result = await peppol.bankAccounts.list({ limit: 10 });
|
|
3018
|
-
* console.log(result.data, result.meta);
|
|
3019
|
-
* ```
|
|
3020
|
-
*/
|
|
3021
|
-
async list(options) {
|
|
3022
|
-
return this.adapter.listBankAccounts(options);
|
|
3023
|
-
}
|
|
3024
|
-
/**
|
|
3025
|
-
* Get a single bank account by ID.
|
|
3026
|
-
*
|
|
3027
|
-
* @example
|
|
3028
|
-
* ```ts
|
|
3029
|
-
* const account = await peppol.bankAccounts.get("123");
|
|
3030
|
-
* console.log(account.name, account.iban);
|
|
3031
|
-
* ```
|
|
3032
|
-
*/
|
|
3033
|
-
async get(id) {
|
|
3034
|
-
return this.adapter.getBankAccount(id);
|
|
3035
|
-
}
|
|
3036
|
-
/**
|
|
3037
|
-
* Create a new bank account.
|
|
3038
|
-
*
|
|
3039
|
-
* @example
|
|
3040
|
-
* ```ts
|
|
3041
|
-
* const account = await peppol.bankAccounts.create({
|
|
3042
|
-
* name: "Main Account",
|
|
3043
|
-
* iban: "BE68539007547034",
|
|
3044
|
-
* bic: "BBRUBEBB",
|
|
3045
|
-
* country: "BE",
|
|
3046
|
-
* });
|
|
3047
|
-
* ```
|
|
3048
|
-
*/
|
|
3049
|
-
async create(input) {
|
|
3050
|
-
return this.adapter.createBankAccount(input);
|
|
3051
|
-
}
|
|
3052
|
-
/**
|
|
3053
|
-
* Update an existing bank account.
|
|
3054
|
-
*
|
|
3055
|
-
* @example
|
|
3056
|
-
* ```ts
|
|
3057
|
-
* const updated = await peppol.bankAccounts.update("123", { name: "Updated Name" });
|
|
3058
|
-
* ```
|
|
3059
|
-
*/
|
|
3060
|
-
async update(id, input) {
|
|
3061
|
-
return this.adapter.updateBankAccount(id, input);
|
|
3062
|
-
}
|
|
3063
|
-
/**
|
|
3064
|
-
* Delete a bank account.
|
|
3065
|
-
*
|
|
3066
|
-
* @example
|
|
3067
|
-
* ```ts
|
|
3068
|
-
* await peppol.bankAccounts.delete("123");
|
|
3069
|
-
* ```
|
|
3070
|
-
*/
|
|
3071
|
-
async delete(id) {
|
|
3072
|
-
return this.adapter.deleteBankAccount(id);
|
|
3073
|
-
}
|
|
3074
|
-
/**
|
|
3075
|
-
* Async iterator over all bank accounts, automatically handling pagination.
|
|
4202
|
+
* **Platform accounts only — requires a master API key.** In the sandbox,
|
|
4203
|
+
* an organisation admin starts the platform sandbox trial from the console
|
|
4204
|
+
* overview and creates a sandbox master key at
|
|
4205
|
+
* https://console.getpeppr.dev/api-keys; production platform access is set
|
|
4206
|
+
* up with our team (hello@getpeppr.dev).
|
|
4207
|
+
*
|
|
4208
|
+
* Transient failures are NOT auto-retried unless you pass `options.idempotencyKey`;
|
|
4209
|
+
* re-issuing mints a fresh token, so a retried call is safe.
|
|
3076
4210
|
*
|
|
3077
4211
|
* @example
|
|
3078
4212
|
* ```ts
|
|
3079
|
-
*
|
|
3080
|
-
* console.log(account.name, account.iban);
|
|
3081
|
-
* }
|
|
4213
|
+
* await peppol.legalEntities.requestAttestation(le.id, { contactEmail: "owner@acme.example" });
|
|
3082
4214
|
* ```
|
|
3083
4215
|
*/
|
|
3084
|
-
|
|
3085
|
-
return
|
|
4216
|
+
async requestAttestation(id, input, options) {
|
|
4217
|
+
return this.adapter.requestLegalEntityAttestation(id, input, options);
|
|
3086
4218
|
}
|
|
3087
4219
|
};
|
|
3088
|
-
var
|
|
4220
|
+
var BankAccountOperations = class {
|
|
3089
4221
|
adapter;
|
|
3090
4222
|
constructor(adapter) {
|
|
3091
4223
|
this.adapter = adapter;
|
|
3092
4224
|
}
|
|
3093
4225
|
/**
|
|
3094
|
-
* List
|
|
3095
|
-
* Returns global transport types (not account-scoped).
|
|
3096
|
-
*
|
|
3097
|
-
* @example
|
|
3098
|
-
* ```ts
|
|
3099
|
-
* const types = await peppol.transports.listTypes();
|
|
3100
|
-
* console.log(types); // [{ code: "peppol", name: "Peppol BIS 3.0" }, ...]
|
|
3101
|
-
* ```
|
|
3102
|
-
*/
|
|
3103
|
-
async listTypes() {
|
|
3104
|
-
return this.adapter.listTransportTypes();
|
|
3105
|
-
}
|
|
3106
|
-
/**
|
|
3107
|
-
* List configured transports for this account.
|
|
4226
|
+
* List bank accounts with optional pagination.
|
|
3108
4227
|
*
|
|
3109
4228
|
* @example
|
|
3110
4229
|
* ```ts
|
|
3111
|
-
* const
|
|
3112
|
-
* console.log(
|
|
4230
|
+
* const result = await peppol.bankAccounts.list({ limit: 10 });
|
|
4231
|
+
* console.log(result.data, result.meta);
|
|
3113
4232
|
* ```
|
|
3114
4233
|
*/
|
|
3115
|
-
async list() {
|
|
3116
|
-
return this.adapter.
|
|
4234
|
+
async list(options) {
|
|
4235
|
+
return this.adapter.listBankAccounts(options);
|
|
3117
4236
|
}
|
|
3118
4237
|
/**
|
|
3119
|
-
* Get a single
|
|
4238
|
+
* Get a single bank account by ID.
|
|
3120
4239
|
*
|
|
3121
4240
|
* @example
|
|
3122
4241
|
* ```ts
|
|
3123
|
-
* const
|
|
4242
|
+
* const account = await peppol.bankAccounts.get("123");
|
|
4243
|
+
* console.log(account.name, account.iban);
|
|
3124
4244
|
* ```
|
|
3125
4245
|
*/
|
|
3126
|
-
async get(
|
|
3127
|
-
return this.adapter.
|
|
4246
|
+
async get(id) {
|
|
4247
|
+
return this.adapter.getBankAccount(id);
|
|
3128
4248
|
}
|
|
3129
4249
|
/**
|
|
3130
|
-
* Create a new
|
|
4250
|
+
* Create a new bank account.
|
|
3131
4251
|
*
|
|
3132
4252
|
* @example
|
|
3133
4253
|
* ```ts
|
|
3134
|
-
* const
|
|
3135
|
-
*
|
|
3136
|
-
*
|
|
4254
|
+
* const account = await peppol.bankAccounts.create({
|
|
4255
|
+
* name: "Main Account",
|
|
4256
|
+
* iban: "BE68539007547034",
|
|
4257
|
+
* bic: "BBRUBEBB",
|
|
4258
|
+
* country: "BE",
|
|
3137
4259
|
* });
|
|
3138
4260
|
* ```
|
|
3139
4261
|
*/
|
|
3140
4262
|
async create(input) {
|
|
3141
|
-
return this.adapter.
|
|
3142
|
-
}
|
|
3143
|
-
/**
|
|
3144
|
-
* Update an existing transport.
|
|
3145
|
-
*
|
|
3146
|
-
* @example
|
|
3147
|
-
* ```ts
|
|
3148
|
-
* const transport = await peppol.transports.update("peppol", { email: "new@acme.com" });
|
|
3149
|
-
* ```
|
|
3150
|
-
*/
|
|
3151
|
-
async update(code, input) {
|
|
3152
|
-
return this.adapter.updateTransport(code, input);
|
|
4263
|
+
return this.adapter.createBankAccount(input);
|
|
3153
4264
|
}
|
|
3154
4265
|
/**
|
|
3155
|
-
*
|
|
4266
|
+
* Update an existing bank account.
|
|
3156
4267
|
*
|
|
3157
4268
|
* @example
|
|
3158
4269
|
* ```ts
|
|
3159
|
-
* await peppol.
|
|
3160
|
-
* ```
|
|
3161
|
-
*/
|
|
3162
|
-
async
|
|
3163
|
-
return this.adapter.
|
|
3164
|
-
}
|
|
3165
|
-
};
|
|
3166
|
-
|
|
3167
|
-
// ../sdk/dist/core/schematron.js
|
|
3168
|
-
function violation(ruleId, severity, message, field) {
|
|
3169
|
-
return { ruleId, severity, message, field };
|
|
3170
|
-
}
|
|
3171
|
-
var _knownUnitCodes;
|
|
3172
|
-
function getKnownUnitCodes() {
|
|
3173
|
-
if (!_knownUnitCodes) {
|
|
3174
|
-
_knownUnitCodes = new Set(getAllUnits().map((u) => u.code));
|
|
3175
|
-
}
|
|
3176
|
-
return _knownUnitCodes;
|
|
3177
|
-
}
|
|
3178
|
-
var VALID_VAT_CATEGORIES = /* @__PURE__ */ new Set(["S", "Z", "E", "AE", "K", "G", "O", "L", "M", "B"]);
|
|
3179
|
-
var SENDABLE_VAT_CATEGORIES = ["S", "Z", "E", "AE", "K", "G", "O"];
|
|
3180
|
-
var UNROUTABLE_VAT_CATEGORIES = /* @__PURE__ */ new Set(["L", "M", "B"]);
|
|
3181
|
-
function computeLineNet(line) {
|
|
3182
|
-
const baseQty = line.baseQuantity ?? 1;
|
|
3183
|
-
if (baseQty === 0)
|
|
3184
|
-
return NaN;
|
|
3185
|
-
const baseAmount = line.quantity * line.unitPrice / baseQty;
|
|
3186
|
-
const chargeTotal = (line.charges ?? []).reduce((sum, c) => sum + c.amount, 0);
|
|
3187
|
-
const allowanceTotal = (line.allowances ?? []).reduce((sum, a) => sum + a.amount, 0);
|
|
3188
|
-
return baseAmount + chargeTotal - allowanceTotal;
|
|
3189
|
-
}
|
|
3190
|
-
var br02 = (input) => {
|
|
3191
|
-
if (!input.number?.trim()) {
|
|
3192
|
-
return [violation("BR-02", "error", "Invoice number is required.", "number")];
|
|
3193
|
-
}
|
|
3194
|
-
return [];
|
|
3195
|
-
};
|
|
3196
|
-
var br03 = (input) => {
|
|
3197
|
-
if (!input.date) {
|
|
3198
|
-
return [
|
|
3199
|
-
violation("BR-03", "warning", "Invoice issue date is not set. The SDK will default to today's date.", "date")
|
|
3200
|
-
];
|
|
3201
|
-
}
|
|
3202
|
-
return [];
|
|
3203
|
-
};
|
|
3204
|
-
var br06 = (input) => {
|
|
3205
|
-
if (input.from && !input.from.vatNumber) {
|
|
3206
|
-
return [
|
|
3207
|
-
violation("BR-06", "warning", "Seller party has no VAT number. The gateway will use the account's VAT registration.", "from.vatNumber")
|
|
3208
|
-
];
|
|
3209
|
-
}
|
|
3210
|
-
return [];
|
|
3211
|
-
};
|
|
3212
|
-
var br07 = (input) => {
|
|
3213
|
-
if (!input.to?.name?.trim()) {
|
|
3214
|
-
return [violation("BR-07", "error", "Buyer name is required.", "to.name")];
|
|
3215
|
-
}
|
|
3216
|
-
return [];
|
|
3217
|
-
};
|
|
3218
|
-
var br08 = (input) => {
|
|
3219
|
-
if (!input.lines || input.lines.length === 0) {
|
|
3220
|
-
return [violation("BR-08", "error", "Invoice must have at least one line item.", "lines")];
|
|
3221
|
-
}
|
|
3222
|
-
return [];
|
|
3223
|
-
};
|
|
3224
|
-
var br09 = (input) => {
|
|
3225
|
-
if (!input.dueDate && !input.paymentTerms) {
|
|
3226
|
-
return [
|
|
3227
|
-
violation("BR-09", "warning", "Neither due date nor payment terms specified. At least one is recommended.", "dueDate")
|
|
3228
|
-
];
|
|
3229
|
-
}
|
|
3230
|
-
return [];
|
|
3231
|
-
};
|
|
3232
|
-
var br10 = (input) => {
|
|
3233
|
-
if (!input.buyerReference && !input.orderReference) {
|
|
3234
|
-
return [
|
|
3235
|
-
violation("BR-10", "warning", "Neither buyerReference nor orderReference specified. Peppol BIS 3.0 requires at least one.", "buyerReference")
|
|
3236
|
-
];
|
|
3237
|
-
}
|
|
3238
|
-
return [];
|
|
3239
|
-
};
|
|
3240
|
-
var brCo10 = (input) => {
|
|
3241
|
-
const violations = [];
|
|
3242
|
-
if (!input.lines)
|
|
3243
|
-
return violations;
|
|
3244
|
-
for (let i = 0; i < input.lines.length; i++) {
|
|
3245
|
-
const line = input.lines[i];
|
|
3246
|
-
const net = computeLineNet(line);
|
|
3247
|
-
if (!Number.isFinite(net)) {
|
|
3248
|
-
const baseQty = line.baseQuantity ?? 1;
|
|
3249
|
-
const detail = baseQty === 0 ? "baseQuantity is 0, causing division by zero." : "Computed line amount is not a finite number.";
|
|
3250
|
-
violations.push(violation("BR-CO-10", "error", `Line ${i}: invalid net amount. ${detail}`, `lines[${i}]`));
|
|
3251
|
-
}
|
|
3252
|
-
}
|
|
3253
|
-
return violations;
|
|
3254
|
-
};
|
|
3255
|
-
var brCo13 = (input) => {
|
|
3256
|
-
if (!input.lines || input.lines.length === 0)
|
|
3257
|
-
return [];
|
|
3258
|
-
let totalVat = 0;
|
|
3259
|
-
for (let i = 0; i < input.lines.length; i++) {
|
|
3260
|
-
const line = input.lines[i];
|
|
3261
|
-
const net = computeLineNet(line);
|
|
3262
|
-
if (!Number.isFinite(net))
|
|
3263
|
-
continue;
|
|
3264
|
-
totalVat += net * (line.vatRate / 100);
|
|
3265
|
-
}
|
|
3266
|
-
for (const allowance of input.allowances ?? []) {
|
|
3267
|
-
totalVat -= allowance.amount * (allowance.vatRate / 100);
|
|
3268
|
-
}
|
|
3269
|
-
for (const charge of input.charges ?? []) {
|
|
3270
|
-
totalVat += charge.amount * (charge.vatRate / 100);
|
|
3271
|
-
}
|
|
3272
|
-
if (!Number.isFinite(totalVat)) {
|
|
3273
|
-
return [
|
|
3274
|
-
violation("BR-CO-13", "error", "Computed total VAT amount is not a finite number. Check line amounts and VAT rates.")
|
|
3275
|
-
];
|
|
3276
|
-
}
|
|
3277
|
-
if (totalVat < -0.01) {
|
|
3278
|
-
return [
|
|
3279
|
-
violation("BR-CO-13", "warning", `Computed total VAT is negative (${totalVat.toFixed(2)}). This is unusual for an invoice.`)
|
|
3280
|
-
];
|
|
3281
|
-
}
|
|
3282
|
-
return [];
|
|
3283
|
-
};
|
|
3284
|
-
var brCo15 = (input) => {
|
|
3285
|
-
if (!input.lines || input.lines.length === 0)
|
|
3286
|
-
return [];
|
|
3287
|
-
let lineTotal = 0;
|
|
3288
|
-
let vatTotal = 0;
|
|
3289
|
-
for (const line of input.lines) {
|
|
3290
|
-
const net = computeLineNet(line);
|
|
3291
|
-
if (!Number.isFinite(net))
|
|
3292
|
-
continue;
|
|
3293
|
-
lineTotal += net;
|
|
3294
|
-
vatTotal += net * (line.vatRate / 100);
|
|
3295
|
-
}
|
|
3296
|
-
for (const allowance of input.allowances ?? []) {
|
|
3297
|
-
lineTotal -= allowance.amount;
|
|
3298
|
-
vatTotal -= allowance.amount * (allowance.vatRate / 100);
|
|
3299
|
-
}
|
|
3300
|
-
for (const charge of input.charges ?? []) {
|
|
3301
|
-
lineTotal += charge.amount;
|
|
3302
|
-
vatTotal += charge.amount * (charge.vatRate / 100);
|
|
3303
|
-
}
|
|
3304
|
-
const taxInclusive = lineTotal + vatTotal;
|
|
3305
|
-
if (!Number.isFinite(taxInclusive)) {
|
|
3306
|
-
return [
|
|
3307
|
-
violation("BR-CO-15", "error", "Computed tax-inclusive amount is not a finite number.")
|
|
3308
|
-
];
|
|
3309
|
-
}
|
|
3310
|
-
if (taxInclusive < -0.01) {
|
|
3311
|
-
return [
|
|
3312
|
-
violation("BR-CO-15", "warning", `Computed tax-inclusive amount is negative (${taxInclusive.toFixed(2)}). Consider using a credit note instead.`)
|
|
3313
|
-
];
|
|
3314
|
-
}
|
|
3315
|
-
return [];
|
|
3316
|
-
};
|
|
3317
|
-
var brCo16 = (input) => {
|
|
3318
|
-
if (!input.lines || input.lines.length === 0)
|
|
3319
|
-
return [];
|
|
3320
|
-
let lineTotal = 0;
|
|
3321
|
-
let vatTotal = 0;
|
|
3322
|
-
for (const line of input.lines) {
|
|
3323
|
-
const net = computeLineNet(line);
|
|
3324
|
-
if (!Number.isFinite(net))
|
|
3325
|
-
continue;
|
|
3326
|
-
lineTotal += net;
|
|
3327
|
-
vatTotal += net * (line.vatRate / 100);
|
|
3328
|
-
}
|
|
3329
|
-
for (const allowance of input.allowances ?? []) {
|
|
3330
|
-
lineTotal -= allowance.amount;
|
|
3331
|
-
vatTotal -= allowance.amount * (allowance.vatRate / 100);
|
|
3332
|
-
}
|
|
3333
|
-
for (const charge of input.charges ?? []) {
|
|
3334
|
-
lineTotal += charge.amount;
|
|
3335
|
-
vatTotal += charge.amount * (charge.vatRate / 100);
|
|
3336
|
-
}
|
|
3337
|
-
const taxInclusive = lineTotal + vatTotal;
|
|
3338
|
-
const prepaid = input.prepaidAmount ?? 0;
|
|
3339
|
-
const rounding = input.roundingAmount ?? 0;
|
|
3340
|
-
const payable = taxInclusive - prepaid + rounding;
|
|
3341
|
-
if (!Number.isFinite(payable)) {
|
|
3342
|
-
return [
|
|
3343
|
-
violation("BR-CO-16", "error", "Computed payable amount is not a finite number.")
|
|
3344
|
-
];
|
|
4270
|
+
* const updated = await peppol.bankAccounts.update("123", { name: "Updated Name" });
|
|
4271
|
+
* ```
|
|
4272
|
+
*/
|
|
4273
|
+
async update(id, input) {
|
|
4274
|
+
return this.adapter.updateBankAccount(id, input);
|
|
3345
4275
|
}
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
4276
|
+
/**
|
|
4277
|
+
* Delete a bank account.
|
|
4278
|
+
*
|
|
4279
|
+
* @example
|
|
4280
|
+
* ```ts
|
|
4281
|
+
* await peppol.bankAccounts.delete("123");
|
|
4282
|
+
* ```
|
|
4283
|
+
*/
|
|
4284
|
+
async delete(id) {
|
|
4285
|
+
return this.adapter.deleteBankAccount(id);
|
|
3350
4286
|
}
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
}
|
|
4287
|
+
/**
|
|
4288
|
+
* Async iterator over all bank accounts, automatically handling pagination.
|
|
4289
|
+
*
|
|
4290
|
+
* @example
|
|
4291
|
+
* ```ts
|
|
4292
|
+
* for await (const account of peppol.bankAccounts.listAll()) {
|
|
4293
|
+
* console.log(account.name, account.iban);
|
|
4294
|
+
* }
|
|
4295
|
+
* ```
|
|
4296
|
+
*/
|
|
4297
|
+
listAll(options) {
|
|
4298
|
+
return paginate((offset, limit) => this.adapter.listBankAccounts({ ...options, offset, limit }), options);
|
|
3363
4299
|
}
|
|
3364
|
-
return violations;
|
|
3365
4300
|
};
|
|
3366
|
-
var
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
for (let i = 0; i < input.lines.length; i++) {
|
|
3371
|
-
const line = input.lines[i];
|
|
3372
|
-
if (line.vatCategory === "Z" && line.vatRate !== 0) {
|
|
3373
|
-
violations.push(violation("BR-Z-05", "error", `Line ${i}: zero-rated (Z) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
|
|
3374
|
-
}
|
|
4301
|
+
var TransportOperations = class {
|
|
4302
|
+
adapter;
|
|
4303
|
+
constructor(adapter) {
|
|
4304
|
+
this.adapter = adapter;
|
|
3375
4305
|
}
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
4306
|
+
/**
|
|
4307
|
+
* List all available transport types in the network.
|
|
4308
|
+
* Returns global transport types (not account-scoped).
|
|
4309
|
+
*
|
|
4310
|
+
* @example
|
|
4311
|
+
* ```ts
|
|
4312
|
+
* const types = await peppol.transports.listTypes();
|
|
4313
|
+
* console.log(types); // [{ code: "peppol", name: "Peppol BIS 3.0" }, ...]
|
|
4314
|
+
* ```
|
|
4315
|
+
*/
|
|
4316
|
+
async listTypes() {
|
|
4317
|
+
return this.adapter.listTransportTypes();
|
|
3387
4318
|
}
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
4319
|
+
/**
|
|
4320
|
+
* List configured transports for this account.
|
|
4321
|
+
*
|
|
4322
|
+
* @example
|
|
4323
|
+
* ```ts
|
|
4324
|
+
* const transports = await peppol.transports.list();
|
|
4325
|
+
* console.log(transports); // [{ id: "t-1", transportTypeCode: "peppol", name: "..." }, ...]
|
|
4326
|
+
* ```
|
|
4327
|
+
*/
|
|
4328
|
+
async list() {
|
|
4329
|
+
return this.adapter.listTransports();
|
|
3399
4330
|
}
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
4331
|
+
/**
|
|
4332
|
+
* Get a single transport by code.
|
|
4333
|
+
*
|
|
4334
|
+
* @example
|
|
4335
|
+
* ```ts
|
|
4336
|
+
* const transport = await peppol.transports.get("peppol");
|
|
4337
|
+
* ```
|
|
4338
|
+
*/
|
|
4339
|
+
async get(code) {
|
|
4340
|
+
return this.adapter.getTransport(code);
|
|
3407
4341
|
}
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
if (UNROUTABLE_VAT_CATEGORIES.has(cat)) {
|
|
3422
|
-
violations.push(violation("unsupported_vat_category", "error", `${label}: VAT category "${echo(cat)}" is valid under EN 16931 but getpeppr cannot route it \u2014 our provider has no vocabulary for it. Sendable categories: ${sendable}.`, field));
|
|
3423
|
-
}
|
|
3424
|
-
};
|
|
3425
|
-
(input.lines ?? []).forEach((line, i) => check(line.vatCategory, `lines[${i}].vatCategory`, `Line ${i}`));
|
|
3426
|
-
(input.allowances ?? []).forEach((a, i) => check(a.vatCategory, `allowances[${i}].vatCategory`, `Allowance ${i}`));
|
|
3427
|
-
(input.charges ?? []).forEach((c, i) => check(c.vatCategory, `charges[${i}].vatCategory`, `Charge ${i}`));
|
|
3428
|
-
return violations;
|
|
3429
|
-
};
|
|
3430
|
-
var peppolR080 = (input) => {
|
|
3431
|
-
const violations = [];
|
|
3432
|
-
if (!input.lines)
|
|
3433
|
-
return violations;
|
|
3434
|
-
const knownCodes = getKnownUnitCodes();
|
|
3435
|
-
for (let i = 0; i < input.lines.length; i++) {
|
|
3436
|
-
const line = input.lines[i];
|
|
3437
|
-
if (line.unit) {
|
|
3438
|
-
const resolved = resolveUnit(line.unit);
|
|
3439
|
-
if (!knownCodes.has(resolved)) {
|
|
3440
|
-
violations.push(violation("PEPPOL-EN16931-R080", "warning", `Line ${i}: unit "${line.unit}" (resolved: "${resolved}") is not a known UN/ECE Rec20 code. Common codes: EA, HUR, DAY, KGM.`, `lines[${i}].unit`));
|
|
3441
|
-
}
|
|
3442
|
-
}
|
|
4342
|
+
/**
|
|
4343
|
+
* Create a new transport.
|
|
4344
|
+
*
|
|
4345
|
+
* @example
|
|
4346
|
+
* ```ts
|
|
4347
|
+
* const transport = await peppol.transports.create({
|
|
4348
|
+
* transportTypeCode: "peppol",
|
|
4349
|
+
* email: "billing@acme.com",
|
|
4350
|
+
* });
|
|
4351
|
+
* ```
|
|
4352
|
+
*/
|
|
4353
|
+
async create(input) {
|
|
4354
|
+
return this.adapter.createTransport(input);
|
|
3443
4355
|
}
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
br10,
|
|
3455
|
-
// Calculations (BR-CO)
|
|
3456
|
-
brCo10,
|
|
3457
|
-
brCo13,
|
|
3458
|
-
brCo15,
|
|
3459
|
-
brCo16,
|
|
3460
|
-
// Tax categories (one family per category)
|
|
3461
|
-
brS05,
|
|
3462
|
-
brZ05,
|
|
3463
|
-
brE05,
|
|
3464
|
-
brAe05,
|
|
3465
|
-
// Peppol-specific
|
|
3466
|
-
peppolR004,
|
|
3467
|
-
vatCategoryCodes,
|
|
3468
|
-
peppolR080
|
|
3469
|
-
];
|
|
3470
|
-
function validateSchematron(input) {
|
|
3471
|
-
const errors = [];
|
|
3472
|
-
const warnings = [];
|
|
3473
|
-
for (const rule of ALL_RULES) {
|
|
3474
|
-
const violations = rule(input);
|
|
3475
|
-
for (const v of violations) {
|
|
3476
|
-
if (v.severity === "error") {
|
|
3477
|
-
errors.push(v);
|
|
3478
|
-
} else {
|
|
3479
|
-
warnings.push(v);
|
|
3480
|
-
}
|
|
3481
|
-
}
|
|
4356
|
+
/**
|
|
4357
|
+
* Update an existing transport.
|
|
4358
|
+
*
|
|
4359
|
+
* @example
|
|
4360
|
+
* ```ts
|
|
4361
|
+
* const transport = await peppol.transports.update("peppol", { email: "new@acme.com" });
|
|
4362
|
+
* ```
|
|
4363
|
+
*/
|
|
4364
|
+
async update(code, input) {
|
|
4365
|
+
return this.adapter.updateTransport(code, input);
|
|
3482
4366
|
}
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
"BR-08",
|
|
3496
|
-
"BR-09",
|
|
3497
|
-
"BR-10",
|
|
3498
|
-
"BR-CL-17",
|
|
3499
|
-
"BR-CO-10",
|
|
3500
|
-
"BR-CO-13",
|
|
3501
|
-
"BR-CO-15",
|
|
3502
|
-
"BR-CO-16",
|
|
3503
|
-
"BR-S-05",
|
|
3504
|
-
"BR-Z-05",
|
|
3505
|
-
"BR-E-05",
|
|
3506
|
-
"BR-AE-05",
|
|
3507
|
-
"PEPPOL-EN16931-R004",
|
|
3508
|
-
"PEPPOL-EN16931-R080"
|
|
3509
|
-
];
|
|
4367
|
+
/**
|
|
4368
|
+
* Delete a transport.
|
|
4369
|
+
*
|
|
4370
|
+
* @example
|
|
4371
|
+
* ```ts
|
|
4372
|
+
* await peppol.transports.delete("peppol");
|
|
4373
|
+
* ```
|
|
4374
|
+
*/
|
|
4375
|
+
async delete(code) {
|
|
4376
|
+
return this.adapter.deleteTransport(code);
|
|
4377
|
+
}
|
|
4378
|
+
};
|
|
3510
4379
|
|
|
3511
4380
|
// src/commands/validate.ts
|
|
3512
4381
|
function runValidation(input) {
|
|
@@ -3927,12 +4796,11 @@ function validatePeppolId(raw) {
|
|
|
3927
4796
|
error: `Invalid Peppol ID format: "${raw}". Expected format: scheme:id (e.g. 0208:0685660237)`
|
|
3928
4797
|
};
|
|
3929
4798
|
}
|
|
3930
|
-
const scheme = raw
|
|
3931
|
-
const id = raw.slice(colonIndex + 1);
|
|
4799
|
+
const { scheme, id } = parsePeppolId(raw);
|
|
3932
4800
|
if (!/^\d{4}$/.test(scheme)) {
|
|
3933
4801
|
return {
|
|
3934
4802
|
ok: false,
|
|
3935
|
-
error: `Invalid scheme "${scheme}". Must be
|
|
4803
|
+
error: `Invalid scheme "${scheme}". Must be a 4-digit EAS code (e.g. 0208) or a published symbolic scheme (e.g. GB:VAT).`
|
|
3936
4804
|
};
|
|
3937
4805
|
}
|
|
3938
4806
|
if (!/^[A-Za-z0-9:.\-]+$/.test(id)) {
|
|
@@ -4179,19 +5047,17 @@ startxref
|
|
|
4179
5047
|
190
|
|
4180
5048
|
%%EOF`;
|
|
4181
5049
|
var MINIMAL_TEST_PDF_BASE64 = Buffer.from(MINIMAL_PDF).toString("base64");
|
|
4182
|
-
var SCHEME_COUNTRY_DEFAULTS = {
|
|
4183
|
-
"0009": "FR",
|
|
4184
|
-
"0204": "DE",
|
|
4185
|
-
"0208": "BE",
|
|
4186
|
-
"9925": "BE"
|
|
4187
|
-
};
|
|
4188
5050
|
function deriveCountryFromPeppolId(peppolId) {
|
|
4189
|
-
const
|
|
4190
|
-
const
|
|
5051
|
+
const { scheme, id } = parsePeppolId(peppolId);
|
|
5052
|
+
const published = countryForScheme(scheme);
|
|
5053
|
+
if (published !== void 0) {
|
|
5054
|
+
return published;
|
|
5055
|
+
}
|
|
5056
|
+
const alphaPrefix = id.slice(0, 2);
|
|
4191
5057
|
if (/^[A-Za-z]{2}$/.test(alphaPrefix)) {
|
|
4192
5058
|
return alphaPrefix.toUpperCase();
|
|
4193
5059
|
}
|
|
4194
|
-
return
|
|
5060
|
+
return "BE";
|
|
4195
5061
|
}
|
|
4196
5062
|
function buildDefaultSendPayload(overrides = {}) {
|
|
4197
5063
|
const today = /* @__PURE__ */ new Date();
|
|
@@ -4225,10 +5091,12 @@ function buildDefaultSendPayload(overrides = {}) {
|
|
|
4225
5091
|
unitPrice: amount,
|
|
4226
5092
|
vatRate: 0,
|
|
4227
5093
|
// vatCategory "O" = "Services outside scope of tax" (UBL 2.1 / EN 16931).
|
|
4228
|
-
// Public entities (SPF Economie) are VAT-exempt.
|
|
4229
|
-
//
|
|
5094
|
+
// Public entities (SPF Economie) are VAT-exempt. The SDK pre-validator
|
|
5095
|
+
// requires the builder-only reason; the send transport strips it so
|
|
5096
|
+
// Storecove can still derive its own provider-specific text.
|
|
4230
5097
|
// If sandbox returns 422 on this combination, fallback is vatRate: 21 + vatCategory: "S".
|
|
4231
|
-
vatCategory: "O"
|
|
5098
|
+
vatCategory: "O",
|
|
5099
|
+
taxExemptReason: "Not subject to VAT"
|
|
4232
5100
|
}
|
|
4233
5101
|
]
|
|
4234
5102
|
};
|