@getpeppr/cli 0.4.7 → 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 +108 -15
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
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:
|
|
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
|
|
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.
|
|
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
|
-
|
|
1668
|
+
const rawStatus = result.status == null ? void 0 : String(result.status);
|
|
1669
|
+
const sendResult = {
|
|
1640
1670
|
id: String(result.id ?? ""),
|
|
1641
|
-
status: mapStatus(
|
|
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(
|
|
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"];
|
|
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 (
|
|
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) {
|
|
@@ -3090,7 +3182,7 @@ function registerInitCommand(program2) {
|
|
|
3090
3182
|
4. Send: getpeppr send ${filename}
|
|
3091
3183
|
|
|
3092
3184
|
${pc2.dim("Sandbox note:")} this template includes VAT. To send it on a sandbox
|
|
3093
|
-
account, first register your VAT
|
|
3185
|
+
account, first register your VAT on the Peppol identity page, or set each
|
|
3094
3186
|
line's "vatCategory" to "O" (outside the scope of VAT) for a no-VAT test send.
|
|
3095
3187
|
`);
|
|
3096
3188
|
process.exit(0);
|
|
@@ -3748,7 +3840,8 @@ var TERMINAL_STATES = /* @__PURE__ */ new Set([
|
|
|
3748
3840
|
"delivered",
|
|
3749
3841
|
"accepted",
|
|
3750
3842
|
"rejected",
|
|
3751
|
-
"failed"
|
|
3843
|
+
"failed",
|
|
3844
|
+
"no_action"
|
|
3752
3845
|
]);
|
|
3753
3846
|
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
3754
3847
|
async function pollUntilTerminal(client, documentId, options = {}) {
|
|
@@ -3797,7 +3890,7 @@ var API_BASE = "https://api.getpeppr.dev/v1";
|
|
|
3797
3890
|
var LOCAL_BASE = "http://localhost:3001/api/v1";
|
|
3798
3891
|
var DASHBOARD_BASE = "https://console.getpeppr.dev/invoices";
|
|
3799
3892
|
function registerSendCommand(program2) {
|
|
3800
|
-
program2.command("send").description("Send an invoice to the Peppol network via getpeppr API").argument("[file]", "optional path to invoice JSON (mutex with --to/--amount/...)").option("--prod", "target production (live keys + confirmation)").option("--local", "target localhost:3001 dev server").option("--key <key>", "override API key \u2014 for CI/scripted use only; visible in `ps` and shell history. Prefer GETPEPPR_API_KEY env var.").option("--to <peppol-id>", "recipient peppol id (e.g., 9925:BE0314595348)").option("--country <iso>", "recipient ISO 3166-1 alpha-2 country override (e.g., BE)").option("--amount <number>", "line amount in major currency units (decimal allowed)").option("--currency <iso>", "ISO 4217 currency (default EUR)").option("--desc <text>", "line description").option("--attachment", "attach the test PDF").option("--watch", "poll status until
|
|
3893
|
+
program2.command("send").description("Send an invoice to the Peppol network via getpeppr API").argument("[file]", "optional path to invoice JSON (mutex with --to/--amount/...)").option("--prod", "target production (live keys + confirmation)").option("--local", "target localhost:3001 dev server").option("--key <key>", "override API key \u2014 for CI/scripted use only; visible in `ps` and shell history. Prefer GETPEPPR_API_KEY env var.").option("--to <peppol-id>", "recipient peppol id (e.g., 9925:BE0314595348)").option("--country <iso>", "recipient ISO 3166-1 alpha-2 country override (e.g., BE)").option("--amount <number>", "line amount in major currency units (decimal allowed)").option("--currency <iso>", "ISO 4217 currency (default EUR)").option("--desc <text>", "line description").option("--attachment", "attach the test PDF").option("--watch", "poll status until a terminal state (60s timeout)").option("-y, --yes", "skip --prod confirmation prompt").option("--no-validate", "skip pre-validation locally").option("--json", "output JSON").option("--quiet", "exit code only, no output").action(async (file, flags) => {
|
|
3801
3894
|
let auth;
|
|
3802
3895
|
try {
|
|
3803
3896
|
auth = resolveApiKey({
|
|
@@ -3907,7 +4000,7 @@ function registerSendCommand(program2) {
|
|
|
3907
4000
|
mode
|
|
3908
4001
|
);
|
|
3909
4002
|
if (output) process.stdout.write(output + "\n");
|
|
3910
|
-
if (finalStatus === "rejected" || finalStatus === "failed") {
|
|
4003
|
+
if (finalStatus === "rejected" || finalStatus === "failed" || finalStatus === "no_action") {
|
|
3911
4004
|
process.exit(1);
|
|
3912
4005
|
}
|
|
3913
4006
|
process.exit(0);
|