@aderis-erp/cli 1.0.17 → 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/dist/commands/access-groups.js +49 -17
- package/dist/commands/account-transaction-allocations.js +46 -14
- package/dist/commands/account-transactions.js +75 -35
- package/dist/commands/accounts.js +54 -22
- package/dist/commands/agent-platform.js +53 -21
- package/dist/commands/{nango-integrations.d.ts → agent-runs.d.ts} +1 -1
- package/dist/commands/agent-runs.js +1155 -0
- package/dist/commands/analytics.js +47 -15
- package/dist/commands/api-keys.js +49 -16
- package/dist/commands/{text-templates.d.ts → async-jobs.d.ts} +1 -1
- package/dist/commands/async-jobs.js +1158 -0
- package/dist/commands/audit-log.js +46 -14
- package/dist/commands/bank-accounts.js +63 -30
- package/dist/commands/bank-integrations.d.ts +1 -0
- package/dist/commands/bank-integrations.js +66 -31
- package/dist/commands/business-partners.js +77 -44
- package/dist/commands/chat.js +52 -19
- package/dist/commands/credit-card-accounts.d.ts +1 -0
- package/dist/commands/credit-card-accounts.js +67 -29
- package/dist/commands/delivery-notes.js +78 -44
- package/dist/commands/dunning-configs.js +49 -17
- package/dist/commands/dunning-reminders.js +52 -20
- package/dist/commands/dunning-runs.js +45 -13
- package/dist/commands/email-transactions.js +47 -15
- package/dist/commands/employees.js +76 -44
- package/dist/commands/entity-configurations.js +47 -15
- package/dist/commands/event-processor-mappings.js +49 -17
- package/dist/commands/files.js +56 -24
- package/dist/commands/health.js +49 -17
- package/dist/commands/imports.d.ts +39 -0
- package/dist/commands/{nango-integrations.js → imports.js} +61 -25
- package/dist/commands/inbox.js +52 -19
- package/dist/commands/integration-identities.d.ts +40 -0
- package/dist/commands/integration-identities.js +1151 -0
- package/dist/commands/integrations.js +47 -15
- package/dist/commands/item-categories.js +49 -17
- package/dist/commands/items.js +49 -17
- package/dist/commands/journal-lines.js +45 -13
- package/dist/commands/journals.js +68 -36
- package/dist/commands/ledgers.d.ts +3 -1
- package/dist/commands/ledgers.js +95 -34
- package/dist/commands/my-account.d.ts +39 -0
- package/dist/commands/my-account.js +1160 -0
- package/dist/commands/office-locations.js +49 -17
- package/dist/commands/payment-methods.js +54 -22
- package/dist/commands/payment-runs.js +61 -29
- package/dist/commands/payment-terms.js +50 -18
- package/dist/commands/payments.js +51 -19
- 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/{text-templates.js → permissions.js} +54 -30
- package/dist/commands/price-lists.js +54 -22
- package/dist/commands/processor-mappings.js +48 -16
- package/dist/commands/processors.js +55 -24
- package/dist/commands/purchase-order-types.js +49 -17
- package/dist/commands/purchase-orders.js +69 -36
- package/dist/commands/reports.js +49 -17
- package/dist/commands/roles.d.ts +39 -0
- package/dist/commands/roles.js +1152 -0
- package/dist/commands/salary-types.js +49 -17
- package/dist/commands/sales-invoices.js +84 -51
- package/dist/commands/sales-orders.js +78 -44
- package/dist/commands/saved-views.js +47 -15
- package/dist/commands/scheduled-processor-mappings.js +49 -17
- package/dist/commands/schedules.js +53 -21
- package/dist/commands/tag-configurations.js +50 -18
- package/dist/commands/tags.js +49 -17
- package/dist/commands/tax-codes.js +52 -20
- package/dist/commands/tax-matrix.js +49 -17
- package/dist/commands/templates.d.ts +39 -0
- package/dist/commands/templates.js +1157 -0
- package/dist/commands/tenants.js +52 -20
- package/dist/commands/tools.js +46 -14
- package/dist/commands/users.d.ts +42 -0
- package/dist/commands/users.js +1174 -0
- package/dist/commands/vat-declarations.d.ts +2 -0
- package/dist/commands/vat-declarations.js +72 -36
- package/dist/commands/vendor-invoices.js +81 -49
- package/dist/commands/webhooks.js +51 -19
- package/dist/commands/workflows.js +47 -15
- package/dist/lib/api-client.d.ts +6 -0
- package/dist/lib/api-client.js +13 -0
- package/dist/lib/auth/index.js +15 -4
- 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 +23 -7
- package/dist/lib/auth/keycloak-provider.js +53 -24
- package/dist/lib/auth/tokens.d.ts +8 -3
- package/dist/lib/auth/tokens.js +8 -7
- package/dist/lib/auth/types.d.ts +5 -1
- package/oclif.manifest.json +2152 -715
- package/package.json +4 -4
|
@@ -17,6 +17,8 @@ export default class VatDeclarations extends Command {
|
|
|
17
17
|
'field-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
18
|
'notice-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
19
|
'file-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
'contribution-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
'exclusion-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
22
|
page: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
23
|
limit: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
24
|
include: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,36 +1,38 @@
|
|
|
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;
|
|
6
6
|
const HAS_EXPORT = true;
|
|
7
7
|
const HAS_CAMT053 = false;
|
|
8
8
|
const NESTED_IMPORTS = ["files"];
|
|
9
|
-
const SUBCOMMANDS = [...["list", "create", "preview", "get", "update", "delete", "regenerate", "replace", "fields-contributions", "fields-contributions-export", "approve", "
|
|
9
|
+
const SUBCOMMANDS = [...["list", "create", "preview", "get", "update", "delete", "regenerate", "replace", "fields-contributions", "fields-contributions-export", "approve", "mark-as-filed", "xml", "notices-dismiss", "notices-restore", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download", "list-payments", "create-payments", "contributions-exclude", "exclusions-restore"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list': { method: 'GET', path: '/v1/vat-declarations', summary: 'List VAT reports (paginated)', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/vat-declarations', summary: 'Create a VAT declaration report (always async-accepted)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
13
|
-
'preview': { method: 'GET', path: '/v1/vat-declarations/preview', summary: 'Preview the in-scope counts and late entries for a candidate period', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'get': { method: 'GET', path: '/v1/vat-declarations/{id}', summary: 'Get a VAT report by id (this is the polling endpoint for async generation)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
-
'update': { method: 'PATCH', path: '/v1/vat-declarations/{id}', summary: 'Update manualAdjustments, submission type, or amount basis on a DRAFT report', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['manualAdjustments'] },
|
|
16
|
-
'delete': { method: 'DELETE', path: '/v1/vat-declarations/{id}', summary: 'Delete a VAT declaration that is still in DRAFT status', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
|
-
'regenerate': { method: 'POST', path: '/v1/vat-declarations/{id}/regenerate', summary: 'Re-run the full generation pipeline on a DRAFT report', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
18
|
-
'replace': { method: 'POST', path: '/v1/vat-declarations/{id}/replace', summary: 'Create a corrective DRAFT (Berichtigungsabrechnung) from a FILED report', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
19
|
-
'fields-contributions': { method: 'GET', path: '/v1/vat-declarations/{id}/fields/{fieldId}/contributions', summary: 'Drill into the contributions for a single Ziffer', pathParams: ['id', 'fieldId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
|
-
'fields-contributions-export': { method: 'GET', path: '/v1/vat-declarations/{id}/fields/{fieldId}/contributions/export', summary: 'Export the drill-down result set as an Excel workbook', pathParams: ['id', 'fieldId'], hasBody: false, isMultipart: false, isBinaryResponse: true, bodyArrayFields: [] },
|
|
21
|
-
'approve': { method: 'POST', path: '/v1/vat-declarations/{id}/approve', summary: 'Approve a VAT report and produce the eCH-0217 XML submission file.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
'list-files': { method: 'GET', path: '/v1/vat-declarations/{parentId}/files', summary: 'List file attachments for a VAT declaration', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
27
|
-
'create-files': { method: 'POST', path: '/v1/vat-declarations/{parentId}/files', summary: 'Upload a file attachment to a VAT declaration', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, bodyArrayFields: [] },
|
|
28
|
-
'get-files': { method: 'GET', path: '/v1/vat-declarations/{parentId}/files/{fileId}', summary: 'Get a file attachment for a VAT declaration', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
29
|
-
'update-files': { method: 'PATCH', path: '/v1/vat-declarations/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a VAT declaration', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
30
|
-
'delete-files': { method: 'DELETE', path: '/v1/vat-declarations/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a VAT declaration', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
31
|
-
'files-download': { method: 'GET', path: '/v1/vat-declarations/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a VAT declaration', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
32
|
-
'list-payments': { method: 'GET', path: '/v1/vat-declarations/{vatDeclarationId}/payments', summary: 'Get all payments for a VAT declaration', pathParams: ['vatDeclarationId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
33
|
-
'create-payments': { method: 'POST', path: '/v1/vat-declarations/{vatDeclarationId}/payments', summary: 'Create the payment for a filed VAT declaration', pathParams: ['vatDeclarationId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/vat-declarations', summary: 'List VAT reports (paginated)', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/vat-declarations', summary: 'Create a VAT declaration report (always async-accepted)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
13
|
+
'preview': { method: 'GET', path: '/v1/vat-declarations/preview', summary: 'Preview the in-scope counts and late entries for a candidate period', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
14
|
+
'get': { method: 'GET', path: '/v1/vat-declarations/{id}', summary: 'Get a VAT report by id (this is the polling endpoint for async generation)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'update': { method: 'PATCH', path: '/v1/vat-declarations/{id}', summary: 'Update manualAdjustments, submission type, or amount basis on a DRAFT report', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['manualAdjustments'] },
|
|
16
|
+
'delete': { method: 'DELETE', path: '/v1/vat-declarations/{id}', summary: 'Delete a VAT declaration that is still in DRAFT status', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
+
'regenerate': { method: 'POST', path: '/v1/vat-declarations/{id}/regenerate', summary: 'Re-run the full generation pipeline on a DRAFT report', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
+
'replace': { method: 'POST', path: '/v1/vat-declarations/{id}/replace', summary: 'Create a corrective DRAFT (Berichtigungsabrechnung) from a FILED report', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
19
|
+
'fields-contributions': { method: 'GET', path: '/v1/vat-declarations/{id}/fields/{fieldId}/contributions', summary: 'Drill into the contributions for a single Ziffer', pathParams: ['id', 'fieldId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
+
'fields-contributions-export': { method: 'GET', path: '/v1/vat-declarations/{id}/fields/{fieldId}/contributions/export', summary: 'Export the drill-down result set as an Excel workbook', pathParams: ['id', 'fieldId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
21
|
+
'approve': { method: 'POST', path: '/v1/vat-declarations/{id}/approve', summary: 'Approve a VAT report and produce the eCH-0217 XML submission file.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
22
|
+
'mark-as-filed': { method: 'POST', path: '/v1/vat-declarations/{id}/mark-as-filed', summary: 'Mark a FINALIZED VAT declaration as filed.', pathParams: ['id'], hasBody: true, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
23
|
+
'xml': { method: 'GET', path: '/v1/vat-declarations/{id}/xml', summary: 'Download the eCH-0217 XML submission file for a FINALIZED or FILED VAT report.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
24
|
+
'notices-dismiss': { method: 'POST', path: '/v1/vat-declarations/{id}/notices/{noticeId}/dismiss', summary: 'Dismiss a generation notice with a mandatory reason.', pathParams: ['id', 'noticeId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
25
|
+
'notices-restore': { method: 'POST', path: '/v1/vat-declarations/{id}/notices/{noticeId}/restore', summary: 'Restore a previously dismissed generation notice.', pathParams: ['id', 'noticeId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
26
|
+
'list-files': { method: 'GET', path: '/v1/vat-declarations/{parentId}/files', summary: 'List file attachments for a VAT declaration', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
27
|
+
'create-files': { method: 'POST', path: '/v1/vat-declarations/{parentId}/files', summary: 'Upload a file attachment to a VAT declaration', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
28
|
+
'get-files': { method: 'GET', path: '/v1/vat-declarations/{parentId}/files/{fileId}', summary: 'Get a file attachment for a VAT declaration', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
29
|
+
'update-files': { method: 'PATCH', path: '/v1/vat-declarations/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a VAT declaration', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
30
|
+
'delete-files': { method: 'DELETE', path: '/v1/vat-declarations/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a VAT declaration', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
31
|
+
'files-download': { method: 'GET', path: '/v1/vat-declarations/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a VAT declaration', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
32
|
+
'list-payments': { method: 'GET', path: '/v1/vat-declarations/{vatDeclarationId}/payments', summary: 'Get all payments for a VAT declaration', pathParams: ['vatDeclarationId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
33
|
+
'create-payments': { method: 'POST', path: '/v1/vat-declarations/{vatDeclarationId}/payments', summary: 'Create the payment for a filed VAT declaration', pathParams: ['vatDeclarationId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
34
|
+
'contributions-exclude': { method: 'POST', path: '/v1/vat-declarations/{id}/contributions/{contributionId}/exclude', summary: 'Take the booking behind one contribution row out of a DRAFT declaration.', pathParams: ['id', 'contributionId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
35
|
+
'exclusions-restore': { method: 'POST', path: '/v1/vat-declarations/{id}/exclusions/{exclusionId}/restore', summary: 'Put an excluded booking back into a DRAFT declaration.', pathParams: ['id', 'exclusionId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
34
36
|
};
|
|
35
37
|
export default class VatDeclarations extends Command {
|
|
36
38
|
static description = 'Manage vat declarations';
|
|
@@ -57,6 +59,8 @@ export default class VatDeclarations extends Command {
|
|
|
57
59
|
'field-id': Flags.string({ description: 'Path param: fieldId' }),
|
|
58
60
|
'notice-id': Flags.string({ description: 'Path param: noticeId' }),
|
|
59
61
|
'file-id': Flags.string({ description: 'Path param: fileId' }),
|
|
62
|
+
'contribution-id': Flags.string({ description: 'Path param: contributionId' }),
|
|
63
|
+
'exclusion-id': Flags.string({ description: 'Path param: exclusionId' }),
|
|
60
64
|
page: Flags.string({ description: 'Page number' }),
|
|
61
65
|
limit: Flags.string({ description: 'Items per page' }),
|
|
62
66
|
include: Flags.string({ description: 'Relations to include' }),
|
|
@@ -285,10 +289,11 @@ export default class VatDeclarations extends Command {
|
|
|
285
289
|
process.stderr.write('\n');
|
|
286
290
|
}
|
|
287
291
|
try {
|
|
288
|
-
// Binary response:
|
|
292
|
+
// Binary response: to -f / --file, or to stdout when it is redirected.
|
|
289
293
|
if (endpoint.isBinaryResponse) {
|
|
290
|
-
|
|
291
|
-
|
|
294
|
+
const toStdout = !flags.file;
|
|
295
|
+
if (toStdout && process.stdout.isTTY) {
|
|
296
|
+
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.`);
|
|
292
297
|
}
|
|
293
298
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
294
299
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
@@ -296,24 +301,55 @@ export default class VatDeclarations extends Command {
|
|
|
296
301
|
contextName: flags.context,
|
|
297
302
|
binary: true,
|
|
298
303
|
});
|
|
299
|
-
const fsModule = await import('node:fs');
|
|
300
304
|
const binData = binResponse.data;
|
|
301
305
|
const buffer = Buffer.isBuffer(binData)
|
|
302
306
|
? binData
|
|
303
307
|
: Buffer.from(binData);
|
|
308
|
+
if (toStdout) {
|
|
309
|
+
// Nothing else may reach stdout here — it is the file.
|
|
310
|
+
process.stdout.write(buffer);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const fsModule = await import('node:fs');
|
|
304
314
|
fsModule.writeFileSync(flags.file, buffer);
|
|
305
315
|
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
306
316
|
return;
|
|
307
317
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
318
|
+
let response;
|
|
319
|
+
if (endpoint.hasBinaryVariant) {
|
|
320
|
+
// This route answers JSON or a file depending on the request body, so the
|
|
321
|
+
// bytes are read first and the response's own content-type decides. Asking
|
|
322
|
+
// for text here would corrupt an xlsx/pdf before anything could inspect it.
|
|
323
|
+
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
324
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
325
|
+
data,
|
|
326
|
+
contextName: flags.context,
|
|
327
|
+
binary: true,
|
|
328
|
+
});
|
|
329
|
+
const rawType = String(raw.headers['content-type'] || '').toLowerCase();
|
|
330
|
+
const rawData = raw.data;
|
|
331
|
+
const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
|
|
332
|
+
if (!isTextualMediaType(rawType)) {
|
|
333
|
+
if (process.stdout.isTTY) {
|
|
334
|
+
this.error(`The API returned ${rawType || 'binary data'}. Redirect stdout to a file, e.g. ... > report.xlsx`);
|
|
335
|
+
}
|
|
336
|
+
// Nothing else may reach stdout here — it is the file.
|
|
337
|
+
process.stdout.write(buffer);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
response = { data: buffer.toString('utf8'), headers: raw.headers, status: raw.status };
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
response = await apiRequest(endpoint.method, apiPath, {
|
|
344
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
345
|
+
data,
|
|
346
|
+
contextName: flags.context,
|
|
347
|
+
acceptAny: true,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
314
350
|
// Check content-type for non-JSON responses (XML, text, etc.)
|
|
315
351
|
const contentType = (response.headers['content-type'] || '').toLowerCase();
|
|
316
|
-
if (contentType
|
|
352
|
+
if (isTextualMediaType(contentType) && !contentType.startsWith('application/json')) {
|
|
317
353
|
// Output raw content for XML/text responses (CSV, plain, HTML, etc.)
|
|
318
354
|
this.log(String(response.data));
|
|
319
355
|
return;
|
|
@@ -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,43 +8,43 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = ["line-items", "notes", "files", "payments"];
|
|
9
9
|
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "payment-derivation", "submit", "approve", "decline", "book", "rebuild-journal", "cancel", "change-vendor", "recalculate-payment-status", "validate", "create-line-items", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download", "list-payments", "create-payments", "get-payments", "update-payments", "delete-payments", "payments-approve", "payments-reopen", "payments-cancel"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list': { method: 'GET', path: '/v1/vendor-invoices', summary: 'Get all vendor invoices with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/vendor-invoices', summary: 'Create a new vendor invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'payments'] },
|
|
13
|
-
'get': { method: 'GET', path: '/v1/vendor-invoices/{id}', summary: 'Get a vendor invoice by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/vendor-invoices/{id}', summary: 'Update a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
|
|
15
|
-
'delete': { method: 'DELETE', path: '/v1/vendor-invoices/{id}', summary: 'Delete a vendor invoice', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
|
-
'payment-derivation': { method: 'GET', path: '/v1/vendor-invoices/{id}/payment-derivation', summary: 'Explain how a vendor invoice\'s paid amount is derived', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
|
-
'submit': { method: 'POST', path: '/v1/vendor-invoices/{id}/submit', summary: 'Submit a vendor invoice for approval (transitions DRAFT to PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
18
|
-
'approve': { method: 'POST', path: '/v1/vendor-invoices/{id}/approve', summary: 'Approve a vendor invoice (transitions DRAFT/PENDING_APPROVAL to APPROVED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
|
|
19
|
-
'decline': { method: 'POST', path: '/v1/vendor-invoices/{id}/decline', summary: 'Decline a vendor invoice (transitions PENDING_APPROVAL to DECLINED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
|
-
'book': { method: 'POST', path: '/v1/vendor-invoices/{id}/book', summary: 'Manually book a vendor invoice (dispatch to ledger-booking-agent)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
21
|
-
'rebuild-journal': { method: 'POST', path: '/v1/vendor-invoices/{id}/rebuild-journal', summary: 'Rebuild a vendor invoice booking journal (dispatch to ledger-booking-agent)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
22
|
-
'cancel': { method: 'POST', path: '/v1/vendor-invoices/{id}/cancel', summary: 'Cancel a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
23
|
-
'change-vendor': { method: 'POST', path: '/v1/vendor-invoices/{id}/change-vendor', summary: 'Change the vendor on a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
24
|
-
'recalculate-payment-status': { method: 'POST', path: '/v1/vendor-invoices/{id}/recalculate-payment-status', summary: 'Recalculate a vendor invoice\'s open amount and payment status', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
25
|
-
'validate': { method: 'POST', path: '/v1/vendor-invoices/{id}/validate', summary: 'Validate a vendor invoice update without persisting changes', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
|
|
26
|
-
'create-line-items': { method: 'POST', path: '/v1/vendor-invoices/{invoiceId}/line-items', summary: 'Add a line item to a vendor invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes', 'purchaseOrderLineItems'] },
|
|
27
|
-
'update-line-items': { method: 'PATCH', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Update a line item on a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes', 'purchaseOrderLineItems'] },
|
|
28
|
-
'delete-line-items': { method: 'DELETE', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Remove a line item from a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
29
|
-
'list-notes': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes', summary: 'Get all notes for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
30
|
-
'create-notes': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes', summary: 'Create a note for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
31
|
-
'get-notes': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes/{noteId}', summary: 'Get a specific note for a vendor invoice', pathParams: ['vendorInvoiceId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
32
|
-
'update-notes': { method: 'PATCH', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes/{noteId}', summary: 'Update a note for a vendor invoice', pathParams: ['vendorInvoiceId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
33
|
-
'delete-notes': { method: 'DELETE', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes/{noteId}', summary: 'Delete a note from a vendor invoice', pathParams: ['vendorInvoiceId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
34
|
-
'list-files': { method: 'GET', path: '/v1/vendor-invoices/{parentId}/files', summary: 'List file attachments for a vendor invoice', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
35
|
-
'create-files': { method: 'POST', path: '/v1/vendor-invoices/{parentId}/files', summary: 'Upload a file attachment to a vendor invoice', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, bodyArrayFields: [] },
|
|
36
|
-
'get-files': { method: 'GET', path: '/v1/vendor-invoices/{parentId}/files/{fileId}', summary: 'Get a file attachment for a vendor invoice', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
37
|
-
'update-files': { method: 'PATCH', path: '/v1/vendor-invoices/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a vendor invoice', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
38
|
-
'delete-files': { method: 'DELETE', path: '/v1/vendor-invoices/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a vendor invoice', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
39
|
-
'files-download': { method: 'GET', path: '/v1/vendor-invoices/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a vendor invoice', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
40
|
-
'list-payments': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments', summary: 'Get all payments for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
41
|
-
'create-payments': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments', summary: 'Create a payment for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
42
|
-
'get-payments': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}', summary: 'Get a specific payment for a vendor invoice', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
43
|
-
'update-payments': { method: 'PATCH', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}', summary: 'Update a payment for a vendor invoice', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
44
|
-
'delete-payments': { method: 'DELETE', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}', summary: 'Delete a payment from a vendor invoice', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
45
|
-
'payments-approve': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/approve', summary: 'Approve a payment entry (OPEN → APPROVED)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
46
|
-
'payments-reopen': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/reopen', summary: 'Reopen an approved payment entry (APPROVED → OPEN)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
47
|
-
'payments-cancel': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/cancel', summary: 'Cancel a sent payment entry (SENT -> ERROR)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/vendor-invoices', summary: 'Get all vendor invoices with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/vendor-invoices', summary: 'Create a new vendor invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'payments'] },
|
|
13
|
+
'get': { method: 'GET', path: '/v1/vendor-invoices/{id}', summary: 'Get a vendor invoice by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
14
|
+
'update': { method: 'PATCH', path: '/v1/vendor-invoices/{id}', summary: 'Update a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
|
|
15
|
+
'delete': { method: 'DELETE', path: '/v1/vendor-invoices/{id}', summary: 'Delete a vendor invoice', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
+
'payment-derivation': { method: 'GET', path: '/v1/vendor-invoices/{id}/payment-derivation', summary: 'Explain how a vendor invoice\'s paid amount is derived', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
+
'submit': { method: 'POST', path: '/v1/vendor-invoices/{id}/submit', summary: 'Submit a vendor invoice for approval (transitions DRAFT to PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
+
'approve': { method: 'POST', path: '/v1/vendor-invoices/{id}/approve', summary: 'Approve a vendor invoice (transitions DRAFT/PENDING_APPROVAL to APPROVED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
|
|
19
|
+
'decline': { method: 'POST', path: '/v1/vendor-invoices/{id}/decline', summary: 'Decline a vendor invoice (transitions PENDING_APPROVAL to DECLINED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
+
'book': { method: 'POST', path: '/v1/vendor-invoices/{id}/book', summary: 'Manually book a vendor invoice (dispatch to ledger-booking-agent)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
21
|
+
'rebuild-journal': { method: 'POST', path: '/v1/vendor-invoices/{id}/rebuild-journal', summary: 'Rebuild a vendor invoice booking journal (dispatch to ledger-booking-agent)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
22
|
+
'cancel': { method: 'POST', path: '/v1/vendor-invoices/{id}/cancel', summary: 'Cancel a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
23
|
+
'change-vendor': { method: 'POST', path: '/v1/vendor-invoices/{id}/change-vendor', summary: 'Change the vendor on a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
24
|
+
'recalculate-payment-status': { method: 'POST', path: '/v1/vendor-invoices/{id}/recalculate-payment-status', summary: 'Recalculate a vendor invoice\'s open amount and payment status', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
25
|
+
'validate': { method: 'POST', path: '/v1/vendor-invoices/{id}/validate', summary: 'Validate a vendor invoice update without persisting changes', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
|
|
26
|
+
'create-line-items': { method: 'POST', path: '/v1/vendor-invoices/{invoiceId}/line-items', summary: 'Add a line item to a vendor invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['tagIds', 'tagCodes', 'purchaseOrderLineItems'] },
|
|
27
|
+
'update-line-items': { method: 'PATCH', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Update a line item on a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['tagIds', 'tagCodes', 'purchaseOrderLineItems'] },
|
|
28
|
+
'delete-line-items': { method: 'DELETE', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Remove a line item from a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
29
|
+
'list-notes': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes', summary: 'Get all notes for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
30
|
+
'create-notes': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes', summary: 'Create a note for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
31
|
+
'get-notes': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes/{noteId}', summary: 'Get a specific note for a vendor invoice', pathParams: ['vendorInvoiceId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
32
|
+
'update-notes': { method: 'PATCH', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes/{noteId}', summary: 'Update a note for a vendor invoice', pathParams: ['vendorInvoiceId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
33
|
+
'delete-notes': { method: 'DELETE', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes/{noteId}', summary: 'Delete a note from a vendor invoice', pathParams: ['vendorInvoiceId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
34
|
+
'list-files': { method: 'GET', path: '/v1/vendor-invoices/{parentId}/files', summary: 'List file attachments for a vendor invoice', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
35
|
+
'create-files': { method: 'POST', path: '/v1/vendor-invoices/{parentId}/files', summary: 'Upload a file attachment to a vendor invoice', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
36
|
+
'get-files': { method: 'GET', path: '/v1/vendor-invoices/{parentId}/files/{fileId}', summary: 'Get a file attachment for a vendor invoice', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
37
|
+
'update-files': { method: 'PATCH', path: '/v1/vendor-invoices/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a vendor invoice', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
38
|
+
'delete-files': { method: 'DELETE', path: '/v1/vendor-invoices/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a vendor invoice', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
39
|
+
'files-download': { method: 'GET', path: '/v1/vendor-invoices/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a vendor invoice', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
40
|
+
'list-payments': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments', summary: 'Get all payments for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
41
|
+
'create-payments': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments', summary: 'Create a payment for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
42
|
+
'get-payments': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}', summary: 'Get a specific payment for a vendor invoice', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
43
|
+
'update-payments': { method: 'PATCH', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}', summary: 'Update a payment for a vendor invoice', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
44
|
+
'delete-payments': { method: 'DELETE', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}', summary: 'Delete a payment from a vendor invoice', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
45
|
+
'payments-approve': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/approve', summary: 'Approve a payment entry (OPEN → APPROVED)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
46
|
+
'payments-reopen': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/reopen', summary: 'Reopen an approved payment entry (APPROVED → OPEN)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
47
|
+
'payments-cancel': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/cancel', summary: 'Cancel a sent payment entry (SENT -> ERROR)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
48
48
|
};
|
|
49
49
|
export default class VendorInvoices extends Command {
|
|
50
50
|
static description = 'Manage vendor invoices';
|
|
@@ -300,10 +300,11 @@ export default class VendorInvoices extends Command {
|
|
|
300
300
|
process.stderr.write('\n');
|
|
301
301
|
}
|
|
302
302
|
try {
|
|
303
|
-
// Binary response:
|
|
303
|
+
// Binary response: to -f / --file, or to stdout when it is redirected.
|
|
304
304
|
if (endpoint.isBinaryResponse) {
|
|
305
|
-
|
|
306
|
-
|
|
305
|
+
const toStdout = !flags.file;
|
|
306
|
+
if (toStdout && process.stdout.isTTY) {
|
|
307
|
+
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.`);
|
|
307
308
|
}
|
|
308
309
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
309
310
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
@@ -311,24 +312,55 @@ export default class VendorInvoices extends Command {
|
|
|
311
312
|
contextName: flags.context,
|
|
312
313
|
binary: true,
|
|
313
314
|
});
|
|
314
|
-
const fsModule = await import('node:fs');
|
|
315
315
|
const binData = binResponse.data;
|
|
316
316
|
const buffer = Buffer.isBuffer(binData)
|
|
317
317
|
? binData
|
|
318
318
|
: Buffer.from(binData);
|
|
319
|
+
if (toStdout) {
|
|
320
|
+
// Nothing else may reach stdout here — it is the file.
|
|
321
|
+
process.stdout.write(buffer);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const fsModule = await import('node:fs');
|
|
319
325
|
fsModule.writeFileSync(flags.file, buffer);
|
|
320
326
|
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
321
327
|
return;
|
|
322
328
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
+
let response;
|
|
330
|
+
if (endpoint.hasBinaryVariant) {
|
|
331
|
+
// This route answers JSON or a file depending on the request body, so the
|
|
332
|
+
// bytes are read first and the response's own content-type decides. Asking
|
|
333
|
+
// for text here would corrupt an xlsx/pdf before anything could inspect it.
|
|
334
|
+
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
335
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
336
|
+
data,
|
|
337
|
+
contextName: flags.context,
|
|
338
|
+
binary: true,
|
|
339
|
+
});
|
|
340
|
+
const rawType = String(raw.headers['content-type'] || '').toLowerCase();
|
|
341
|
+
const rawData = raw.data;
|
|
342
|
+
const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
|
|
343
|
+
if (!isTextualMediaType(rawType)) {
|
|
344
|
+
if (process.stdout.isTTY) {
|
|
345
|
+
this.error(`The API returned ${rawType || 'binary data'}. Redirect stdout to a file, e.g. ... > report.xlsx`);
|
|
346
|
+
}
|
|
347
|
+
// Nothing else may reach stdout here — it is the file.
|
|
348
|
+
process.stdout.write(buffer);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
response = { data: buffer.toString('utf8'), headers: raw.headers, status: raw.status };
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
response = await apiRequest(endpoint.method, apiPath, {
|
|
355
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
356
|
+
data,
|
|
357
|
+
contextName: flags.context,
|
|
358
|
+
acceptAny: true,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
329
361
|
// Check content-type for non-JSON responses (XML, text, etc.)
|
|
330
362
|
const contentType = (response.headers['content-type'] || '').toLowerCase();
|
|
331
|
-
if (contentType
|
|
363
|
+
if (isTextualMediaType(contentType) && !contentType.startsWith('application/json')) {
|
|
332
364
|
// Output raw content for XML/text responses (CSV, plain, HTML, etc.)
|
|
333
365
|
this.log(String(response.data));
|
|
334
366
|
return;
|
|
@@ -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,13 +8,13 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
9
|
const SUBCOMMANDS = [...["list", "create", "events", "events-relations", "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/webhooks', summary: 'List all webhook subscriptions', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/webhooks', summary: 'Create a new webhook subscription', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tenantIds'] },
|
|
13
|
-
'events': { method: 'GET', path: '/v1/webhooks/events', summary: 'Get available event types', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'events-relations': { method: 'GET', path: '/v1/webhooks/events/{eventType}/relations', summary: 'Get valid relations for an event type', pathParams: ['eventType'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
-
'get': { method: 'GET', path: '/v1/webhooks/{id}', summary: 'Get a webhook subscription by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
|
-
'update': { method: 'PATCH', path: '/v1/webhooks/{id}', summary: 'Update a webhook subscription', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tenantIds'] },
|
|
17
|
-
'delete': { method: 'DELETE', path: '/v1/webhooks/{id}', summary: 'Delete a webhook subscription', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/webhooks', summary: 'List all webhook subscriptions', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/webhooks', summary: 'Create a new webhook subscription', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['tenantIds'] },
|
|
13
|
+
'events': { method: 'GET', path: '/v1/webhooks/events', summary: 'Get available event types', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
14
|
+
'events-relations': { method: 'GET', path: '/v1/webhooks/events/{eventType}/relations', summary: 'Get valid relations for an event type', pathParams: ['eventType'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'get': { method: 'GET', path: '/v1/webhooks/{id}', summary: 'Get a webhook subscription by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
+
'update': { method: 'PATCH', path: '/v1/webhooks/{id}', summary: 'Update a webhook subscription', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['tenantIds'] },
|
|
17
|
+
'delete': { method: 'DELETE', path: '/v1/webhooks/{id}', summary: 'Delete a webhook subscription', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
18
|
};
|
|
19
19
|
export default class Webhooks extends Command {
|
|
20
20
|
static description = 'Manage webhooks';
|
|
@@ -266,10 +266,11 @@ export default class Webhooks extends Command {
|
|
|
266
266
|
process.stderr.write('\n');
|
|
267
267
|
}
|
|
268
268
|
try {
|
|
269
|
-
// Binary response:
|
|
269
|
+
// Binary response: to -f / --file, or to stdout when it is redirected.
|
|
270
270
|
if (endpoint.isBinaryResponse) {
|
|
271
|
-
|
|
272
|
-
|
|
271
|
+
const toStdout = !flags.file;
|
|
272
|
+
if (toStdout && process.stdout.isTTY) {
|
|
273
|
+
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.`);
|
|
273
274
|
}
|
|
274
275
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
275
276
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
@@ -277,24 +278,55 @@ export default class Webhooks extends Command {
|
|
|
277
278
|
contextName: flags.context,
|
|
278
279
|
binary: true,
|
|
279
280
|
});
|
|
280
|
-
const fsModule = await import('node:fs');
|
|
281
281
|
const binData = binResponse.data;
|
|
282
282
|
const buffer = Buffer.isBuffer(binData)
|
|
283
283
|
? binData
|
|
284
284
|
: Buffer.from(binData);
|
|
285
|
+
if (toStdout) {
|
|
286
|
+
// Nothing else may reach stdout here — it is the file.
|
|
287
|
+
process.stdout.write(buffer);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const fsModule = await import('node:fs');
|
|
285
291
|
fsModule.writeFileSync(flags.file, buffer);
|
|
286
292
|
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
287
293
|
return;
|
|
288
294
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
+
let response;
|
|
296
|
+
if (endpoint.hasBinaryVariant) {
|
|
297
|
+
// This route answers JSON or a file depending on the request body, so the
|
|
298
|
+
// bytes are read first and the response's own content-type decides. Asking
|
|
299
|
+
// for text here would corrupt an xlsx/pdf before anything could inspect it.
|
|
300
|
+
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
301
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
302
|
+
data,
|
|
303
|
+
contextName: flags.context,
|
|
304
|
+
binary: true,
|
|
305
|
+
});
|
|
306
|
+
const rawType = String(raw.headers['content-type'] || '').toLowerCase();
|
|
307
|
+
const rawData = raw.data;
|
|
308
|
+
const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
|
|
309
|
+
if (!isTextualMediaType(rawType)) {
|
|
310
|
+
if (process.stdout.isTTY) {
|
|
311
|
+
this.error(`The API returned ${rawType || 'binary data'}. Redirect stdout to a file, e.g. ... > report.xlsx`);
|
|
312
|
+
}
|
|
313
|
+
// Nothing else may reach stdout here — it is the file.
|
|
314
|
+
process.stdout.write(buffer);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
response = { data: buffer.toString('utf8'), headers: raw.headers, status: raw.status };
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
response = await apiRequest(endpoint.method, apiPath, {
|
|
321
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
322
|
+
data,
|
|
323
|
+
contextName: flags.context,
|
|
324
|
+
acceptAny: true,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
295
327
|
// Check content-type for non-JSON responses (XML, text, etc.)
|
|
296
328
|
const contentType = (response.headers['content-type'] || '').toLowerCase();
|
|
297
|
-
if (contentType
|
|
329
|
+
if (isTextualMediaType(contentType) && !contentType.startsWith('application/json')) {
|
|
298
330
|
// Output raw content for XML/text responses (CSV, plain, HTML, etc.)
|
|
299
331
|
this.log(String(response.data));
|
|
300
332
|
return;
|