@aderis-erp/cli 1.0.15 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -8
- package/dist/commands/access-groups.d.ts +0 -1
- package/dist/commands/access-groups.js +54 -78
- package/dist/commands/account-transaction-allocations.d.ts +0 -1
- package/dist/commands/account-transaction-allocations.js +51 -75
- package/dist/commands/account-transactions.d.ts +0 -1
- package/dist/commands/account-transactions.js +80 -94
- package/dist/commands/accounts.d.ts +0 -1
- package/dist/commands/accounts.js +59 -83
- package/dist/commands/agent-platform.d.ts +0 -1
- package/dist/commands/agent-platform.js +58 -82
- package/dist/commands/agent-runs.d.ts +39 -0
- package/dist/commands/agent-runs.js +1155 -0
- package/dist/commands/analytics.d.ts +0 -1
- package/dist/commands/analytics.js +52 -76
- package/dist/commands/{text-templates.d.ts → api-keys.d.ts} +1 -2
- package/dist/commands/api-keys.js +1150 -0
- package/dist/commands/async-jobs.d.ts +39 -0
- package/dist/commands/async-jobs.js +1158 -0
- package/dist/commands/audit-log.d.ts +0 -1
- package/dist/commands/audit-log.js +51 -75
- package/dist/commands/auth/login.d.ts +9 -0
- package/dist/commands/auth/login.js +73 -20
- package/dist/commands/auth/logout.d.ts +4 -0
- package/dist/commands/auth/logout.js +4 -0
- package/dist/commands/auth/status.js +26 -17
- package/dist/commands/auth/tenant.js +3 -6
- package/dist/commands/auth.d.ts +0 -1
- package/dist/commands/auth.js +5 -34
- package/dist/commands/bank-accounts.d.ts +0 -1
- package/dist/commands/bank-accounts.js +68 -91
- package/dist/commands/bank-integrations.d.ts +1 -1
- package/dist/commands/bank-integrations.js +71 -92
- package/dist/commands/business-partners.d.ts +1 -1
- package/dist/commands/business-partners.js +84 -100
- package/dist/commands/chat.d.ts +0 -1
- package/dist/commands/chat.js +57 -80
- package/dist/commands/config/add.d.ts +3 -0
- package/dist/commands/config/add.js +34 -16
- package/dist/commands/config/current.js +3 -0
- package/dist/commands/config/list.js +3 -0
- package/dist/commands/credit-card-accounts.d.ts +1 -1
- package/dist/commands/credit-card-accounts.js +72 -90
- package/dist/commands/delivery-notes.d.ts +0 -1
- package/dist/commands/delivery-notes.js +83 -105
- package/dist/commands/dunning-configs.d.ts +0 -1
- package/dist/commands/dunning-configs.js +54 -78
- package/dist/commands/dunning-reminders.d.ts +0 -1
- package/dist/commands/dunning-reminders.js +57 -81
- package/dist/commands/dunning-runs.d.ts +0 -1
- package/dist/commands/dunning-runs.js +50 -74
- package/dist/commands/email-transactions.d.ts +0 -1
- package/dist/commands/email-transactions.js +52 -76
- package/dist/commands/employees.d.ts +0 -1
- package/dist/commands/employees.js +81 -105
- package/dist/commands/entity-configurations.d.ts +0 -1
- package/dist/commands/entity-configurations.js +52 -76
- package/dist/commands/event-processor-mappings.d.ts +0 -1
- package/dist/commands/event-processor-mappings.js +54 -78
- package/dist/commands/files.d.ts +0 -1
- package/dist/commands/files.js +61 -85
- package/dist/commands/health.d.ts +0 -1
- package/dist/commands/health.js +54 -78
- package/dist/commands/imports.d.ts +39 -0
- package/dist/commands/imports.js +1151 -0
- package/dist/commands/inbox.d.ts +0 -1
- package/dist/commands/inbox.js +57 -80
- package/dist/commands/integration-identities.d.ts +40 -0
- package/dist/commands/integration-identities.js +1151 -0
- package/dist/commands/integrations.d.ts +0 -1
- package/dist/commands/integrations.js +52 -76
- package/dist/commands/item-categories.d.ts +0 -1
- package/dist/commands/item-categories.js +54 -78
- package/dist/commands/items.d.ts +0 -1
- package/dist/commands/items.js +54 -78
- package/dist/commands/journal-lines.d.ts +0 -1
- package/dist/commands/journal-lines.js +50 -74
- package/dist/commands/journals.d.ts +1 -1
- package/dist/commands/journals.js +75 -96
- package/dist/commands/ledgers.d.ts +3 -2
- package/dist/commands/ledgers.js +100 -95
- package/dist/commands/my-account.d.ts +39 -0
- package/dist/commands/my-account.js +1160 -0
- package/dist/commands/office-locations.d.ts +0 -1
- package/dist/commands/office-locations.js +54 -78
- package/dist/commands/payment-methods.d.ts +0 -1
- package/dist/commands/payment-methods.js +59 -83
- package/dist/commands/payment-runs.d.ts +0 -1
- package/dist/commands/payment-runs.js +66 -90
- package/dist/commands/payment-terms.d.ts +0 -1
- package/dist/commands/payment-terms.js +55 -79
- package/dist/commands/payments.d.ts +0 -1
- package/dist/commands/payments.js +56 -80
- package/dist/commands/payroll-runs.d.ts +43 -0
- package/dist/commands/payroll-runs.js +1188 -0
- package/dist/commands/permissions.d.ts +39 -0
- package/dist/commands/permissions.js +1146 -0
- package/dist/commands/price-lists.d.ts +0 -1
- package/dist/commands/price-lists.js +59 -83
- package/dist/commands/processor-mappings.d.ts +0 -1
- package/dist/commands/processor-mappings.js +53 -77
- package/dist/commands/processors.d.ts +0 -1
- package/dist/commands/processors.js +60 -85
- package/dist/commands/purchase-order-types.d.ts +0 -1
- package/dist/commands/purchase-order-types.js +54 -78
- package/dist/commands/purchase-orders.d.ts +0 -1
- package/dist/commands/purchase-orders.js +74 -97
- package/dist/commands/reports.d.ts +0 -1
- package/dist/commands/reports.js +54 -78
- package/dist/commands/roles.d.ts +39 -0
- package/dist/commands/{text-templates.js → roles.js} +64 -90
- package/dist/commands/salary-types.d.ts +0 -1
- package/dist/commands/salary-types.js +54 -78
- package/dist/commands/sales-invoices.d.ts +0 -1
- package/dist/commands/sales-invoices.js +90 -110
- package/dist/commands/sales-orders.d.ts +0 -1
- package/dist/commands/sales-orders.js +83 -105
- package/dist/commands/saved-views.d.ts +0 -1
- package/dist/commands/saved-views.js +52 -76
- package/dist/commands/scheduled-processor-mappings.d.ts +0 -1
- package/dist/commands/scheduled-processor-mappings.js +54 -78
- package/dist/commands/schedules.d.ts +0 -1
- package/dist/commands/schedules.js +58 -82
- package/dist/commands/switch.js +4 -4
- package/dist/commands/tag-configurations.d.ts +0 -1
- package/dist/commands/tag-configurations.js +56 -79
- package/dist/commands/tags.d.ts +0 -1
- package/dist/commands/tags.js +54 -78
- package/dist/commands/tax-codes.d.ts +0 -1
- package/dist/commands/tax-codes.js +57 -81
- package/dist/commands/tax-matrix.d.ts +0 -1
- package/dist/commands/tax-matrix.js +54 -78
- package/dist/commands/templates.d.ts +39 -0
- package/dist/commands/templates.js +1157 -0
- package/dist/commands/tenants.d.ts +0 -1
- package/dist/commands/tenants.js +58 -81
- package/dist/commands/tools.d.ts +0 -1
- package/dist/commands/tools.js +51 -75
- package/dist/commands/users.d.ts +42 -0
- package/dist/commands/users.js +1174 -0
- package/dist/commands/vat-declarations.d.ts +3 -1
- package/dist/commands/vat-declarations.js +78 -93
- package/dist/commands/vendor-invoices.d.ts +0 -1
- package/dist/commands/vendor-invoices.js +87 -110
- package/dist/commands/webhooks.d.ts +0 -1
- package/dist/commands/webhooks.js +56 -80
- package/dist/commands/workflows.d.ts +0 -1
- package/dist/commands/workflows.js +52 -76
- package/dist/lib/api-client.d.ts +6 -0
- package/dist/lib/api-client.js +16 -6
- package/dist/lib/auth/constants.d.ts +11 -0
- package/dist/lib/auth/constants.js +11 -0
- package/dist/lib/auth/errors.d.ts +17 -0
- package/dist/lib/auth/errors.js +22 -0
- package/dist/lib/auth/flow.d.ts +27 -0
- package/dist/lib/auth/flow.js +163 -0
- package/dist/lib/auth/guards.d.ts +5 -0
- package/dist/lib/auth/guards.js +14 -0
- package/dist/lib/auth/http.d.ts +16 -0
- package/dist/lib/auth/http.js +49 -0
- package/dist/lib/auth/index.d.ts +49 -0
- package/dist/lib/auth/index.js +228 -0
- package/dist/lib/auth/jwks.d.ts +19 -0
- package/dist/lib/auth/jwks.js +112 -0
- package/dist/lib/auth/keycloak-provider.d.ts +91 -0
- package/dist/lib/auth/keycloak-provider.js +244 -0
- package/dist/lib/auth/pages.d.ts +25 -0
- package/dist/lib/auth/pages.js +123 -0
- package/dist/lib/auth/pkce.d.ts +11 -0
- package/dist/lib/auth/pkce.js +17 -0
- package/dist/lib/auth/tokens.d.ts +59 -0
- package/dist/lib/auth/tokens.js +105 -0
- package/dist/lib/auth/types.d.ts +61 -0
- package/dist/lib/auth/types.js +1 -0
- package/dist/lib/auth/validated-url.d.ts +6 -0
- package/dist/lib/auth/validated-url.js +53 -0
- package/dist/lib/config.d.ts +13 -3
- package/dist/lib/config.js +24 -19
- package/oclif.manifest.json +2447 -641
- package/package.json +5 -4
- package/dist/lib/auth.d.ts +0 -43
- package/dist/lib/auth.js +0 -610
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
|
-
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
2
|
+
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
4
|
// Add 'import' action if both create and update exist
|
|
5
5
|
const HAS_IMPORT = true;
|
|
@@ -8,23 +8,23 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
9
|
const SUBCOMMANDS = [...["open-submissions", "eligible-payments", "list", "create", "preview", "schedule", "status", "get", "update", "delete", "create-payments", "delete-payments", "send", "painfile", "sync-status", "sync-status-xml", "sync-all"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'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, bodyArrayFields: [] },
|
|
12
|
-
'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, bodyArrayFields: [] },
|
|
13
|
-
'list': { method: 'GET', path: '/v1/payment-runs', summary: 'List all payment runs with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'create': { method: 'POST', path: '/v1/payment-runs', summary: 'Create a DRAFT payment run from payment IDs', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['paymentIds'] },
|
|
15
|
-
'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, bodyArrayFields: ['invoiceIds'] },
|
|
16
|
-
'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, bodyArrayFields: [] },
|
|
17
|
-
'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, bodyArrayFields: [] },
|
|
18
|
-
'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, bodyArrayFields: [] },
|
|
19
|
-
'update': { method: 'PATCH', path: '/v1/payment-runs/{id}', summary: 'Update a DRAFT payment run (mode)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
|
-
'delete': { method: 'DELETE', path: '/v1/payment-runs/{id}', summary: 'Delete a DRAFT payment run (releases payments)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
21
|
-
'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, bodyArrayFields: ['paymentIds'] },
|
|
22
|
-
'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, bodyArrayFields: [] },
|
|
23
|
-
'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, bodyArrayFields: [] },
|
|
24
|
-
'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, bodyArrayFields: [] },
|
|
25
|
-
'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, bodyArrayFields: [] },
|
|
26
|
-
'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, bodyArrayFields: [] },
|
|
27
|
-
'sync-all': { method: 'POST', path: '/v1/payment-runs/status', summary: 'Sync payment status for all pending submissions', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
11
|
+
'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: [] },
|
|
12
|
+
'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: [] },
|
|
13
|
+
'list': { method: 'GET', path: '/v1/payment-runs', summary: 'List all payment runs with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
14
|
+
'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'] },
|
|
15
|
+
'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'] },
|
|
16
|
+
'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: [] },
|
|
17
|
+
'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: [] },
|
|
18
|
+
'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: [] },
|
|
19
|
+
'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: [] },
|
|
20
|
+
'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: [] },
|
|
21
|
+
'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'] },
|
|
22
|
+
'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: [] },
|
|
23
|
+
'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: [] },
|
|
24
|
+
'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: [] },
|
|
25
|
+
'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: [] },
|
|
26
|
+
'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: [] },
|
|
27
|
+
'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: [] },
|
|
28
28
|
};
|
|
29
29
|
export default class PaymentRuns extends Command {
|
|
30
30
|
static description = 'Manage payment runs';
|
|
@@ -57,7 +57,7 @@ export default class PaymentRuns extends Command {
|
|
|
57
57
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
58
58
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
59
59
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
60
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
60
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
61
61
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
62
62
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
63
63
|
};
|
|
@@ -129,6 +129,8 @@ export default class PaymentRuns extends Command {
|
|
|
129
129
|
queryParams.search = flags.search;
|
|
130
130
|
if (flags['sort-by'])
|
|
131
131
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
132
|
+
if (flags.fields)
|
|
133
|
+
queryParams.fields = flags.fields;
|
|
132
134
|
if (flags.query) {
|
|
133
135
|
try {
|
|
134
136
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -276,10 +278,11 @@ export default class PaymentRuns extends Command {
|
|
|
276
278
|
process.stderr.write('\n');
|
|
277
279
|
}
|
|
278
280
|
try {
|
|
279
|
-
// Binary response:
|
|
281
|
+
// Binary response: to -f / --file, or to stdout when it is redirected.
|
|
280
282
|
if (endpoint.isBinaryResponse) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
+
const toStdout = !flags.file;
|
|
284
|
+
if (toStdout && process.stdout.isTTY) {
|
|
285
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx), or redirect stdout to a file.`);
|
|
283
286
|
}
|
|
284
287
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
285
288
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
@@ -287,24 +290,55 @@ export default class PaymentRuns extends Command {
|
|
|
287
290
|
contextName: flags.context,
|
|
288
291
|
binary: true,
|
|
289
292
|
});
|
|
290
|
-
const fsModule = await import('node:fs');
|
|
291
293
|
const binData = binResponse.data;
|
|
292
294
|
const buffer = Buffer.isBuffer(binData)
|
|
293
295
|
? binData
|
|
294
296
|
: Buffer.from(binData);
|
|
297
|
+
if (toStdout) {
|
|
298
|
+
// Nothing else may reach stdout here — it is the file.
|
|
299
|
+
process.stdout.write(buffer);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const fsModule = await import('node:fs');
|
|
295
303
|
fsModule.writeFileSync(flags.file, buffer);
|
|
296
304
|
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
297
305
|
return;
|
|
298
306
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
307
|
+
let response;
|
|
308
|
+
if (endpoint.hasBinaryVariant) {
|
|
309
|
+
// This route answers JSON or a file depending on the request body, so the
|
|
310
|
+
// bytes are read first and the response's own content-type decides. Asking
|
|
311
|
+
// for text here would corrupt an xlsx/pdf before anything could inspect it.
|
|
312
|
+
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
313
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
314
|
+
data,
|
|
315
|
+
contextName: flags.context,
|
|
316
|
+
binary: true,
|
|
317
|
+
});
|
|
318
|
+
const rawType = String(raw.headers['content-type'] || '').toLowerCase();
|
|
319
|
+
const rawData = raw.data;
|
|
320
|
+
const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
|
|
321
|
+
if (!isTextualMediaType(rawType)) {
|
|
322
|
+
if (process.stdout.isTTY) {
|
|
323
|
+
this.error(`The API returned ${rawType || 'binary data'}. Redirect stdout to a file, e.g. ... > report.xlsx`);
|
|
324
|
+
}
|
|
325
|
+
// Nothing else may reach stdout here — it is the file.
|
|
326
|
+
process.stdout.write(buffer);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
response = { data: buffer.toString('utf8'), headers: raw.headers, status: raw.status };
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
response = await apiRequest(endpoint.method, apiPath, {
|
|
333
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
334
|
+
data,
|
|
335
|
+
contextName: flags.context,
|
|
336
|
+
acceptAny: true,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
305
339
|
// Check content-type for non-JSON responses (XML, text, etc.)
|
|
306
340
|
const contentType = (response.headers['content-type'] || '').toLowerCase();
|
|
307
|
-
if (contentType
|
|
341
|
+
if (isTextualMediaType(contentType) && !contentType.startsWith('application/json')) {
|
|
308
342
|
// Output raw content for XML/text responses (CSV, plain, HTML, etc.)
|
|
309
343
|
this.log(String(response.data));
|
|
310
344
|
return;
|
|
@@ -331,11 +365,6 @@ export default class PaymentRuns extends Command {
|
|
|
331
365
|
output = obj[arrayKeys[0]];
|
|
332
366
|
}
|
|
333
367
|
}
|
|
334
|
-
// Filter fields if --fields flag is provided
|
|
335
|
-
if (flags.fields) {
|
|
336
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
337
|
-
output = this.filterFields(output, fieldList);
|
|
338
|
-
}
|
|
339
368
|
// Sanitize output if requested
|
|
340
369
|
if (flags.sanitize) {
|
|
341
370
|
output = this.sanitizeOutput(output);
|
|
@@ -590,6 +619,8 @@ export default class PaymentRuns extends Command {
|
|
|
590
619
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
591
620
|
if (flags.include)
|
|
592
621
|
baseParams.include = flags.include;
|
|
622
|
+
if (flags.fields)
|
|
623
|
+
baseParams.fields = flags.fields;
|
|
593
624
|
if (flags.query) {
|
|
594
625
|
try {
|
|
595
626
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -644,12 +675,7 @@ export default class PaymentRuns extends Command {
|
|
|
644
675
|
}
|
|
645
676
|
}
|
|
646
677
|
process.stderr.write(' done\n');
|
|
647
|
-
// Filter fields if specified
|
|
648
678
|
let output = allItems;
|
|
649
|
-
if (flags.fields) {
|
|
650
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
651
|
-
output = this.filterFields(allItems, fieldList);
|
|
652
|
-
}
|
|
653
679
|
// Sanitize output if requested
|
|
654
680
|
if (flags.sanitize) {
|
|
655
681
|
output = this.sanitizeOutput(output);
|
|
@@ -978,56 +1004,6 @@ export default class PaymentRuns extends Command {
|
|
|
978
1004
|
return `\$${op}`;
|
|
979
1005
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
980
1006
|
}
|
|
981
|
-
filterFields(data, fields) {
|
|
982
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
983
|
-
const pick = (obj, keys) => {
|
|
984
|
-
// Group keys by their first segment so sibling sub-paths
|
|
985
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
986
|
-
// and don't clobber each other on merge.
|
|
987
|
-
const directKeys = new Set();
|
|
988
|
-
const nestedKeys = new Map();
|
|
989
|
-
for (const key of keys) {
|
|
990
|
-
if (key.split('.').some(isUnsafeKey))
|
|
991
|
-
continue;
|
|
992
|
-
if (key.includes('.')) {
|
|
993
|
-
const [first, ...rest] = key.split('.');
|
|
994
|
-
if (isUnsafeKey(first))
|
|
995
|
-
continue;
|
|
996
|
-
const list = nestedKeys.get(first);
|
|
997
|
-
if (list) {
|
|
998
|
-
list.push(rest.join('.'));
|
|
999
|
-
}
|
|
1000
|
-
else {
|
|
1001
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
else {
|
|
1005
|
-
directKeys.add(key);
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
const result = {};
|
|
1009
|
-
for (const key of directKeys) {
|
|
1010
|
-
if (key in obj)
|
|
1011
|
-
result[key] = obj[key];
|
|
1012
|
-
}
|
|
1013
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
1014
|
-
const val = obj[first];
|
|
1015
|
-
if (!val || typeof val !== 'object')
|
|
1016
|
-
continue;
|
|
1017
|
-
if (Array.isArray(val)) {
|
|
1018
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1019
|
-
}
|
|
1020
|
-
else {
|
|
1021
|
-
result[first] = pick(val, restKeys);
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
return result;
|
|
1025
|
-
};
|
|
1026
|
-
if (Array.isArray(data)) {
|
|
1027
|
-
return data.map(item => pick(item, fields));
|
|
1028
|
-
}
|
|
1029
|
-
return pick(data, fields);
|
|
1030
|
-
}
|
|
1031
1007
|
sanitizeOutput(data) {
|
|
1032
1008
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1033
1009
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
|
-
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
2
|
+
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
4
|
// Add 'import' action if both create and update exist
|
|
5
5
|
const HAS_IMPORT = true;
|
|
@@ -8,12 +8,12 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
9
|
const SUBCOMMANDS = [...["list", "create", "default", "get", "update", "delete"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list': { method: 'GET', path: '/v1/payment-terms', summary: 'Get all payment terms with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/payment-terms', summary: 'Create a new payment term', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts'] },
|
|
13
|
-
'default': { method: 'GET', path: '/v1/payment-terms/default', summary: 'Get the default payment term for the tenant', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'get': { method: 'GET', path: '/v1/payment-terms/{id}', summary: 'Get a payment term by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
-
'update': { method: 'PATCH', path: '/v1/payment-terms/{id}', summary: 'Update a payment term', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
|
-
'delete': { method: 'DELETE', path: '/v1/payment-terms/{id}', summary: 'Delete a payment term', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/payment-terms', summary: 'Get all payment terms with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/payment-terms', summary: 'Create a new payment term', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts'] },
|
|
13
|
+
'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: [] },
|
|
14
|
+
'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: [] },
|
|
15
|
+
'update': { method: 'PATCH', path: '/v1/payment-terms/{id}', summary: 'Update a payment term', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
+
'delete': { method: 'DELETE', path: '/v1/payment-terms/{id}', summary: 'Delete a payment term', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
17
|
};
|
|
18
18
|
export default class PaymentTerms extends Command {
|
|
19
19
|
static description = 'Manage payment terms';
|
|
@@ -44,7 +44,7 @@ export default class PaymentTerms extends Command {
|
|
|
44
44
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
45
45
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
46
46
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
47
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
47
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
48
48
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
49
49
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
50
50
|
};
|
|
@@ -116,6 +116,8 @@ export default class PaymentTerms extends Command {
|
|
|
116
116
|
queryParams.search = flags.search;
|
|
117
117
|
if (flags['sort-by'])
|
|
118
118
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
119
|
+
if (flags.fields)
|
|
120
|
+
queryParams.fields = flags.fields;
|
|
119
121
|
if (flags.query) {
|
|
120
122
|
try {
|
|
121
123
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -263,10 +265,11 @@ export default class PaymentTerms extends Command {
|
|
|
263
265
|
process.stderr.write('\n');
|
|
264
266
|
}
|
|
265
267
|
try {
|
|
266
|
-
// Binary response:
|
|
268
|
+
// Binary response: to -f / --file, or to stdout when it is redirected.
|
|
267
269
|
if (endpoint.isBinaryResponse) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
+
const toStdout = !flags.file;
|
|
271
|
+
if (toStdout && process.stdout.isTTY) {
|
|
272
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx), or redirect stdout to a file.`);
|
|
270
273
|
}
|
|
271
274
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
272
275
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
@@ -274,24 +277,55 @@ export default class PaymentTerms extends Command {
|
|
|
274
277
|
contextName: flags.context,
|
|
275
278
|
binary: true,
|
|
276
279
|
});
|
|
277
|
-
const fsModule = await import('node:fs');
|
|
278
280
|
const binData = binResponse.data;
|
|
279
281
|
const buffer = Buffer.isBuffer(binData)
|
|
280
282
|
? binData
|
|
281
283
|
: Buffer.from(binData);
|
|
284
|
+
if (toStdout) {
|
|
285
|
+
// Nothing else may reach stdout here — it is the file.
|
|
286
|
+
process.stdout.write(buffer);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const fsModule = await import('node:fs');
|
|
282
290
|
fsModule.writeFileSync(flags.file, buffer);
|
|
283
291
|
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
284
292
|
return;
|
|
285
293
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
294
|
+
let response;
|
|
295
|
+
if (endpoint.hasBinaryVariant) {
|
|
296
|
+
// This route answers JSON or a file depending on the request body, so the
|
|
297
|
+
// bytes are read first and the response's own content-type decides. Asking
|
|
298
|
+
// for text here would corrupt an xlsx/pdf before anything could inspect it.
|
|
299
|
+
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
300
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
301
|
+
data,
|
|
302
|
+
contextName: flags.context,
|
|
303
|
+
binary: true,
|
|
304
|
+
});
|
|
305
|
+
const rawType = String(raw.headers['content-type'] || '').toLowerCase();
|
|
306
|
+
const rawData = raw.data;
|
|
307
|
+
const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
|
|
308
|
+
if (!isTextualMediaType(rawType)) {
|
|
309
|
+
if (process.stdout.isTTY) {
|
|
310
|
+
this.error(`The API returned ${rawType || 'binary data'}. Redirect stdout to a file, e.g. ... > report.xlsx`);
|
|
311
|
+
}
|
|
312
|
+
// Nothing else may reach stdout here — it is the file.
|
|
313
|
+
process.stdout.write(buffer);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
response = { data: buffer.toString('utf8'), headers: raw.headers, status: raw.status };
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
response = await apiRequest(endpoint.method, apiPath, {
|
|
320
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
321
|
+
data,
|
|
322
|
+
contextName: flags.context,
|
|
323
|
+
acceptAny: true,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
292
326
|
// Check content-type for non-JSON responses (XML, text, etc.)
|
|
293
327
|
const contentType = (response.headers['content-type'] || '').toLowerCase();
|
|
294
|
-
if (contentType
|
|
328
|
+
if (isTextualMediaType(contentType) && !contentType.startsWith('application/json')) {
|
|
295
329
|
// Output raw content for XML/text responses (CSV, plain, HTML, etc.)
|
|
296
330
|
this.log(String(response.data));
|
|
297
331
|
return;
|
|
@@ -318,11 +352,6 @@ export default class PaymentTerms extends Command {
|
|
|
318
352
|
output = obj[arrayKeys[0]];
|
|
319
353
|
}
|
|
320
354
|
}
|
|
321
|
-
// Filter fields if --fields flag is provided
|
|
322
|
-
if (flags.fields) {
|
|
323
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
324
|
-
output = this.filterFields(output, fieldList);
|
|
325
|
-
}
|
|
326
355
|
// Sanitize output if requested
|
|
327
356
|
if (flags.sanitize) {
|
|
328
357
|
output = this.sanitizeOutput(output);
|
|
@@ -577,6 +606,8 @@ export default class PaymentTerms extends Command {
|
|
|
577
606
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
578
607
|
if (flags.include)
|
|
579
608
|
baseParams.include = flags.include;
|
|
609
|
+
if (flags.fields)
|
|
610
|
+
baseParams.fields = flags.fields;
|
|
580
611
|
if (flags.query) {
|
|
581
612
|
try {
|
|
582
613
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -631,12 +662,7 @@ export default class PaymentTerms extends Command {
|
|
|
631
662
|
}
|
|
632
663
|
}
|
|
633
664
|
process.stderr.write(' done\n');
|
|
634
|
-
// Filter fields if specified
|
|
635
665
|
let output = allItems;
|
|
636
|
-
if (flags.fields) {
|
|
637
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
638
|
-
output = this.filterFields(allItems, fieldList);
|
|
639
|
-
}
|
|
640
666
|
// Sanitize output if requested
|
|
641
667
|
if (flags.sanitize) {
|
|
642
668
|
output = this.sanitizeOutput(output);
|
|
@@ -965,56 +991,6 @@ export default class PaymentTerms extends Command {
|
|
|
965
991
|
return `\$${op}`;
|
|
966
992
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
967
993
|
}
|
|
968
|
-
filterFields(data, fields) {
|
|
969
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
970
|
-
const pick = (obj, keys) => {
|
|
971
|
-
// Group keys by their first segment so sibling sub-paths
|
|
972
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
973
|
-
// and don't clobber each other on merge.
|
|
974
|
-
const directKeys = new Set();
|
|
975
|
-
const nestedKeys = new Map();
|
|
976
|
-
for (const key of keys) {
|
|
977
|
-
if (key.split('.').some(isUnsafeKey))
|
|
978
|
-
continue;
|
|
979
|
-
if (key.includes('.')) {
|
|
980
|
-
const [first, ...rest] = key.split('.');
|
|
981
|
-
if (isUnsafeKey(first))
|
|
982
|
-
continue;
|
|
983
|
-
const list = nestedKeys.get(first);
|
|
984
|
-
if (list) {
|
|
985
|
-
list.push(rest.join('.'));
|
|
986
|
-
}
|
|
987
|
-
else {
|
|
988
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
else {
|
|
992
|
-
directKeys.add(key);
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
const result = {};
|
|
996
|
-
for (const key of directKeys) {
|
|
997
|
-
if (key in obj)
|
|
998
|
-
result[key] = obj[key];
|
|
999
|
-
}
|
|
1000
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
1001
|
-
const val = obj[first];
|
|
1002
|
-
if (!val || typeof val !== 'object')
|
|
1003
|
-
continue;
|
|
1004
|
-
if (Array.isArray(val)) {
|
|
1005
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1006
|
-
}
|
|
1007
|
-
else {
|
|
1008
|
-
result[first] = pick(val, restKeys);
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
return result;
|
|
1012
|
-
};
|
|
1013
|
-
if (Array.isArray(data)) {
|
|
1014
|
-
return data.map(item => pick(item, fields));
|
|
1015
|
-
}
|
|
1016
|
-
return pick(data, fields);
|
|
1017
|
-
}
|
|
1018
994
|
sanitizeOutput(data) {
|
|
1019
995
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1020
996
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|