@getpeppr/cli 0.8.2 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -130,7 +130,7 @@ Validating: ${pc.bold(filename)}
130
130
  return lines.join("\n");
131
131
  }
132
132
 
133
- // ../sdk/dist/core/canonical-schemes.js
133
+ // ../../../getpeppr/packages/sdk/dist/core/canonical-schemes.js
134
134
  var ALIAS_TO_EAS = /* @__PURE__ */ new Map([
135
135
  ["AD:VAT", "9922"],
136
136
  ["AE:TIN", "0235"],
@@ -356,7 +356,7 @@ function countryForScheme(scheme) {
356
356
  var CANONICAL_SCHEME_COUNT = ALIAS_TO_EAS.size;
357
357
  var SCHEME_COUNTRY_COUNT = EAS_TO_COUNTRY.size;
358
358
 
359
- // ../sdk/dist/core/peppol-id.js
359
+ // ../../../getpeppr/packages/sdk/dist/core/peppol-id.js
360
360
  function isWellFormedPeppolId(peppolId) {
361
361
  if (!peppolId.includes(":"))
362
362
  return false;
@@ -382,7 +382,7 @@ function parsePeppolId(peppolId) {
382
382
  };
383
383
  }
384
384
 
385
- // ../sdk/dist/core/iso6523-icd-codes.js
385
+ // ../../../getpeppr/packages/sdk/dist/core/iso6523-icd-codes.js
386
386
  var ICD_CODES = /* @__PURE__ */ new Set([
387
387
  "0002",
388
388
  "0003",
@@ -642,7 +642,7 @@ function canCarryPartyIdentification(scheme, context) {
642
642
  return scheme === "SEPA" && context !== "AccountingCustomerParty";
643
643
  }
644
644
 
645
- // ../sdk/dist/core/ubl-builder.js
645
+ // ../../../getpeppr/packages/sdk/dist/core/ubl-builder.js
646
646
  var UBL_NS = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2";
647
647
  var CAC_NS = "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2";
648
648
  var CBC_NS = "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2";
@@ -651,6 +651,24 @@ var PEPPOL_CUSTOMIZATION_ID = "urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.
651
651
  var PEPPOL_PROFILE_ID = "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0";
652
652
  var DEFAULT_UNIT = "EA";
653
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
+ };
654
672
  var UNIT_CODE_MAP = {
655
673
  each: "EA",
656
674
  piece: "EA",
@@ -693,9 +711,51 @@ function formatDate(dateStr) {
693
711
  function formatAmount(amount) {
694
712
  return amount.toFixed(2);
695
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
+ }
720
+ function formatBaseQuantity(baseQuantity, field) {
721
+ if (typeof baseQuantity !== "number" || !Number.isFinite(baseQuantity) || baseQuantity <= 0) {
722
+ throw new UblBuilderInputError("baseQuantity must be a finite number greater than zero.", field, "PEPPOL-EN16931-R121");
723
+ }
724
+ const numeric = String(baseQuantity);
725
+ const exponentMarker = numeric.search(/[eE]/);
726
+ if (exponentMarker === -1)
727
+ return numeric;
728
+ const coefficient = numeric.slice(0, exponentMarker);
729
+ const exponent = Number(numeric.slice(exponentMarker + 1));
730
+ const decimalPoint = coefficient.indexOf(".");
731
+ const digits = coefficient.replace(".", "");
732
+ const integerDigits = decimalPoint === -1 ? coefficient.length : decimalPoint;
733
+ const outputPoint = integerDigits + exponent;
734
+ if (outputPoint <= 0) {
735
+ return `0.${"0".repeat(-outputPoint)}${digits}`;
736
+ }
737
+ if (outputPoint >= digits.length) {
738
+ return `${digits}${"0".repeat(outputPoint - digits.length)}`;
739
+ }
740
+ return `${digits.slice(0, outputPoint)}.${digits.slice(outputPoint)}`;
741
+ }
696
742
  function round2(n) {
697
743
  return Math.round(n * 100) / 100;
698
744
  }
745
+ function normalizedTaxExemptReason(vatCategory, reason) {
746
+ if (!EXEMPTION_REASON_CATEGORIES.has(vatCategory) || typeof reason !== "string") {
747
+ return void 0;
748
+ }
749
+ const normalized = reason.trim();
750
+ for (const character of normalized) {
751
+ const codePoint = character.codePointAt(0);
752
+ const allowed = codePoint === 9 || codePoint === 10 || codePoint === 13 || codePoint >= 32 && codePoint <= 55295 || codePoint >= 57344 && codePoint <= 65533 || codePoint >= 65536 && codePoint <= 1114111;
753
+ if (!allowed) {
754
+ throw new UblBuilderInputError("taxExemptReason contains an invalid XML character.", "taxExemptReason");
755
+ }
756
+ }
757
+ return normalized || void 0;
758
+ }
699
759
  function buildPartyXml(party, role) {
700
760
  const { scheme: endpointScheme, id: endpointId } = parsePeppolId(party.peppolId);
701
761
  return `
@@ -876,8 +936,8 @@ function buildDocumentAllowanceChargeXml(item, isCharge, currency) {
876
936
  <cbc:AllowanceChargeReason>${escapeXml(item.reason)}</cbc:AllowanceChargeReason>
877
937
  <cbc:Amount currencyID="${escapeXml(currency)}">${formatAmount(item.amount)}</cbc:Amount>
878
938
  <cac:TaxCategory>
879
- <cbc:ID>${vatCategory}</cbc:ID>
880
- <cbc:Percent>${item.vatRate}</cbc:Percent>
939
+ <cbc:ID>${escapeXml(vatCategory)}</cbc:ID>
940
+ ${vatCategory === "O" ? "" : `<cbc:Percent>${formatVatRate(item.vatRate, "vatRate")}</cbc:Percent>`}
881
941
  <cac:TaxScheme>
882
942
  <cbc:ID>VAT</cbc:ID>
883
943
  </cac:TaxScheme>
@@ -917,8 +977,8 @@ function buildDocumentLineXml(line, index, currency, lineTag, qtyTag) {
917
977
  <cbc:ID>${escapeXml(line.itemId)}</cbc:ID>
918
978
  </cac:SellersItemIdentification>` : ""}
919
979
  <cac:ClassifiedTaxCategory>
920
- <cbc:ID>${vatCategory}</cbc:ID>
921
- <cbc:Percent>${line.vatRate}</cbc:Percent>
980
+ <cbc:ID>${escapeXml(vatCategory)}</cbc:ID>
981
+ ${vatCategory === "O" ? "" : `<cbc:Percent>${formatVatRate(line.vatRate, "vatRate")}</cbc:Percent>`}
922
982
  <cac:TaxScheme>
923
983
  <cbc:ID>VAT</cbc:ID>
924
984
  </cac:TaxScheme>
@@ -936,46 +996,67 @@ function buildDocumentLineXml(line, index, currency, lineTag, qtyTag) {
936
996
  </cac:Item>
937
997
  <cac:Price>
938
998
  <cbc:PriceAmount currencyID="${escapeXml(currency)}">${formatAmount(line.unitPrice)}</cbc:PriceAmount>
939
- ${line.baseQuantity != null ? `<cbc:BaseQuantity unitCode="${escapeXml(resolveUnitCode(line.baseQuantityUnit ?? line.unit ?? DEFAULT_UNIT))}">${line.baseQuantity}</cbc:BaseQuantity>` : ""}
999
+ ${line.baseQuantity !== void 0 ? `<cbc:BaseQuantity unitCode="${escapeXml(resolveUnitCode(line.baseQuantityUnit ?? line.unit ?? DEFAULT_UNIT))}">${formatBaseQuantity(line.baseQuantity, `lines[${index}].baseQuantity`)}</cbc:BaseQuantity>` : ""}
940
1000
  </cac:Price>
941
1001
  </cac:${lineTag}>`;
942
1002
  }
943
1003
  function buildInvoiceLineXml(line, index, currency) {
944
1004
  return buildDocumentLineXml(line, index, currency, "InvoiceLine", "InvoicedQuantity");
945
1005
  }
946
- function calculateTaxSubtotals(lines, allowances, charges) {
1006
+ function calculateTaxSubtotals(lines, allowances, charges, options = {}) {
947
1007
  const groups = /* @__PURE__ */ new Map();
948
- function addToGroup(vatCategory, vatRate, amount) {
949
- const key = `${vatCategory}-${vatRate}`;
1008
+ function addToGroup(vatCategory, vatRate, amount, taxExemptReason, field = "taxExemptReason") {
1009
+ if (typeof vatCategory !== "string") {
1010
+ throw new UblBuilderInputError("vatCategory must be a string.", `${field}.vatCategory`);
1011
+ }
1012
+ formatVatRate(vatRate, `${field}.vatRate`);
1013
+ if (options.forUbl && vatCategory === "O" && vatRate !== 0) {
1014
+ throw new UblBuilderInputError("Category O must use vatRate 0 in SDK input.", `${field}.vatRate`);
1015
+ }
1016
+ const effectiveVatRate = options.forUbl && vatCategory === "O" ? 0 : vatRate;
1017
+ const reason = options.forUbl ? normalizedTaxExemptReason(vatCategory, taxExemptReason) : void 0;
1018
+ const key = `${vatCategory}-${effectiveVatRate}`;
950
1019
  const existing = groups.get(key);
951
1020
  if (existing) {
1021
+ if (reason && existing.taxExemptReason && reason !== existing.taxExemptReason) {
1022
+ throw new UblBuilderInputError(`Conflicting taxExemptReason values for VAT group ${vatCategory}/${effectiveVatRate}.`, "taxExemptReason");
1023
+ }
1024
+ existing.taxExemptReason ??= reason;
952
1025
  existing.taxableAmount = round2(existing.taxableAmount + amount);
953
1026
  } else {
954
1027
  groups.set(key, {
955
- vatRate,
1028
+ vatRate: effectiveVatRate,
956
1029
  vatCategory,
1030
+ taxExemptReason: reason,
957
1031
  taxableAmount: amount,
958
1032
  taxAmount: 0
959
1033
  // computed once per group below (BR-CO-17)
960
1034
  });
961
1035
  }
962
1036
  }
963
- for (const line of lines) {
964
- addToGroup(line.vatCategory ?? "S", line.vatRate, calculateLineExtensionAmount(line));
1037
+ for (const [index, line] of lines.entries()) {
1038
+ addToGroup(line.vatCategory ?? "S", line.vatRate, calculateLineExtensionAmount(line), line.taxExemptReason, `lines[${index}]`);
965
1039
  }
966
- for (const a of allowances ?? []) {
967
- addToGroup(a.vatCategory ?? "S", a.vatRate, -a.amount);
1040
+ for (const [index, a] of (allowances ?? []).entries()) {
1041
+ addToGroup(a.vatCategory ?? "S", a.vatRate, -a.amount, a.taxExemptReason, `allowances[${index}]`);
968
1042
  }
969
- for (const c of charges ?? []) {
970
- addToGroup(c.vatCategory ?? "S", c.vatRate, c.amount);
1043
+ for (const [index, c] of (charges ?? []).entries()) {
1044
+ addToGroup(c.vatCategory ?? "S", c.vatRate, c.amount, c.taxExemptReason, `charges[${index}]`);
1045
+ }
1046
+ if (options.forUbl) {
1047
+ for (const subtotal of groups.values()) {
1048
+ if (EXEMPTION_REASON_CATEGORIES.has(subtotal.vatCategory) && !subtotal.taxExemptReason) {
1049
+ throw new UblBuilderInputError(`VAT category ${subtotal.vatCategory} requires a non-empty taxExemptReason.`, "taxExemptReason", EXEMPTION_REASON_RULES[subtotal.vatCategory]);
1050
+ }
1051
+ }
971
1052
  }
972
1053
  return Array.from(groups.values()).map((subtotal) => ({
973
1054
  ...subtotal,
974
1055
  taxAmount: round2(subtotal.taxableAmount * (subtotal.vatRate / 100))
975
1056
  }));
976
1057
  }
977
- function calculateDocumentTotals(lines, allowances, charges) {
978
- const taxSubtotals = calculateTaxSubtotals(lines, allowances, charges);
1058
+ function calculateDocumentTotals(lines, allowances, charges, options = {}) {
1059
+ const taxSubtotals = calculateTaxSubtotals(lines, allowances, charges, options);
979
1060
  const lineExtensionAmount = lines.reduce((sum, line) => sum + calculateLineExtensionAmount(line), 0);
980
1061
  const allowanceTotalAmount = (allowances ?? []).reduce((sum, a) => sum + a.amount, 0);
981
1062
  const chargeTotalAmount = (charges ?? []).reduce((sum, c) => sum + c.amount, 0);
@@ -999,8 +1080,9 @@ function buildTaxTotalXml(taxSubtotals, totalTax, currency) {
999
1080
  <cbc:TaxableAmount currencyID="${escapeXml(currency)}">${formatAmount(st.taxableAmount)}</cbc:TaxableAmount>
1000
1081
  <cbc:TaxAmount currencyID="${escapeXml(currency)}">${formatAmount(st.taxAmount)}</cbc:TaxAmount>
1001
1082
  <cac:TaxCategory>
1002
- <cbc:ID>${st.vatCategory}</cbc:ID>
1003
- <cbc:Percent>${st.vatRate}</cbc:Percent>
1083
+ <cbc:ID>${escapeXml(st.vatCategory)}</cbc:ID>
1084
+ ${st.vatCategory === "O" ? "" : `<cbc:Percent>${formatVatRate(st.vatRate, "vatRate")}</cbc:Percent>`}
1085
+ ${st.taxExemptReason ? `<cbc:TaxExemptionReason>${escapeXml(st.taxExemptReason)}</cbc:TaxExemptionReason>` : ""}
1004
1086
  <cac:TaxScheme>
1005
1087
  <cbc:ID>VAT</cbc:ID>
1006
1088
  </cac:TaxScheme>
@@ -1069,7 +1151,7 @@ function buildInvoiceXml(input) {
1069
1151
  const date = formatDate(input.date);
1070
1152
  const dueDate = input.dueDate ? formatDate(input.dueDate) : void 0;
1071
1153
  const hasTaxCurrency = input.taxCurrency && input.taxCurrency !== currency;
1072
- const totals = calculateDocumentTotals(input.lines, input.allowances, input.charges);
1154
+ const totals = calculateDocumentTotals(input.lines, input.allowances, input.charges, { forUbl: true });
1073
1155
  const linesXml = input.lines.map((line, i) => buildInvoiceLineXml(line, i, currency)).join("");
1074
1156
  return `<?xml version="1.0" encoding="UTF-8"?>
1075
1157
  <Invoice xmlns="${UBL_NS}"
@@ -1116,7 +1198,7 @@ function buildCreditNoteXml(input) {
1116
1198
  const date = formatDate(input.date);
1117
1199
  const dueDate = input.dueDate ? formatDate(input.dueDate) : void 0;
1118
1200
  const hasTaxCurrency = input.taxCurrency && input.taxCurrency !== currency;
1119
- const totals = calculateDocumentTotals(input.lines, input.allowances, input.charges);
1201
+ const totals = calculateDocumentTotals(input.lines, input.allowances, input.charges, { forUbl: true });
1120
1202
  const linesXml = input.lines.map((line, i) => buildCreditNoteLineXml(line, i, currency)).join("");
1121
1203
  return `<?xml version="1.0" encoding="UTF-8"?>
1122
1204
  <CreditNote xmlns="${CREDIT_NOTE_NS}"
@@ -1160,7 +1242,7 @@ function buildCreditNoteXml(input) {
1160
1242
  </CreditNote>`;
1161
1243
  }
1162
1244
 
1163
- // ../sdk/dist/core/checksums/luhn.js
1245
+ // ../../../getpeppr/packages/sdk/dist/core/checksums/luhn.js
1164
1246
  function isValidLuhn(input) {
1165
1247
  if (typeof input !== "string")
1166
1248
  return false;
@@ -1201,7 +1283,7 @@ function isValidLuhnSiret(input) {
1201
1283
  return false;
1202
1284
  }
1203
1285
 
1204
- // ../sdk/dist/core/country-rules.js
1286
+ // ../../../getpeppr/packages/sdk/dist/core/country-rules.js
1205
1287
  function warn(field, message, ruleId) {
1206
1288
  return { field, message, ruleId };
1207
1289
  }
@@ -1342,7 +1424,7 @@ function validateCountryRules(input) {
1342
1424
  return { errors, warnings };
1343
1425
  }
1344
1426
 
1345
- // ../sdk/dist/core/code-lists.js
1427
+ // ../../../getpeppr/packages/sdk/dist/core/code-lists.js
1346
1428
  var CURRENCIES = /* @__PURE__ */ new Map([
1347
1429
  ["EUR", { code: "EUR", name: "Euro", minorUnits: 2 }],
1348
1430
  ["USD", { code: "USD", name: "US Dollar", minorUnits: 2 }],
@@ -1467,7 +1549,7 @@ function getAllUnits() {
1467
1549
  return Array.from(UNIT_CODES.entries()).map(([code, name]) => ({ code, name })).sort((a, b) => a.code.localeCompare(b.code));
1468
1550
  }
1469
1551
 
1470
- // ../sdk/dist/core/validator.js
1552
+ // ../../../getpeppr/packages/sdk/dist/core/validator.js
1471
1553
  function error(field, message, ruleId, suggestion) {
1472
1554
  return { field, message, ruleId, suggestion };
1473
1555
  }
@@ -1545,6 +1627,11 @@ function validateLine(line, index, isCreditNote = false) {
1545
1627
  } else if (line.unitPrice < 0) {
1546
1628
  errors.push(error(`${path}.unitPrice`, `Unit price cannot be negative, got ${line.unitPrice}`, void 0, "For discounts, use a negative quantity or a separate discount line"));
1547
1629
  }
1630
+ if (line.baseQuantity !== void 0) {
1631
+ if (typeof line.baseQuantity !== "number" || !Number.isFinite(line.baseQuantity) || line.baseQuantity <= 0) {
1632
+ errors.push(error(`${path}.baseQuantity`, "Base quantity must be a finite number greater than zero", "PEPPOL-EN16931-R121", "Use a positive number for the item price base quantity"));
1633
+ }
1634
+ }
1548
1635
  if (line.vatRate === void 0 || line.vatRate === null) {
1549
1636
  errors.push(error(`${path}.vatRate`, "VAT rate is required", "BR-CO-17"));
1550
1637
  } else if (line.vatRate < 0 || line.vatRate > 100) {
@@ -1607,11 +1694,32 @@ function validateInvoice(input) {
1607
1694
  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.'));
1608
1695
  }
1609
1696
  }
1610
- if (!input.lines || input.lines.length === 0) {
1697
+ const linesValue = input.lines;
1698
+ if (!Array.isArray(linesValue)) {
1699
+ errors.push(error("lines", "Line items must be an array", void 0));
1700
+ } else if (linesValue.length === 0) {
1611
1701
  errors.push(error("lines", "At least one line item is required", "BR-16", "Add items to the lines array"));
1612
1702
  } else {
1613
- for (let i = 0; i < input.lines.length; i++) {
1614
- errors.push(...validateLine(input.lines[i], i, input.isCreditNote));
1703
+ for (const [i, line] of linesValue.entries()) {
1704
+ if (line === null || typeof line !== "object") {
1705
+ errors.push(error(`lines[${i}]`, `Line item ${i} must be an object`, void 0));
1706
+ continue;
1707
+ }
1708
+ errors.push(...validateLine(line, i, input.isCreditNote));
1709
+ }
1710
+ }
1711
+ for (const field of ["allowances", "charges"]) {
1712
+ const value = input[field];
1713
+ if (value === void 0)
1714
+ continue;
1715
+ if (!Array.isArray(value)) {
1716
+ errors.push(error(field, `${field} must be an array`, void 0));
1717
+ continue;
1718
+ }
1719
+ for (const [index, item] of value.entries()) {
1720
+ if (item === null || typeof item !== "object") {
1721
+ errors.push(error(`${field}[${index}]`, `${field}[${index}] must be an object`, void 0));
1722
+ }
1615
1723
  }
1616
1724
  }
1617
1725
  if (input.date) {
@@ -1673,174 +1781,699 @@ function validateInvoice(input) {
1673
1781
  };
1674
1782
  }
1675
1783
 
1676
- // ../sdk/dist/core/status-precedence.js
1677
- var STATUS_PRECEDENCE = [
1678
- { status: "failed", family: "terminal-failure" },
1679
- { status: "rejected", family: "terminal-failure" },
1680
- { status: "paid", family: "terminal-success" },
1681
- { status: "partially_paid", family: "progress" },
1682
- { status: "accepted", family: "progress" },
1683
- { status: "conditionally_accepted", family: "progress" },
1684
- { status: "under_query", family: "progress" },
1685
- { status: "in_process", family: "progress" },
1686
- { status: "cleared", family: "progress" },
1687
- { status: "delivered", family: "progress" },
1688
- { status: "acknowledged", family: "progress" },
1689
- // Terminal for developer wait semantics only — stays rank 40 (non-terminal)
1690
- // in the projection guard (§3.12 two-level terminality).
1691
- { status: "no_action", family: "terminal-failure" },
1692
- { status: "submitted", family: "progress" },
1693
- { status: "unknown", family: "fallback" }
1694
- ];
1695
- function statusFamily(status) {
1696
- return STATUS_PRECEDENCE.find((e) => e.status === status)?.family ?? "fallback";
1697
- }
1698
- var TERMINAL_FAILURE_STATUSES = STATUS_PRECEDENCE.filter((e) => e.family === "terminal-failure").map((e) => e.status);
1699
-
1700
- // ../sdk/dist/version.js
1701
- var SDK_VERSION = "4.5.0";
1702
-
1703
- // ../sdk/dist/core/client.js
1704
- function findHeaderCaseInsensitive(headers, name) {
1705
- if (!headers)
1706
- return void 0;
1707
- const target = name.toLowerCase();
1708
- for (const [key, value] of Object.entries(headers)) {
1709
- if (key.toLowerCase() === target)
1710
- return value;
1711
- }
1712
- return void 0;
1713
- }
1714
- var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
1715
- function sleep(ms) {
1716
- return new Promise((resolve4) => setTimeout(resolve4, ms));
1717
- }
1718
- function calculateRetryDelay(attempt, initialDelayMs, maxDelayMs, retryAfterMs) {
1719
- if (retryAfterMs !== void 0)
1720
- return Math.min(retryAfterMs, maxDelayMs);
1721
- const exponentialDelay = initialDelayMs * Math.pow(2, attempt);
1722
- const jitter = Math.random() * initialDelayMs;
1723
- return Math.min(exponentialDelay + jitter, maxDelayMs);
1784
+ // ../../../getpeppr/packages/sdk/dist/core/schematron.js
1785
+ function violation(ruleId, severity, message, field) {
1786
+ return { ruleId, severity, message, field };
1724
1787
  }
1725
- function parseRetryAfter(headerValue) {
1726
- if (!headerValue)
1727
- return void 0;
1728
- const seconds = Number(headerValue);
1729
- if (Number.isFinite(seconds) && seconds >= 0) {
1730
- return seconds * 1e3;
1731
- }
1732
- const dateMs = Date.parse(headerValue);
1733
- if (!Number.isNaN(dateMs)) {
1734
- const delayMs = dateMs - Date.now();
1735
- return delayMs > 0 ? delayMs : 0;
1788
+ var _knownUnitCodes;
1789
+ function getKnownUnitCodes() {
1790
+ if (!_knownUnitCodes) {
1791
+ _knownUnitCodes = new Set(getAllUnits().map((u) => u.code));
1736
1792
  }
1737
- return void 0;
1738
- }
1739
- var CONTROL_CHARACTERS = /[\u0000-\u001F\u007F-\u009F]/g;
1740
- function stripControls(value) {
1741
- return value.replace(CONTROL_CHARACTERS, " ");
1742
- }
1743
- function readOwn(source, key) {
1744
- return Object.hasOwn(source, key) ? source[key] : void 0;
1793
+ return _knownUnitCodes;
1745
1794
  }
1746
- function readSentence(source, key) {
1747
- const value = readOwn(source, key);
1748
- if (typeof value !== "string")
1749
- return null;
1750
- const cleaned = stripControls(value).trim();
1751
- return cleaned === "" ? null : cleaned;
1795
+ var VALID_VAT_CATEGORIES = /* @__PURE__ */ new Set(["S", "Z", "E", "AE", "K", "G", "O", "L", "M", "B"]);
1796
+ var SENDABLE_VAT_CATEGORIES = ["S", "Z", "E", "AE", "K", "G", "O"];
1797
+ var UNROUTABLE_VAT_CATEGORIES = /* @__PURE__ */ new Set(["L", "M", "B"]);
1798
+ function computeLineNet(line) {
1799
+ const baseQty = line.baseQuantity ?? 1;
1800
+ if (baseQty === 0)
1801
+ return NaN;
1802
+ const baseAmount = line.quantity * line.unitPrice / baseQty;
1803
+ const chargeTotal = (line.charges ?? []).reduce((sum, c) => sum + c.amount, 0);
1804
+ const allowanceTotal = (line.allowances ?? []).reduce((sum, a) => sum + a.amount, 0);
1805
+ return baseAmount + chargeTotal - allowanceTotal;
1752
1806
  }
1753
- function safeDocsUrl(value) {
1754
- if (typeof value !== "string")
1755
- return null;
1756
- let parsed;
1757
- try {
1758
- parsed = new URL(value);
1759
- } catch {
1760
- return null;
1807
+ var br02 = (input) => {
1808
+ if (!input.number?.trim()) {
1809
+ return [violation("BR-02", "error", "Invoice number is required.", "number")];
1761
1810
  }
1762
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
1763
- return null;
1764
- return parsed.href;
1765
- }
1766
- function formatApiErrorMessage(status, rawBody) {
1767
- const verbatim = `getpeppr API error (${status}): ${stripControls(rawBody)}`;
1768
- let parsed;
1769
- try {
1770
- parsed = JSON.parse(rawBody);
1771
- } catch {
1772
- return verbatim;
1811
+ return [];
1812
+ };
1813
+ var br03 = (input) => {
1814
+ if (!input.date) {
1815
+ return [
1816
+ violation("BR-03", "warning", "Invoice issue date is not set. The SDK will default to today's date.", "date")
1817
+ ];
1773
1818
  }
1774
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1775
- return verbatim;
1819
+ return [];
1820
+ };
1821
+ var br06 = (input) => {
1822
+ if (input.from && !input.from.vatNumber) {
1823
+ return [
1824
+ violation("BR-06", "warning", "Seller party has no VAT number. The gateway will use the account's VAT registration.", "from.vatNumber")
1825
+ ];
1776
1826
  }
1777
- const sentence = readSentence(parsed, "message") ?? readSentence(parsed, "error");
1778
- if (sentence === null)
1779
- return verbatim;
1780
- const link = safeDocsUrl(readOwn(parsed, "docs"));
1781
- return `getpeppr API error (${status}): ${sentence}${link ? ` See ${link}` : ""}`;
1782
- }
1783
- function isRetryableError(error2) {
1784
- if (error2 instanceof PeppolApiError) {
1785
- return RETRYABLE_STATUS_CODES.has(error2.statusCode);
1827
+ return [];
1828
+ };
1829
+ var br07 = (input) => {
1830
+ if (!input.to?.name?.trim()) {
1831
+ return [violation("BR-07", "error", "Buyer name is required.", "to.name")];
1786
1832
  }
1787
- if (error2 instanceof Error && error2.name === "AbortError") {
1788
- return true;
1833
+ return [];
1834
+ };
1835
+ var br08 = (input) => {
1836
+ if (!input.lines || input.lines.length === 0) {
1837
+ return [violation("BR-08", "error", "Invoice must have at least one line item.", "lines")];
1789
1838
  }
1790
- if (error2 instanceof TypeError && /fetch failed|network|ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT/i.test(error2.message)) {
1791
- return true;
1839
+ return [];
1840
+ };
1841
+ var br09 = (input) => {
1842
+ if (!input.dueDate && !input.paymentTerms) {
1843
+ return [
1844
+ violation("BR-09", "warning", "Neither due date nor payment terms specified. At least one is recommended.", "dueDate")
1845
+ ];
1792
1846
  }
1793
- return false;
1794
- }
1795
- var DEFAULT_BASE_URL = "https://api.getpeppr.dev/v1";
1796
- var GetpepprAdapter = class {
1797
- name = "getpeppr";
1798
- baseUrl;
1799
- apiKey;
1800
- timeout;
1801
- retryConfig;
1802
- onRequest;
1803
- onResponse;
1804
- constructor(config) {
1805
- this.apiKey = config.apiKey;
1806
- this.timeout = config.timeout ?? 3e4;
1807
- this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
1808
- this.retryConfig = {
1809
- maxRetries: config.retry?.maxRetries ?? 3,
1810
- initialDelayMs: config.retry?.initialDelayMs ?? 500,
1811
- maxDelayMs: config.retry?.maxDelayMs ?? 3e4
1812
- };
1813
- this.onRequest = config.onRequest;
1814
- this.onResponse = config.onResponse;
1847
+ return [];
1848
+ };
1849
+ var br10 = (input) => {
1850
+ if (!input.buyerReference && !input.orderReference) {
1851
+ return [
1852
+ violation("BR-10", "warning", "Neither buyerReference nor orderReference specified. Peppol BIS 3.0 requires at least one.", "buyerReference")
1853
+ ];
1815
1854
  }
1816
- async request(method, path, body, extraHeaders) {
1817
- const { maxRetries, initialDelayMs, maxDelayMs } = this.retryConfig;
1818
- let lastError;
1819
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
1820
- try {
1821
- return await this.doRequest(method, path, body, extraHeaders);
1822
- } catch (err) {
1823
- lastError = err;
1824
- const is429 = err instanceof PeppolApiError && err.statusCode === 429;
1825
- const isSafeMethod = /^(GET|DELETE|HEAD)$/i.test(method);
1826
- const hasIdempotencyKey = !!findHeaderCaseInsensitive(extraHeaders, "Idempotency-Key");
1827
- const canRetry = is429 || isSafeMethod || hasIdempotencyKey;
1828
- if (attempt < maxRetries && canRetry && isRetryableError(err)) {
1829
- const retryAfterMs = err instanceof PeppolApiError ? err.retryAfterMs : void 0;
1830
- await sleep(calculateRetryDelay(attempt, initialDelayMs, maxDelayMs, retryAfterMs));
1831
- continue;
1832
- }
1833
- throw err;
1834
- }
1855
+ return [];
1856
+ };
1857
+ var brCo10 = (input) => {
1858
+ const violations = [];
1859
+ if (!input.lines)
1860
+ return violations;
1861
+ for (let i = 0; i < input.lines.length; i++) {
1862
+ const line = input.lines[i];
1863
+ const net = computeLineNet(line);
1864
+ if (!Number.isFinite(net)) {
1865
+ const baseQty = line.baseQuantity ?? 1;
1866
+ const detail = baseQty === 0 ? "baseQuantity is 0, causing division by zero." : "Computed line amount is not a finite number.";
1867
+ violations.push(violation("BR-CO-10", "error", `Line ${i}: invalid net amount. ${detail}`, `lines[${i}]`));
1835
1868
  }
1836
- throw lastError;
1837
1869
  }
1838
- async doRequest(method, path, body, extraHeaders) {
1839
- const url = `${this.baseUrl}${path}`;
1840
- const controller = new AbortController();
1841
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
1842
- const requestHeaders = {
1843
- Authorization: `Bearer ${this.apiKey}`,
1870
+ return violations;
1871
+ };
1872
+ var brCo13 = (input) => {
1873
+ if (!input.lines || input.lines.length === 0)
1874
+ return [];
1875
+ let totalVat = 0;
1876
+ for (let i = 0; i < input.lines.length; i++) {
1877
+ const line = input.lines[i];
1878
+ const net = computeLineNet(line);
1879
+ if (!Number.isFinite(net))
1880
+ continue;
1881
+ totalVat += net * (line.vatRate / 100);
1882
+ }
1883
+ for (const allowance of input.allowances ?? []) {
1884
+ totalVat -= allowance.amount * (allowance.vatRate / 100);
1885
+ }
1886
+ for (const charge of input.charges ?? []) {
1887
+ totalVat += charge.amount * (charge.vatRate / 100);
1888
+ }
1889
+ if (!Number.isFinite(totalVat)) {
1890
+ return [
1891
+ violation("BR-CO-13", "error", "Computed total VAT amount is not a finite number. Check line amounts and VAT rates.")
1892
+ ];
1893
+ }
1894
+ if (totalVat < -0.01) {
1895
+ return [
1896
+ violation("BR-CO-13", "warning", `Computed total VAT is negative (${totalVat.toFixed(2)}). This is unusual for an invoice.`)
1897
+ ];
1898
+ }
1899
+ return [];
1900
+ };
1901
+ var brCo15 = (input) => {
1902
+ if (!input.lines || input.lines.length === 0)
1903
+ return [];
1904
+ let lineTotal = 0;
1905
+ let vatTotal = 0;
1906
+ for (const line of input.lines) {
1907
+ const net = computeLineNet(line);
1908
+ if (!Number.isFinite(net))
1909
+ continue;
1910
+ lineTotal += net;
1911
+ vatTotal += net * (line.vatRate / 100);
1912
+ }
1913
+ for (const allowance of input.allowances ?? []) {
1914
+ lineTotal -= allowance.amount;
1915
+ vatTotal -= allowance.amount * (allowance.vatRate / 100);
1916
+ }
1917
+ for (const charge of input.charges ?? []) {
1918
+ lineTotal += charge.amount;
1919
+ vatTotal += charge.amount * (charge.vatRate / 100);
1920
+ }
1921
+ const taxInclusive = lineTotal + vatTotal;
1922
+ if (!Number.isFinite(taxInclusive)) {
1923
+ return [
1924
+ violation("BR-CO-15", "error", "Computed tax-inclusive amount is not a finite number.")
1925
+ ];
1926
+ }
1927
+ if (taxInclusive < -0.01) {
1928
+ return [
1929
+ violation("BR-CO-15", "warning", `Computed tax-inclusive amount is negative (${taxInclusive.toFixed(2)}). Consider using a credit note instead.`)
1930
+ ];
1931
+ }
1932
+ return [];
1933
+ };
1934
+ var brCo16 = (input) => {
1935
+ if (!input.lines || input.lines.length === 0)
1936
+ return [];
1937
+ let lineTotal = 0;
1938
+ let vatTotal = 0;
1939
+ for (const line of input.lines) {
1940
+ const net = computeLineNet(line);
1941
+ if (!Number.isFinite(net))
1942
+ continue;
1943
+ lineTotal += net;
1944
+ vatTotal += net * (line.vatRate / 100);
1945
+ }
1946
+ for (const allowance of input.allowances ?? []) {
1947
+ lineTotal -= allowance.amount;
1948
+ vatTotal -= allowance.amount * (allowance.vatRate / 100);
1949
+ }
1950
+ for (const charge of input.charges ?? []) {
1951
+ lineTotal += charge.amount;
1952
+ vatTotal += charge.amount * (charge.vatRate / 100);
1953
+ }
1954
+ const taxInclusive = lineTotal + vatTotal;
1955
+ const prepaid = input.prepaidAmount ?? 0;
1956
+ const rounding = input.roundingAmount ?? 0;
1957
+ const payable = taxInclusive - prepaid + rounding;
1958
+ if (!Number.isFinite(payable)) {
1959
+ return [
1960
+ violation("BR-CO-16", "error", "Computed payable amount is not a finite number.")
1961
+ ];
1962
+ }
1963
+ if (payable < -0.01) {
1964
+ return [
1965
+ violation("BR-CO-16", "warning", `Computed payable amount is negative (${payable.toFixed(2)}). Prepaid amount (${prepaid}) exceeds the invoice total.`)
1966
+ ];
1967
+ }
1968
+ return [];
1969
+ };
1970
+ var brS05 = (input) => {
1971
+ const violations = [];
1972
+ if (!input.lines)
1973
+ return violations;
1974
+ for (let i = 0; i < input.lines.length; i++) {
1975
+ const line = input.lines[i];
1976
+ const category = line.vatCategory ?? "S";
1977
+ if (category === "S" && (line.vatRate === void 0 || line.vatRate <= 0)) {
1978
+ violations.push(violation("BR-S-05", "error", `Line ${i}: standard rate (S) requires vatRate > 0, got ${line.vatRate ?? "undefined"}.`, `lines[${i}].vatRate`));
1979
+ }
1980
+ }
1981
+ return violations;
1982
+ };
1983
+ var brZ05 = (input) => {
1984
+ const violations = [];
1985
+ if (!input.lines)
1986
+ return violations;
1987
+ for (let i = 0; i < input.lines.length; i++) {
1988
+ const line = input.lines[i];
1989
+ if (line.vatCategory === "Z" && line.vatRate !== 0) {
1990
+ violations.push(violation("BR-Z-05", "error", `Line ${i}: zero-rated (Z) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
1991
+ }
1992
+ }
1993
+ return violations;
1994
+ };
1995
+ var brE05 = (input) => {
1996
+ const violations = [];
1997
+ if (!input.lines)
1998
+ return violations;
1999
+ for (let i = 0; i < input.lines.length; i++) {
2000
+ const line = input.lines[i];
2001
+ if (line.vatCategory === "E" && line.vatRate !== 0) {
2002
+ violations.push(violation("BR-E-05", "error", `Line ${i}: exempt (E) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
2003
+ }
2004
+ }
2005
+ return violations;
2006
+ };
2007
+ var brAe05 = (input) => {
2008
+ const violations = [];
2009
+ if (!input.lines)
2010
+ return violations;
2011
+ for (let i = 0; i < input.lines.length; i++) {
2012
+ const line = input.lines[i];
2013
+ if (line.vatCategory === "AE" && line.vatRate !== 0) {
2014
+ violations.push(violation("BR-AE-05", "error", `Line ${i}: reverse charge (AE) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
2015
+ }
2016
+ }
2017
+ return violations;
2018
+ };
2019
+ var brG05 = (input) => {
2020
+ const violations = [];
2021
+ for (let i = 0; i < (input.lines ?? []).length; i++) {
2022
+ const line = input.lines[i];
2023
+ if (line.vatCategory === "G" && line.vatRate !== 0) {
2024
+ violations.push(violation("BR-G-05", "error", `Line ${i}: export outside the EU (G) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
2025
+ }
2026
+ }
2027
+ return violations;
2028
+ };
2029
+ var brIc05 = (input) => {
2030
+ const violations = [];
2031
+ for (let i = 0; i < (input.lines ?? []).length; i++) {
2032
+ const line = input.lines[i];
2033
+ if (line.vatCategory === "K" && line.vatRate !== 0) {
2034
+ violations.push(violation("BR-IC-05", "error", `Line ${i}: intra-community supply (K) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
2035
+ }
2036
+ }
2037
+ return violations;
2038
+ };
2039
+ var documentAdjustmentVatRates = (input) => {
2040
+ const violations = [];
2041
+ const configs = /* @__PURE__ */ new Map([
2042
+ ["S", { allowanceRule: "BR-S-06", chargeRule: "BR-S-07", valid: (rate) => rate > 0, label: "standard rate (S)" }],
2043
+ ["Z", { allowanceRule: "BR-Z-06", chargeRule: "BR-Z-07", valid: (rate) => rate === 0, label: "zero-rated (Z)" }],
2044
+ ["E", { allowanceRule: "BR-E-06", chargeRule: "BR-E-07", valid: (rate) => rate === 0, label: "exempt (E)" }],
2045
+ ["AE", { allowanceRule: "BR-AE-06", chargeRule: "BR-AE-07", valid: (rate) => rate === 0, label: "reverse charge (AE)" }],
2046
+ ["G", { allowanceRule: "BR-G-06", chargeRule: "BR-G-07", valid: (rate) => rate === 0, label: "export outside the EU (G)" }],
2047
+ ["K", { allowanceRule: "BR-IC-06", chargeRule: "BR-IC-07", valid: (rate) => rate === 0, label: "intra-community supply (K)" }]
2048
+ ]);
2049
+ const check = (items, kind) => {
2050
+ (items ?? []).forEach((item, index) => {
2051
+ const category = item.vatCategory ?? "S";
2052
+ const config = configs.get(category);
2053
+ if (!config || config.valid(item.vatRate))
2054
+ return;
2055
+ const ruleId = kind === "allowance" ? config.allowanceRule : config.chargeRule;
2056
+ violations.push(violation(ruleId, "error", `Document ${kind} ${index}: ${config.label} has an invalid vatRate (${item.vatRate}).`, `${kind === "allowance" ? "allowances" : "charges"}[${index}].vatRate`));
2057
+ });
2058
+ };
2059
+ check(input.allowances, "allowance");
2060
+ check(input.charges, "charge");
2061
+ return violations;
2062
+ };
2063
+ function exemptionReasonRule(vatCategory, ruleId, label) {
2064
+ return (input) => {
2065
+ const groups = /* @__PURE__ */ new Map();
2066
+ const add = (category, rate, reason, field) => {
2067
+ if (category !== vatCategory)
2068
+ return;
2069
+ const effectiveRate = category === "O" ? 0 : rate;
2070
+ const key = `${category}-${effectiveRate}`;
2071
+ const hasReason = typeof reason === "string" && reason.trim().length > 0;
2072
+ const existing = groups.get(key);
2073
+ if (existing) {
2074
+ existing.hasReason ||= hasReason;
2075
+ } else {
2076
+ groups.set(key, { hasReason, field });
2077
+ }
2078
+ };
2079
+ (input.lines ?? []).forEach((line, index) => {
2080
+ add(line.vatCategory ?? "S", line.vatRate, line.taxExemptReason, `lines[${index}].taxExemptReason`);
2081
+ });
2082
+ (input.allowances ?? []).forEach((item, index) => {
2083
+ add(item.vatCategory ?? "S", item.vatRate, item.taxExemptReason, `allowances[${index}].taxExemptReason`);
2084
+ });
2085
+ (input.charges ?? []).forEach((item, index) => {
2086
+ add(item.vatCategory ?? "S", item.vatRate, item.taxExemptReason, `charges[${index}].taxExemptReason`);
2087
+ });
2088
+ return [...groups.values()].filter((group) => !group.hasReason).map((group) => violation(ruleId, "error", `${label} requires a non-empty taxExemptReason in its VAT breakdown.`, group.field));
2089
+ };
2090
+ }
2091
+ var brE10 = exemptionReasonRule("E", "BR-E-10", "Exempt from VAT (E)");
2092
+ var brAe10 = exemptionReasonRule("AE", "BR-AE-10", "Reverse charge (AE)");
2093
+ var brG10 = exemptionReasonRule("G", "BR-G-10", "Export outside the EU (G)");
2094
+ var brO10 = exemptionReasonRule("O", "BR-O-10", "Not subject to VAT (O)");
2095
+ var brIc10 = exemptionReasonRule("K", "BR-IC-10", "Intra-community supply (K)");
2096
+ var builderVatCategoryValidity = (input) => {
2097
+ const violations = [];
2098
+ const check = (category, field) => {
2099
+ if (category !== void 0 && !VALID_VAT_CATEGORIES.has(category)) {
2100
+ violations.push(violation("BR-CL-17", "error", "VAT category is not an EN 16931 code.", field));
2101
+ }
2102
+ };
2103
+ (input.lines ?? []).forEach((item, index) => check(item.vatCategory, `lines[${index}].vatCategory`));
2104
+ (input.allowances ?? []).forEach((item, index) => check(item.vatCategory, `allowances[${index}].vatCategory`));
2105
+ (input.charges ?? []).forEach((item, index) => check(item.vatCategory, `charges[${index}].vatCategory`));
2106
+ return violations;
2107
+ };
2108
+ var UBL_BUILDER_VAT_RULES = [
2109
+ builderVatCategoryValidity,
2110
+ brS05,
2111
+ brZ05,
2112
+ brE05,
2113
+ brAe05,
2114
+ brG05,
2115
+ brIc05,
2116
+ documentAdjustmentVatRates,
2117
+ brE10,
2118
+ brAe10,
2119
+ brG10,
2120
+ brO10,
2121
+ brIc10
2122
+ ];
2123
+ function validateUblBuilderVat(input) {
2124
+ const shapeViolations = [];
2125
+ const checkCollection = (value, field) => {
2126
+ if (field === "lines" || value !== void 0) {
2127
+ if (!Array.isArray(value)) {
2128
+ shapeViolations.push(violation("SDK-INPUT", "error", `${field} must be an array.`, field));
2129
+ return;
2130
+ }
2131
+ }
2132
+ if (!Array.isArray(value))
2133
+ return;
2134
+ for (const [index, item] of value.entries()) {
2135
+ const itemField = `${field}[${index}]`;
2136
+ if (item === null || typeof item !== "object") {
2137
+ shapeViolations.push(violation("SDK-INPUT", "error", `${itemField} must be an object.`, itemField));
2138
+ continue;
2139
+ }
2140
+ const candidate = item;
2141
+ if (typeof candidate.vatRate !== "number" || !Number.isFinite(candidate.vatRate)) {
2142
+ shapeViolations.push(violation("SDK-INPUT", "error", `${itemField}.vatRate must be a finite number.`, `${itemField}.vatRate`));
2143
+ }
2144
+ if (candidate.vatCategory !== void 0 && typeof candidate.vatCategory !== "string") {
2145
+ shapeViolations.push(violation("SDK-INPUT", "error", `${itemField}.vatCategory must be a string.`, `${itemField}.vatCategory`));
2146
+ }
2147
+ if (candidate.taxExemptReason !== void 0 && typeof candidate.taxExemptReason !== "string") {
2148
+ shapeViolations.push(violation("SDK-INPUT", "error", `${itemField}.taxExemptReason must be a string.`, `${itemField}.taxExemptReason`));
2149
+ }
2150
+ }
2151
+ };
2152
+ checkCollection(input.lines, "lines");
2153
+ checkCollection(input.allowances, "allowances");
2154
+ checkCollection(input.charges, "charges");
2155
+ if (shapeViolations.length > 0)
2156
+ return shapeViolations;
2157
+ const oRateViolations = [];
2158
+ const checkORates = (items, field) => {
2159
+ (items ?? []).forEach((item, index) => {
2160
+ if (item.vatCategory === "O" && item.vatRate !== 0) {
2161
+ oRateViolations.push(violation("SDK-INPUT", "error", `Category O must use vatRate 0 in SDK input.`, `${field}[${index}].vatRate`));
2162
+ }
2163
+ });
2164
+ };
2165
+ checkORates(input.lines, "lines");
2166
+ checkORates(input.allowances, "allowances");
2167
+ checkORates(input.charges, "charges");
2168
+ return [
2169
+ ...oRateViolations,
2170
+ ...UBL_BUILDER_VAT_RULES.flatMap((rule) => rule(input))
2171
+ ];
2172
+ }
2173
+ var peppolR004 = (input) => {
2174
+ if (!input.to?.peppolId) {
2175
+ return [
2176
+ violation("PEPPOL-EN16931-R004", "error", "Buyer electronic address (peppolId) is required for Peppol delivery.", "to.peppolId")
2177
+ ];
2178
+ }
2179
+ return [];
2180
+ };
2181
+ var vatCategoryCodes = (input) => {
2182
+ const violations = [];
2183
+ const sendable = SENDABLE_VAT_CATEGORIES.join(", ");
2184
+ const echo = (v) => v.length <= 16 ? v : `${v.slice(0, 16)}\u2026`;
2185
+ const check = (cat, field, label) => {
2186
+ if (cat === void 0 || cat === null)
2187
+ return;
2188
+ if (!VALID_VAT_CATEGORIES.has(cat)) {
2189
+ 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));
2190
+ return;
2191
+ }
2192
+ if (UNROUTABLE_VAT_CATEGORIES.has(cat)) {
2193
+ 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));
2194
+ }
2195
+ };
2196
+ (input.lines ?? []).forEach((line, i) => check(line.vatCategory, `lines[${i}].vatCategory`, `Line ${i}`));
2197
+ (input.allowances ?? []).forEach((a, i) => check(a.vatCategory, `allowances[${i}].vatCategory`, `Allowance ${i}`));
2198
+ (input.charges ?? []).forEach((c, i) => check(c.vatCategory, `charges[${i}].vatCategory`, `Charge ${i}`));
2199
+ return violations;
2200
+ };
2201
+ var peppolR080 = (input) => {
2202
+ const violations = [];
2203
+ if (!input.lines)
2204
+ return violations;
2205
+ const knownCodes = getKnownUnitCodes();
2206
+ for (let i = 0; i < input.lines.length; i++) {
2207
+ const line = input.lines[i];
2208
+ if (line.unit) {
2209
+ const resolved = resolveUnit(line.unit);
2210
+ if (!knownCodes.has(resolved)) {
2211
+ 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`));
2212
+ }
2213
+ }
2214
+ }
2215
+ return violations;
2216
+ };
2217
+ var ALL_RULES = [
2218
+ // Required fields (BR)
2219
+ br02,
2220
+ br03,
2221
+ br06,
2222
+ br07,
2223
+ br08,
2224
+ br09,
2225
+ br10,
2226
+ // Calculations (BR-CO)
2227
+ brCo10,
2228
+ brCo13,
2229
+ brCo15,
2230
+ brCo16,
2231
+ // Tax categories (one family per category)
2232
+ brS05,
2233
+ brZ05,
2234
+ brE05,
2235
+ brAe05,
2236
+ brG05,
2237
+ brIc05,
2238
+ documentAdjustmentVatRates,
2239
+ brE10,
2240
+ brAe10,
2241
+ brG10,
2242
+ brO10,
2243
+ brIc10,
2244
+ // Peppol-specific
2245
+ peppolR004,
2246
+ vatCategoryCodes,
2247
+ peppolR080
2248
+ ];
2249
+ function validateSchematron(input) {
2250
+ const errors = [];
2251
+ const warnings = [];
2252
+ for (const rule of ALL_RULES) {
2253
+ const violations = rule(input);
2254
+ for (const v of violations) {
2255
+ if (v.severity === "error") {
2256
+ errors.push(v);
2257
+ } else {
2258
+ warnings.push(v);
2259
+ }
2260
+ }
2261
+ }
2262
+ return {
2263
+ valid: errors.length === 0,
2264
+ coverage: { rulesChecked: SDK_SCHEMATRON_RULE_IDS.length, ofNetworkFatalRules: "partial" },
2265
+ errors,
2266
+ warnings
2267
+ };
2268
+ }
2269
+ var SDK_SCHEMATRON_RULE_IDS = [
2270
+ "BR-02",
2271
+ "BR-03",
2272
+ "BR-06",
2273
+ "BR-07",
2274
+ "BR-08",
2275
+ "BR-09",
2276
+ "BR-10",
2277
+ "BR-CL-17",
2278
+ "BR-CO-10",
2279
+ "BR-CO-13",
2280
+ "BR-CO-15",
2281
+ "BR-CO-16",
2282
+ "BR-S-05",
2283
+ "BR-Z-05",
2284
+ "BR-E-05",
2285
+ "BR-AE-05",
2286
+ "BR-G-05",
2287
+ "BR-IC-05",
2288
+ "BR-S-06",
2289
+ "BR-S-07",
2290
+ "BR-Z-06",
2291
+ "BR-Z-07",
2292
+ "BR-E-06",
2293
+ "BR-E-07",
2294
+ "BR-AE-06",
2295
+ "BR-AE-07",
2296
+ "BR-G-06",
2297
+ "BR-G-07",
2298
+ "BR-IC-06",
2299
+ "BR-IC-07",
2300
+ "BR-E-10",
2301
+ "BR-AE-10",
2302
+ "BR-G-10",
2303
+ "BR-O-10",
2304
+ "BR-IC-10",
2305
+ "PEPPOL-EN16931-R004",
2306
+ "PEPPOL-EN16931-R080"
2307
+ ];
2308
+
2309
+ // ../../../getpeppr/packages/sdk/dist/core/status-precedence.js
2310
+ var STATUS_PRECEDENCE = [
2311
+ { status: "failed", family: "terminal-failure" },
2312
+ { status: "rejected", family: "terminal-failure" },
2313
+ { status: "paid", family: "terminal-success" },
2314
+ { status: "partially_paid", family: "progress" },
2315
+ { status: "accepted", family: "progress" },
2316
+ { status: "conditionally_accepted", family: "progress" },
2317
+ { status: "under_query", family: "progress" },
2318
+ { status: "in_process", family: "progress" },
2319
+ { status: "cleared", family: "progress" },
2320
+ { status: "delivered", family: "progress" },
2321
+ { status: "acknowledged", family: "progress" },
2322
+ // Terminal for developer wait semantics only — stays rank 40 (non-terminal)
2323
+ // in the projection guard (§3.12 two-level terminality).
2324
+ { status: "no_action", family: "terminal-failure" },
2325
+ { status: "submitted", family: "progress" },
2326
+ { status: "unknown", family: "fallback" }
2327
+ ];
2328
+ function statusFamily(status) {
2329
+ return STATUS_PRECEDENCE.find((e) => e.status === status)?.family ?? "fallback";
2330
+ }
2331
+ var TERMINAL_FAILURE_STATUSES = STATUS_PRECEDENCE.filter((e) => e.family === "terminal-failure").map((e) => e.status);
2332
+
2333
+ // ../../../getpeppr/packages/sdk/dist/version.js
2334
+ var SDK_VERSION = "4.7.0";
2335
+
2336
+ // ../../../getpeppr/packages/sdk/dist/core/client.js
2337
+ function findHeaderCaseInsensitive(headers, name) {
2338
+ if (!headers)
2339
+ return void 0;
2340
+ const target = name.toLowerCase();
2341
+ for (const [key, value] of Object.entries(headers)) {
2342
+ if (key.toLowerCase() === target)
2343
+ return value;
2344
+ }
2345
+ return void 0;
2346
+ }
2347
+ var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
2348
+ function sleep(ms) {
2349
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
2350
+ }
2351
+ function calculateRetryDelay(attempt, initialDelayMs, maxDelayMs, retryAfterMs) {
2352
+ if (retryAfterMs !== void 0)
2353
+ return Math.min(retryAfterMs, maxDelayMs);
2354
+ const exponentialDelay = initialDelayMs * Math.pow(2, attempt);
2355
+ const jitter = Math.random() * initialDelayMs;
2356
+ return Math.min(exponentialDelay + jitter, maxDelayMs);
2357
+ }
2358
+ function parseRetryAfter(headerValue) {
2359
+ if (!headerValue)
2360
+ return void 0;
2361
+ const seconds = Number(headerValue);
2362
+ if (Number.isFinite(seconds) && seconds >= 0) {
2363
+ return seconds * 1e3;
2364
+ }
2365
+ const dateMs = Date.parse(headerValue);
2366
+ if (!Number.isNaN(dateMs)) {
2367
+ const delayMs = dateMs - Date.now();
2368
+ return delayMs > 0 ? delayMs : 0;
2369
+ }
2370
+ return void 0;
2371
+ }
2372
+ var CONTROL_CHARACTERS = /[\u0000-\u001F\u007F-\u009F]/g;
2373
+ function stripControls(value) {
2374
+ return value.replace(CONTROL_CHARACTERS, " ");
2375
+ }
2376
+ function readOwn(source, key) {
2377
+ return Object.hasOwn(source, key) ? source[key] : void 0;
2378
+ }
2379
+ function readSentence(source, key) {
2380
+ const value = readOwn(source, key);
2381
+ if (typeof value !== "string")
2382
+ return null;
2383
+ const cleaned = stripControls(value).trim();
2384
+ return cleaned === "" ? null : cleaned;
2385
+ }
2386
+ function safeDocsUrl(value) {
2387
+ if (typeof value !== "string")
2388
+ return null;
2389
+ let parsed;
2390
+ try {
2391
+ parsed = new URL(value);
2392
+ } catch {
2393
+ return null;
2394
+ }
2395
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
2396
+ return null;
2397
+ return parsed.href;
2398
+ }
2399
+ function formatApiErrorMessage(status, rawBody) {
2400
+ const verbatim = `getpeppr API error (${status}): ${stripControls(rawBody)}`;
2401
+ let parsed;
2402
+ try {
2403
+ parsed = JSON.parse(rawBody);
2404
+ } catch {
2405
+ return verbatim;
2406
+ }
2407
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
2408
+ return verbatim;
2409
+ }
2410
+ const sentence = readSentence(parsed, "message") ?? readSentence(parsed, "error");
2411
+ if (sentence === null)
2412
+ return verbatim;
2413
+ const link = safeDocsUrl(readOwn(parsed, "docs"));
2414
+ return `getpeppr API error (${status}): ${sentence}${link ? ` See ${link}` : ""}`;
2415
+ }
2416
+ function isRetryableError(error2) {
2417
+ if (error2 instanceof PeppolApiError) {
2418
+ return RETRYABLE_STATUS_CODES.has(error2.statusCode);
2419
+ }
2420
+ if (error2 instanceof Error && error2.name === "AbortError") {
2421
+ return true;
2422
+ }
2423
+ if (error2 instanceof TypeError && /fetch failed|network|ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT/i.test(error2.message)) {
2424
+ return true;
2425
+ }
2426
+ return false;
2427
+ }
2428
+ var DEFAULT_BASE_URL = "https://api.getpeppr.dev/v1";
2429
+ var GetpepprAdapter = class {
2430
+ name = "getpeppr";
2431
+ baseUrl;
2432
+ apiKey;
2433
+ timeout;
2434
+ retryConfig;
2435
+ onRequest;
2436
+ onResponse;
2437
+ constructor(config) {
2438
+ this.apiKey = config.apiKey;
2439
+ this.timeout = config.timeout ?? 3e4;
2440
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
2441
+ this.retryConfig = {
2442
+ maxRetries: config.retry?.maxRetries ?? 3,
2443
+ initialDelayMs: config.retry?.initialDelayMs ?? 500,
2444
+ maxDelayMs: config.retry?.maxDelayMs ?? 3e4
2445
+ };
2446
+ this.onRequest = config.onRequest;
2447
+ this.onResponse = config.onResponse;
2448
+ }
2449
+ async request(method, path, body, extraHeaders) {
2450
+ const { maxRetries, initialDelayMs, maxDelayMs } = this.retryConfig;
2451
+ let lastError;
2452
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
2453
+ try {
2454
+ return await this.doRequest(method, path, body, extraHeaders);
2455
+ } catch (err) {
2456
+ lastError = err;
2457
+ const is429 = err instanceof PeppolApiError && err.statusCode === 429;
2458
+ const isSafeMethod = /^(GET|DELETE|HEAD)$/i.test(method);
2459
+ const hasIdempotencyKey = !!findHeaderCaseInsensitive(extraHeaders, "Idempotency-Key");
2460
+ const canRetry = is429 || isSafeMethod || hasIdempotencyKey;
2461
+ if (attempt < maxRetries && canRetry && isRetryableError(err)) {
2462
+ const retryAfterMs = err instanceof PeppolApiError ? err.retryAfterMs : void 0;
2463
+ await sleep(calculateRetryDelay(attempt, initialDelayMs, maxDelayMs, retryAfterMs));
2464
+ continue;
2465
+ }
2466
+ throw err;
2467
+ }
2468
+ }
2469
+ throw lastError;
2470
+ }
2471
+ async doRequest(method, path, body, extraHeaders) {
2472
+ const url = `${this.baseUrl}${path}`;
2473
+ const controller = new AbortController();
2474
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2475
+ const requestHeaders = {
2476
+ Authorization: `Bearer ${this.apiKey}`,
1844
2477
  "Content-Type": "application/json",
1845
2478
  Accept: "application/json",
1846
2479
  "User-Agent": `getpeppr-sdk/${SDK_VERSION}`,
@@ -1931,9 +2564,8 @@ var GetpepprAdapter = class {
1931
2564
  clearTimeout(timeoutId);
1932
2565
  }
1933
2566
  }
1934
- // NOTE: sendInvoice and createInvoice hit the same SDK endpoint (POST /invoices).
1935
- // The gateway (Tasks 9-10) differentiates them: sendInvoice wraps with
1936
- // { invoice: {...}, send_after_import: true }, createInvoice omits the flag.
2567
+ // sendInvoice and createInvoice hit the same endpoint. The latter adds the
2568
+ // legacy `_draft` marker, which the current gateway rejects explicitly.
1937
2569
  async sendInvoice(input, options) {
1938
2570
  const headers = {};
1939
2571
  if (options?.idempotencyKey) {
@@ -2835,8 +3467,9 @@ var Peppol = class {
2835
3467
  this.legalEntities = new LegalEntityOperations(this.adapter);
2836
3468
  }
2837
3469
  /**
2838
- * Validate an invoice without sending it.
2839
- * Useful for pre-flight checks in your UI.
3470
+ * Validate the structured JSON send payload without sending it.
3471
+ * Useful for pre-flight checks in your UI; provider-side normalization still
3472
+ * applies on send. `toXml()` adds the stricter direct-UBL builder checks.
2840
3473
  */
2841
3474
  validate(input) {
2842
3475
  return validateInvoice(input);
@@ -2846,14 +3479,39 @@ var Peppol = class {
2846
3479
  * Useful for debugging or manual submission.
2847
3480
  */
2848
3481
  toXml(input) {
2849
- const validation = validateInvoice(input);
3482
+ const baseValidation = validateInvoice(input);
3483
+ const vatViolations = baseValidation.valid ? validateUblBuilderVat(input) : [];
3484
+ const validation = {
3485
+ valid: baseValidation.valid && vatViolations.every((item) => item.severity !== "error"),
3486
+ errors: [
3487
+ ...baseValidation.errors,
3488
+ ...vatViolations.filter((item) => item.severity === "error").map(({ field, message, ruleId }) => ({
3489
+ field: field ?? "invoice",
3490
+ message,
3491
+ ruleId: ruleId === "SDK-INPUT" ? void 0 : ruleId
3492
+ }))
3493
+ ],
3494
+ warnings: baseValidation.warnings
3495
+ };
2850
3496
  if (!validation.valid) {
2851
3497
  throw new PeppolValidationError(`Invoice validation failed: ${validation.errors.map((e) => e.message).join("; ")}`, validation);
2852
3498
  }
2853
- if (input.isCreditNote) {
2854
- return buildCreditNoteXml(input);
3499
+ try {
3500
+ if (input.isCreditNote) {
3501
+ return buildCreditNoteXml(input);
3502
+ }
3503
+ return buildInvoiceXml(input);
3504
+ } catch (error2) {
3505
+ if (error2 instanceof UblBuilderInputError) {
3506
+ const builderValidation = {
3507
+ valid: false,
3508
+ errors: [{ field: error2.field, message: error2.message, ruleId: error2.ruleId }],
3509
+ warnings: validation.warnings
3510
+ };
3511
+ throw new PeppolValidationError(`Invoice validation failed: ${error2.message}`, builderValidation);
3512
+ }
3513
+ throw error2;
2855
3514
  }
2856
- return buildInvoiceXml(input);
2857
3515
  }
2858
3516
  };
2859
3517
  async function* paginate(fetchPage, options) {
@@ -2871,22 +3529,30 @@ async function* paginate(fetchPage, options) {
2871
3529
  offset += page.data.length;
2872
3530
  }
2873
3531
  }
3532
+ function toGatewayInvoiceInput(input) {
3533
+ const stripReason = (item) => {
3534
+ const { taxExemptReason: _builderOnly, ...gatewayItem } = item;
3535
+ return gatewayItem;
3536
+ };
3537
+ return {
3538
+ ...input,
3539
+ lines: input.lines.map(stripReason),
3540
+ ...input.allowances ? { allowances: input.allowances.map(stripReason) } : {},
3541
+ ...input.charges ? { charges: input.charges.map(stripReason) } : {}
3542
+ };
3543
+ }
2874
3544
  var InvoiceOperations = class {
2875
3545
  adapter;
2876
3546
  constructor(adapter) {
2877
3547
  this.adapter = adapter;
2878
3548
  }
2879
3549
  /**
2880
- * Create a draft invoice without sending it.
2881
- * Validates input client-side, then creates the invoice via the gateway.
2882
- * Use `sendById()` to send the draft when ready.
3550
+ * Request draft creation from the gateway.
2883
3551
  *
2884
- * @example
2885
- * ```ts
2886
- * const draft = await peppol.invoices.create({ number: "INV-001", to, lines });
2887
- * // Later, when ready:
2888
- * await peppol.invoices.sendById(draft.id);
2889
- * ```
3552
+ * @deprecated The current Storecove-backed gateway does not support drafts
3553
+ * and returns 422 `drafts_not_supported`. Submit the final document with
3554
+ * `invoices.send()` instead.
3555
+ * @throws {PeppolApiError} 422 with code `drafts_not_supported`
2890
3556
  */
2891
3557
  async create(input, options) {
2892
3558
  const validation = validateInvoice(input);
@@ -2894,21 +3560,18 @@ var InvoiceOperations = class {
2894
3560
  throw new PeppolValidationError(`Invoice validation failed:
2895
3561
  ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ""}`).join("\n")}`, validation);
2896
3562
  }
2897
- const result = await this.adapter.createInvoice(input, options);
3563
+ const result = await this.adapter.createInvoice(toGatewayInvoiceInput(input), options);
2898
3564
  if (validation.warnings.length > 0) {
2899
3565
  result.warnings = validation.warnings;
2900
3566
  }
2901
3567
  return result;
2902
3568
  }
2903
3569
  /**
2904
- * Send an existing draft invoice by ID.
2905
- * The invoice must have been previously created with `create()`.
3570
+ * Request sending of an existing draft invoice by ID.
2906
3571
  *
2907
- * @example
2908
- * ```ts
2909
- * await peppol.invoices.sendById("inv-1");
2910
- * ```
2911
- * @throws {PeppolApiError} 501 if the gateway provider does not support draft sending
3572
+ * @deprecated The current Storecove-backed gateway has no draft lifecycle and
3573
+ * always returns 501. Submit the final document with `invoices.send()`.
3574
+ * @throws {PeppolApiError} 501 with the current gateway provider
2912
3575
  */
2913
3576
  async sendById(id) {
2914
3577
  return this.adapter.sendInvoiceById(id);
@@ -2934,7 +3597,7 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
2934
3597
  throw new PeppolValidationError(`Invoice validation failed:
2935
3598
  ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ""}`).join("\n")}`, validation);
2936
3599
  }
2937
- const result = await this.adapter.sendInvoice(input, options);
3600
+ const result = await this.adapter.sendInvoice(toGatewayInvoiceInput(input), options);
2938
3601
  if (validation.warnings.length > 0) {
2939
3602
  result.warnings = validation.warnings;
2940
3603
  }
@@ -3079,52 +3742,39 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
3079
3742
  return this.adapter.importInvoice(options);
3080
3743
  }
3081
3744
  /**
3082
- * Acknowledge a received invoice.
3745
+ * Request acknowledgement of a received invoice.
3083
3746
  *
3084
- * @example
3085
- * ```ts
3086
- * const result = await peppol.invoices.acknowledge("inv-123");
3087
- * console.log(result.status); // "accepted"
3088
- * ```
3089
- * @throws {PeppolApiError} 501 if the gateway provider does not support acknowledgement
3747
+ * @deprecated The current Storecove-backed gateway does not support
3748
+ * acknowledgement and always returns 501.
3749
+ * @throws {PeppolApiError} 501 with the current gateway provider
3090
3750
  */
3091
3751
  async acknowledge(id) {
3092
3752
  return this.adapter.acknowledgeInvoice(id);
3093
3753
  }
3094
3754
  /**
3095
- * Update an existing invoice (draft only).
3096
- * Only include the fields you want to change — partial updates are supported.
3755
+ * Request an update to an existing invoice.
3097
3756
  *
3098
- * @example
3099
- * ```ts
3100
- * const updated = await peppol.invoices.update("inv-123", {
3101
- * dueDate: "2026-04-01",
3102
- * lines: [
3103
- * { id: "line-1", quantity: 5 },
3104
- * { id: "line-2", _destroy: true },
3105
- * ],
3106
- * });
3107
- * ```
3108
- * @throws {PeppolApiError} 501 if the gateway provider does not support invoice updates
3757
+ * @deprecated Storecove documents are immutable after submission. The
3758
+ * current gateway always returns 501; issue a credit note instead.
3759
+ * @throws {PeppolApiError} 501 with the current gateway provider
3109
3760
  */
3110
3761
  async update(id, input) {
3111
3762
  return this.adapter.updateInvoice(id, input);
3112
3763
  }
3113
3764
  /**
3114
- * Delete an invoice.
3765
+ * Request deletion of an invoice.
3115
3766
  *
3116
- * @example
3117
- * ```ts
3118
- * const deleted = await peppol.invoices.delete("inv-123");
3119
- * ```
3120
- * @throws {PeppolApiError} 501 if the gateway provider does not support invoice deletion
3767
+ * @deprecated The current Storecove-backed gateway does not support invoice
3768
+ * deletion and always returns 501.
3769
+ * @throws {PeppolApiError} 501 with the current gateway provider
3121
3770
  */
3122
3771
  async delete(id) {
3123
3772
  return this.adapter.deleteInvoice(id);
3124
3773
  }
3125
3774
  /**
3126
- * Transition an invoice to a new state.
3127
- * The gateway validates the state machine invalid transitions return an error.
3775
+ * Report a French CTC invoice as paid.
3776
+ * Other state transitions are retained for API compatibility but the current
3777
+ * Storecove-backed gateway returns 501 for them.
3128
3778
  *
3129
3779
  * `"paid"` on a French CTC invoice reports the payment collection
3130
3780
  * (« signalement d'encaissement ») to the tax authority via the gateway —
@@ -3243,7 +3893,7 @@ var CreditNoteOperations = class {
3243
3893
  throw new PeppolValidationError(`Credit note validation failed:
3244
3894
  ${validation.errors.map((e) => ` - ${e.field}: ${e.message}`).join("\n")}`, validation);
3245
3895
  }
3246
- return this.adapter.sendInvoice(invoiceInput);
3896
+ return this.adapter.sendInvoice(toGatewayInvoiceInput(invoiceInput));
3247
3897
  }
3248
3898
  };
3249
3899
  var DirectoryOperations = class {
@@ -3559,523 +4209,179 @@ var LegalEntityOperations = class {
3559
4209
  * an organisation admin starts the platform sandbox trial from the console
3560
4210
  * overview and creates a sandbox master key at
3561
4211
  * https://console.getpeppr.dev/api-keys; production platform access is set
3562
- * up with our team (hello@getpeppr.dev).
3563
- *
3564
- * Transient failures are NOT auto-retried unless you pass `options.idempotencyKey`;
3565
- * re-issuing mints a fresh token, so a retried call is safe.
3566
- *
3567
- * @example
3568
- * ```ts
3569
- * await peppol.legalEntities.requestAttestation(le.id, { contactEmail: "owner@acme.example" });
3570
- * ```
3571
- */
3572
- async requestAttestation(id, input, options) {
3573
- return this.adapter.requestLegalEntityAttestation(id, input, options);
3574
- }
3575
- };
3576
- var BankAccountOperations = class {
3577
- adapter;
3578
- constructor(adapter) {
3579
- this.adapter = adapter;
3580
- }
3581
- /**
3582
- * List bank accounts with optional pagination.
3583
- *
3584
- * @example
3585
- * ```ts
3586
- * const result = await peppol.bankAccounts.list({ limit: 10 });
3587
- * console.log(result.data, result.meta);
3588
- * ```
3589
- */
3590
- async list(options) {
3591
- return this.adapter.listBankAccounts(options);
3592
- }
3593
- /**
3594
- * Get a single bank account by ID.
3595
- *
3596
- * @example
3597
- * ```ts
3598
- * const account = await peppol.bankAccounts.get("123");
3599
- * console.log(account.name, account.iban);
3600
- * ```
3601
- */
3602
- async get(id) {
3603
- return this.adapter.getBankAccount(id);
3604
- }
3605
- /**
3606
- * Create a new bank account.
3607
- *
3608
- * @example
3609
- * ```ts
3610
- * const account = await peppol.bankAccounts.create({
3611
- * name: "Main Account",
3612
- * iban: "BE68539007547034",
3613
- * bic: "BBRUBEBB",
3614
- * country: "BE",
3615
- * });
3616
- * ```
3617
- */
3618
- async create(input) {
3619
- return this.adapter.createBankAccount(input);
3620
- }
3621
- /**
3622
- * Update an existing bank account.
3623
- *
3624
- * @example
3625
- * ```ts
3626
- * const updated = await peppol.bankAccounts.update("123", { name: "Updated Name" });
3627
- * ```
3628
- */
3629
- async update(id, input) {
3630
- return this.adapter.updateBankAccount(id, input);
3631
- }
3632
- /**
3633
- * Delete a bank account.
3634
- *
3635
- * @example
3636
- * ```ts
3637
- * await peppol.bankAccounts.delete("123");
3638
- * ```
3639
- */
3640
- async delete(id) {
3641
- return this.adapter.deleteBankAccount(id);
3642
- }
3643
- /**
3644
- * Async iterator over all bank accounts, automatically handling pagination.
4212
+ * up with our team (hello@getpeppr.dev).
4213
+ *
4214
+ * Transient failures are NOT auto-retried unless you pass `options.idempotencyKey`;
4215
+ * re-issuing mints a fresh token, so a retried call is safe.
3645
4216
  *
3646
4217
  * @example
3647
4218
  * ```ts
3648
- * for await (const account of peppol.bankAccounts.listAll()) {
3649
- * console.log(account.name, account.iban);
3650
- * }
4219
+ * await peppol.legalEntities.requestAttestation(le.id, { contactEmail: "owner@acme.example" });
3651
4220
  * ```
3652
4221
  */
3653
- listAll(options) {
3654
- return paginate((offset, limit) => this.adapter.listBankAccounts({ ...options, offset, limit }), options);
4222
+ async requestAttestation(id, input, options) {
4223
+ return this.adapter.requestLegalEntityAttestation(id, input, options);
3655
4224
  }
3656
4225
  };
3657
- var TransportOperations = class {
4226
+ var BankAccountOperations = class {
3658
4227
  adapter;
3659
4228
  constructor(adapter) {
3660
4229
  this.adapter = adapter;
3661
4230
  }
3662
4231
  /**
3663
- * List all available transport types in the network.
3664
- * Returns global transport types (not account-scoped).
3665
- *
3666
- * @example
3667
- * ```ts
3668
- * const types = await peppol.transports.listTypes();
3669
- * console.log(types); // [{ code: "peppol", name: "Peppol BIS 3.0" }, ...]
3670
- * ```
3671
- */
3672
- async listTypes() {
3673
- return this.adapter.listTransportTypes();
3674
- }
3675
- /**
3676
- * List configured transports for this account.
4232
+ * List bank accounts with optional pagination.
3677
4233
  *
3678
4234
  * @example
3679
4235
  * ```ts
3680
- * const transports = await peppol.transports.list();
3681
- * console.log(transports); // [{ id: "t-1", transportTypeCode: "peppol", name: "..." }, ...]
4236
+ * const result = await peppol.bankAccounts.list({ limit: 10 });
4237
+ * console.log(result.data, result.meta);
3682
4238
  * ```
3683
4239
  */
3684
- async list() {
3685
- return this.adapter.listTransports();
4240
+ async list(options) {
4241
+ return this.adapter.listBankAccounts(options);
3686
4242
  }
3687
4243
  /**
3688
- * Get a single transport by code.
4244
+ * Get a single bank account by ID.
3689
4245
  *
3690
4246
  * @example
3691
4247
  * ```ts
3692
- * const transport = await peppol.transports.get("peppol");
4248
+ * const account = await peppol.bankAccounts.get("123");
4249
+ * console.log(account.name, account.iban);
3693
4250
  * ```
3694
4251
  */
3695
- async get(code) {
3696
- return this.adapter.getTransport(code);
4252
+ async get(id) {
4253
+ return this.adapter.getBankAccount(id);
3697
4254
  }
3698
4255
  /**
3699
- * Create a new transport.
4256
+ * Create a new bank account.
3700
4257
  *
3701
4258
  * @example
3702
4259
  * ```ts
3703
- * const transport = await peppol.transports.create({
3704
- * transportTypeCode: "peppol",
3705
- * email: "billing@acme.com",
4260
+ * const account = await peppol.bankAccounts.create({
4261
+ * name: "Main Account",
4262
+ * iban: "BE68539007547034",
4263
+ * bic: "BBRUBEBB",
4264
+ * country: "BE",
3706
4265
  * });
3707
4266
  * ```
3708
4267
  */
3709
4268
  async create(input) {
3710
- return this.adapter.createTransport(input);
3711
- }
3712
- /**
3713
- * Update an existing transport.
3714
- *
3715
- * @example
3716
- * ```ts
3717
- * const transport = await peppol.transports.update("peppol", { email: "new@acme.com" });
3718
- * ```
3719
- */
3720
- async update(code, input) {
3721
- return this.adapter.updateTransport(code, input);
4269
+ return this.adapter.createBankAccount(input);
3722
4270
  }
3723
4271
  /**
3724
- * Delete a transport.
4272
+ * Update an existing bank account.
3725
4273
  *
3726
4274
  * @example
3727
4275
  * ```ts
3728
- * await peppol.transports.delete("peppol");
3729
- * ```
3730
- */
3731
- async delete(code) {
3732
- return this.adapter.deleteTransport(code);
3733
- }
3734
- };
3735
-
3736
- // ../sdk/dist/core/schematron.js
3737
- function violation(ruleId, severity, message, field) {
3738
- return { ruleId, severity, message, field };
3739
- }
3740
- var _knownUnitCodes;
3741
- function getKnownUnitCodes() {
3742
- if (!_knownUnitCodes) {
3743
- _knownUnitCodes = new Set(getAllUnits().map((u) => u.code));
3744
- }
3745
- return _knownUnitCodes;
3746
- }
3747
- var VALID_VAT_CATEGORIES = /* @__PURE__ */ new Set(["S", "Z", "E", "AE", "K", "G", "O", "L", "M", "B"]);
3748
- var SENDABLE_VAT_CATEGORIES = ["S", "Z", "E", "AE", "K", "G", "O"];
3749
- var UNROUTABLE_VAT_CATEGORIES = /* @__PURE__ */ new Set(["L", "M", "B"]);
3750
- function computeLineNet(line) {
3751
- const baseQty = line.baseQuantity ?? 1;
3752
- if (baseQty === 0)
3753
- return NaN;
3754
- const baseAmount = line.quantity * line.unitPrice / baseQty;
3755
- const chargeTotal = (line.charges ?? []).reduce((sum, c) => sum + c.amount, 0);
3756
- const allowanceTotal = (line.allowances ?? []).reduce((sum, a) => sum + a.amount, 0);
3757
- return baseAmount + chargeTotal - allowanceTotal;
3758
- }
3759
- var br02 = (input) => {
3760
- if (!input.number?.trim()) {
3761
- return [violation("BR-02", "error", "Invoice number is required.", "number")];
3762
- }
3763
- return [];
3764
- };
3765
- var br03 = (input) => {
3766
- if (!input.date) {
3767
- return [
3768
- violation("BR-03", "warning", "Invoice issue date is not set. The SDK will default to today's date.", "date")
3769
- ];
3770
- }
3771
- return [];
3772
- };
3773
- var br06 = (input) => {
3774
- if (input.from && !input.from.vatNumber) {
3775
- return [
3776
- violation("BR-06", "warning", "Seller party has no VAT number. The gateway will use the account's VAT registration.", "from.vatNumber")
3777
- ];
3778
- }
3779
- return [];
3780
- };
3781
- var br07 = (input) => {
3782
- if (!input.to?.name?.trim()) {
3783
- return [violation("BR-07", "error", "Buyer name is required.", "to.name")];
3784
- }
3785
- return [];
3786
- };
3787
- var br08 = (input) => {
3788
- if (!input.lines || input.lines.length === 0) {
3789
- return [violation("BR-08", "error", "Invoice must have at least one line item.", "lines")];
3790
- }
3791
- return [];
3792
- };
3793
- var br09 = (input) => {
3794
- if (!input.dueDate && !input.paymentTerms) {
3795
- return [
3796
- violation("BR-09", "warning", "Neither due date nor payment terms specified. At least one is recommended.", "dueDate")
3797
- ];
3798
- }
3799
- return [];
3800
- };
3801
- var br10 = (input) => {
3802
- if (!input.buyerReference && !input.orderReference) {
3803
- return [
3804
- violation("BR-10", "warning", "Neither buyerReference nor orderReference specified. Peppol BIS 3.0 requires at least one.", "buyerReference")
3805
- ];
3806
- }
3807
- return [];
3808
- };
3809
- var brCo10 = (input) => {
3810
- const violations = [];
3811
- if (!input.lines)
3812
- return violations;
3813
- for (let i = 0; i < input.lines.length; i++) {
3814
- const line = input.lines[i];
3815
- const net = computeLineNet(line);
3816
- if (!Number.isFinite(net)) {
3817
- const baseQty = line.baseQuantity ?? 1;
3818
- const detail = baseQty === 0 ? "baseQuantity is 0, causing division by zero." : "Computed line amount is not a finite number.";
3819
- violations.push(violation("BR-CO-10", "error", `Line ${i}: invalid net amount. ${detail}`, `lines[${i}]`));
3820
- }
3821
- }
3822
- return violations;
3823
- };
3824
- var brCo13 = (input) => {
3825
- if (!input.lines || input.lines.length === 0)
3826
- return [];
3827
- let totalVat = 0;
3828
- for (let i = 0; i < input.lines.length; i++) {
3829
- const line = input.lines[i];
3830
- const net = computeLineNet(line);
3831
- if (!Number.isFinite(net))
3832
- continue;
3833
- totalVat += net * (line.vatRate / 100);
3834
- }
3835
- for (const allowance of input.allowances ?? []) {
3836
- totalVat -= allowance.amount * (allowance.vatRate / 100);
3837
- }
3838
- for (const charge of input.charges ?? []) {
3839
- totalVat += charge.amount * (charge.vatRate / 100);
3840
- }
3841
- if (!Number.isFinite(totalVat)) {
3842
- return [
3843
- violation("BR-CO-13", "error", "Computed total VAT amount is not a finite number. Check line amounts and VAT rates.")
3844
- ];
3845
- }
3846
- if (totalVat < -0.01) {
3847
- return [
3848
- violation("BR-CO-13", "warning", `Computed total VAT is negative (${totalVat.toFixed(2)}). This is unusual for an invoice.`)
3849
- ];
3850
- }
3851
- return [];
3852
- };
3853
- var brCo15 = (input) => {
3854
- if (!input.lines || input.lines.length === 0)
3855
- return [];
3856
- let lineTotal = 0;
3857
- let vatTotal = 0;
3858
- for (const line of input.lines) {
3859
- const net = computeLineNet(line);
3860
- if (!Number.isFinite(net))
3861
- continue;
3862
- lineTotal += net;
3863
- vatTotal += net * (line.vatRate / 100);
3864
- }
3865
- for (const allowance of input.allowances ?? []) {
3866
- lineTotal -= allowance.amount;
3867
- vatTotal -= allowance.amount * (allowance.vatRate / 100);
3868
- }
3869
- for (const charge of input.charges ?? []) {
3870
- lineTotal += charge.amount;
3871
- vatTotal += charge.amount * (charge.vatRate / 100);
3872
- }
3873
- const taxInclusive = lineTotal + vatTotal;
3874
- if (!Number.isFinite(taxInclusive)) {
3875
- return [
3876
- violation("BR-CO-15", "error", "Computed tax-inclusive amount is not a finite number.")
3877
- ];
3878
- }
3879
- if (taxInclusive < -0.01) {
3880
- return [
3881
- violation("BR-CO-15", "warning", `Computed tax-inclusive amount is negative (${taxInclusive.toFixed(2)}). Consider using a credit note instead.`)
3882
- ];
3883
- }
3884
- return [];
3885
- };
3886
- var brCo16 = (input) => {
3887
- if (!input.lines || input.lines.length === 0)
3888
- return [];
3889
- let lineTotal = 0;
3890
- let vatTotal = 0;
3891
- for (const line of input.lines) {
3892
- const net = computeLineNet(line);
3893
- if (!Number.isFinite(net))
3894
- continue;
3895
- lineTotal += net;
3896
- vatTotal += net * (line.vatRate / 100);
3897
- }
3898
- for (const allowance of input.allowances ?? []) {
3899
- lineTotal -= allowance.amount;
3900
- vatTotal -= allowance.amount * (allowance.vatRate / 100);
3901
- }
3902
- for (const charge of input.charges ?? []) {
3903
- lineTotal += charge.amount;
3904
- vatTotal += charge.amount * (charge.vatRate / 100);
3905
- }
3906
- const taxInclusive = lineTotal + vatTotal;
3907
- const prepaid = input.prepaidAmount ?? 0;
3908
- const rounding = input.roundingAmount ?? 0;
3909
- const payable = taxInclusive - prepaid + rounding;
3910
- if (!Number.isFinite(payable)) {
3911
- return [
3912
- violation("BR-CO-16", "error", "Computed payable amount is not a finite number.")
3913
- ];
4276
+ * const updated = await peppol.bankAccounts.update("123", { name: "Updated Name" });
4277
+ * ```
4278
+ */
4279
+ async update(id, input) {
4280
+ return this.adapter.updateBankAccount(id, input);
3914
4281
  }
3915
- if (payable < -0.01) {
3916
- return [
3917
- violation("BR-CO-16", "warning", `Computed payable amount is negative (${payable.toFixed(2)}). Prepaid amount (${prepaid}) exceeds the invoice total.`)
3918
- ];
4282
+ /**
4283
+ * Delete a bank account.
4284
+ *
4285
+ * @example
4286
+ * ```ts
4287
+ * await peppol.bankAccounts.delete("123");
4288
+ * ```
4289
+ */
4290
+ async delete(id) {
4291
+ return this.adapter.deleteBankAccount(id);
3919
4292
  }
3920
- return [];
3921
- };
3922
- var brS05 = (input) => {
3923
- const violations = [];
3924
- if (!input.lines)
3925
- return violations;
3926
- for (let i = 0; i < input.lines.length; i++) {
3927
- const line = input.lines[i];
3928
- const category = line.vatCategory ?? "S";
3929
- if (category === "S" && (line.vatRate === void 0 || line.vatRate <= 0)) {
3930
- violations.push(violation("BR-S-05", "error", `Line ${i}: standard rate (S) requires vatRate > 0, got ${line.vatRate ?? "undefined"}.`, `lines[${i}].vatRate`));
3931
- }
4293
+ /**
4294
+ * Async iterator over all bank accounts, automatically handling pagination.
4295
+ *
4296
+ * @example
4297
+ * ```ts
4298
+ * for await (const account of peppol.bankAccounts.listAll()) {
4299
+ * console.log(account.name, account.iban);
4300
+ * }
4301
+ * ```
4302
+ */
4303
+ listAll(options) {
4304
+ return paginate((offset, limit) => this.adapter.listBankAccounts({ ...options, offset, limit }), options);
3932
4305
  }
3933
- return violations;
3934
4306
  };
3935
- var brZ05 = (input) => {
3936
- const violations = [];
3937
- if (!input.lines)
3938
- return violations;
3939
- for (let i = 0; i < input.lines.length; i++) {
3940
- const line = input.lines[i];
3941
- if (line.vatCategory === "Z" && line.vatRate !== 0) {
3942
- violations.push(violation("BR-Z-05", "error", `Line ${i}: zero-rated (Z) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
3943
- }
4307
+ var TransportOperations = class {
4308
+ adapter;
4309
+ constructor(adapter) {
4310
+ this.adapter = adapter;
3944
4311
  }
3945
- return violations;
3946
- };
3947
- var brE05 = (input) => {
3948
- const violations = [];
3949
- if (!input.lines)
3950
- return violations;
3951
- for (let i = 0; i < input.lines.length; i++) {
3952
- const line = input.lines[i];
3953
- if (line.vatCategory === "E" && line.vatRate !== 0) {
3954
- violations.push(violation("BR-E-05", "error", `Line ${i}: exempt (E) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
3955
- }
4312
+ /**
4313
+ * List all available transport types in the network.
4314
+ * Returns global transport types (not account-scoped).
4315
+ *
4316
+ * @example
4317
+ * ```ts
4318
+ * const types = await peppol.transports.listTypes();
4319
+ * console.log(types); // [{ code: "peppol", name: "Peppol BIS 3.0" }, ...]
4320
+ * ```
4321
+ */
4322
+ async listTypes() {
4323
+ return this.adapter.listTransportTypes();
3956
4324
  }
3957
- return violations;
3958
- };
3959
- var brAe05 = (input) => {
3960
- const violations = [];
3961
- if (!input.lines)
3962
- return violations;
3963
- for (let i = 0; i < input.lines.length; i++) {
3964
- const line = input.lines[i];
3965
- if (line.vatCategory === "AE" && line.vatRate !== 0) {
3966
- violations.push(violation("BR-AE-05", "error", `Line ${i}: reverse charge (AE) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
3967
- }
4325
+ /**
4326
+ * List configured transports for this account.
4327
+ *
4328
+ * @example
4329
+ * ```ts
4330
+ * const transports = await peppol.transports.list();
4331
+ * console.log(transports); // [{ id: "t-1", transportTypeCode: "peppol", name: "..." }, ...]
4332
+ * ```
4333
+ */
4334
+ async list() {
4335
+ return this.adapter.listTransports();
3968
4336
  }
3969
- return violations;
3970
- };
3971
- var peppolR004 = (input) => {
3972
- if (!input.to?.peppolId) {
3973
- return [
3974
- violation("PEPPOL-EN16931-R004", "error", "Buyer electronic address (peppolId) is required for Peppol delivery.", "to.peppolId")
3975
- ];
4337
+ /**
4338
+ * Get a single transport by code.
4339
+ *
4340
+ * @example
4341
+ * ```ts
4342
+ * const transport = await peppol.transports.get("peppol");
4343
+ * ```
4344
+ */
4345
+ async get(code) {
4346
+ return this.adapter.getTransport(code);
3976
4347
  }
3977
- return [];
3978
- };
3979
- var vatCategoryCodes = (input) => {
3980
- const violations = [];
3981
- const sendable = SENDABLE_VAT_CATEGORIES.join(", ");
3982
- const echo = (v) => v.length <= 16 ? v : `${v.slice(0, 16)}\u2026`;
3983
- const check = (cat, field, label) => {
3984
- if (cat === void 0 || cat === null)
3985
- return;
3986
- if (!VALID_VAT_CATEGORIES.has(cat)) {
3987
- 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));
3988
- return;
3989
- }
3990
- if (UNROUTABLE_VAT_CATEGORIES.has(cat)) {
3991
- 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));
3992
- }
3993
- };
3994
- (input.lines ?? []).forEach((line, i) => check(line.vatCategory, `lines[${i}].vatCategory`, `Line ${i}`));
3995
- (input.allowances ?? []).forEach((a, i) => check(a.vatCategory, `allowances[${i}].vatCategory`, `Allowance ${i}`));
3996
- (input.charges ?? []).forEach((c, i) => check(c.vatCategory, `charges[${i}].vatCategory`, `Charge ${i}`));
3997
- return violations;
3998
- };
3999
- var peppolR080 = (input) => {
4000
- const violations = [];
4001
- if (!input.lines)
4002
- return violations;
4003
- const knownCodes = getKnownUnitCodes();
4004
- for (let i = 0; i < input.lines.length; i++) {
4005
- const line = input.lines[i];
4006
- if (line.unit) {
4007
- const resolved = resolveUnit(line.unit);
4008
- if (!knownCodes.has(resolved)) {
4009
- 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`));
4010
- }
4011
- }
4348
+ /**
4349
+ * Create a new transport.
4350
+ *
4351
+ * @example
4352
+ * ```ts
4353
+ * const transport = await peppol.transports.create({
4354
+ * transportTypeCode: "peppol",
4355
+ * email: "billing@acme.com",
4356
+ * });
4357
+ * ```
4358
+ */
4359
+ async create(input) {
4360
+ return this.adapter.createTransport(input);
4012
4361
  }
4013
- return violations;
4014
- };
4015
- var ALL_RULES = [
4016
- // Required fields (BR)
4017
- br02,
4018
- br03,
4019
- br06,
4020
- br07,
4021
- br08,
4022
- br09,
4023
- br10,
4024
- // Calculations (BR-CO)
4025
- brCo10,
4026
- brCo13,
4027
- brCo15,
4028
- brCo16,
4029
- // Tax categories (one family per category)
4030
- brS05,
4031
- brZ05,
4032
- brE05,
4033
- brAe05,
4034
- // Peppol-specific
4035
- peppolR004,
4036
- vatCategoryCodes,
4037
- peppolR080
4038
- ];
4039
- function validateSchematron(input) {
4040
- const errors = [];
4041
- const warnings = [];
4042
- for (const rule of ALL_RULES) {
4043
- const violations = rule(input);
4044
- for (const v of violations) {
4045
- if (v.severity === "error") {
4046
- errors.push(v);
4047
- } else {
4048
- warnings.push(v);
4049
- }
4050
- }
4362
+ /**
4363
+ * Update an existing transport.
4364
+ *
4365
+ * @example
4366
+ * ```ts
4367
+ * const transport = await peppol.transports.update("peppol", { email: "new@acme.com" });
4368
+ * ```
4369
+ */
4370
+ async update(code, input) {
4371
+ return this.adapter.updateTransport(code, input);
4051
4372
  }
4052
- return {
4053
- valid: errors.length === 0,
4054
- coverage: { rulesChecked: SDK_SCHEMATRON_RULE_IDS.length, ofNetworkFatalRules: "partial" },
4055
- errors,
4056
- warnings
4057
- };
4058
- }
4059
- var SDK_SCHEMATRON_RULE_IDS = [
4060
- "BR-02",
4061
- "BR-03",
4062
- "BR-06",
4063
- "BR-07",
4064
- "BR-08",
4065
- "BR-09",
4066
- "BR-10",
4067
- "BR-CL-17",
4068
- "BR-CO-10",
4069
- "BR-CO-13",
4070
- "BR-CO-15",
4071
- "BR-CO-16",
4072
- "BR-S-05",
4073
- "BR-Z-05",
4074
- "BR-E-05",
4075
- "BR-AE-05",
4076
- "PEPPOL-EN16931-R004",
4077
- "PEPPOL-EN16931-R080"
4078
- ];
4373
+ /**
4374
+ * Delete a transport.
4375
+ *
4376
+ * @example
4377
+ * ```ts
4378
+ * await peppol.transports.delete("peppol");
4379
+ * ```
4380
+ */
4381
+ async delete(code) {
4382
+ return this.adapter.deleteTransport(code);
4383
+ }
4384
+ };
4079
4385
 
4080
4386
  // src/commands/validate.ts
4081
4387
  function runValidation(input) {
@@ -4148,14 +4454,17 @@ var INVOICE_TEMPLATE = {
4148
4454
  description: "Conseil en transformation num\xE9rique",
4149
4455
  quantity: 10,
4150
4456
  unitPrice: 950,
4151
- vatRate: 21
4457
+ vatRate: 0,
4458
+ vatCategory: "O",
4459
+ taxExemptReason: "Integration test"
4152
4460
  },
4153
4461
  {
4154
4462
  description: "Software license \u2014 annual subscription",
4155
4463
  quantity: 1,
4156
4464
  unitPrice: 2400,
4157
4465
  vatRate: 0,
4158
- vatCategory: "AE"
4466
+ vatCategory: "O",
4467
+ taxExemptReason: "Integration test"
4159
4468
  }
4160
4469
  ],
4161
4470
  paymentTerms: "Net 30 days",
@@ -4191,7 +4500,9 @@ var CREDIT_NOTE_TEMPLATE = {
4191
4500
  description: "Avoir partiel \u2014 Conseil en transformation num\xE9rique",
4192
4501
  quantity: 2,
4193
4502
  unitPrice: 950,
4194
- vatRate: 21
4503
+ vatRate: 0,
4504
+ vatCategory: "O",
4505
+ taxExemptReason: "Integration test"
4195
4506
  }
4196
4507
  ],
4197
4508
  note: "Avoir pour prestations non r\xE9alis\xE9es \u2014 r\xE9f. INV-2026-001"
@@ -4225,9 +4536,9 @@ function registerInitCommand(program2) {
4225
4536
  3. Convert to XML: getpeppr convert ${filename}
4226
4537
  4. Send: getpeppr send ${filename}
4227
4538
 
4228
- ${pc2.dim("Sandbox note:")} this template includes VAT. To send it on a sandbox
4229
- account, first register your VAT on the Peppol identity page, or set each
4230
- line's "vatCategory" to "O" (outside the scope of VAT) for a no-VAT test send.
4539
+ ${pc2.dim("Sandbox note:")} ready for a fresh sandbox account. This template
4540
+ targets the test receiver and uses O/0 tax lines. Replace the fixture tax
4541
+ treatment with the real category after registering the sender's VAT details.
4231
4542
  `);
4232
4543
  process.exit(0);
4233
4544
  }
@@ -4790,11 +5101,14 @@ function buildDefaultSendPayload(overrides = {}) {
4790
5101
  quantity: 1,
4791
5102
  unitPrice: amount,
4792
5103
  vatRate: 0,
4793
- // vatCategory "O" = "Services outside scope of tax" (UBL 2.1 / EN 16931).
4794
- // Public entities (SPF Economie) are VAT-exempt. No `taxExemptReason` field
4795
- // exists in InvoiceLine Storecove derives exemption from the category code.
4796
- // If sandbox returns 422 on this combination, fallback is vatRate: 21 + vatCategory: "S".
4797
- vatCategory: "O"
5104
+ // vatCategory "O" = outside the scope of VAT (UBL 2.1 / EN 16931).
5105
+ // A fresh sandbox sender has no VAT identifier, so this integration
5106
+ // fixture deliberately uses O/0. The SDK pre-validator requires the
5107
+ // builder-only reason; the send transport strips it so Storecove can
5108
+ // derive its own provider-specific text.
5109
+ // Do not fall back to VAT-bearing category "S": a fresh sandbox sender has no VAT number.
5110
+ vatCategory: "O",
5111
+ taxExemptReason: "Not subject to VAT"
4798
5112
  }
4799
5113
  ]
4800
5114
  };