@getpeppr/cli 0.5.0 → 0.5.1

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
@@ -418,17 +418,16 @@ function calculateTaxSubtotals(lines, allowances, charges) {
418
418
  const groups = /* @__PURE__ */ new Map();
419
419
  function addToGroup(vatCategory, vatRate, amount) {
420
420
  const key = `${vatCategory}-${vatRate}`;
421
- const lineTax = round2(amount * (vatRate / 100));
422
421
  const existing = groups.get(key);
423
422
  if (existing) {
424
423
  existing.taxableAmount = round2(existing.taxableAmount + amount);
425
- existing.taxAmount = round2(existing.taxAmount + lineTax);
426
424
  } else {
427
425
  groups.set(key, {
428
426
  vatRate,
429
427
  vatCategory,
430
428
  taxableAmount: amount,
431
- taxAmount: lineTax
429
+ taxAmount: 0
430
+ // computed once per group below (BR-CO-17)
432
431
  });
433
432
  }
434
433
  }
@@ -441,7 +440,10 @@ function calculateTaxSubtotals(lines, allowances, charges) {
441
440
  for (const c of charges ?? []) {
442
441
  addToGroup(c.vatCategory ?? "S", c.vatRate, c.amount);
443
442
  }
444
- return Array.from(groups.values());
443
+ return Array.from(groups.values()).map((subtotal) => ({
444
+ ...subtotal,
445
+ taxAmount: round2(subtotal.taxableAmount * (subtotal.vatRate / 100))
446
+ }));
445
447
  }
446
448
  function calculateDocumentTotals(lines, allowances, charges) {
447
449
  const taxSubtotals = calculateTaxSubtotals(lines, allowances, charges);
@@ -486,10 +488,13 @@ function buildTaxCurrencyTotalXml(totalTax, taxCurrency, rate) {
486
488
  <cbc:TaxAmount currencyID="${escapeXml(taxCurrency)}">${formatAmount(convertedAmount)}</cbc:TaxAmount>
487
489
  </cac:TaxTotal>`;
488
490
  }
491
+ function payableFromTaxInclusive(taxInclusiveAmount, prepaidAmount, roundingAmount) {
492
+ return Number((taxInclusiveAmount - (prepaidAmount ?? 0) + (roundingAmount ?? 0)).toFixed(2));
493
+ }
489
494
  function buildLegalMonetaryTotalXml(totals, currency, options) {
490
495
  const prepaid = options?.prepaidAmount;
491
496
  const rounding = options?.roundingAmount;
492
- const payableAmount = totals.taxInclusiveAmount - (prepaid ?? 0) + (rounding ?? 0);
497
+ const payableAmount = payableFromTaxInclusive(totals.taxInclusiveAmount, prepaid, rounding);
493
498
  return `<cac:LegalMonetaryTotal>
494
499
  <cbc:LineExtensionAmount currencyID="${escapeXml(currency)}">${formatAmount(totals.lineExtensionAmount)}</cbc:LineExtensionAmount>
495
500
  <cbc:TaxExclusiveAmount currencyID="${escapeXml(currency)}">${formatAmount(totals.taxExclusiveAmount)}</cbc:TaxExclusiveAmount>
@@ -1058,8 +1063,32 @@ function validateInvoice(input) {
1058
1063
  };
1059
1064
  }
1060
1065
 
1066
+ // ../sdk/dist/core/status-precedence.js
1067
+ var STATUS_PRECEDENCE = [
1068
+ { status: "failed", family: "terminal-failure" },
1069
+ { status: "rejected", family: "terminal-failure" },
1070
+ { status: "paid", family: "terminal-success" },
1071
+ { status: "partially_paid", family: "progress" },
1072
+ { status: "accepted", family: "progress" },
1073
+ { status: "conditionally_accepted", family: "progress" },
1074
+ { status: "under_query", family: "progress" },
1075
+ { status: "in_process", family: "progress" },
1076
+ { status: "cleared", family: "progress" },
1077
+ { status: "delivered", family: "progress" },
1078
+ { status: "acknowledged", family: "progress" },
1079
+ // Terminal for developer wait semantics only — stays rank 40 (non-terminal)
1080
+ // in the projection guard (§3.12 two-level terminality).
1081
+ { status: "no_action", family: "terminal-failure" },
1082
+ { status: "submitted", family: "progress" },
1083
+ { status: "unknown", family: "fallback" }
1084
+ ];
1085
+ function statusFamily(status) {
1086
+ return STATUS_PRECEDENCE.find((e) => e.status === status)?.family ?? "fallback";
1087
+ }
1088
+ var TERMINAL_FAILURE_STATUSES = STATUS_PRECEDENCE.filter((e) => e.family === "terminal-failure").map((e) => e.status);
1089
+
1061
1090
  // ../sdk/dist/version.js
1062
- var SDK_VERSION = "2.0.0";
1091
+ var SDK_VERSION = "2.3.0";
1063
1092
 
1064
1093
  // ../sdk/dist/core/client.js
1065
1094
  function findHeaderCaseInsensitive(headers, name) {
@@ -1636,14 +1665,21 @@ function detectMimeType(filename) {
1636
1665
  }
1637
1666
  }
1638
1667
  function parseSendResult(result) {
1639
- return {
1668
+ const rawStatus = result.status == null ? void 0 : String(result.status);
1669
+ const sendResult = {
1640
1670
  id: String(result.id ?? ""),
1641
- status: mapStatus(String(result.status ?? "submitted")),
1671
+ status: mapStatus(rawStatus ?? "submitted"),
1642
1672
  peppolMessageId: result.peppolMessageId ?? result.peppol_message_id,
1643
1673
  createdAt: String(result.createdAt ?? result.updatedAt ?? result.created_at ?? (/* @__PURE__ */ new Date()).toISOString()),
1644
1674
  ublXml: result.ublXml,
1645
1675
  warnings: Array.isArray(result.warnings) ? result.warnings : void 0
1646
1676
  };
1677
+ if (rawStatus !== void 0)
1678
+ sendResult.rawStatus = rawStatus;
1679
+ const detail = parseStatusDetail(result.detail);
1680
+ if (detail)
1681
+ sendResult.detail = detail;
1682
+ return sendResult;
1647
1683
  }
1648
1684
  function parseDirectoryEntry(result) {
1649
1685
  const participant = isRecord(result.participant) ? result.participant : result;
@@ -1679,6 +1715,51 @@ function formatPeppolId(scheme, id) {
1679
1715
  function isRecord(value) {
1680
1716
  return typeof value === "object" && value !== null && !Array.isArray(value);
1681
1717
  }
1718
+ var STATUS_DETAIL_AXES = ["platformFiscal", "delivery", "businessDisposition", "settlement"];
1719
+ function parseStatusDetailEntry(raw) {
1720
+ if (!isRecord(raw) || typeof raw.axis !== "string" || typeof raw.jurisdiction !== "string" || typeof raw.code !== "string" || typeof raw.label !== "string" || typeof raw.codeSystem !== "string" || typeof raw.codeVersion !== "string") {
1721
+ return void 0;
1722
+ }
1723
+ const entry = {
1724
+ axis: raw.axis,
1725
+ jurisdiction: raw.jurisdiction,
1726
+ code: raw.code,
1727
+ label: raw.label,
1728
+ codeSystem: raw.codeSystem,
1729
+ codeVersion: raw.codeVersion
1730
+ };
1731
+ if (isRecord(raw.standardCode) && typeof raw.standardCode.system === "string" && typeof raw.standardCode.code === "string") {
1732
+ entry.standardCode = { system: raw.standardCode.system, code: raw.standardCode.code };
1733
+ }
1734
+ if (typeof raw.reason === "string")
1735
+ entry.reason = raw.reason;
1736
+ if (Array.isArray(raw.warnings) && raw.warnings.every((w) => typeof w === "string")) {
1737
+ entry.warnings = [...raw.warnings];
1738
+ }
1739
+ if (typeof raw.failureCategory === "string") {
1740
+ entry.failureCategory = raw.failureCategory;
1741
+ }
1742
+ if (isRecord(raw.payment) && typeof raw.payment.amount === "number" && typeof raw.payment.currency === "string" && typeof raw.payment.date === "string") {
1743
+ entry.payment = { amount: raw.payment.amount, currency: raw.payment.currency, date: raw.payment.date };
1744
+ }
1745
+ if (typeof raw.paymentSemantics === "string") {
1746
+ entry.paymentSemantics = raw.paymentSemantics;
1747
+ }
1748
+ if (typeof raw.actor === "string")
1749
+ entry.actor = raw.actor;
1750
+ return entry;
1751
+ }
1752
+ function parseStatusDetail(raw) {
1753
+ if (!isRecord(raw))
1754
+ return void 0;
1755
+ const detail = {};
1756
+ for (const axis of STATUS_DETAIL_AXES) {
1757
+ const entry = parseStatusDetailEntry(raw[axis]);
1758
+ if (entry)
1759
+ detail[axis] = entry;
1760
+ }
1761
+ return Object.keys(detail).length > 0 ? detail : void 0;
1762
+ }
1682
1763
  function parseContact(raw) {
1683
1764
  const contact = {
1684
1765
  id: String(raw.id ?? ""),
@@ -1734,11 +1815,17 @@ function parseLegalEntity(raw) {
1734
1815
  return le;
1735
1816
  }
1736
1817
  function parseInvoiceSummary(raw) {
1818
+ const rawStatus = (raw.state ?? raw.status) == null ? void 0 : String(raw.state ?? raw.status);
1737
1819
  const summary = {
1738
1820
  id: String(raw.id ?? ""),
1739
1821
  number: String(raw.invoiceNumber ?? raw.number ?? ""),
1740
- status: mapStatus(String(raw.state ?? raw.status ?? "submitted"))
1822
+ status: mapStatus(rawStatus ?? "submitted")
1741
1823
  };
1824
+ if (rawStatus !== void 0)
1825
+ summary.rawStatus = rawStatus;
1826
+ const detail = parseStatusDetail(raw.detail);
1827
+ if (detail)
1828
+ summary.detail = detail;
1742
1829
  if (raw.createdAt != null)
1743
1830
  summary.createdAt = String(raw.createdAt);
1744
1831
  if (typeof raw.isCreditNote === "boolean")
@@ -2166,14 +2253,19 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
2166
2253
  const timeout = options?.timeout ?? 12e4;
2167
2254
  const interval = options?.interval ?? 5e3;
2168
2255
  const targets = Array.isArray(targetStatus) ? targetStatus : [targetStatus];
2169
- const terminalFailures = ["failed", "rejected", "no_action"];
2170
2256
  const startTime = Date.now();
2171
2257
  while (true) {
2172
2258
  const result = await this.getStatus(documentId);
2173
2259
  if (targets.includes(result.status)) {
2174
2260
  return result;
2175
2261
  }
2176
- if (terminalFailures.includes(result.status)) {
2262
+ if (TERMINAL_FAILURE_STATUSES.includes(result.status)) {
2263
+ throw new PeppolError(`Document ${documentId} reached terminal status "${result.status}" while waiting for "${targets.join('" or "')}"`);
2264
+ }
2265
+ if (statusFamily(result.status) === "terminal-success") {
2266
+ if (targets.every((t) => statusFamily(t) === "progress")) {
2267
+ return result;
2268
+ }
2177
2269
  throw new PeppolError(`Document ${documentId} reached terminal status "${result.status}" while waiting for "${targets.join('" or "')}"`);
2178
2270
  }
2179
2271
  if (Date.now() - startTime >= timeout) {