@getpeppr/cli 0.11.1 → 0.12.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/CHANGELOG.md +35 -0
- package/README.md +13 -4
- package/dist/index.js +153 -19
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,41 @@ 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.12.1] — 2026-09-06
|
|
8
|
+
|
|
9
|
+
Rebundles `@getpeppr/sdk` 5.3.0 (GPR-1280).
|
|
10
|
+
|
|
11
|
+
- 0.12.0 was published with a stale SDK 5.2.0 bundle: its `validate` did not
|
|
12
|
+
enforce the billing-profile rules and its `structure` rule IDs predated
|
|
13
|
+
GPR-1267. Version 0.12.0 is deprecated on the registry; 0.12.1 carries the
|
|
14
|
+
intended bundle and the changes below as originally described.
|
|
15
|
+
|
|
16
|
+
## [0.12.0] — 2026-09-06 (deprecated — stale SDK bundle, superseded by 0.12.1)
|
|
17
|
+
|
|
18
|
+
Rebundles `@getpeppr/sdk` 5.3.0 (GPR-1280).
|
|
19
|
+
|
|
20
|
+
- `getpeppr validate` and `getpeppr convert --validate` now enforce the three
|
|
21
|
+
Peppol billing-profile rules on `invoiceTypeCode` (`P0100`, `P0101`,
|
|
22
|
+
`P0112`) because they validate or build UBL locally (GPR-884, GPR-1273).
|
|
23
|
+
Documents outside the profile now fail these commands where they previously
|
|
24
|
+
passed — pipelines gating on the exit code change verdict, hence the minor.
|
|
25
|
+
- The `structure` section rule IDs now designate the rules they actually
|
|
26
|
+
verify (GPR-1267): buyer name `BR-06` → `BR-07`, buyer street/city/postal
|
|
27
|
+
code → `GETPEPPR-BUYER-ADDRESS`, line VAT rate `BR-CO-17` →
|
|
28
|
+
`GETPEPPR-LINE-VAT-RATE`, `taxCurrencyRate` `BR-53` →
|
|
29
|
+
`GETPEPPR-TAX-CURRENCY-RATE`, the buyer-reference warning `BR-10` →
|
|
30
|
+
`PEPPOL-EN16931-R003`, and the due-date warning carries no id.
|
|
31
|
+
- The `structure` section no longer repeats the country advisory warnings
|
|
32
|
+
that the `countryRules` section already reports: every warning is
|
|
33
|
+
displayed and counted once, and `totalWarnings` drops accordingly
|
|
34
|
+
(GPR-1267). Scripts that relied on the duplicated entries in
|
|
35
|
+
`structure.warnings` must read them from `countryRules.warnings`.
|
|
36
|
+
- `getpeppr send` now uses an explicit JSON-send preflight aligned with
|
|
37
|
+
`POST /v1/invoices` (GPR-1285). Because that route currently ignores
|
|
38
|
+
`invoiceTypeCode`, profile rules `P0100`, `P0101`, and `P0112` no longer
|
|
39
|
+
block that inert field before sending. Structured `BR-CL-01` validation
|
|
40
|
+
and all other applicable offline and country checks remain active.
|
|
41
|
+
|
|
7
42
|
## [0.11.1] — 2026-09-04
|
|
8
43
|
|
|
9
44
|
Rebundles `@getpeppr/sdk` 5.2.0 (GPR-1084).
|
package/README.md
CHANGED
|
@@ -126,6 +126,14 @@ API key resolution order: `--key` flag → `GETPEPPR_API_KEY` env var → stored
|
|
|
126
126
|
|
|
127
127
|
Send an invoice through the getpeppr API to the Peppol network. Targets sandbox by default; pass `--prod` for production.
|
|
128
128
|
|
|
129
|
+
`getpeppr send` runs a JSON preflight aligned with `POST /v1/invoices`. That
|
|
130
|
+
endpoint currently ignores `invoiceTypeCode` and lets the provider generate the
|
|
131
|
+
UBL, so the send preflight does not apply `P0100`, `P0101`, or `P0112` to that
|
|
132
|
+
inert field. Structure checks including `BR-CL-01`, plus every other applicable
|
|
133
|
+
offline and country check, remain active. Use `getpeppr validate` or
|
|
134
|
+
`getpeppr convert --validate` for full local-UBL semantics; both retain those
|
|
135
|
+
profile checks because they validate or build the UBL locally.
|
|
136
|
+
|
|
129
137
|
```bash
|
|
130
138
|
# Send a JSON file
|
|
131
139
|
getpeppr send invoice.json
|
|
@@ -154,7 +162,7 @@ getpeppr send invoice.json --prod
|
|
|
154
162
|
| `--attachment` | Attach the bundled test PDF |
|
|
155
163
|
| `--watch` | Poll status until terminal (60s timeout) |
|
|
156
164
|
| `-y, --yes` | Skip the `--prod` confirmation prompt |
|
|
157
|
-
| `--no-validate` | Skip
|
|
165
|
+
| `--no-validate` | Skip the JSON send preflight |
|
|
158
166
|
| `--json` | Machine-readable JSON output |
|
|
159
167
|
| `--quiet` | Exit code only, no output |
|
|
160
168
|
|
|
@@ -232,10 +240,11 @@ GETPEPPR_API_KEY=$SANDBOX_KEY getpeppr send invoice.json --json
|
|
|
232
240
|
|
|
233
241
|
## What it validates
|
|
234
242
|
|
|
235
|
-
|
|
243
|
+
`getpeppr validate` and `getpeppr convert --validate` run three validation
|
|
244
|
+
engines from the [@getpeppr/sdk](https://www.npmjs.com/package/@getpeppr/sdk):
|
|
236
245
|
|
|
237
246
|
1. **Structure** — Required fields, type checks, format validation
|
|
238
|
-
2. **Offline Checks** —
|
|
247
|
+
2. **Offline Checks** — 40 registered partial pre-flight checks: exact network-rule equivalents plus explicit `GETPEPPR-*` diagnostics. The legacy provider-capability diagnostic `unsupported_vat_category` can also appear, but is outside that count. This is not a Peppol conformance verdict.
|
|
239
248
|
3. **Country Rules** — Belgium (BE), France (FR), Italy (IT), Netherlands (NL), Germany (DE)
|
|
240
249
|
|
|
241
250
|
Scaffolding (`init`), validation, and conversion run fully offline — no API key or network connection required. Only `lookup`, `send`, and `whoami` need a network connection. Before a sandbox `send`, the CLI reads `GET /v1/identity`: its own generated fixture is adapted to the returned O/0 or AE/0 profile, while a user-edited file is never rewritten silently and is refused locally if its tax mode conflicts with the sender.
|
|
@@ -294,7 +303,7 @@ This is the exact template generated by `getpeppr init`. See the [full type refe
|
|
|
294
303
|
|
|
295
304
|
## Ready to send?
|
|
296
305
|
|
|
297
|
-
|
|
306
|
+
When your invoice JSON is ready, send it to the Peppol network — straight from the CLI:
|
|
298
307
|
|
|
299
308
|
```bash
|
|
300
309
|
getpeppr login # store your sandbox key once
|
package/dist/index.js
CHANGED
|
@@ -1698,16 +1698,19 @@ function assertString(value, fieldPath, errors) {
|
|
|
1698
1698
|
var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
1699
1699
|
var ALLOWANCE_CHARGE_CONTRACT_RULE = "GETPEPPR-ALLOWANCE-CHARGE-AMOUNT";
|
|
1700
1700
|
var DERIVED_AMOUNT_CONTRACT_RULE2 = "GETPEPPR-DERIVED-AMOUNT";
|
|
1701
|
+
var BUYER_ADDRESS_DELIVERY_RULE = "GETPEPPR-BUYER-ADDRESS";
|
|
1702
|
+
var LINE_VAT_RATE_RULE = "GETPEPPR-LINE-VAT-RATE";
|
|
1703
|
+
var TAX_CURRENCY_RATE_RULE = "GETPEPPR-TAX-CURRENCY-RATE";
|
|
1701
1704
|
function survivesCents2(value) {
|
|
1702
1705
|
return Number.isFinite(value) && Number.isFinite(value * 100);
|
|
1703
1706
|
}
|
|
1704
1707
|
function validateParty(party, path) {
|
|
1705
1708
|
const errors = [];
|
|
1706
1709
|
if (party.name === void 0 || party.name === null || party.name === "") {
|
|
1707
|
-
errors.push(error(`${path}.name`, "Business name is required", "BR-
|
|
1710
|
+
errors.push(error(`${path}.name`, "Business name is required", "BR-07"));
|
|
1708
1711
|
} else if (!assertString(party.name, `${path}.name`, errors)) {
|
|
1709
1712
|
} else if (!party.name.trim()) {
|
|
1710
|
-
errors.push(error(`${path}.name`, "Business name is required", "BR-
|
|
1713
|
+
errors.push(error(`${path}.name`, "Business name is required", "BR-07"));
|
|
1711
1714
|
}
|
|
1712
1715
|
if (party.peppolId === void 0 || party.peppolId === null || party.peppolId === "") {
|
|
1713
1716
|
errors.push(error(`${path}.peppolId`, "Peppol participant ID is required", void 0, 'Format: "scheme:id", e.g. "0208:0685660237" for Belgian companies'));
|
|
@@ -1726,22 +1729,22 @@ function validateParty(party, path) {
|
|
|
1726
1729
|
function validateBuyerAddress(party, path) {
|
|
1727
1730
|
const errors = [];
|
|
1728
1731
|
if (party.street === void 0 || party.street === null || party.street === "") {
|
|
1729
|
-
errors.push(error(`${path}.street`, "Street address is required for the buyer",
|
|
1732
|
+
errors.push(error(`${path}.street`, "Street address is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "123 Business Street"'));
|
|
1730
1733
|
} else if (!assertString(party.street, `${path}.street`, errors)) {
|
|
1731
1734
|
} else if (!party.street.trim()) {
|
|
1732
|
-
errors.push(error(`${path}.street`, "Street address is required for the buyer",
|
|
1735
|
+
errors.push(error(`${path}.street`, "Street address is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "123 Business Street"'));
|
|
1733
1736
|
}
|
|
1734
1737
|
if (party.city === void 0 || party.city === null || party.city === "") {
|
|
1735
|
-
errors.push(error(`${path}.city`, "City is required for the buyer",
|
|
1738
|
+
errors.push(error(`${path}.city`, "City is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "Brussels"'));
|
|
1736
1739
|
} else if (!assertString(party.city, `${path}.city`, errors)) {
|
|
1737
1740
|
} else if (!party.city.trim()) {
|
|
1738
|
-
errors.push(error(`${path}.city`, "City is required for the buyer",
|
|
1741
|
+
errors.push(error(`${path}.city`, "City is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "Brussels"'));
|
|
1739
1742
|
}
|
|
1740
1743
|
if (party.postalCode === void 0 || party.postalCode === null || party.postalCode === "") {
|
|
1741
|
-
errors.push(error(`${path}.postalCode`, "Postal code is required for the buyer",
|
|
1744
|
+
errors.push(error(`${path}.postalCode`, "Postal code is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "1000"'));
|
|
1742
1745
|
} else if (!assertString(party.postalCode, `${path}.postalCode`, errors)) {
|
|
1743
1746
|
} else if (!party.postalCode.trim()) {
|
|
1744
|
-
errors.push(error(`${path}.postalCode`, "Postal code is required for the buyer",
|
|
1747
|
+
errors.push(error(`${path}.postalCode`, "Postal code is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "1000"'));
|
|
1745
1748
|
}
|
|
1746
1749
|
return errors;
|
|
1747
1750
|
}
|
|
@@ -1770,7 +1773,7 @@ function validateLine(line, index, isCreditNote = false) {
|
|
|
1770
1773
|
}
|
|
1771
1774
|
}
|
|
1772
1775
|
if (line.vatRate === void 0 || line.vatRate === null) {
|
|
1773
|
-
errors.push(error(`${path}.vatRate`, "VAT rate is required",
|
|
1776
|
+
errors.push(error(`${path}.vatRate`, "VAT rate is required", LINE_VAT_RATE_RULE));
|
|
1774
1777
|
} else if (line.vatRate < 0 || line.vatRate > 100) {
|
|
1775
1778
|
errors.push(error(`${path}.vatRate`, `VAT rate must be between 0 and 100, got ${line.vatRate}`, void 0, "Use 0 for zero-rated, 21 for standard Belgian VAT, etc."));
|
|
1776
1779
|
}
|
|
@@ -1945,10 +1948,25 @@ function validateInvoice(input) {
|
|
|
1945
1948
|
}
|
|
1946
1949
|
}
|
|
1947
1950
|
if (!input.buyerReference && !input.orderReference) {
|
|
1948
|
-
warnings.push(warning(
|
|
1951
|
+
warnings.push(warning(
|
|
1952
|
+
"buyerReference",
|
|
1953
|
+
"Either buyerReference or orderReference is required by Peppol BIS 3.0 (BT-10).",
|
|
1954
|
+
// GPR-1267 — PEPPOL-EN16931-R003 IS this rule: "A buyer reference or
|
|
1955
|
+
// purchase order reference MUST be provided." BR-10 is the buyer
|
|
1956
|
+
// postal address.
|
|
1957
|
+
"PEPPOL-EN16931-R003"
|
|
1958
|
+
));
|
|
1949
1959
|
}
|
|
1950
1960
|
if (input.taxCurrency && input.taxCurrency !== (input.currency ?? "EUR") && !input.taxCurrencyRate) {
|
|
1951
|
-
errors.push(error(
|
|
1961
|
+
errors.push(error(
|
|
1962
|
+
"taxCurrencyRate",
|
|
1963
|
+
"Tax currency rate is required when taxCurrency differs from document currency",
|
|
1964
|
+
// GPR-1267 — BR-53 mandates the BT-111 total in accounting currency,
|
|
1965
|
+
// which the builder derives itself; the client-supplied rate is an
|
|
1966
|
+
// input-contract requirement, not that rule.
|
|
1967
|
+
TAX_CURRENCY_RATE_RULE,
|
|
1968
|
+
"Set taxCurrencyRate to the exchange rate from document currency to tax currency"
|
|
1969
|
+
));
|
|
1952
1970
|
}
|
|
1953
1971
|
if (input.taxCurrency && input.taxCurrency === (input.currency ?? "EUR")) {
|
|
1954
1972
|
warnings.push(warning("taxCurrency", "Tax currency is the same as document currency \u2014 TaxCurrencyCode will be omitted"));
|
|
@@ -1963,7 +1981,7 @@ function validateInvoice(input) {
|
|
|
1963
1981
|
errors.push(error("taxCurrency", `Invalid tax currency code: "${input.taxCurrency}"`, void 0, 'Use ISO 4217 (e.g., "EUR", "USD")'));
|
|
1964
1982
|
}
|
|
1965
1983
|
if (!input.dueDate) {
|
|
1966
|
-
warnings.push(warning("dueDate", "No due date specified. Recommended for payment terms."
|
|
1984
|
+
warnings.push(warning("dueDate", "No due date specified. Recommended for payment terms."));
|
|
1967
1985
|
}
|
|
1968
1986
|
if (!input.to?.vatNumber) {
|
|
1969
1987
|
warnings.push(warning("to.vatNumber", "Buyer VAT number not provided. May be required for B2B."));
|
|
@@ -2380,6 +2398,90 @@ var buyerPeppolIdRequired = (input) => {
|
|
|
2380
2398
|
}
|
|
2381
2399
|
return [];
|
|
2382
2400
|
};
|
|
2401
|
+
var PROFILE_INVOICE_TYPE_CODES = /* @__PURE__ */ new Set([
|
|
2402
|
+
"71",
|
|
2403
|
+
"80",
|
|
2404
|
+
"82",
|
|
2405
|
+
"84",
|
|
2406
|
+
"102",
|
|
2407
|
+
"218",
|
|
2408
|
+
"219",
|
|
2409
|
+
"326",
|
|
2410
|
+
"331",
|
|
2411
|
+
"380",
|
|
2412
|
+
"382",
|
|
2413
|
+
"383",
|
|
2414
|
+
"384",
|
|
2415
|
+
"386",
|
|
2416
|
+
"388",
|
|
2417
|
+
"393",
|
|
2418
|
+
"395",
|
|
2419
|
+
"553",
|
|
2420
|
+
"575",
|
|
2421
|
+
"623",
|
|
2422
|
+
"780",
|
|
2423
|
+
"817",
|
|
2424
|
+
"870",
|
|
2425
|
+
"875",
|
|
2426
|
+
"876",
|
|
2427
|
+
"877"
|
|
2428
|
+
]);
|
|
2429
|
+
var PROFILE_CREDIT_NOTE_TYPE_CODES = /* @__PURE__ */ new Set([
|
|
2430
|
+
"381",
|
|
2431
|
+
"396",
|
|
2432
|
+
"81",
|
|
2433
|
+
"83",
|
|
2434
|
+
"532"
|
|
2435
|
+
]);
|
|
2436
|
+
function normalizeXmlSpace(value) {
|
|
2437
|
+
return value.replace(/[\u0009\u000A\u000D\u0020]+/g, " ").replace(/^ /, "").replace(/ $/, "");
|
|
2438
|
+
}
|
|
2439
|
+
function normalizedTypeCode(value, fallback) {
|
|
2440
|
+
if (value === void 0 || value === null)
|
|
2441
|
+
return String(fallback);
|
|
2442
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
2443
|
+
return String(value);
|
|
2444
|
+
if (typeof value === "string" && value.length <= 16)
|
|
2445
|
+
return normalizeXmlSpace(value);
|
|
2446
|
+
return void 0;
|
|
2447
|
+
}
|
|
2448
|
+
function normalizedCountry(value) {
|
|
2449
|
+
return typeof value === "string" ? normalizeXmlSpace(value).toUpperCase() : void 0;
|
|
2450
|
+
}
|
|
2451
|
+
var invoiceTypeCodeForProfile = (input) => {
|
|
2452
|
+
if (input.isCreditNote)
|
|
2453
|
+
return [];
|
|
2454
|
+
const invoiceTypeCode = normalizedTypeCode(input.invoiceTypeCode, 380);
|
|
2455
|
+
if (invoiceTypeCode !== void 0 && PROFILE_INVOICE_TYPE_CODES.has(invoiceTypeCode))
|
|
2456
|
+
return [];
|
|
2457
|
+
return [
|
|
2458
|
+
violation("PEPPOL-EN16931-P0100", "error", "Invoice type code is not allowed by the Peppol billing profile used by the local UBL builder.", "invoiceTypeCode")
|
|
2459
|
+
];
|
|
2460
|
+
};
|
|
2461
|
+
var creditNoteTypeCodeForProfile = (input) => {
|
|
2462
|
+
if (!input.isCreditNote)
|
|
2463
|
+
return [];
|
|
2464
|
+
const invoiceTypeCode = normalizedTypeCode(input.invoiceTypeCode, 381);
|
|
2465
|
+
if (invoiceTypeCode !== void 0 && PROFILE_CREDIT_NOTE_TYPE_CODES.has(invoiceTypeCode))
|
|
2466
|
+
return [];
|
|
2467
|
+
return [
|
|
2468
|
+
violation("PEPPOL-EN16931-P0101", "error", "Credit note type code is not allowed by the Peppol billing profile used by the local UBL builder.", "invoiceTypeCode")
|
|
2469
|
+
];
|
|
2470
|
+
};
|
|
2471
|
+
var germanInvoiceTypeCode = (input) => {
|
|
2472
|
+
if (input.isCreditNote)
|
|
2473
|
+
return [];
|
|
2474
|
+
const invoiceTypeCode = normalizedTypeCode(input.invoiceTypeCode, 380);
|
|
2475
|
+
if (invoiceTypeCode !== "326" && invoiceTypeCode !== "384")
|
|
2476
|
+
return [];
|
|
2477
|
+
const sellerCountry = normalizedCountry(input.from?.country);
|
|
2478
|
+
const buyerCountry = normalizedCountry(input.to?.country);
|
|
2479
|
+
if (sellerCountry === "DE" && buyerCountry === "DE")
|
|
2480
|
+
return [];
|
|
2481
|
+
return [
|
|
2482
|
+
violation("PEPPOL-EN16931-P0112", "error", "Invoice type code 326 or 384 is only allowed when both buyer and seller are German organizations.", "invoiceTypeCode")
|
|
2483
|
+
];
|
|
2484
|
+
};
|
|
2383
2485
|
var vatCategoryCodes = (input) => {
|
|
2384
2486
|
const violations = [];
|
|
2385
2487
|
const sendable = SENDABLE_VAT_CATEGORIES.join(", ");
|
|
@@ -2445,6 +2547,9 @@ var ALL_RULES = [
|
|
|
2445
2547
|
brIc10,
|
|
2446
2548
|
// Peppol-specific
|
|
2447
2549
|
buyerPeppolIdRequired,
|
|
2550
|
+
invoiceTypeCodeForProfile,
|
|
2551
|
+
creditNoteTypeCodeForProfile,
|
|
2552
|
+
germanInvoiceTypeCode,
|
|
2448
2553
|
vatCategoryCodes,
|
|
2449
2554
|
unitCodeRecognised
|
|
2450
2555
|
];
|
|
@@ -2505,6 +2610,9 @@ var SDK_SCHEMATRON_RULE_IDS = [
|
|
|
2505
2610
|
"BR-O-10",
|
|
2506
2611
|
"BR-IC-10",
|
|
2507
2612
|
"GETPEPPR-BUYER-PEPPOL-ID-REQUIRED",
|
|
2613
|
+
"PEPPOL-EN16931-P0100",
|
|
2614
|
+
"PEPPOL-EN16931-P0101",
|
|
2615
|
+
"PEPPOL-EN16931-P0112",
|
|
2508
2616
|
"GETPEPPR-UNIT-CODE-RECOGNISED"
|
|
2509
2617
|
];
|
|
2510
2618
|
|
|
@@ -2533,7 +2641,7 @@ function statusFamily(status) {
|
|
|
2533
2641
|
var TERMINAL_FAILURE_STATUSES = STATUS_PRECEDENCE.filter((e) => e.family === "terminal-failure").map((e) => e.status);
|
|
2534
2642
|
|
|
2535
2643
|
// ../sdk/dist/version.js
|
|
2536
|
-
var SDK_VERSION = "5.
|
|
2644
|
+
var SDK_VERSION = "5.3.0";
|
|
2537
2645
|
|
|
2538
2646
|
// ../sdk/dist/core/api-result.js
|
|
2539
2647
|
var API_RESULT_HEADER_NAMES = {
|
|
@@ -4424,6 +4532,8 @@ var DirectoryOperations = class {
|
|
|
4424
4532
|
}
|
|
4425
4533
|
/**
|
|
4426
4534
|
* Search the Peppol Directory for participants.
|
|
4535
|
+
* Pagination is exact and participant-based. Queries wider than the public
|
|
4536
|
+
* Directory's accessible result window are rejected; add narrower criteria.
|
|
4427
4537
|
*
|
|
4428
4538
|
* @example
|
|
4429
4539
|
* ```ts
|
|
@@ -4889,14 +4999,32 @@ var TransportOperations = class {
|
|
|
4889
4999
|
};
|
|
4890
5000
|
|
|
4891
5001
|
// src/commands/validate.ts
|
|
4892
|
-
|
|
5002
|
+
var SEND_INERT_INVOICE_TYPE_PROFILE_RULES = /* @__PURE__ */ new Set([
|
|
5003
|
+
"PEPPOL-EN16931-P0100",
|
|
5004
|
+
"PEPPOL-EN16931-P0101",
|
|
5005
|
+
"PEPPOL-EN16931-P0112"
|
|
5006
|
+
]);
|
|
5007
|
+
function isSendRelevantSchematronViolation(violation2) {
|
|
5008
|
+
return !(violation2.field === "invoiceTypeCode" && SEND_INERT_INVOICE_TYPE_PROFILE_RULES.has(violation2.ruleId));
|
|
5009
|
+
}
|
|
5010
|
+
function runMergedValidation(input, schematronFilter) {
|
|
4893
5011
|
const structure = validateInvoice(input);
|
|
4894
|
-
const
|
|
5012
|
+
const completeSchematron = validateSchematron(input);
|
|
5013
|
+
const schematron = {
|
|
5014
|
+
errors: completeSchematron.errors.filter(schematronFilter),
|
|
5015
|
+
warnings: completeSchematron.warnings
|
|
5016
|
+
};
|
|
4895
5017
|
const countryRules = validateCountryRules(input);
|
|
5018
|
+
const countryKeys = new Set(
|
|
5019
|
+
countryRules.warnings.map((w) => JSON.stringify([w.field, w.message, w.ruleId]))
|
|
5020
|
+
);
|
|
5021
|
+
const structureWarnings = structure.warnings.filter(
|
|
5022
|
+
(w) => !countryKeys.has(JSON.stringify([w.field, w.message, w.ruleId]))
|
|
5023
|
+
);
|
|
4896
5024
|
const totalErrors = structure.errors.length + schematron.errors.length + countryRules.errors.length;
|
|
4897
|
-
const totalWarnings =
|
|
5025
|
+
const totalWarnings = structureWarnings.length + schematron.warnings.length + countryRules.warnings.length;
|
|
4898
5026
|
return {
|
|
4899
|
-
structure: { errors: structure.errors, warnings:
|
|
5027
|
+
structure: { errors: structure.errors, warnings: structureWarnings },
|
|
4900
5028
|
schematron: { errors: schematron.errors, warnings: schematron.warnings },
|
|
4901
5029
|
countryRules: {
|
|
4902
5030
|
errors: countryRules.errors,
|
|
@@ -4907,6 +5035,12 @@ function runValidation(input) {
|
|
|
4907
5035
|
valid: totalErrors === 0
|
|
4908
5036
|
};
|
|
4909
5037
|
}
|
|
5038
|
+
function runValidation(input) {
|
|
5039
|
+
return runMergedValidation(input, () => true);
|
|
5040
|
+
}
|
|
5041
|
+
function runSendPreflight(input) {
|
|
5042
|
+
return runMergedValidation(input, isSendRelevantSchematronViolation);
|
|
5043
|
+
}
|
|
4910
5044
|
function registerValidateCommand(program2) {
|
|
4911
5045
|
program2.command("validate").description("Validate a Peppol invoice JSON file").argument("<file>", "path to invoice JSON file").option("--json", "output results as JSON").option("--quiet", "exit code only, no output").action(async (file, options) => {
|
|
4912
5046
|
const input = readAndValidateInvoiceJson(file);
|
|
@@ -5754,7 +5888,7 @@ function formatSendResult(result, mode) {
|
|
|
5754
5888
|
var API_BASE = "https://api.getpeppr.dev/v1";
|
|
5755
5889
|
var LOCAL_BASE = "http://localhost:3001/api/v1";
|
|
5756
5890
|
function registerSendCommand(program2) {
|
|
5757
|
-
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
|
|
5891
|
+
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 the JSON send preflight").option("--json", "output JSON").option("--quiet", "exit code only, no output").action(async (file, flags) => {
|
|
5758
5892
|
let auth;
|
|
5759
5893
|
try {
|
|
5760
5894
|
auth = resolveApiKey({
|
|
@@ -5828,7 +5962,7 @@ function registerSendCommand(program2) {
|
|
|
5828
5962
|
}
|
|
5829
5963
|
}
|
|
5830
5964
|
if (flags.validate !== false) {
|
|
5831
|
-
const result2 =
|
|
5965
|
+
const result2 = runSendPreflight(payload);
|
|
5832
5966
|
if (!result2.valid) {
|
|
5833
5967
|
process.stderr.write(
|
|
5834
5968
|
`${pc6.red("\u2717")} Pre-validation failed (${result2.totalErrors} errors). Use --no-validate to skip.
|