@getpeppr/cli 0.4.5 → 0.4.7
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 +194 -19
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1016,7 +1016,7 @@ function validateInvoice(input) {
|
|
|
1016
1016
|
}
|
|
1017
1017
|
if (input.roundingAmount !== void 0 && input.roundingAmount !== null) {
|
|
1018
1018
|
if (input.roundingAmount < -0.99 || input.roundingAmount > 0.99) {
|
|
1019
|
-
errors.push(error("roundingAmount", `Rounding amount must be between -0.99 and 0.99, got ${input.roundingAmount}`, void 0, "
|
|
1019
|
+
errors.push(error("roundingAmount", `Rounding amount must be between -0.99 and 0.99, got ${input.roundingAmount}`, void 0, "Rounding is stored as integer cents (\xB199). Use values like 0.50 or -0.25."));
|
|
1020
1020
|
}
|
|
1021
1021
|
}
|
|
1022
1022
|
if (!input.buyerReference && !input.orderReference) {
|
|
@@ -1059,7 +1059,7 @@ function validateInvoice(input) {
|
|
|
1059
1059
|
}
|
|
1060
1060
|
|
|
1061
1061
|
// ../sdk/dist/version.js
|
|
1062
|
-
var SDK_VERSION = "
|
|
1062
|
+
var SDK_VERSION = "2.0.0";
|
|
1063
1063
|
|
|
1064
1064
|
// ../sdk/dist/core/client.js
|
|
1065
1065
|
function findHeaderCaseInsensitive(headers, name) {
|
|
@@ -1286,12 +1286,7 @@ var GetpepprAdapter = class {
|
|
|
1286
1286
|
const invoices = result.invoices ?? result.data ?? [];
|
|
1287
1287
|
const meta = result.meta;
|
|
1288
1288
|
return {
|
|
1289
|
-
data: invoices.map(
|
|
1290
|
-
id: String(inv.id ?? ""),
|
|
1291
|
-
number: String(inv.number ?? ""),
|
|
1292
|
-
status: mapStatus(String(inv.state ?? inv.status ?? "submitted")),
|
|
1293
|
-
createdAt: inv.created_at ? String(inv.created_at) : void 0
|
|
1294
|
-
})),
|
|
1289
|
+
data: invoices.map(parseInvoiceSummary),
|
|
1295
1290
|
meta: {
|
|
1296
1291
|
totalCount: Number(meta?.total_count ?? invoices.length),
|
|
1297
1292
|
offset: Number(meta?.offset ?? options?.offset ?? 0),
|
|
@@ -1408,22 +1403,24 @@ var GetpepprAdapter = class {
|
|
|
1408
1403
|
params.set("dateTo", options.dateTo);
|
|
1409
1404
|
const query = params.toString() ? `?${params.toString()}` : "";
|
|
1410
1405
|
const result = await this.request("GET", `/events${query}`);
|
|
1411
|
-
const events = result.
|
|
1406
|
+
const events = result.data ?? [];
|
|
1412
1407
|
const meta = result.meta;
|
|
1413
1408
|
return {
|
|
1414
1409
|
data: events.map((evt) => ({
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
contact: evt.contact
|
|
1410
|
+
id: String(evt.id ?? ""),
|
|
1411
|
+
eventType: String(evt.eventType ?? evt.event_type ?? ""),
|
|
1412
|
+
documentId: evt.documentId ?? evt.document_id ?? null,
|
|
1413
|
+
metadata: evt.metadata ?? null,
|
|
1414
|
+
createdAt: String(evt.createdAt ?? evt.created_at ?? "")
|
|
1421
1415
|
})),
|
|
1422
1416
|
meta: {
|
|
1423
1417
|
totalCount: Number(meta?.total_count ?? meta?.totalCount ?? events.length),
|
|
1424
1418
|
offset: Number(meta?.offset ?? options?.offset ?? 0),
|
|
1425
1419
|
limit: Number(meta?.limit ?? options?.limit ?? 25),
|
|
1426
|
-
|
|
1420
|
+
// Prefer the gateway's authoritative has_more; fall back to computing it
|
|
1421
|
+
// from total_count/offset/limit so listAll() paginates correctly even if
|
|
1422
|
+
// a response omits the flag.
|
|
1423
|
+
hasMore: meta?.has_more != null || meta?.hasMore != null ? Boolean(meta.has_more ?? meta.hasMore) : Number(meta?.total_count ?? meta?.totalCount ?? 0) > Number(meta?.offset ?? options?.offset ?? 0) + Number(meta?.limit ?? options?.limit ?? 0),
|
|
1427
1424
|
truncated: Boolean(meta?.truncated ?? false)
|
|
1428
1425
|
}
|
|
1429
1426
|
};
|
|
@@ -1491,6 +1488,58 @@ var GetpepprAdapter = class {
|
|
|
1491
1488
|
async deleteContact(id) {
|
|
1492
1489
|
await this.request("DELETE", `/contacts/${id}`);
|
|
1493
1490
|
}
|
|
1491
|
+
async createLegalEntity(input, options) {
|
|
1492
|
+
const headers = {};
|
|
1493
|
+
if (options?.idempotencyKey)
|
|
1494
|
+
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
1495
|
+
const result = await this.request("POST", "/legal-entities", input, headers);
|
|
1496
|
+
return parseLegalEntity(result);
|
|
1497
|
+
}
|
|
1498
|
+
async getLegalEntity(id) {
|
|
1499
|
+
const result = await this.request("GET", `/legal-entities/${id}`);
|
|
1500
|
+
return parseLegalEntity(result);
|
|
1501
|
+
}
|
|
1502
|
+
async listLegalEntities(options) {
|
|
1503
|
+
const params = new URLSearchParams();
|
|
1504
|
+
if (options?.limit != null)
|
|
1505
|
+
params.set("limit", String(options.limit));
|
|
1506
|
+
if (options?.offset != null)
|
|
1507
|
+
params.set("offset", String(options.offset));
|
|
1508
|
+
const query = params.toString() ? `?${params.toString()}` : "";
|
|
1509
|
+
const result = await this.request("GET", `/legal-entities${query}`);
|
|
1510
|
+
const rows = result.data ?? [];
|
|
1511
|
+
const pagination = result.pagination;
|
|
1512
|
+
return {
|
|
1513
|
+
data: rows.map(parseLegalEntity),
|
|
1514
|
+
meta: {
|
|
1515
|
+
totalCount: Number(pagination?.total_count ?? rows.length),
|
|
1516
|
+
offset: Number(pagination?.offset ?? options?.offset ?? 0),
|
|
1517
|
+
limit: Number(pagination?.limit ?? options?.limit ?? 50),
|
|
1518
|
+
hasMore: Boolean(pagination?.has_more ?? false),
|
|
1519
|
+
truncated: false
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1522
|
+
}
|
|
1523
|
+
async archiveLegalEntity(id) {
|
|
1524
|
+
const result = await this.request("DELETE", `/legal-entities/${id}`);
|
|
1525
|
+
return {
|
|
1526
|
+
id: String(result.id ?? id),
|
|
1527
|
+
externalId: result.externalId != null ? String(result.externalId) : null,
|
|
1528
|
+
status: "archived"
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
async requestLegalEntityAttestation(id, input, options) {
|
|
1532
|
+
const headers = {};
|
|
1533
|
+
if (options?.idempotencyKey)
|
|
1534
|
+
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
1535
|
+
const result = await this.request("POST", `/legal-entities/${id}/attestation`, input, headers);
|
|
1536
|
+
return {
|
|
1537
|
+
id: String(result.id ?? id),
|
|
1538
|
+
externalId: result.externalId != null ? String(result.externalId) : null,
|
|
1539
|
+
status: String(result.status ?? ""),
|
|
1540
|
+
expiresAt: String(result.expiresAt ?? "")
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1494
1543
|
async listBankAccounts(options) {
|
|
1495
1544
|
const params = new URLSearchParams();
|
|
1496
1545
|
if (options?.limit != null)
|
|
@@ -1667,6 +1716,44 @@ function parseContact(raw) {
|
|
|
1667
1716
|
contact.directoryLastChecked = String(raw.directoryLastChecked);
|
|
1668
1717
|
return contact;
|
|
1669
1718
|
}
|
|
1719
|
+
function parseLegalEntity(raw) {
|
|
1720
|
+
const idObj = raw.identifier;
|
|
1721
|
+
const le = {
|
|
1722
|
+
id: String(raw.id ?? ""),
|
|
1723
|
+
externalId: raw.externalId != null ? String(raw.externalId) : null,
|
|
1724
|
+
companyName: raw.companyName != null ? String(raw.companyName) : null,
|
|
1725
|
+
country: raw.country != null ? String(raw.country) : null,
|
|
1726
|
+
identifier: idObj && idObj.scheme != null && idObj.value != null ? { scheme: String(idObj.scheme), value: String(idObj.value) } : null,
|
|
1727
|
+
status: String(raw.status ?? "pending"),
|
|
1728
|
+
environment: String(raw.environment ?? ""),
|
|
1729
|
+
createdAt: String(raw.createdAt ?? "")
|
|
1730
|
+
};
|
|
1731
|
+
if (raw.verificationDetail != null) {
|
|
1732
|
+
le.verificationDetail = raw.verificationDetail;
|
|
1733
|
+
}
|
|
1734
|
+
return le;
|
|
1735
|
+
}
|
|
1736
|
+
function parseInvoiceSummary(raw) {
|
|
1737
|
+
const summary = {
|
|
1738
|
+
id: String(raw.id ?? ""),
|
|
1739
|
+
number: String(raw.invoiceNumber ?? raw.number ?? ""),
|
|
1740
|
+
status: mapStatus(String(raw.state ?? raw.status ?? "submitted"))
|
|
1741
|
+
};
|
|
1742
|
+
if (raw.createdAt != null)
|
|
1743
|
+
summary.createdAt = String(raw.createdAt);
|
|
1744
|
+
if (typeof raw.isCreditNote === "boolean")
|
|
1745
|
+
summary.isCreditNote = raw.isCreditNote;
|
|
1746
|
+
if (raw.recipientName != null)
|
|
1747
|
+
summary.recipientName = String(raw.recipientName);
|
|
1748
|
+
if (raw.totalAmount != null && Number.isFinite(Number(raw.totalAmount))) {
|
|
1749
|
+
summary.totalAmount = Number(raw.totalAmount);
|
|
1750
|
+
}
|
|
1751
|
+
if (raw.currency != null)
|
|
1752
|
+
summary.currency = String(raw.currency);
|
|
1753
|
+
if (raw.environment != null)
|
|
1754
|
+
summary.environment = String(raw.environment);
|
|
1755
|
+
return summary;
|
|
1756
|
+
}
|
|
1670
1757
|
function parseBankAccount(raw) {
|
|
1671
1758
|
const account = {
|
|
1672
1759
|
id: String(raw.id ?? ""),
|
|
@@ -1745,6 +1832,19 @@ var PeppolApiError = class extends PeppolError {
|
|
|
1745
1832
|
this.name = "PeppolApiError";
|
|
1746
1833
|
this.retryAfterMs = retryAfterMs;
|
|
1747
1834
|
}
|
|
1835
|
+
/**
|
|
1836
|
+
* The gateway's machine-readable error code, parsed from the JSON response body
|
|
1837
|
+
* (e.g. "le_cap_exceeded", "identifier_immutable", "legal_entity_locked", "forbidden").
|
|
1838
|
+
* Returns undefined when the body is not JSON or carries no string `code`.
|
|
1839
|
+
*/
|
|
1840
|
+
get code() {
|
|
1841
|
+
try {
|
|
1842
|
+
const parsed = JSON.parse(this.responseBody);
|
|
1843
|
+
return typeof parsed?.code === "string" ? parsed.code : void 0;
|
|
1844
|
+
} catch {
|
|
1845
|
+
return void 0;
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1748
1848
|
};
|
|
1749
1849
|
var Peppol = class {
|
|
1750
1850
|
adapter;
|
|
@@ -1755,6 +1855,7 @@ var Peppol = class {
|
|
|
1755
1855
|
contacts;
|
|
1756
1856
|
bankAccounts;
|
|
1757
1857
|
transports;
|
|
1858
|
+
legalEntities;
|
|
1758
1859
|
constructor(config) {
|
|
1759
1860
|
if (!config.apiKey) {
|
|
1760
1861
|
throw new PeppolError("API key is required. Sign up at https://console.getpeppr.dev to get your sandbox key.");
|
|
@@ -1767,6 +1868,7 @@ var Peppol = class {
|
|
|
1767
1868
|
this.contacts = new ContactOperations(this.adapter);
|
|
1768
1869
|
this.bankAccounts = new BankAccountOperations(this.adapter);
|
|
1769
1870
|
this.transports = new TransportOperations(this.adapter);
|
|
1871
|
+
this.legalEntities = new LegalEntityOperations(this.adapter);
|
|
1770
1872
|
}
|
|
1771
1873
|
/**
|
|
1772
1874
|
* Validate an invoice without sending it.
|
|
@@ -1812,7 +1914,7 @@ var InvoiceOperations = class {
|
|
|
1812
1914
|
}
|
|
1813
1915
|
/**
|
|
1814
1916
|
* Create a draft invoice without sending it.
|
|
1815
|
-
* Validates input client-side, then creates the invoice
|
|
1917
|
+
* Validates input client-side, then creates the invoice via the gateway.
|
|
1816
1918
|
* Use `sendById()` to send the draft when ready.
|
|
1817
1919
|
*
|
|
1818
1920
|
* @example
|
|
@@ -1931,7 +2033,7 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
|
|
|
1931
2033
|
/**
|
|
1932
2034
|
* Import an invoice from a file (XML, PDF, JSON).
|
|
1933
2035
|
* The file is base64-encoded and sent to the gateway, which forwards it
|
|
1934
|
-
* as multipart/form-data to
|
|
2036
|
+
* as multipart/form-data to the provider's import endpoint.
|
|
1935
2037
|
*
|
|
1936
2038
|
* @example
|
|
1937
2039
|
* ```ts
|
|
@@ -1993,7 +2095,7 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
|
|
|
1993
2095
|
}
|
|
1994
2096
|
/**
|
|
1995
2097
|
* Transition an invoice to a new state.
|
|
1996
|
-
*
|
|
2098
|
+
* The gateway validates the state machine — invalid transitions return an error.
|
|
1997
2099
|
*
|
|
1998
2100
|
* @example
|
|
1999
2101
|
* ```ts
|
|
@@ -2288,6 +2390,74 @@ var ContactOperations = class {
|
|
|
2288
2390
|
return paginate((offset, limit) => this.adapter.listContacts({ ...options, offset, limit }), options);
|
|
2289
2391
|
}
|
|
2290
2392
|
};
|
|
2393
|
+
var LegalEntityOperations = class {
|
|
2394
|
+
adapter;
|
|
2395
|
+
constructor(adapter) {
|
|
2396
|
+
this.adapter = adapter;
|
|
2397
|
+
}
|
|
2398
|
+
/**
|
|
2399
|
+
* Create a sub-tenant Legal Entity for one of your customers.
|
|
2400
|
+
*
|
|
2401
|
+
* Idempotent on `externalId`: repeated calls with the same `externalId` return
|
|
2402
|
+
* the existing entity (HTTP 200) instead of creating a duplicate. Transient 5xx
|
|
2403
|
+
* failures are NOT auto-retried unless you pass `options.idempotencyKey`.
|
|
2404
|
+
*
|
|
2405
|
+
* @example
|
|
2406
|
+
* ```ts
|
|
2407
|
+
* const le = await peppol.legalEntities.create({
|
|
2408
|
+
* externalId: "tenant-42",
|
|
2409
|
+
* companyName: "Acme Health AB",
|
|
2410
|
+
* country: "SE",
|
|
2411
|
+
* address: { line1: "Storgatan 1", city: "Stockholm", zip: "11122" },
|
|
2412
|
+
* identifier: { scheme: "0007", value: "5560000001" },
|
|
2413
|
+
* }, { idempotencyKey: "tenant-42-create" });
|
|
2414
|
+
* ```
|
|
2415
|
+
*/
|
|
2416
|
+
async create(input, options) {
|
|
2417
|
+
return this.adapter.createLegalEntity(input, options);
|
|
2418
|
+
}
|
|
2419
|
+
/**
|
|
2420
|
+
* Fetch a single sub-tenant Legal Entity by id. For production entities the
|
|
2421
|
+
* `status` reflects the attestation lifecycle (awaiting_authz → attested → active).
|
|
2422
|
+
*/
|
|
2423
|
+
async get(id) {
|
|
2424
|
+
return this.adapter.getLegalEntity(id);
|
|
2425
|
+
}
|
|
2426
|
+
/** List your sub-tenant Legal Entities, newest first. */
|
|
2427
|
+
async list(options) {
|
|
2428
|
+
return this.adapter.listLegalEntities(options);
|
|
2429
|
+
}
|
|
2430
|
+
/**
|
|
2431
|
+
* Async iterator over all sub-tenant Legal Entities, handling pagination.
|
|
2432
|
+
*
|
|
2433
|
+
* @example
|
|
2434
|
+
* ```ts
|
|
2435
|
+
* for await (const le of peppol.legalEntities.listAll()) console.log(le.id, le.status);
|
|
2436
|
+
* ```
|
|
2437
|
+
*/
|
|
2438
|
+
listAll(options) {
|
|
2439
|
+
return paginate((offset, limit) => this.adapter.listLegalEntities({ ...options, offset, limit }), options);
|
|
2440
|
+
}
|
|
2441
|
+
/** Archive (soft-delete) a sub-tenant Legal Entity. The id stays resolvable for audit. */
|
|
2442
|
+
async archive(id) {
|
|
2443
|
+
return this.adapter.archiveLegalEntity(id);
|
|
2444
|
+
}
|
|
2445
|
+
/**
|
|
2446
|
+
* Request a sub-tenant attestation (production only). Emails the co-branded
|
|
2447
|
+
* confirmation link to the sub-tenant contact and returns the pending status.
|
|
2448
|
+
*
|
|
2449
|
+
* Transient failures are NOT auto-retried unless you pass `options.idempotencyKey`;
|
|
2450
|
+
* re-issuing mints a fresh token, so a retried call is safe.
|
|
2451
|
+
*
|
|
2452
|
+
* @example
|
|
2453
|
+
* ```ts
|
|
2454
|
+
* await peppol.legalEntities.requestAttestation(le.id, { contactEmail: "owner@acme.example" });
|
|
2455
|
+
* ```
|
|
2456
|
+
*/
|
|
2457
|
+
async requestAttestation(id, input, options) {
|
|
2458
|
+
return this.adapter.requestLegalEntityAttestation(id, input, options);
|
|
2459
|
+
}
|
|
2460
|
+
};
|
|
2291
2461
|
var BankAccountOperations = class {
|
|
2292
2462
|
adapter;
|
|
2293
2463
|
constructor(adapter) {
|
|
@@ -2917,6 +3087,11 @@ function registerInitCommand(program2) {
|
|
|
2917
3087
|
1. Edit the file with your invoice data
|
|
2918
3088
|
2. Validate: getpeppr validate ${filename}
|
|
2919
3089
|
3. Convert to XML: getpeppr convert ${filename}
|
|
3090
|
+
4. Send: getpeppr send ${filename}
|
|
3091
|
+
|
|
3092
|
+
${pc2.dim("Sandbox note:")} this template includes VAT. To send it on a sandbox
|
|
3093
|
+
account, first register your VAT in Settings \u2192 Peppol Identity, or set each
|
|
3094
|
+
line's "vatCategory" to "O" (outside the scope of VAT) for a no-VAT test send.
|
|
2920
3095
|
`);
|
|
2921
3096
|
process.exit(0);
|
|
2922
3097
|
}
|