@aderis-erp/cli 1.1.3 → 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/README.md +1 -0
- 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.d.ts +58 -0
- package/dist/commands/api-clients.js +1375 -0
- 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.d.ts +4 -0
- package/dist/commands/credit-card-accounts.js +64 -25
- package/dist/commands/currencies.d.ts +56 -0
- package/dist/commands/currencies.js +1361 -0
- package/dist/commands/custody-accounts.d.ts +56 -0
- package/dist/commands/custody-accounts.js +1369 -0
- package/dist/commands/{integration-identities.d.ts → dashboards.d.ts} +3 -3
- package/dist/commands/dashboards.js +1381 -0
- package/dist/commands/delivery-notes.js +69 -35
- package/dist/commands/document-checksums.d.ts +57 -0
- package/dist/commands/{integration-identities.js → document-checksums.js} +48 -21
- 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.d.ts +56 -0
- package/dist/commands/financial-instrument-types.js +1369 -0
- package/dist/commands/financial-instruments.d.ts +56 -0
- package/dist/commands/financial-instruments.js +1369 -0
- 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/{workflows.d.ts → kpis.d.ts} +1 -1
- package/dist/commands/kpis.js +1371 -0
- package/dist/commands/ledgers.js +83 -49
- package/dist/commands/my-account.js +54 -20
- package/dist/commands/notifications.d.ts +56 -0
- package/dist/commands/notifications.js +1360 -0
- 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.d.ts +56 -0
- package/dist/commands/settings.js +1361 -0
- 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 +50 -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 +82 -47
- package/dist/commands/webhooks.js +43 -9
- package/dist/commands/work-summary.d.ts +56 -0
- package/dist/commands/{workflows.js → work-summary.js} +49 -18
- package/dist/lib/auth/index.js +5 -0
- package/dist/lib/paths.d.ts +8 -0
- package/dist/lib/paths.js +16 -0
- package/dist/lib/production-guard.d.ts +42 -0
- package/dist/lib/production-guard.js +94 -0
- package/oclif.manifest.json +2142 -333
- 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,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/office-locations', summary: 'Get all office locations with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/office-locations', summary: 'Create a new office location', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
-
'get': { method: 'GET', path: '/v1/office-locations/{id}', summary: 'Get an office location by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'update': { method: 'PATCH', path: '/v1/office-locations/{id}', summary: 'Update an office location', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
-
'delete': { method: 'DELETE', path: '/v1/office-locations/{id}', summary: 'Delete an office location', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/office-locations', summary: 'Get all office locations with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/office-locations', summary: 'Create a new office location', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'get': { method: 'GET', path: '/v1/office-locations/{id}', summary: 'Get an office location by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'update': { method: 'PATCH', path: '/v1/office-locations/{id}', summary: 'Update an office location', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'delete': { method: 'DELETE', path: '/v1/office-locations/{id}', summary: 'Delete an office location', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
20
|
};
|
|
20
21
|
export default class OfficeLocations extends Command {
|
|
21
22
|
static description = 'Manage office locations';
|
|
@@ -37,7 +38,7 @@ export default class OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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,16 +12,16 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = ["ledger-mappings"];
|
|
12
13
|
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "list-ledger-mappings", "create-ledger-mappings", "get-ledger-mappings", "update-ledger-mappings", "delete-ledger-mappings"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'list': { method: 'GET', path: '/v1/payment-methods', summary: 'Get all payment methods with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/payment-methods', summary: 'Create a new payment method', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['alternativeBankAccountIds', 'alternativeCreditCardAccountIds'] },
|
|
16
|
-
'get': { method: 'GET', path: '/v1/payment-methods/{id}', summary: 'Get a payment method by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'update': { method: 'PATCH', path: '/v1/payment-methods/{id}', summary: 'Update a payment method', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['alternativeBankAccountIds', 'alternativeCreditCardAccountIds'] },
|
|
18
|
-
'delete': { method: 'DELETE', path: '/v1/payment-methods/{id}', summary: 'Delete a payment method', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
19
|
-
'list-ledger-mappings': { method: 'GET', path: '/v1/payment-methods/{paymentMethodId}/ledger-mappings', summary: 'List ledger mappings for a payment method', pathParams: ['paymentMethodId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
-
'create-ledger-mappings': { method: 'POST', path: '/v1/payment-methods/{paymentMethodId}/ledger-mappings', summary: 'Create payment method ledger mapping', pathParams: ['paymentMethodId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
21
|
-
'get-ledger-mappings': { method: 'GET', path: '/v1/payment-methods/{paymentMethodId}/ledger-mappings/{id}', summary: 'Get payment method ledger mapping by ID', pathParams: ['paymentMethodId', 'id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
22
|
-
'update-ledger-mappings': { method: 'PATCH', path: '/v1/payment-methods/{paymentMethodId}/ledger-mappings/{id}', summary: 'Update payment method ledger mapping', pathParams: ['paymentMethodId', 'id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
23
|
-
'delete-ledger-mappings': { method: 'DELETE', path: '/v1/payment-methods/{paymentMethodId}/ledger-mappings/{id}', summary: 'Delete payment method ledger mapping', pathParams: ['paymentMethodId', 'id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/payment-methods', summary: 'Get all payment methods with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/payment-methods', summary: 'Create a new payment method', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['alternativeBankAccountIds', 'alternativeCreditCardAccountIds'], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'get': { method: 'GET', path: '/v1/payment-methods/{id}', summary: 'Get a payment method by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'update': { method: 'PATCH', path: '/v1/payment-methods/{id}', summary: 'Update a payment method', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['alternativeBankAccountIds', 'alternativeCreditCardAccountIds'], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'delete': { method: 'DELETE', path: '/v1/payment-methods/{id}', summary: 'Delete a payment method', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
20
|
+
'list-ledger-mappings': { method: 'GET', path: '/v1/payment-methods/{paymentMethodId}/ledger-mappings', summary: 'List ledger mappings for a payment method', pathParams: ['paymentMethodId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
21
|
+
'create-ledger-mappings': { method: 'POST', path: '/v1/payment-methods/{paymentMethodId}/ledger-mappings', summary: 'Create payment method ledger mapping', pathParams: ['paymentMethodId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
22
|
+
'get-ledger-mappings': { method: 'GET', path: '/v1/payment-methods/{paymentMethodId}/ledger-mappings/{id}', summary: 'Get payment method ledger mapping by ID', pathParams: ['paymentMethodId', 'id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
23
|
+
'update-ledger-mappings': { method: 'PATCH', path: '/v1/payment-methods/{paymentMethodId}/ledger-mappings/{id}', summary: 'Update payment method ledger mapping', pathParams: ['paymentMethodId', 'id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
24
|
+
'delete-ledger-mappings': { method: 'DELETE', path: '/v1/payment-methods/{paymentMethodId}/ledger-mappings/{id}', summary: 'Delete payment method ledger mapping', pathParams: ['paymentMethodId', 'id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
24
25
|
};
|
|
25
26
|
export default class PaymentMethods extends Command {
|
|
26
27
|
static description = 'Manage payment methods';
|
|
@@ -42,7 +43,7 @@ export default class PaymentMethods extends Command {
|
|
|
42
43
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
43
44
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
44
45
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
45
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
46
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
46
47
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
47
48
|
'id': Flags.string({ description: 'Path param: id' }),
|
|
48
49
|
page: Flags.string({ description: 'Page number' }),
|
|
@@ -174,6 +175,17 @@ export default class PaymentMethods extends Command {
|
|
|
174
175
|
}
|
|
175
176
|
}
|
|
176
177
|
}
|
|
178
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
179
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
180
|
+
for (const param of endpoint.specParams) {
|
|
181
|
+
const value = flags[param.flag];
|
|
182
|
+
if (value === undefined || value === '') {
|
|
183
|
+
if (param.required)
|
|
184
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
queryParams[param.name] = value;
|
|
188
|
+
}
|
|
177
189
|
let data;
|
|
178
190
|
if (endpoint.isMultipart) {
|
|
179
191
|
if (!flags.file)
|
|
@@ -194,6 +206,25 @@ export default class PaymentMethods extends Command {
|
|
|
194
206
|
}
|
|
195
207
|
data = form;
|
|
196
208
|
}
|
|
209
|
+
else if (endpoint.bodyMediaType) {
|
|
210
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
211
|
+
// verbatim, under the content type the endpoint asks for.
|
|
212
|
+
if (flags.file) {
|
|
213
|
+
const fsModule = await import('node:fs');
|
|
214
|
+
try {
|
|
215
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else if (flags.data) {
|
|
222
|
+
data = flags.data;
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
197
228
|
else if (endpoint.hasBody) {
|
|
198
229
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
199
230
|
// input file — skip the read-as-body branch.
|
|
@@ -289,7 +320,7 @@ export default class PaymentMethods extends Command {
|
|
|
289
320
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
290
321
|
}
|
|
291
322
|
else if (data) {
|
|
292
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
323
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
293
324
|
}
|
|
294
325
|
process.stderr.write('\n');
|
|
295
326
|
}
|
|
@@ -303,6 +334,7 @@ export default class PaymentMethods extends Command {
|
|
|
303
334
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
304
335
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
305
336
|
data,
|
|
337
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
306
338
|
contextName: flags.context,
|
|
307
339
|
binary: true,
|
|
308
340
|
});
|
|
@@ -328,6 +360,7 @@ export default class PaymentMethods extends Command {
|
|
|
328
360
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
329
361
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
330
362
|
data,
|
|
363
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
331
364
|
contextName: flags.context,
|
|
332
365
|
binary: true,
|
|
333
366
|
});
|
|
@@ -348,6 +381,7 @@ export default class PaymentMethods extends Command {
|
|
|
348
381
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
349
382
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
350
383
|
data,
|
|
384
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
351
385
|
contextName: flags.context,
|
|
352
386
|
acceptAny: true,
|
|
353
387
|
});
|
|
@@ -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,24 +12,24 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
13
|
const SUBCOMMANDS = [...["open-submissions", "eligible-payments", "list", "create", "preview", "schedule", "status", "get", "update", "delete", "create-payments", "delete-payments", "send", "painfile", "beneficiary-check", "sync-status", "sync-status-xml", "sync-all"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'open-submissions': { method: 'GET', path: '/v1/payment-runs/bank-accounts/{bankAccountId}/open-submissions', summary: 'Get open submission IDs for a bank account', pathParams: ['bankAccountId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'eligible-payments': { method: 'GET', path: '/v1/payment-runs/eligible-payments/{bankAccountId}', summary: 'Show what a payment run from this bank account would pay', pathParams: ['bankAccountId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
-
'list': { method: 'GET', path: '/v1/payment-runs', summary: 'List all payment runs with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'create': { method: 'POST', path: '/v1/payment-runs', summary: 'Create a DRAFT payment run from payment IDs', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['paymentIds'] },
|
|
18
|
-
'preview': { method: 'POST', path: '/v1/payment-runs/preview/{bankAccountId}', summary: 'Preview eligible and filtered invoices for a payment run', pathParams: ['bankAccountId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['invoiceIds'] },
|
|
19
|
-
'schedule': { method: 'POST', path: '/v1/payment-runs/schedule/{bankAccountId}', summary: 'Auto-create DRAFT from eligible payments for a bank account', pathParams: ['bankAccountId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
-
'status': { method: 'GET', path: '/v1/payment-runs/status/{providerId}/{submissionId}', summary: 'Fetch live payment status from provider (read-only)', pathParams: ['providerId', 'submissionId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
21
|
-
'get': { method: 'GET', path: '/v1/payment-runs/{id}', summary: 'Get a payment run details by ID and show the currently persisted status.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
22
|
-
'update': { method: 'PATCH', path: '/v1/payment-runs/{id}', summary: 'Update a DRAFT payment run (mode, execution date)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
23
|
-
'delete': { method: 'DELETE', path: '/v1/payment-runs/{id}', summary: 'Delete a DRAFT payment run (releases payments)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
24
|
-
'create-payments': { method: 'POST', path: '/v1/payment-runs/{id}/payments', summary: 'Add payments to a DRAFT payment run', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['paymentIds'] },
|
|
25
|
-
'delete-payments': { method: 'DELETE', path: '/v1/payment-runs/{id}/payments/{paymentId}', summary: 'Remove an item from a DRAFT payment run', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
26
|
-
'send': { method: 'POST', path: '/v1/payment-runs/{id}/send', summary: 'Execute a DRAFT payment run (with dryRun support)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
27
|
-
'painfile': { method: 'GET', path: '/v1/payment-runs/{id}/painfile', summary: 'Download or preview PAIN XML for a payment run', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
28
|
-
'beneficiary-check': { method: 'GET', path: '/v1/payment-runs/{id}/beneficiary-check', summary: 'Check a payment run\'s beneficiary accounts without sending it', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
29
|
-
'sync-status': { method: 'POST', path: '/v1/payment-runs/update/status/{submissionId}', summary: 'Sync payment status for a specific submission', pathParams: ['submissionId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
30
|
-
'sync-status-xml': { method: 'POST', path: '/v1/payment-runs/update/status/{submissionId}/xml', summary: 'Sync payment status from pain.002 XML for a specific submission', pathParams: ['submissionId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
31
|
-
'sync-all': { method: 'POST', path: '/v1/payment-runs/status', summary: 'Sync payment status for all pending submissions', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'open-submissions': { method: 'GET', path: '/v1/payment-runs/bank-accounts/{bankAccountId}/open-submissions', summary: 'Get open submission IDs for a bank account', pathParams: ['bankAccountId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'eligible-payments': { method: 'GET', path: '/v1/payment-runs/eligible-payments/{bankAccountId}', summary: 'Show what a payment run from this bank account would pay', pathParams: ['bankAccountId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'list': { method: 'GET', path: '/v1/payment-runs', summary: 'List all payment runs with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'create': { method: 'POST', path: '/v1/payment-runs', summary: 'Create a DRAFT payment run from payment IDs', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['paymentIds'], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'preview': { method: 'POST', path: '/v1/payment-runs/preview/{bankAccountId}', summary: 'Preview eligible and filtered invoices for a payment run', pathParams: ['bankAccountId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['invoiceIds'], bodyMediaType: '', specParams: [] },
|
|
20
|
+
'schedule': { method: 'POST', path: '/v1/payment-runs/schedule/{bankAccountId}', summary: 'Auto-create DRAFT from eligible payments for a bank account', pathParams: ['bankAccountId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
21
|
+
'status': { method: 'GET', path: '/v1/payment-runs/status/{providerId}/{submissionId}', summary: 'Fetch live payment status from provider (read-only)', pathParams: ['providerId', 'submissionId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
22
|
+
'get': { method: 'GET', path: '/v1/payment-runs/{id}', summary: 'Get a payment run details by ID and show the currently persisted status.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
23
|
+
'update': { method: 'PATCH', path: '/v1/payment-runs/{id}', summary: 'Update a DRAFT payment run (mode, execution date)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
24
|
+
'delete': { method: 'DELETE', path: '/v1/payment-runs/{id}', summary: 'Delete a DRAFT payment run (releases payments)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
25
|
+
'create-payments': { method: 'POST', path: '/v1/payment-runs/{id}/payments', summary: 'Add payments to a DRAFT payment run', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['paymentIds'], bodyMediaType: '', specParams: [] },
|
|
26
|
+
'delete-payments': { method: 'DELETE', path: '/v1/payment-runs/{id}/payments/{paymentId}', summary: 'Remove an item from a DRAFT payment run', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
27
|
+
'send': { method: 'POST', path: '/v1/payment-runs/{id}/send', summary: 'Execute a DRAFT payment run (with dryRun support)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
28
|
+
'painfile': { method: 'GET', path: '/v1/payment-runs/{id}/painfile', summary: 'Download or preview PAIN XML for a payment run', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
29
|
+
'beneficiary-check': { method: 'GET', path: '/v1/payment-runs/{id}/beneficiary-check', summary: 'Check a payment run\'s beneficiary accounts without sending it', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
30
|
+
'sync-status': { method: 'POST', path: '/v1/payment-runs/update/status/{submissionId}', summary: 'Sync payment status for a specific submission', pathParams: ['submissionId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
31
|
+
'sync-status-xml': { method: 'POST', path: '/v1/payment-runs/update/status/{submissionId}/xml', summary: 'Sync payment status from pain.002 XML for a specific submission', pathParams: ['submissionId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
32
|
+
'sync-all': { method: 'POST', path: '/v1/payment-runs/status', summary: 'Sync payment status for all pending submissions', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
32
33
|
};
|
|
33
34
|
export default class PaymentRuns extends Command {
|
|
34
35
|
static description = 'Manage payment runs';
|
|
@@ -50,7 +51,7 @@ export default class PaymentRuns extends Command {
|
|
|
50
51
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
51
52
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
52
53
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
53
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
54
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
54
55
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
55
56
|
'submission-id': Flags.string({ description: 'Path param: submissionId' }),
|
|
56
57
|
'payment-id': Flags.string({ description: 'Path param: paymentId' }),
|
|
@@ -183,6 +184,17 @@ export default class PaymentRuns extends Command {
|
|
|
183
184
|
}
|
|
184
185
|
}
|
|
185
186
|
}
|
|
187
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
188
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
189
|
+
for (const param of endpoint.specParams) {
|
|
190
|
+
const value = flags[param.flag];
|
|
191
|
+
if (value === undefined || value === '') {
|
|
192
|
+
if (param.required)
|
|
193
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
queryParams[param.name] = value;
|
|
197
|
+
}
|
|
186
198
|
let data;
|
|
187
199
|
if (endpoint.isMultipart) {
|
|
188
200
|
if (!flags.file)
|
|
@@ -203,6 +215,25 @@ export default class PaymentRuns extends Command {
|
|
|
203
215
|
}
|
|
204
216
|
data = form;
|
|
205
217
|
}
|
|
218
|
+
else if (endpoint.bodyMediaType) {
|
|
219
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
220
|
+
// verbatim, under the content type the endpoint asks for.
|
|
221
|
+
if (flags.file) {
|
|
222
|
+
const fsModule = await import('node:fs');
|
|
223
|
+
try {
|
|
224
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
else if (flags.data) {
|
|
231
|
+
data = flags.data;
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
206
237
|
else if (endpoint.hasBody) {
|
|
207
238
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
208
239
|
// input file — skip the read-as-body branch.
|
|
@@ -298,7 +329,7 @@ export default class PaymentRuns extends Command {
|
|
|
298
329
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
299
330
|
}
|
|
300
331
|
else if (data) {
|
|
301
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
332
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
302
333
|
}
|
|
303
334
|
process.stderr.write('\n');
|
|
304
335
|
}
|
|
@@ -312,6 +343,7 @@ export default class PaymentRuns extends Command {
|
|
|
312
343
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
313
344
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
314
345
|
data,
|
|
346
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
315
347
|
contextName: flags.context,
|
|
316
348
|
binary: true,
|
|
317
349
|
});
|
|
@@ -337,6 +369,7 @@ export default class PaymentRuns extends Command {
|
|
|
337
369
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
338
370
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
339
371
|
data,
|
|
372
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
340
373
|
contextName: flags.context,
|
|
341
374
|
binary: true,
|
|
342
375
|
});
|
|
@@ -357,6 +390,7 @@ export default class PaymentRuns extends Command {
|
|
|
357
390
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
358
391
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
359
392
|
data,
|
|
393
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
360
394
|
contextName: flags.context,
|
|
361
395
|
acceptAny: true,
|
|
362
396
|
});
|
|
@@ -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,12 +12,12 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
13
|
const SUBCOMMANDS = [...["list", "create", "default", "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/payment-terms', summary: 'Get all payment terms with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/payment-terms', summary: 'Create a new payment term', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts'] },
|
|
16
|
-
'default': { method: 'GET', path: '/v1/payment-terms/default', summary: 'Get the default payment term for the tenant', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'get': { method: 'GET', path: '/v1/payment-terms/{id}', summary: 'Get a payment term by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
-
'update': { method: 'PATCH', path: '/v1/payment-terms/{id}', summary: 'Update a payment term', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts'] },
|
|
19
|
-
'delete': { method: 'DELETE', path: '/v1/payment-terms/{id}', summary: 'Delete a payment term', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/payment-terms', summary: 'Get all payment terms with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/payment-terms', summary: 'Create a new payment term', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts'], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'default': { method: 'GET', path: '/v1/payment-terms/default', summary: 'Get the default payment term for the tenant', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'get': { method: 'GET', path: '/v1/payment-terms/{id}', summary: 'Get a payment term by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'update': { method: 'PATCH', path: '/v1/payment-terms/{id}', summary: 'Update a payment term', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts'], bodyMediaType: '', specParams: [] },
|
|
20
|
+
'delete': { method: 'DELETE', path: '/v1/payment-terms/{id}', summary: 'Delete a payment term', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
20
21
|
};
|
|
21
22
|
export default class PaymentTerms extends Command {
|
|
22
23
|
static description = 'Manage payment terms';
|
|
@@ -38,7 +39,7 @@ export default class PaymentTerms extends Command {
|
|
|
38
39
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
39
40
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
40
41
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
41
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
42
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
42
43
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
43
44
|
page: Flags.string({ description: 'Page number' }),
|
|
44
45
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -169,6 +170,17 @@ export default class PaymentTerms extends Command {
|
|
|
169
170
|
}
|
|
170
171
|
}
|
|
171
172
|
}
|
|
173
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
174
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
175
|
+
for (const param of endpoint.specParams) {
|
|
176
|
+
const value = flags[param.flag];
|
|
177
|
+
if (value === undefined || value === '') {
|
|
178
|
+
if (param.required)
|
|
179
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
queryParams[param.name] = value;
|
|
183
|
+
}
|
|
172
184
|
let data;
|
|
173
185
|
if (endpoint.isMultipart) {
|
|
174
186
|
if (!flags.file)
|
|
@@ -189,6 +201,25 @@ export default class PaymentTerms extends Command {
|
|
|
189
201
|
}
|
|
190
202
|
data = form;
|
|
191
203
|
}
|
|
204
|
+
else if (endpoint.bodyMediaType) {
|
|
205
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
206
|
+
// verbatim, under the content type the endpoint asks for.
|
|
207
|
+
if (flags.file) {
|
|
208
|
+
const fsModule = await import('node:fs');
|
|
209
|
+
try {
|
|
210
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else if (flags.data) {
|
|
217
|
+
data = flags.data;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
192
223
|
else if (endpoint.hasBody) {
|
|
193
224
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
194
225
|
// input file — skip the read-as-body branch.
|
|
@@ -284,7 +315,7 @@ export default class PaymentTerms extends Command {
|
|
|
284
315
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
285
316
|
}
|
|
286
317
|
else if (data) {
|
|
287
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
318
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
288
319
|
}
|
|
289
320
|
process.stderr.write('\n');
|
|
290
321
|
}
|
|
@@ -298,6 +329,7 @@ export default class PaymentTerms extends Command {
|
|
|
298
329
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
299
330
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
300
331
|
data,
|
|
332
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
301
333
|
contextName: flags.context,
|
|
302
334
|
binary: true,
|
|
303
335
|
});
|
|
@@ -323,6 +355,7 @@ export default class PaymentTerms extends Command {
|
|
|
323
355
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
324
356
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
325
357
|
data,
|
|
358
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
326
359
|
contextName: flags.context,
|
|
327
360
|
binary: true,
|
|
328
361
|
});
|
|
@@ -343,6 +376,7 @@ export default class PaymentTerms extends Command {
|
|
|
343
376
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
344
377
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
345
378
|
data,
|
|
379
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
346
380
|
contextName: flags.context,
|
|
347
381
|
acceptAny: true,
|
|
348
382
|
});
|
|
@@ -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,13 +12,13 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
13
|
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "approve", "cancel"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'list': { method: 'GET', path: '/v1/payments', summary: 'List payments', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/payments', summary: 'Create a standalone payment', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
-
'get': { method: 'GET', path: '/v1/payments/{paymentId}', summary: 'Get a payment by ID', pathParams: ['paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'update': { method: 'PATCH', path: '/v1/payments/{paymentId}', summary: 'Update a payment', pathParams: ['paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
-
'delete': { method: 'DELETE', path: '/v1/payments/{paymentId}', summary: 'Delete a payment', pathParams: ['paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
19
|
-
'approve': { method: 'POST', path: '/v1/payments/{paymentId}/approve', summary: 'Approve a payment (OPEN → APPROVED)', pathParams: ['paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
-
'cancel': { method: 'POST', path: '/v1/payments/{paymentId}/cancel', summary: 'Cancel a sent payment (SENT → ERROR)', pathParams: ['paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/payments', summary: 'List payments', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/payments', summary: 'Create a standalone payment', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'get': { method: 'GET', path: '/v1/payments/{paymentId}', summary: 'Get a payment by ID', pathParams: ['paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'update': { method: 'PATCH', path: '/v1/payments/{paymentId}', summary: 'Update a payment', pathParams: ['paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'delete': { method: 'DELETE', path: '/v1/payments/{paymentId}', summary: 'Delete a payment', pathParams: ['paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
20
|
+
'approve': { method: 'POST', path: '/v1/payments/{paymentId}/approve', summary: 'Approve a payment (OPEN → APPROVED)', pathParams: ['paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
21
|
+
'cancel': { method: 'POST', path: '/v1/payments/{paymentId}/cancel', summary: 'Cancel a sent payment (SENT → ERROR)', pathParams: ['paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
21
22
|
};
|
|
22
23
|
export default class Payments extends Command {
|
|
23
24
|
static description = 'Manage payments';
|
|
@@ -39,7 +40,7 @@ export default class Payments extends Command {
|
|
|
39
40
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
40
41
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
41
42
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
42
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
43
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
43
44
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
44
45
|
page: Flags.string({ description: 'Page number' }),
|
|
45
46
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -170,6 +171,17 @@ export default class Payments extends Command {
|
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
173
|
}
|
|
174
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
175
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
176
|
+
for (const param of endpoint.specParams) {
|
|
177
|
+
const value = flags[param.flag];
|
|
178
|
+
if (value === undefined || value === '') {
|
|
179
|
+
if (param.required)
|
|
180
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
queryParams[param.name] = value;
|
|
184
|
+
}
|
|
173
185
|
let data;
|
|
174
186
|
if (endpoint.isMultipart) {
|
|
175
187
|
if (!flags.file)
|
|
@@ -190,6 +202,25 @@ export default class Payments extends Command {
|
|
|
190
202
|
}
|
|
191
203
|
data = form;
|
|
192
204
|
}
|
|
205
|
+
else if (endpoint.bodyMediaType) {
|
|
206
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
207
|
+
// verbatim, under the content type the endpoint asks for.
|
|
208
|
+
if (flags.file) {
|
|
209
|
+
const fsModule = await import('node:fs');
|
|
210
|
+
try {
|
|
211
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else if (flags.data) {
|
|
218
|
+
data = flags.data;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
193
224
|
else if (endpoint.hasBody) {
|
|
194
225
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
195
226
|
// input file — skip the read-as-body branch.
|
|
@@ -285,7 +316,7 @@ export default class Payments extends Command {
|
|
|
285
316
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
286
317
|
}
|
|
287
318
|
else if (data) {
|
|
288
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
319
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
289
320
|
}
|
|
290
321
|
process.stderr.write('\n');
|
|
291
322
|
}
|
|
@@ -299,6 +330,7 @@ export default class Payments extends Command {
|
|
|
299
330
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
300
331
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
301
332
|
data,
|
|
333
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
302
334
|
contextName: flags.context,
|
|
303
335
|
binary: true,
|
|
304
336
|
});
|
|
@@ -324,6 +356,7 @@ export default class Payments extends Command {
|
|
|
324
356
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
325
357
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
326
358
|
data,
|
|
359
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
327
360
|
contextName: flags.context,
|
|
328
361
|
binary: true,
|
|
329
362
|
});
|
|
@@ -344,6 +377,7 @@ export default class Payments extends Command {
|
|
|
344
377
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
345
378
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
346
379
|
data,
|
|
380
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
347
381
|
contextName: flags.context,
|
|
348
382
|
acceptAny: true,
|
|
349
383
|
});
|