@aderis-erp/cli 1.1.4 → 1.1.5
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/commands/access-groups.js +42 -8
- package/dist/commands/account-transaction-allocations.js +38 -4
- package/dist/commands/account-transactions.d.ts +2 -0
- package/dist/commands/account-transactions.js +68 -32
- package/dist/commands/accounts.js +46 -12
- package/dist/commands/agent-platform.js +37 -3
- package/dist/commands/agent-runs.js +49 -15
- package/dist/commands/analytics.js +39 -5
- package/dist/commands/api-clients.js +47 -13
- package/dist/commands/api-keys.js +40 -6
- package/dist/commands/async-jobs.js +45 -11
- package/dist/commands/audit-log.js +38 -4
- package/dist/commands/bank-accounts.js +54 -20
- package/dist/commands/bank-integrations.js +56 -22
- package/dist/commands/business-partners.js +77 -43
- package/dist/commands/chat.js +43 -9
- package/dist/commands/credit-card-accounts.js +57 -23
- package/dist/commands/currencies.js +37 -3
- package/dist/commands/custody-accounts.js +41 -7
- package/dist/commands/dashboards.js +52 -18
- package/dist/commands/delivery-notes.js +69 -35
- package/dist/commands/document-checksums.d.ts +1 -0
- package/dist/commands/document-checksums.js +38 -3
- package/dist/commands/dunning-configs.js +41 -7
- package/dist/commands/dunning-reminders.js +44 -10
- package/dist/commands/dunning-runs.js +37 -3
- package/dist/commands/email-transactions.js +39 -5
- package/dist/commands/employees.js +77 -43
- package/dist/commands/entity-configurations.js +39 -5
- package/dist/commands/event-mapping-rules.js +37 -3
- package/dist/commands/event-processor-mappings.js +41 -7
- package/dist/commands/files.js +48 -14
- package/dist/commands/financial-instrument-transactions.d.ts +56 -0
- package/dist/commands/financial-instrument-transactions.js +1369 -0
- package/dist/commands/financial-instrument-types.js +41 -7
- package/dist/commands/financial-instruments.js +41 -7
- package/dist/commands/health.js +41 -7
- package/dist/commands/imports.js +39 -5
- package/dist/commands/inbox.js +42 -8
- package/dist/commands/integrations.js +39 -5
- package/dist/commands/item-categories.js +41 -7
- package/dist/commands/items.js +42 -8
- package/dist/commands/journal-lines.js +37 -3
- package/dist/commands/journals.js +61 -27
- package/dist/commands/kpis.js +44 -10
- package/dist/commands/ledgers.js +83 -49
- package/dist/commands/my-account.js +54 -20
- package/dist/commands/notifications.js +37 -3
- package/dist/commands/office-locations.js +41 -7
- package/dist/commands/payment-methods.js +46 -12
- package/dist/commands/payment-runs.js +54 -20
- package/dist/commands/payment-terms.js +42 -8
- package/dist/commands/payments.js +43 -9
- package/dist/commands/payroll-runs.js +71 -37
- package/dist/commands/permissions.js +37 -3
- package/dist/commands/price-lists.js +48 -14
- package/dist/commands/processor-mappings.js +40 -6
- package/dist/commands/processors.js +46 -12
- package/dist/commands/purchase-order-types.js +41 -7
- package/dist/commands/purchase-orders.js +60 -26
- package/dist/commands/reports.js +43 -8
- package/dist/commands/roles.js +40 -6
- package/dist/commands/salary-types.js +41 -7
- package/dist/commands/sales-invoices.js +85 -51
- package/dist/commands/sales-orders.js +69 -35
- package/dist/commands/saved-views.js +39 -5
- package/dist/commands/scheduled-processor-mappings.js +41 -7
- package/dist/commands/schedules.js +45 -11
- package/dist/commands/settings.js +38 -4
- package/dist/commands/tags.js +42 -8
- package/dist/commands/tax-codes.js +44 -10
- package/dist/commands/tax-matrix.js +41 -7
- package/dist/commands/templates.js +49 -15
- package/dist/commands/tenants.js +44 -10
- package/dist/commands/tools.js +38 -4
- package/dist/commands/users.js +58 -24
- package/dist/commands/vat-declarations.js +61 -27
- package/dist/commands/vendor-invoices.js +81 -47
- package/dist/commands/webhooks.js +43 -9
- package/dist/commands/work-summary.js +37 -3
- package/dist/lib/paths.d.ts +8 -0
- package/dist/lib/paths.js +16 -0
- package/dist/lib/production-guard.js +2 -0
- package/oclif.manifest.json +199 -2
- package/package.json +9 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -11,7 +12,7 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
13
|
const SUBCOMMANDS = [...["list"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'list': { method: 'GET', path: '/v1/currencies', summary: 'GET /v1/currencies', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/currencies', summary: 'GET /v1/currencies', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
15
16
|
};
|
|
16
17
|
export default class Currencies extends Command {
|
|
17
18
|
static description = 'Manage currencies';
|
|
@@ -29,7 +30,7 @@ export default class Currencies extends Command {
|
|
|
29
30
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
30
31
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
31
32
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
32
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
33
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
33
34
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
34
35
|
page: Flags.string({ description: 'Page number' }),
|
|
35
36
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -160,6 +161,17 @@ export default class Currencies extends Command {
|
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
163
|
}
|
|
164
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
165
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
166
|
+
for (const param of endpoint.specParams) {
|
|
167
|
+
const value = flags[param.flag];
|
|
168
|
+
if (value === undefined || value === '') {
|
|
169
|
+
if (param.required)
|
|
170
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
queryParams[param.name] = value;
|
|
174
|
+
}
|
|
163
175
|
let data;
|
|
164
176
|
if (endpoint.isMultipart) {
|
|
165
177
|
if (!flags.file)
|
|
@@ -180,6 +192,25 @@ export default class Currencies extends Command {
|
|
|
180
192
|
}
|
|
181
193
|
data = form;
|
|
182
194
|
}
|
|
195
|
+
else if (endpoint.bodyMediaType) {
|
|
196
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
197
|
+
// verbatim, under the content type the endpoint asks for.
|
|
198
|
+
if (flags.file) {
|
|
199
|
+
const fsModule = await import('node:fs');
|
|
200
|
+
try {
|
|
201
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else if (flags.data) {
|
|
208
|
+
data = flags.data;
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
183
214
|
else if (endpoint.hasBody) {
|
|
184
215
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
185
216
|
// input file — skip the read-as-body branch.
|
|
@@ -275,7 +306,7 @@ export default class Currencies extends Command {
|
|
|
275
306
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
276
307
|
}
|
|
277
308
|
else if (data) {
|
|
278
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
309
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
279
310
|
}
|
|
280
311
|
process.stderr.write('\n');
|
|
281
312
|
}
|
|
@@ -289,6 +320,7 @@ export default class Currencies extends Command {
|
|
|
289
320
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
290
321
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
291
322
|
data,
|
|
323
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
292
324
|
contextName: flags.context,
|
|
293
325
|
binary: true,
|
|
294
326
|
});
|
|
@@ -314,6 +346,7 @@ export default class Currencies extends Command {
|
|
|
314
346
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
315
347
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
316
348
|
data,
|
|
349
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
317
350
|
contextName: flags.context,
|
|
318
351
|
binary: true,
|
|
319
352
|
});
|
|
@@ -334,6 +367,7 @@ export default class Currencies extends Command {
|
|
|
334
367
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
335
368
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
336
369
|
data,
|
|
370
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
337
371
|
contextName: flags.context,
|
|
338
372
|
acceptAny: true,
|
|
339
373
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -11,11 +12,11 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
13
|
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'list': { method: 'GET', path: '/v1/custody-accounts', summary: 'List custody accounts', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/custody-accounts', summary: 'Create a custody account', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
-
'get': { method: 'GET', path: '/v1/custody-accounts/{id}', summary: 'Read a custody account', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'update': { method: 'PATCH', path: '/v1/custody-accounts/{id}', summary: 'Update a custody account', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
-
'delete': { method: 'DELETE', path: '/v1/custody-accounts/{id}', summary: 'Delete a custody account', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/custody-accounts', summary: 'List custody accounts', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/custody-accounts', summary: 'Create a custody account', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'get': { method: 'GET', path: '/v1/custody-accounts/{id}', summary: 'Read a custody account', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'update': { method: 'PATCH', path: '/v1/custody-accounts/{id}', summary: 'Update a custody account', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'delete': { method: 'DELETE', path: '/v1/custody-accounts/{id}', summary: 'Delete a custody account', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
20
|
};
|
|
20
21
|
export default class CustodyAccounts extends Command {
|
|
21
22
|
static description = 'Manage custody accounts';
|
|
@@ -37,7 +38,7 @@ export default class CustodyAccounts extends Command {
|
|
|
37
38
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
38
39
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
39
40
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
40
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
41
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
41
42
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
42
43
|
page: Flags.string({ description: 'Page number' }),
|
|
43
44
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -168,6 +169,17 @@ export default class CustodyAccounts extends Command {
|
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
171
|
}
|
|
172
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
173
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
174
|
+
for (const param of endpoint.specParams) {
|
|
175
|
+
const value = flags[param.flag];
|
|
176
|
+
if (value === undefined || value === '') {
|
|
177
|
+
if (param.required)
|
|
178
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
queryParams[param.name] = value;
|
|
182
|
+
}
|
|
171
183
|
let data;
|
|
172
184
|
if (endpoint.isMultipart) {
|
|
173
185
|
if (!flags.file)
|
|
@@ -188,6 +200,25 @@ export default class CustodyAccounts extends Command {
|
|
|
188
200
|
}
|
|
189
201
|
data = form;
|
|
190
202
|
}
|
|
203
|
+
else if (endpoint.bodyMediaType) {
|
|
204
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
205
|
+
// verbatim, under the content type the endpoint asks for.
|
|
206
|
+
if (flags.file) {
|
|
207
|
+
const fsModule = await import('node:fs');
|
|
208
|
+
try {
|
|
209
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (flags.data) {
|
|
216
|
+
data = flags.data;
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
191
222
|
else if (endpoint.hasBody) {
|
|
192
223
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
193
224
|
// input file — skip the read-as-body branch.
|
|
@@ -283,7 +314,7 @@ export default class CustodyAccounts extends Command {
|
|
|
283
314
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
284
315
|
}
|
|
285
316
|
else if (data) {
|
|
286
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
317
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
287
318
|
}
|
|
288
319
|
process.stderr.write('\n');
|
|
289
320
|
}
|
|
@@ -297,6 +328,7 @@ export default class CustodyAccounts extends Command {
|
|
|
297
328
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
298
329
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
299
330
|
data,
|
|
331
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
300
332
|
contextName: flags.context,
|
|
301
333
|
binary: true,
|
|
302
334
|
});
|
|
@@ -322,6 +354,7 @@ export default class CustodyAccounts extends Command {
|
|
|
322
354
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
323
355
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
324
356
|
data,
|
|
357
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
325
358
|
contextName: flags.context,
|
|
326
359
|
binary: true,
|
|
327
360
|
});
|
|
@@ -342,6 +375,7 @@ export default class CustodyAccounts extends Command {
|
|
|
342
375
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
343
376
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
344
377
|
data,
|
|
378
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
345
379
|
contextName: flags.context,
|
|
346
380
|
acceptAny: true,
|
|
347
381
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -11,22 +12,22 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = ["widgets"];
|
|
12
13
|
const SUBCOMMANDS = [...["list-templates", "list-catalog", "home", "list", "create", "duplicate", "get", "update", "delete", "update-layout", "create-widgets", "update-widgets", "delete-widgets", "home-data", "data", "widgets-data"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'list-templates': { method: 'GET', path: '/v1/dashboards/templates', summary: 'List the dashboard templates', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'list-catalog': { method: 'GET', path: '/v1/dashboards/catalog', summary: 'List widgets ready to add', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
-
'home': { method: 'GET', path: '/v1/dashboards/home', summary: 'Open the caller\'s home dashboard', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'list': { method: 'GET', path: '/v1/dashboards', summary: 'List the dashboards the caller can open', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
-
'create': { method: 'POST', path: '/v1/dashboards', summary: 'Create a dashboard', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
19
|
-
'duplicate': { method: 'POST', path: '/v1/dashboards/{id}/duplicate', summary: 'Copy a dashboard', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
-
'get': { method: 'GET', path: '/v1/dashboards/{id}', summary: 'Open one dashboard', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
21
|
-
'update': { method: 'PATCH', path: '/v1/dashboards/{id}', summary: 'Change a dashboard', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
22
|
-
'delete': { method: 'DELETE', path: '/v1/dashboards/{id}', summary: 'Delete a dashboard and its widgets', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
23
|
-
'update-layout': { method: 'PATCH', path: '/v1/dashboards/{id}/layout', summary: 'Save the grid of a dashboard', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
24
|
-
'create-widgets': { method: 'POST', path: '/v1/dashboards/{id}/widgets', summary: 'Add a widget to a dashboard', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
25
|
-
'update-widgets': { method: 'PATCH', path: '/v1/dashboards/{id}/widgets/{widgetId}', summary: 'Replace the config of a widget', pathParams: ['id', 'widgetId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
26
|
-
'delete-widgets': { method: 'DELETE', path: '/v1/dashboards/{id}/widgets/{widgetId}', summary: 'Remove a widget from a dashboard', pathParams: ['id', 'widgetId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
27
|
-
'home-data': { method: 'GET', path: '/v1/dashboards/home/data', summary: 'Evaluate every widget of the caller\'s home dashboard', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
28
|
-
'data': { method: 'GET', path: '/v1/dashboards/{id}/data', summary: 'Evaluate every widget of a dashboard for a period', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
29
|
-
'widgets-data': { method: 'GET', path: '/v1/dashboards/{id}/widgets/{widgetId}/data', summary: 'Evaluate one widget of a dashboard for a period', pathParams: ['id', 'widgetId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list-templates': { method: 'GET', path: '/v1/dashboards/templates', summary: 'List the dashboard templates', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'list-catalog': { method: 'GET', path: '/v1/dashboards/catalog', summary: 'List widgets ready to add', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'home': { method: 'GET', path: '/v1/dashboards/home', summary: 'Open the caller\'s home dashboard', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'list': { method: 'GET', path: '/v1/dashboards', summary: 'List the dashboards the caller can open', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'create': { method: 'POST', path: '/v1/dashboards', summary: 'Create a dashboard', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
20
|
+
'duplicate': { method: 'POST', path: '/v1/dashboards/{id}/duplicate', summary: 'Copy a dashboard', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
21
|
+
'get': { method: 'GET', path: '/v1/dashboards/{id}', summary: 'Open one dashboard', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
22
|
+
'update': { method: 'PATCH', path: '/v1/dashboards/{id}', summary: 'Change a dashboard', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
23
|
+
'delete': { method: 'DELETE', path: '/v1/dashboards/{id}', summary: 'Delete a dashboard and its widgets', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
24
|
+
'update-layout': { method: 'PATCH', path: '/v1/dashboards/{id}/layout', summary: 'Save the grid of a dashboard', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
25
|
+
'create-widgets': { method: 'POST', path: '/v1/dashboards/{id}/widgets', summary: 'Add a widget to a dashboard', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
26
|
+
'update-widgets': { method: 'PATCH', path: '/v1/dashboards/{id}/widgets/{widgetId}', summary: 'Replace the config of a widget', pathParams: ['id', 'widgetId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
27
|
+
'delete-widgets': { method: 'DELETE', path: '/v1/dashboards/{id}/widgets/{widgetId}', summary: 'Remove a widget from a dashboard', pathParams: ['id', 'widgetId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
28
|
+
'home-data': { method: 'GET', path: '/v1/dashboards/home/data', summary: 'Evaluate every widget of the caller\'s home dashboard', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
29
|
+
'data': { method: 'GET', path: '/v1/dashboards/{id}/data', summary: 'Evaluate every widget of a dashboard for a period', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
30
|
+
'widgets-data': { method: 'GET', path: '/v1/dashboards/{id}/widgets/{widgetId}/data', summary: 'Evaluate one widget of a dashboard for a period', pathParams: ['id', 'widgetId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
30
31
|
};
|
|
31
32
|
export default class Dashboards extends Command {
|
|
32
33
|
static description = 'Manage dashboards';
|
|
@@ -48,7 +49,7 @@ export default class Dashboards extends Command {
|
|
|
48
49
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
49
50
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
50
51
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
51
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
52
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
52
53
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
53
54
|
'widget-id': Flags.string({ description: 'Path param: widgetId' }),
|
|
54
55
|
page: Flags.string({ description: 'Page number' }),
|
|
@@ -180,6 +181,17 @@ export default class Dashboards extends Command {
|
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
183
|
}
|
|
184
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
185
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
186
|
+
for (const param of endpoint.specParams) {
|
|
187
|
+
const value = flags[param.flag];
|
|
188
|
+
if (value === undefined || value === '') {
|
|
189
|
+
if (param.required)
|
|
190
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
queryParams[param.name] = value;
|
|
194
|
+
}
|
|
183
195
|
let data;
|
|
184
196
|
if (endpoint.isMultipart) {
|
|
185
197
|
if (!flags.file)
|
|
@@ -200,6 +212,25 @@ export default class Dashboards extends Command {
|
|
|
200
212
|
}
|
|
201
213
|
data = form;
|
|
202
214
|
}
|
|
215
|
+
else if (endpoint.bodyMediaType) {
|
|
216
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
217
|
+
// verbatim, under the content type the endpoint asks for.
|
|
218
|
+
if (flags.file) {
|
|
219
|
+
const fsModule = await import('node:fs');
|
|
220
|
+
try {
|
|
221
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else if (flags.data) {
|
|
228
|
+
data = flags.data;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
203
234
|
else if (endpoint.hasBody) {
|
|
204
235
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
205
236
|
// input file — skip the read-as-body branch.
|
|
@@ -295,7 +326,7 @@ export default class Dashboards extends Command {
|
|
|
295
326
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
296
327
|
}
|
|
297
328
|
else if (data) {
|
|
298
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
329
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
299
330
|
}
|
|
300
331
|
process.stderr.write('\n');
|
|
301
332
|
}
|
|
@@ -309,6 +340,7 @@ export default class Dashboards extends Command {
|
|
|
309
340
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
310
341
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
311
342
|
data,
|
|
343
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
312
344
|
contextName: flags.context,
|
|
313
345
|
binary: true,
|
|
314
346
|
});
|
|
@@ -334,6 +366,7 @@ export default class Dashboards extends Command {
|
|
|
334
366
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
335
367
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
336
368
|
data,
|
|
369
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
337
370
|
contextName: flags.context,
|
|
338
371
|
binary: true,
|
|
339
372
|
});
|
|
@@ -354,6 +387,7 @@ export default class Dashboards extends Command {
|
|
|
354
387
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
355
388
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
356
389
|
data,
|
|
390
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
357
391
|
contextName: flags.context,
|
|
358
392
|
acceptAny: true,
|
|
359
393
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -11,39 +12,39 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = ["line-items", "notes", "files"];
|
|
12
13
|
const SUBCOMMANDS = [...["list", "create", "create-from-orders", "create-from-invoice", "create-invoice", "get", "update", "delete", "get-pdf", "create-pdf", "submit", "approve", "cancel", "replace", "copy", "deliver", "ship", "send", "create-line-items", "update-line-items-reorder", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'list': { method: 'GET', path: '/v1/delivery-notes', summary: 'Get all delivery notes with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/delivery-notes', summary: 'Create a new delivery note', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'tagCodes', 'lineItems'] },
|
|
16
|
-
'create-from-orders': { method: 'POST', path: '/v1/delivery-notes/create-from-orders', summary: 'Create a DRAFT delivery note from several sales orders', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['salesOrderIds'] },
|
|
17
|
-
'create-from-invoice': { method: 'POST', path: '/v1/delivery-notes/create-from-invoice', summary: 'Create a DRAFT delivery note from a sales invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
-
'create-invoice': { method: 'POST', path: '/v1/delivery-notes/create-invoice', summary: 'Create ONE draft sales invoice from several delivery notes (same customer)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['deliveryNoteIds'] },
|
|
19
|
-
'get': { method: 'GET', path: '/v1/delivery-notes/{id}', summary: 'Get a delivery note by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
-
'update': { method: 'PATCH', path: '/v1/delivery-notes/{id}', summary: 'Update a delivery note', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'tagCodes', 'removeFileIds'] },
|
|
21
|
-
'delete': { method: 'DELETE', path: '/v1/delivery-notes/{id}', summary: 'Delete a delivery note', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
22
|
-
'get-pdf': { method: 'GET', path: '/v1/delivery-notes/{id}/pdf', summary: 'Render a delivery note PDF as the mapped event would, or preview a stored template', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
23
|
-
'create-pdf': { method: 'POST', path: '/v1/delivery-notes/{id}/pdf', summary: 'Render a delivery note PDF from unsaved HTML posted in the body', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
24
|
-
'submit': { method: 'POST', path: '/v1/delivery-notes/{id}/submit', summary: 'Submit a delivery note for approval (DRAFT → PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
25
|
-
'approve': { method: 'POST', path: '/v1/delivery-notes/{id}/approve', summary: 'Approve a delivery note (DRAFT or PENDING_APPROVAL → APPROVED)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
26
|
-
'cancel': { method: 'POST', path: '/v1/delivery-notes/{id}/cancel', summary: 'Cancel a delivery note (DRAFT / PENDING_APPROVAL / APPROVED / SHIPPED / DELIVERED → CANCELLED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
27
|
-
'replace': { method: 'POST', path: '/v1/delivery-notes/{id}/replace', summary: 'Replace a delivery note (APPROVED / SHIPPED / DELIVERED / CANCELLED → REPLACED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
28
|
-
'copy': { method: 'POST', path: '/v1/delivery-notes/{id}/copy', summary: 'Create a copy of a delivery note as a new draft', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
29
|
-
'deliver': { method: 'POST', path: '/v1/delivery-notes/{id}/deliver', summary: 'Mark a delivery note as delivered (SHIPPED → DELIVERED)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
30
|
-
'ship': { method: 'POST', path: '/v1/delivery-notes/{id}/ship', summary: 'Ship a delivery note (APPROVED → SHIPPED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['lineItems'] },
|
|
31
|
-
'send': { method: 'POST', path: '/v1/delivery-notes/{id}/send', summary: 'Send a delivery note to its recipient by email', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['emailCc', 'emailBcc'] },
|
|
32
|
-
'create-line-items': { method: 'POST', path: '/v1/delivery-notes/{noteId}/line-items', summary: 'Add a line item to a delivery note', pathParams: ['noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['salesOrderLineItems', 'tagIds', 'tagCodes'] },
|
|
33
|
-
'update-line-items-reorder': { method: 'PATCH', path: '/v1/delivery-notes/{noteId}/line-items/reorder', summary: 'Bulk reorder line items (drag-and-drop)', pathParams: ['noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['items'] },
|
|
34
|
-
'update-line-items': { method: 'PATCH', path: '/v1/delivery-notes/{noteId}/line-items/{lineItemId}', summary: 'Update a line item on a delivery note', pathParams: ['noteId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['salesOrderLineItems', 'tagIds', 'tagCodes'] },
|
|
35
|
-
'delete-line-items': { method: 'DELETE', path: '/v1/delivery-notes/{noteId}/line-items/{lineItemId}', summary: 'Remove a line item from a delivery note', pathParams: ['noteId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
36
|
-
'list-notes': { method: 'GET', path: '/v1/delivery-notes/{deliveryNoteId}/notes', summary: 'Get all notes for a delivery note', pathParams: ['deliveryNoteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
37
|
-
'create-notes': { method: 'POST', path: '/v1/delivery-notes/{deliveryNoteId}/notes', summary: 'Create a note for a delivery note', pathParams: ['deliveryNoteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
38
|
-
'get-notes': { method: 'GET', path: '/v1/delivery-notes/{deliveryNoteId}/notes/{noteId}', summary: 'Get a specific note for a delivery note', pathParams: ['deliveryNoteId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
39
|
-
'update-notes': { method: 'PATCH', path: '/v1/delivery-notes/{deliveryNoteId}/notes/{noteId}', summary: 'Update a note for a delivery note', pathParams: ['deliveryNoteId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
40
|
-
'delete-notes': { method: 'DELETE', path: '/v1/delivery-notes/{deliveryNoteId}/notes/{noteId}', summary: 'Delete a note from a delivery note', pathParams: ['deliveryNoteId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
41
|
-
'list-files': { method: 'GET', path: '/v1/delivery-notes/{parentId}/files', summary: 'List file attachments for a delivery note', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
42
|
-
'create-files': { method: 'POST', path: '/v1/delivery-notes/{parentId}/files', summary: 'Upload a file attachment to a delivery note', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
43
|
-
'get-files': { method: 'GET', path: '/v1/delivery-notes/{parentId}/files/{fileId}', summary: 'Get a file attachment for a delivery note', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
44
|
-
'update-files': { method: 'PATCH', path: '/v1/delivery-notes/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a delivery note', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
45
|
-
'delete-files': { method: 'DELETE', path: '/v1/delivery-notes/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a delivery note', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
46
|
-
'files-download': { method: 'GET', path: '/v1/delivery-notes/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a delivery note', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/delivery-notes', summary: 'Get all delivery notes with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/delivery-notes', summary: 'Create a new delivery note', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'tagCodes', 'lineItems'], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'create-from-orders': { method: 'POST', path: '/v1/delivery-notes/create-from-orders', summary: 'Create a DRAFT delivery note from several sales orders', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['salesOrderIds'], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'create-from-invoice': { method: 'POST', path: '/v1/delivery-notes/create-from-invoice', summary: 'Create a DRAFT delivery note from a sales invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'create-invoice': { method: 'POST', path: '/v1/delivery-notes/create-invoice', summary: 'Create ONE draft sales invoice from several delivery notes (same customer)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['deliveryNoteIds'], bodyMediaType: '', specParams: [] },
|
|
20
|
+
'get': { method: 'GET', path: '/v1/delivery-notes/{id}', summary: 'Get a delivery note by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
21
|
+
'update': { method: 'PATCH', path: '/v1/delivery-notes/{id}', summary: 'Update a delivery note', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'tagCodes', 'removeFileIds'], bodyMediaType: '', specParams: [] },
|
|
22
|
+
'delete': { method: 'DELETE', path: '/v1/delivery-notes/{id}', summary: 'Delete a delivery note', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
23
|
+
'get-pdf': { method: 'GET', path: '/v1/delivery-notes/{id}/pdf', summary: 'Render a delivery note PDF as the mapped event would, or preview a stored template', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
24
|
+
'create-pdf': { method: 'POST', path: '/v1/delivery-notes/{id}/pdf', summary: 'Render a delivery note PDF from unsaved HTML posted in the body', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
25
|
+
'submit': { method: 'POST', path: '/v1/delivery-notes/{id}/submit', summary: 'Submit a delivery note for approval (DRAFT → PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
26
|
+
'approve': { method: 'POST', path: '/v1/delivery-notes/{id}/approve', summary: 'Approve a delivery note (DRAFT or PENDING_APPROVAL → APPROVED)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
27
|
+
'cancel': { method: 'POST', path: '/v1/delivery-notes/{id}/cancel', summary: 'Cancel a delivery note (DRAFT / PENDING_APPROVAL / APPROVED / SHIPPED / DELIVERED → CANCELLED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
28
|
+
'replace': { method: 'POST', path: '/v1/delivery-notes/{id}/replace', summary: 'Replace a delivery note (APPROVED / SHIPPED / DELIVERED / CANCELLED → REPLACED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
29
|
+
'copy': { method: 'POST', path: '/v1/delivery-notes/{id}/copy', summary: 'Create a copy of a delivery note as a new draft', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
30
|
+
'deliver': { method: 'POST', path: '/v1/delivery-notes/{id}/deliver', summary: 'Mark a delivery note as delivered (SHIPPED → DELIVERED)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
31
|
+
'ship': { method: 'POST', path: '/v1/delivery-notes/{id}/ship', summary: 'Ship a delivery note (APPROVED → SHIPPED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['lineItems'], bodyMediaType: '', specParams: [] },
|
|
32
|
+
'send': { method: 'POST', path: '/v1/delivery-notes/{id}/send', summary: 'Send a delivery note to its recipient by email', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['emailCc', 'emailBcc'], bodyMediaType: '', specParams: [] },
|
|
33
|
+
'create-line-items': { method: 'POST', path: '/v1/delivery-notes/{noteId}/line-items', summary: 'Add a line item to a delivery note', pathParams: ['noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['salesOrderLineItems', 'tagIds', 'tagCodes'], bodyMediaType: '', specParams: [] },
|
|
34
|
+
'update-line-items-reorder': { method: 'PATCH', path: '/v1/delivery-notes/{noteId}/line-items/reorder', summary: 'Bulk reorder line items (drag-and-drop)', pathParams: ['noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['items'], bodyMediaType: '', specParams: [] },
|
|
35
|
+
'update-line-items': { method: 'PATCH', path: '/v1/delivery-notes/{noteId}/line-items/{lineItemId}', summary: 'Update a line item on a delivery note', pathParams: ['noteId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['salesOrderLineItems', 'tagIds', 'tagCodes'], bodyMediaType: '', specParams: [] },
|
|
36
|
+
'delete-line-items': { method: 'DELETE', path: '/v1/delivery-notes/{noteId}/line-items/{lineItemId}', summary: 'Remove a line item from a delivery note', pathParams: ['noteId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
37
|
+
'list-notes': { method: 'GET', path: '/v1/delivery-notes/{deliveryNoteId}/notes', summary: 'Get all notes for a delivery note', pathParams: ['deliveryNoteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
38
|
+
'create-notes': { method: 'POST', path: '/v1/delivery-notes/{deliveryNoteId}/notes', summary: 'Create a note for a delivery note', pathParams: ['deliveryNoteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
39
|
+
'get-notes': { method: 'GET', path: '/v1/delivery-notes/{deliveryNoteId}/notes/{noteId}', summary: 'Get a specific note for a delivery note', pathParams: ['deliveryNoteId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
40
|
+
'update-notes': { method: 'PATCH', path: '/v1/delivery-notes/{deliveryNoteId}/notes/{noteId}', summary: 'Update a note for a delivery note', pathParams: ['deliveryNoteId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
41
|
+
'delete-notes': { method: 'DELETE', path: '/v1/delivery-notes/{deliveryNoteId}/notes/{noteId}', summary: 'Delete a note from a delivery note', pathParams: ['deliveryNoteId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
42
|
+
'list-files': { method: 'GET', path: '/v1/delivery-notes/{parentId}/files', summary: 'List file attachments for a delivery note', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
43
|
+
'create-files': { method: 'POST', path: '/v1/delivery-notes/{parentId}/files', summary: 'Upload a file attachment to a delivery note', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
44
|
+
'get-files': { method: 'GET', path: '/v1/delivery-notes/{parentId}/files/{fileId}', summary: 'Get a file attachment for a delivery note', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
45
|
+
'update-files': { method: 'PATCH', path: '/v1/delivery-notes/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a delivery note', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
46
|
+
'delete-files': { method: 'DELETE', path: '/v1/delivery-notes/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a delivery note', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
47
|
+
'files-download': { method: 'GET', path: '/v1/delivery-notes/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a delivery note', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
47
48
|
};
|
|
48
49
|
export default class DeliveryNotes extends Command {
|
|
49
50
|
static description = 'Manage delivery notes';
|
|
@@ -65,7 +66,7 @@ export default class DeliveryNotes extends Command {
|
|
|
65
66
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
66
67
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
67
68
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
68
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
69
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
69
70
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
70
71
|
'line-item-id': Flags.string({ description: 'Path param: lineItemId' }),
|
|
71
72
|
'note-id': Flags.string({ description: 'Path param: noteId' }),
|
|
@@ -199,6 +200,17 @@ export default class DeliveryNotes extends Command {
|
|
|
199
200
|
}
|
|
200
201
|
}
|
|
201
202
|
}
|
|
203
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
204
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
205
|
+
for (const param of endpoint.specParams) {
|
|
206
|
+
const value = flags[param.flag];
|
|
207
|
+
if (value === undefined || value === '') {
|
|
208
|
+
if (param.required)
|
|
209
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
queryParams[param.name] = value;
|
|
213
|
+
}
|
|
202
214
|
let data;
|
|
203
215
|
if (endpoint.isMultipart) {
|
|
204
216
|
if (!flags.file)
|
|
@@ -219,6 +231,25 @@ export default class DeliveryNotes extends Command {
|
|
|
219
231
|
}
|
|
220
232
|
data = form;
|
|
221
233
|
}
|
|
234
|
+
else if (endpoint.bodyMediaType) {
|
|
235
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
236
|
+
// verbatim, under the content type the endpoint asks for.
|
|
237
|
+
if (flags.file) {
|
|
238
|
+
const fsModule = await import('node:fs');
|
|
239
|
+
try {
|
|
240
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
else if (flags.data) {
|
|
247
|
+
data = flags.data;
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
222
253
|
else if (endpoint.hasBody) {
|
|
223
254
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
224
255
|
// input file — skip the read-as-body branch.
|
|
@@ -314,7 +345,7 @@ export default class DeliveryNotes extends Command {
|
|
|
314
345
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
315
346
|
}
|
|
316
347
|
else if (data) {
|
|
317
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
348
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
318
349
|
}
|
|
319
350
|
process.stderr.write('\n');
|
|
320
351
|
}
|
|
@@ -328,6 +359,7 @@ export default class DeliveryNotes extends Command {
|
|
|
328
359
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
329
360
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
330
361
|
data,
|
|
362
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
331
363
|
contextName: flags.context,
|
|
332
364
|
binary: true,
|
|
333
365
|
});
|
|
@@ -353,6 +385,7 @@ export default class DeliveryNotes extends Command {
|
|
|
353
385
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
354
386
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
355
387
|
data,
|
|
388
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
356
389
|
contextName: flags.context,
|
|
357
390
|
binary: true,
|
|
358
391
|
});
|
|
@@ -373,6 +406,7 @@ export default class DeliveryNotes extends Command {
|
|
|
373
406
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
374
407
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
375
408
|
data,
|
|
409
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
376
410
|
contextName: flags.context,
|
|
377
411
|
acceptAny: true,
|
|
378
412
|
});
|
|
@@ -14,6 +14,7 @@ export default class DocumentChecksums extends Command {
|
|
|
14
14
|
data: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
15
|
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
16
|
set: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
percentage: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
18
|
page: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
19
|
limit: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
20
|
include: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|