@getpeppr/cli 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/LICENSE +21 -0
- package/README.md +3 -2
- package/dist/index.js +92 -15
- package/dist/index.js.map +1 -1
- package/package.json +6 -7
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to `@getpeppr/cli` are documented here.
|
|
|
4
4
|
|
|
5
5
|
The CLI bundles `@getpeppr/sdk` into its published artifact (tsup `noExternal`), so `validate`, `convert` and `init` run the SDK's validators, country rules and UBL builder locally — an SDK release only reaches CLI users once the CLI is rebundled. Rebundle-only releases are listed with the SDK version they ship.
|
|
6
6
|
|
|
7
|
+
## [0.11.0] — 2026-09-04
|
|
8
|
+
|
|
9
|
+
Rebundles `@getpeppr/sdk` 5.1.0.
|
|
10
|
+
|
|
11
|
+
- **`getpeppr validate` follows BR-CL-01's two vocabularies** instead of a hand-written list of seven codes. Measured against the published 0.10.0 (which bundles SDK 5.0.0):
|
|
12
|
+
- **44 legal invoice codes stop being falsely refused** — `326` (partial invoice, named by `DE-R-017`), `382` and 42 others now pass. On 0.10.0 each exits 1.
|
|
13
|
+
- **Credit notes carrying `383`, `384`, `386`, `389` or `751` are now refused locally** with a `BR-CL-01` error suggesting legal credit-note codes. On 0.10.0 each passed local validation — every one is fatal on the network, so the document failed later and less clearly.
|
|
14
|
+
- The default is unchanged: omit `invoiceTypeCode` and the document kind decides (`380` invoice, `381` credit note).
|
|
15
|
+
- **The tarball now ships its `LICENSE` file** (the published 0.10.0 does not contain one) and the corrected README.
|
|
16
|
+
|
|
7
17
|
## [0.10.0] — 2026-08-28
|
|
8
18
|
|
|
9
19
|
Bundles SDK **5.0.0**. Minor rather than patch because inputs that used to pass
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zero Loop Labs Ltd
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -103,7 +103,7 @@ getpeppr lookup 0208:0685660237 --json
|
|
|
103
103
|
|
|
104
104
|
### `getpeppr login` — Save your API key
|
|
105
105
|
|
|
106
|
-
Store a getpeppr API key in `~/.config/getpeppr/credentials.json`
|
|
106
|
+
Store a getpeppr API key in `$XDG_CONFIG_HOME/getpeppr/credentials.json` — `~/.config/getpeppr/credentials.json` by default, `%APPDATA%\getpeppr\credentials.json` on Windows. On Unix-like systems the file is created and checked with mode `0600`; Windows has no POSIX mode. Sandbox and live keys can coexist.
|
|
107
107
|
|
|
108
108
|
```bash
|
|
109
109
|
# Interactive (recommended) — prompts for the key with masked input
|
|
@@ -149,6 +149,7 @@ getpeppr send invoice.json --prod
|
|
|
149
149
|
| `--to <peppol-id>` | Recipient Peppol ID (when no file) |
|
|
150
150
|
| `--amount <number>` | Line amount in major currency units |
|
|
151
151
|
| `--currency <iso>` | ISO 4217 code (default `EUR`) |
|
|
152
|
+
| `--country <iso>` | Recipient country (ISO 3166-1 alpha-2) when no file |
|
|
152
153
|
| `--desc <text>` | Line description |
|
|
153
154
|
| `--attachment` | Attach the bundled test PDF |
|
|
154
155
|
| `--watch` | Poll status until terminal (60s timeout) |
|
|
@@ -189,7 +190,7 @@ If no legal entity exists in the environment yet, `whoami` says so and points to
|
|
|
189
190
|
getpeppr logout
|
|
190
191
|
```
|
|
191
192
|
|
|
192
|
-
Removes
|
|
193
|
+
Removes the credentials file. No-op if the file doesn't exist.
|
|
193
194
|
|
|
194
195
|
## Exit codes
|
|
195
196
|
|
package/dist/index.js
CHANGED
|
@@ -773,8 +773,9 @@ function formatBaseQuantity(baseQuantity, field) {
|
|
|
773
773
|
}
|
|
774
774
|
return `${digits.slice(0, outputPoint)}.${digits.slice(outputPoint)}`;
|
|
775
775
|
}
|
|
776
|
-
function
|
|
777
|
-
|
|
776
|
+
function roundUblCurrencyAmount(value) {
|
|
777
|
+
const rounded = Math.round((value + Math.sign(value) * Number.EPSILON) * 100) / 100;
|
|
778
|
+
return Object.is(rounded, -0) ? 0 : rounded;
|
|
778
779
|
}
|
|
779
780
|
function normalizedTaxExemptReason(vatCategory, reason) {
|
|
780
781
|
if (!EXEMPTION_REASON_CATEGORIES.has(vatCategory) || typeof reason !== "string") {
|
|
@@ -1063,7 +1064,7 @@ function calculateTaxSubtotals(lines, allowances, charges, options = {}) {
|
|
|
1063
1064
|
throw new UblBuilderInputError(`Conflicting taxExemptReason values for VAT group ${vatCategory}/${effectiveVatRate}.`, "taxExemptReason");
|
|
1064
1065
|
}
|
|
1065
1066
|
existing.taxExemptReason ??= reason;
|
|
1066
|
-
existing.taxableAmount
|
|
1067
|
+
existing.taxableAmount += amount;
|
|
1067
1068
|
} else {
|
|
1068
1069
|
groups.set(key, {
|
|
1069
1070
|
vatRate: effectiveVatRate,
|
|
@@ -1092,8 +1093,8 @@ function calculateTaxSubtotals(lines, allowances, charges, options = {}) {
|
|
|
1092
1093
|
}
|
|
1093
1094
|
}
|
|
1094
1095
|
return Array.from(groups.values()).map((subtotal) => {
|
|
1095
|
-
const taxableAmount =
|
|
1096
|
-
const taxAmount =
|
|
1096
|
+
const taxableAmount = roundUblCurrencyAmount(subtotal.taxableAmount);
|
|
1097
|
+
const taxAmount = roundUblCurrencyAmount(taxableAmount * (subtotal.vatRate / 100));
|
|
1097
1098
|
if (!survivesCents(taxableAmount) || !survivesCents(taxAmount)) {
|
|
1098
1099
|
throw new UblBuilderInputError(NON_FINITE_DERIVED_AMOUNT_MESSAGE, "totals", DERIVED_AMOUNT_CONTRACT_RULE);
|
|
1099
1100
|
}
|
|
@@ -1105,12 +1106,12 @@ function calculateDocumentTotals(lines, allowances, charges, options = {}) {
|
|
|
1105
1106
|
const lineExtensionAmount = lines.reduce((sum, line, index) => sum + calculateLineExtensionAmount(line, index), 0);
|
|
1106
1107
|
const allowanceTotalAmount = (allowances ?? []).reduce((sum, a) => sum + a.amount, 0);
|
|
1107
1108
|
const chargeTotalAmount = (charges ?? []).reduce((sum, c) => sum + c.amount, 0);
|
|
1108
|
-
const taxExclusiveAmount =
|
|
1109
|
+
const taxExclusiveAmount = roundUblCurrencyAmount(lineExtensionAmount - allowanceTotalAmount + chargeTotalAmount);
|
|
1109
1110
|
if (!survivesCents(allowanceTotalAmount) || !survivesCents(chargeTotalAmount) || !survivesCents(taxExclusiveAmount)) {
|
|
1110
1111
|
throw new UblBuilderInputError(NON_FINITE_DERIVED_AMOUNT_MESSAGE, "totals", DERIVED_AMOUNT_CONTRACT_RULE);
|
|
1111
1112
|
}
|
|
1112
|
-
const totalTax =
|
|
1113
|
-
const taxInclusiveAmount =
|
|
1113
|
+
const totalTax = roundUblCurrencyAmount(taxSubtotals.reduce((sum, st) => sum + st.taxAmount, 0));
|
|
1114
|
+
const taxInclusiveAmount = roundUblCurrencyAmount(taxExclusiveAmount + totalTax);
|
|
1114
1115
|
if (!survivesCents(totalTax) || !survivesCents(taxInclusiveAmount)) {
|
|
1115
1116
|
throw new UblBuilderInputError(NON_FINITE_DERIVED_AMOUNT_MESSAGE, "totals", DERIVED_AMOUNT_CONTRACT_RULE);
|
|
1116
1117
|
}
|
|
@@ -1145,7 +1146,7 @@ function buildTaxTotalXml(taxSubtotals, totalTax, currency) {
|
|
|
1145
1146
|
</cac:TaxTotal>`;
|
|
1146
1147
|
}
|
|
1147
1148
|
function buildTaxCurrencyTotalXml(totalTax, taxCurrency, rate) {
|
|
1148
|
-
const convertedAmount =
|
|
1149
|
+
const convertedAmount = roundUblCurrencyAmount(totalTax * rate);
|
|
1149
1150
|
return `<cac:TaxTotal>
|
|
1150
1151
|
<cbc:TaxAmount currencyID="${escapeXml(taxCurrency)}">${formatAmount(convertedAmount)}</cbc:TaxAmount>
|
|
1151
1152
|
</cac:TaxTotal>`;
|
|
@@ -1606,6 +1607,79 @@ function resolveUnit(input) {
|
|
|
1606
1607
|
function getAllUnits() {
|
|
1607
1608
|
return Array.from(UNIT_CODES.entries()).map(([code, name]) => ({ code, name })).sort((a, b) => a.code.localeCompare(b.code));
|
|
1608
1609
|
}
|
|
1610
|
+
var INVOICE_TYPE_CODES = [
|
|
1611
|
+
71,
|
|
1612
|
+
80,
|
|
1613
|
+
81,
|
|
1614
|
+
82,
|
|
1615
|
+
84,
|
|
1616
|
+
102,
|
|
1617
|
+
130,
|
|
1618
|
+
202,
|
|
1619
|
+
203,
|
|
1620
|
+
204,
|
|
1621
|
+
211,
|
|
1622
|
+
218,
|
|
1623
|
+
219,
|
|
1624
|
+
295,
|
|
1625
|
+
325,
|
|
1626
|
+
326,
|
|
1627
|
+
331,
|
|
1628
|
+
380,
|
|
1629
|
+
382,
|
|
1630
|
+
383,
|
|
1631
|
+
384,
|
|
1632
|
+
385,
|
|
1633
|
+
386,
|
|
1634
|
+
387,
|
|
1635
|
+
388,
|
|
1636
|
+
389,
|
|
1637
|
+
390,
|
|
1638
|
+
393,
|
|
1639
|
+
394,
|
|
1640
|
+
395,
|
|
1641
|
+
456,
|
|
1642
|
+
457,
|
|
1643
|
+
471,
|
|
1644
|
+
472,
|
|
1645
|
+
473,
|
|
1646
|
+
500,
|
|
1647
|
+
501,
|
|
1648
|
+
527,
|
|
1649
|
+
553,
|
|
1650
|
+
575,
|
|
1651
|
+
623,
|
|
1652
|
+
633,
|
|
1653
|
+
751,
|
|
1654
|
+
780,
|
|
1655
|
+
817,
|
|
1656
|
+
870,
|
|
1657
|
+
875,
|
|
1658
|
+
876,
|
|
1659
|
+
877,
|
|
1660
|
+
935
|
|
1661
|
+
];
|
|
1662
|
+
var CREDIT_NOTE_TYPE_CODES = [
|
|
1663
|
+
81,
|
|
1664
|
+
83,
|
|
1665
|
+
261,
|
|
1666
|
+
262,
|
|
1667
|
+
296,
|
|
1668
|
+
308,
|
|
1669
|
+
381,
|
|
1670
|
+
396,
|
|
1671
|
+
420,
|
|
1672
|
+
458,
|
|
1673
|
+
502,
|
|
1674
|
+
503,
|
|
1675
|
+
532
|
|
1676
|
+
];
|
|
1677
|
+
function getInvoiceTypeCodes() {
|
|
1678
|
+
return [...INVOICE_TYPE_CODES];
|
|
1679
|
+
}
|
|
1680
|
+
function getCreditNoteTypeCodes() {
|
|
1681
|
+
return [...CREDIT_NOTE_TYPE_CODES];
|
|
1682
|
+
}
|
|
1609
1683
|
|
|
1610
1684
|
// ../sdk/dist/core/validator.js
|
|
1611
1685
|
function error(field, message, ruleId, suggestion) {
|
|
@@ -1736,9 +1810,12 @@ function validateInvoice(input) {
|
|
|
1736
1810
|
} else if (!input.number.trim()) {
|
|
1737
1811
|
errors.push(error("number", "Invoice number is required", "BR-02", "Must be unique per supplier"));
|
|
1738
1812
|
}
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1813
|
+
if (input.invoiceTypeCode != null) {
|
|
1814
|
+
const isCreditNote = input.isCreditNote === true;
|
|
1815
|
+
const legalCodes = isCreditNote ? getCreditNoteTypeCodes() : getInvoiceTypeCodes();
|
|
1816
|
+
if (!legalCodes.includes(input.invoiceTypeCode)) {
|
|
1817
|
+
errors.push(error("invoiceTypeCode", `Invalid ${isCreditNote ? "credit note" : "invoice"} type code: ${input.invoiceTypeCode}`, "BR-CL-01", `Valid ${isCreditNote ? "credit note" : "invoice"} type codes: ${legalCodes.join(", ")}`));
|
|
1818
|
+
}
|
|
1742
1819
|
}
|
|
1743
1820
|
if (input.isCreditNote) {
|
|
1744
1821
|
const ref = input.invoiceReference;
|
|
@@ -2456,7 +2533,7 @@ function statusFamily(status) {
|
|
|
2456
2533
|
var TERMINAL_FAILURE_STATUSES = STATUS_PRECEDENCE.filter((e) => e.family === "terminal-failure").map((e) => e.status);
|
|
2457
2534
|
|
|
2458
2535
|
// ../sdk/dist/version.js
|
|
2459
|
-
var SDK_VERSION = "5.
|
|
2536
|
+
var SDK_VERSION = "5.1.0";
|
|
2460
2537
|
|
|
2461
2538
|
// ../sdk/dist/core/api-result.js
|
|
2462
2539
|
var API_RESULT_HEADER_NAMES = {
|
|
@@ -5880,7 +5957,7 @@ async function promptEnvironment() {
|
|
|
5880
5957
|
});
|
|
5881
5958
|
}
|
|
5882
5959
|
function registerLoginCommand(program2) {
|
|
5883
|
-
program2.command("login").description("Save a getpeppr API key to
|
|
5960
|
+
program2.command("login").description("Save a getpeppr API key to the credentials file ($XDG_CONFIG_HOME/getpeppr, %APPDATA%\\getpeppr on Windows)").option("--key <key>", "API key \u2014 for CI/scripted use only; visible in `ps` and shell history. Prefer the interactive prompt or GETPEPPR_API_KEY env var.").option("--sandbox", "store as sandbox key (default)").option("--live", "store as live (production) key").action(async (flags) => {
|
|
5884
5961
|
if (!flags.live && !flags.sandbox && process.stdin.isTTY !== true) {
|
|
5885
5962
|
exitWithError("Error: --sandbox or --live required when stdin is not a TTY (CI mode).");
|
|
5886
5963
|
}
|
|
@@ -6012,7 +6089,7 @@ function registerWhoamiCommand(program2) {
|
|
|
6012
6089
|
// src/commands/logout.ts
|
|
6013
6090
|
import pc9 from "picocolors";
|
|
6014
6091
|
function registerLogoutCommand(program2) {
|
|
6015
|
-
program2.command("logout").description("Remove
|
|
6092
|
+
program2.command("logout").description("Remove the stored credentials file").action(() => {
|
|
6016
6093
|
const path = getCredentialsPath();
|
|
6017
6094
|
const removed = deleteCredentials();
|
|
6018
6095
|
if (removed) {
|