@aderis-erp/cli 1.0.2 → 1.0.4
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 +6 -0
- package/dist/commands/access-groups 3.d.ts +40 -0
- package/dist/commands/{access-groups 2.js → access-groups 3.js } +106 -40
- package/dist/commands/access-groups.d.ts +40 -0
- package/dist/commands/access-groups.js +1074 -0
- package/dist/commands/account-transactions.d.ts +42 -0
- package/dist/commands/account-transactions.js +1088 -0
- package/dist/commands/accounts 2.d.ts +41 -0
- package/dist/commands/accounts 2.js +1078 -0
- package/dist/commands/accounts.d.ts +41 -0
- package/dist/commands/accounts.js +1080 -0
- package/dist/commands/agent-runs.d.ts +40 -0
- package/dist/commands/agent-runs.js +1071 -0
- package/dist/commands/async-endpoints-internal 3.d.ts +40 -0
- package/dist/commands/{processing-logs.js → async-endpoints-internal 3.js } +110 -46
- package/dist/commands/async-endpoints-internal.d.ts +40 -0
- package/dist/commands/async-endpoints-internal.js +1064 -0
- package/dist/commands/audit-log 3.d.ts +40 -0
- package/dist/commands/audit-log 3.js +1066 -0
- package/dist/commands/audit-log.d.ts +40 -0
- package/dist/commands/audit-log.js +1068 -0
- package/dist/commands/auth/login.d.ts +14 -0
- package/dist/commands/auth/login.js +116 -0
- package/dist/commands/auth/logout.d.ts +10 -0
- package/dist/commands/auth/logout.js +58 -0
- package/dist/commands/auth/status.d.ts +12 -0
- package/dist/commands/auth/status.js +101 -0
- package/dist/commands/auth/tenant.d.ts +19 -0
- package/dist/commands/auth/tenant.js +184 -0
- package/dist/commands/auth.d.ts +37 -0
- package/dist/commands/auth.js +871 -0
- package/dist/commands/bank-accounts.d.ts +42 -0
- package/dist/commands/bank-accounts.js +1088 -0
- package/dist/commands/bank-integrations.d.ts +1 -0
- package/dist/commands/bank-integrations.js +122 -54
- package/dist/commands/business-partners 3.d.ts +41 -0
- package/dist/commands/business-partners 3.js +1078 -0
- package/dist/commands/business-partners---contacts 3.d.ts +42 -0
- package/dist/commands/business-partners---contacts 3.js +1072 -0
- package/dist/commands/business-partners---contacts.d.ts +1 -0
- package/dist/commands/business-partners---contacts.js +114 -46
- package/dist/commands/business-partners.d.ts +1 -0
- package/dist/commands/business-partners.js +114 -46
- package/dist/commands/config/add.d.ts +13 -0
- package/dist/commands/config/add.js +120 -0
- package/dist/commands/config/current.d.ts +9 -0
- package/dist/commands/config/current.js +43 -0
- package/dist/commands/config/delete.d.ts +12 -0
- package/dist/commands/config/delete.js +67 -0
- package/dist/commands/config/list.d.ts +9 -0
- package/dist/commands/config/list.js +50 -0
- package/dist/commands/config/use.d.ts +9 -0
- package/dist/commands/config/use.js +40 -0
- package/dist/commands/credit-card-accounts 3.d.ts +42 -0
- package/dist/commands/credit-card-accounts 3.js +1084 -0
- package/dist/commands/credit-card-accounts.d.ts +1 -0
- package/dist/commands/credit-card-accounts.js +119 -51
- package/dist/commands/documents.d.ts +1 -0
- package/dist/commands/documents.js +110 -42
- package/dist/commands/dunning-configs 3.d.ts +40 -0
- package/dist/commands/dunning-configs 3.js +1072 -0
- package/dist/commands/dunning-configs.d.ts +1 -0
- package/dist/commands/dunning-configs.js +109 -41
- package/dist/commands/dunning-reminders.d.ts +1 -0
- package/dist/commands/dunning-reminders.js +112 -44
- package/dist/commands/dunning-runs 3.d.ts +40 -0
- package/dist/commands/{pubsub.js → dunning-runs 3.js } +108 -43
- package/dist/commands/dunning-runs.d.ts +1 -0
- package/dist/commands/dunning-runs.js +105 -37
- package/dist/commands/employees.d.ts +1 -0
- package/dist/commands/employees.js +136 -68
- package/dist/commands/event-processor-mappings 3.d.ts +40 -0
- package/dist/commands/event-processor-mappings 3.js +1071 -0
- package/dist/commands/event-processor-mappings.d.ts +1 -0
- package/dist/commands/event-processor-mappings.js +109 -41
- package/dist/commands/files-internal 3.d.ts +40 -0
- package/dist/commands/files-internal 3.js +1073 -0
- package/dist/commands/files-internal.d.ts +1 -0
- package/dist/commands/files-internal.js +113 -45
- package/dist/commands/health 3.d.ts +40 -0
- package/dist/commands/health 3.js +1067 -0
- package/dist/commands/health.d.ts +1 -0
- package/dist/commands/health.js +109 -41
- package/dist/commands/{pubsub.d.ts → inbox 3.d.ts } +2 -1
- package/dist/commands/inbox 3.js +1065 -0
- package/dist/commands/inbox.d.ts +1 -0
- package/dist/commands/inbox.js +110 -40
- package/dist/commands/integrations 3.d.ts +41 -0
- package/dist/commands/integrations 3.js +1067 -0
- package/dist/commands/integrations.d.ts +1 -0
- package/dist/commands/integrations.js +107 -39
- package/dist/commands/item-categories 3.d.ts +40 -0
- package/dist/commands/item-categories 3.js +1072 -0
- package/dist/commands/item-categories.d.ts +1 -0
- package/dist/commands/item-categories.js +109 -41
- package/dist/commands/items 3.d.ts +40 -0
- package/dist/commands/items 3.js +1072 -0
- package/dist/commands/items.d.ts +1 -0
- package/dist/commands/items.js +109 -41
- package/dist/commands/journal-lines.d.ts +1 -0
- package/dist/commands/journal-lines.js +105 -37
- package/dist/commands/journals 3.d.ts +42 -0
- package/dist/commands/journals 3.js +1088 -0
- package/dist/commands/journals.d.ts +1 -0
- package/dist/commands/journals.js +123 -55
- package/dist/commands/ledgers.d.ts +1 -0
- package/dist/commands/ledgers.js +120 -52
- package/dist/commands/office-locations 3.d.ts +40 -0
- package/dist/commands/office-locations 3.js +1072 -0
- package/dist/commands/office-locations.d.ts +1 -0
- package/dist/commands/office-locations.js +109 -41
- package/dist/commands/payment-method-ledger-mappings 3.d.ts +41 -0
- package/dist/commands/payment-method-ledger-mappings 3.js +1066 -0
- package/dist/commands/payment-method-ledger-mappings.d.ts +1 -0
- package/dist/commands/payment-method-ledger-mappings.js +109 -41
- package/dist/commands/payment-methods 3.d.ts +40 -0
- package/dist/commands/payment-methods 3.js +1072 -0
- package/dist/commands/payment-methods.d.ts +1 -0
- package/dist/commands/payment-methods.js +109 -41
- package/dist/commands/payment-runs 3.d.ts +42 -0
- package/dist/commands/payment-runs 3.js +1087 -0
- package/dist/commands/payment-runs.d.ts +1 -0
- package/dist/commands/payment-runs.js +122 -54
- package/dist/commands/payment-terms 3.d.ts +40 -0
- package/dist/commands/payment-terms 3.js +1073 -0
- package/dist/commands/payment-terms.d.ts +1 -0
- package/dist/commands/payment-terms.js +110 -42
- package/dist/commands/price-lists 3.d.ts +41 -0
- package/dist/commands/price-lists 3.js +1078 -0
- package/dist/commands/price-lists.d.ts +1 -0
- package/dist/commands/price-lists.js +114 -46
- package/dist/commands/processor-mappings 2.d.ts +40 -0
- package/dist/commands/processor-mappings 2.js +1070 -0
- package/dist/commands/processor-mappings.d.ts +1 -0
- package/dist/commands/processor-mappings.js +108 -40
- package/dist/commands/processors.d.ts +1 -0
- package/dist/commands/processors.js +113 -45
- package/dist/commands/reconciliation 3.d.ts +40 -0
- package/dist/commands/reconciliation 3.js +1069 -0
- package/dist/commands/reconciliation.d.ts +1 -0
- package/dist/commands/reconciliation.js +110 -42
- package/dist/commands/reports.d.ts +1 -0
- package/dist/commands/reports.js +110 -39
- package/dist/commands/salary-types 3.d.ts +40 -0
- package/dist/commands/salary-types 3.js +1072 -0
- package/dist/commands/salary-types.d.ts +1 -0
- package/dist/commands/salary-types.js +1074 -0
- package/dist/commands/sales-invoices 3.d.ts +43 -0
- package/dist/commands/sales-invoices 3.js +1100 -0
- package/dist/commands/sales-invoices.d.ts +1 -0
- package/dist/commands/sales-invoices.js +134 -66
- package/dist/commands/sales-orders 3.d.ts +43 -0
- package/dist/commands/sales-orders 3.js +1098 -0
- package/dist/commands/sales-orders.d.ts +1 -0
- package/dist/commands/sales-orders.js +133 -64
- package/dist/commands/switch.d.ts +1 -0
- package/dist/commands/switch.js +58 -3
- package/dist/commands/{processing-logs.d.ts → tags 3.d.ts } +2 -1
- package/dist/commands/tags 3.js +1072 -0
- package/dist/commands/tags.d.ts +1 -0
- package/dist/commands/tags.js +109 -41
- package/dist/commands/tax-codes 3.d.ts +40 -0
- package/dist/commands/tax-codes 3.js +1075 -0
- package/dist/commands/tax-codes.d.ts +1 -0
- package/dist/commands/tax-codes.js +112 -44
- package/dist/commands/tax-matrix.d.ts +1 -0
- package/dist/commands/tax-matrix.js +109 -41
- package/dist/commands/tenants.d.ts +1 -0
- package/dist/commands/tenants.js +109 -41
- package/dist/commands/text-templates 3.d.ts +40 -0
- package/dist/commands/text-templates 3.js +1072 -0
- package/dist/commands/text-templates.d.ts +1 -0
- package/dist/commands/text-templates.js +109 -41
- package/dist/commands/tools.d.ts +1 -0
- package/dist/commands/tools.js +106 -38
- package/dist/commands/vendor-invoices.d.ts +1 -0
- package/dist/commands/vendor-invoices.js +139 -69
- package/dist/commands/webhooks 3.d.ts +40 -0
- package/dist/commands/webhooks 3.js +1074 -0
- package/dist/commands/webhooks.d.ts +1 -0
- package/dist/commands/webhooks.js +111 -43
- package/dist/commands/workflows.d.ts +1 -0
- package/dist/commands/workflows.js +107 -39
- package/dist/lib/api-client.d.ts +5 -1
- package/dist/lib/api-client.js +19 -5
- package/dist/lib/auth.d.ts +16 -1
- package/dist/lib/auth.js +170 -45
- package/dist/lib/config.d.ts +7 -0
- package/dist/lib/config.js +7 -1
- package/oclif.manifest.json +7464 -653
- package/package.json +2 -2
|
@@ -6,35 +6,36 @@ const HAS_IMPORT = true;
|
|
|
6
6
|
const HAS_EXPORT = true;
|
|
7
7
|
const HAS_CAMT053 = false;
|
|
8
8
|
const NESTED_IMPORTS = ["line-items", "notes", "files"];
|
|
9
|
-
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "submit", "approve", "accept", "terminate", "cancel", "replace", "create-invoice", "create-line-items", "update-line-items-reorder", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
9
|
+
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "pdf", "submit", "approve", "accept", "terminate", "cancel", "replace", "create-invoice", "create-line-items", "update-line-items-reorder", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list': { method: 'GET', path: '/v1/sales-orders', summary: 'Get all sales orders with pagination', pathParams: [], hasBody: false, isMultipart: false },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/sales-orders', summary: 'Create a new sales order', pathParams: [], hasBody: true, isMultipart: false },
|
|
13
|
-
'get': { method: 'GET', path: '/v1/sales-orders/{id}', summary: 'Get a sales order by ID', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/sales-orders/{id}', summary: 'Update a sales order', pathParams: ['id'], hasBody: true, isMultipart: false },
|
|
15
|
-
'delete': { method: 'DELETE', path: '/v1/sales-orders/{id}', summary: 'Delete a sales order', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'create-
|
|
24
|
-
'
|
|
25
|
-
'update-line-items': { method: 'PATCH', path: '/v1/sales-orders/{orderId}/line-items/
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'files
|
|
11
|
+
'list': { method: 'GET', path: '/v1/sales-orders', summary: 'Get all sales orders with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/sales-orders', summary: 'Create a new sales order', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'lineItems'] },
|
|
13
|
+
'get': { method: 'GET', path: '/v1/sales-orders/{id}', summary: 'Get a sales order by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
+
'update': { method: 'PATCH', path: '/v1/sales-orders/{id}', summary: 'Update a sales order', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'lineItems', 'removeFileIds'] },
|
|
15
|
+
'delete': { method: 'DELETE', path: '/v1/sales-orders/{id}', summary: 'Delete a sales order', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
|
+
'pdf': { method: 'GET', path: '/v1/sales-orders/{id}/pdf', summary: 'Render a sales order PDF using the configured PDF processor', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
|
+
'submit': { method: 'POST', path: '/v1/sales-orders/{id}/submit', summary: 'Submit order for approval (DRAFT → PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
18
|
+
'approve': { method: 'POST', path: '/v1/sales-orders/{id}/approve', summary: 'Approve order (PENDING_APPROVAL → APPROVED). Generates order number.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
19
|
+
'accept': { method: 'POST', path: '/v1/sales-orders/{id}/accept', summary: 'Accept order (APPROVED → ACCEPTED). Adjusts billing start date.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
|
+
'terminate': { method: 'POST', path: '/v1/sales-orders/{id}/terminate', summary: 'Terminate order (ACCEPTED → TERMINATED). Requires billingEndDate.', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
21
|
+
'cancel': { method: 'POST', path: '/v1/sales-orders/{id}/cancel', summary: 'Cancel order (any pre-active status → CANCELLED)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
22
|
+
'replace': { method: 'POST', path: '/v1/sales-orders/{id}/replace', summary: 'Replace order with corrected version (APPROVED → creates new DRAFT)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
23
|
+
'create-invoice': { method: 'POST', path: '/v1/sales-orders/{id}/create-invoice', summary: 'Create invoice from sales order (ACCEPTED → creates DRAFT invoice)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
24
|
+
'create-line-items': { method: 'POST', path: '/v1/sales-orders/{orderId}/line-items', summary: 'Add a line item to a sales order', pathParams: ['orderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
25
|
+
'update-line-items-reorder': { method: 'PATCH', path: '/v1/sales-orders/{orderId}/line-items/reorder', summary: 'Bulk reorder line items (drag-and-drop)', pathParams: ['orderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['items'] },
|
|
26
|
+
'update-line-items': { method: 'PATCH', path: '/v1/sales-orders/{orderId}/line-items/{lineItemId}', summary: 'Update a line item on a sales order', pathParams: ['orderId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
27
|
+
'delete-line-items': { method: 'DELETE', path: '/v1/sales-orders/{orderId}/line-items/{lineItemId}', summary: 'Remove a line item from a sales order', pathParams: ['orderId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
28
|
+
'list-notes': { method: 'GET', path: '/v1/sales-orders/{salesOrderId}/notes', summary: 'Get all notes for a sales order', pathParams: ['salesOrderId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
29
|
+
'create-notes': { method: 'POST', path: '/v1/sales-orders/{salesOrderId}/notes', summary: 'Create a note for a sales order', pathParams: ['salesOrderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
30
|
+
'get-notes': { method: 'GET', path: '/v1/sales-orders/{salesOrderId}/notes/{noteId}', summary: 'Get a specific note for a sales order', pathParams: ['salesOrderId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
31
|
+
'update-notes': { method: 'PATCH', path: '/v1/sales-orders/{salesOrderId}/notes/{noteId}', summary: 'Update a note for a sales order', pathParams: ['salesOrderId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
32
|
+
'delete-notes': { method: 'DELETE', path: '/v1/sales-orders/{salesOrderId}/notes/{noteId}', summary: 'Delete a note from a sales order', pathParams: ['salesOrderId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
33
|
+
'list-files': { method: 'GET', path: '/v1/sales-orders/{parentId}/files', summary: 'List file attachments for a sales order', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
34
|
+
'create-files': { method: 'POST', path: '/v1/sales-orders/{parentId}/files', summary: 'Upload a file attachment to a sales order', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, bodyArrayFields: [] },
|
|
35
|
+
'get-files': { method: 'GET', path: '/v1/sales-orders/{parentId}/files/{fileId}', summary: 'Get a file attachment for a sales order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
36
|
+
'update-files': { method: 'PATCH', path: '/v1/sales-orders/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a sales order', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
37
|
+
'delete-files': { method: 'DELETE', path: '/v1/sales-orders/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a sales order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
38
|
+
'files-download': { method: 'GET', path: '/v1/sales-orders/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a sales order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
38
39
|
};
|
|
39
40
|
export default class SalesOrders extends Command {
|
|
40
41
|
static description = 'Manage sales orders';
|
|
@@ -155,15 +156,7 @@ export default class SalesOrders extends Command {
|
|
|
155
156
|
if (eqIndex === -1)
|
|
156
157
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
157
158
|
const key = f.slice(0, eqIndex);
|
|
158
|
-
|
|
159
|
-
// Check for operator prefix (with or without $)
|
|
160
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
161
|
-
if (opMatch) {
|
|
162
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
value = `\$eq:${value}`;
|
|
166
|
-
}
|
|
159
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
167
160
|
const filterKey = `filter.${key}`;
|
|
168
161
|
if (filterKey in queryParams) {
|
|
169
162
|
// Multiple filters on same field: collect as array
|
|
@@ -196,7 +189,9 @@ export default class SalesOrders extends Command {
|
|
|
196
189
|
data = form;
|
|
197
190
|
}
|
|
198
191
|
else if (endpoint.hasBody) {
|
|
199
|
-
|
|
192
|
+
// For binary-response endpoints, --file is the output path, not a JSON
|
|
193
|
+
// input file — skip the read-as-body branch.
|
|
194
|
+
if (flags.file && !endpoint.isBinaryResponse) {
|
|
200
195
|
const fsModule = await import('node:fs');
|
|
201
196
|
try {
|
|
202
197
|
data = JSON.parse(fsModule.readFileSync(flags.file, 'utf-8'));
|
|
@@ -218,6 +213,7 @@ export default class SalesOrders extends Command {
|
|
|
218
213
|
if (!data)
|
|
219
214
|
data = {};
|
|
220
215
|
const body = data;
|
|
216
|
+
const arrayFields = new Set(endpoint.bodyArrayFields);
|
|
221
217
|
for (const pair of flags.set) {
|
|
222
218
|
const eqIndex = pair.indexOf('=');
|
|
223
219
|
if (eqIndex === -1)
|
|
@@ -229,6 +225,19 @@ export default class SalesOrders extends Command {
|
|
|
229
225
|
value = JSON.parse(value);
|
|
230
226
|
}
|
|
231
227
|
catch { /* keep as string */ }
|
|
228
|
+
// For top-level keys typed as array in the API schema, split bare
|
|
229
|
+
// comma-separated input (e.g. "1171,1170" -> ["1171","1170"]) and
|
|
230
|
+
// wrap a lone scalar in a single-element array. Skips when --set
|
|
231
|
+
// already provided a JSON array.
|
|
232
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
233
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
234
|
+
if (typeof value === 'string') {
|
|
235
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
236
|
+
}
|
|
237
|
+
else if (value !== undefined && value !== null) {
|
|
238
|
+
value = [value];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
232
241
|
// Support nested keys like "address.city"
|
|
233
242
|
const keys = key.split('.');
|
|
234
243
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
@@ -279,6 +288,26 @@ export default class SalesOrders extends Command {
|
|
|
279
288
|
process.stderr.write('\n');
|
|
280
289
|
}
|
|
281
290
|
try {
|
|
291
|
+
// Binary response: download to file
|
|
292
|
+
if (endpoint.isBinaryResponse) {
|
|
293
|
+
if (!flags.file) {
|
|
294
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx).`);
|
|
295
|
+
}
|
|
296
|
+
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
297
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
298
|
+
data,
|
|
299
|
+
contextName: flags.context,
|
|
300
|
+
binary: true,
|
|
301
|
+
});
|
|
302
|
+
const fsModule = await import('node:fs');
|
|
303
|
+
const binData = binResponse.data;
|
|
304
|
+
const buffer = Buffer.isBuffer(binData)
|
|
305
|
+
? binData
|
|
306
|
+
: Buffer.from(binData);
|
|
307
|
+
fsModule.writeFileSync(flags.file, buffer);
|
|
308
|
+
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
282
311
|
const response = await apiRequest(endpoint.method, apiPath, {
|
|
283
312
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
284
313
|
data,
|
|
@@ -369,6 +398,7 @@ export default class SalesOrders extends Command {
|
|
|
369
398
|
}
|
|
370
399
|
else if (flags.set && flags.set.length > 0) {
|
|
371
400
|
const item = {};
|
|
401
|
+
const arrayFields = new Set(ENDPOINTS['create']?.bodyArrayFields ?? []);
|
|
372
402
|
for (const pair of flags.set) {
|
|
373
403
|
const eqIndex = pair.indexOf('=');
|
|
374
404
|
if (eqIndex === -1)
|
|
@@ -379,6 +409,15 @@ export default class SalesOrders extends Command {
|
|
|
379
409
|
value = JSON.parse(value);
|
|
380
410
|
}
|
|
381
411
|
catch { /* keep as string */ }
|
|
412
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
413
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
414
|
+
if (typeof value === 'string') {
|
|
415
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
416
|
+
}
|
|
417
|
+
else if (value !== undefined && value !== null) {
|
|
418
|
+
value = [value];
|
|
419
|
+
}
|
|
420
|
+
}
|
|
382
421
|
const keys = key.split('.');
|
|
383
422
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
384
423
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -528,14 +567,7 @@ export default class SalesOrders extends Command {
|
|
|
528
567
|
if (eqIndex === -1)
|
|
529
568
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
530
569
|
const key = f.slice(0, eqIndex);
|
|
531
|
-
|
|
532
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
533
|
-
if (opMatch) {
|
|
534
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
535
|
-
}
|
|
536
|
-
else {
|
|
537
|
-
value = `\$eq:${value}`;
|
|
538
|
-
}
|
|
570
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
539
571
|
const filterKey = `filter.${key}`;
|
|
540
572
|
if (filterKey in baseParams) {
|
|
541
573
|
const existing = baseParams[filterKey];
|
|
@@ -712,6 +744,7 @@ export default class SalesOrders extends Command {
|
|
|
712
744
|
}
|
|
713
745
|
else if (flags.set && flags.set.length > 0) {
|
|
714
746
|
const item = {};
|
|
747
|
+
const arrayFields = new Set(ENDPOINTS[`create-${suffix}`]?.bodyArrayFields ?? []);
|
|
715
748
|
for (const pair of flags.set) {
|
|
716
749
|
const eqIndex = pair.indexOf('=');
|
|
717
750
|
if (eqIndex === -1)
|
|
@@ -722,6 +755,15 @@ export default class SalesOrders extends Command {
|
|
|
722
755
|
value = JSON.parse(value);
|
|
723
756
|
}
|
|
724
757
|
catch { /* keep as string */ }
|
|
758
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
759
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
760
|
+
if (typeof value === 'string') {
|
|
761
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
762
|
+
}
|
|
763
|
+
else if (value !== undefined && value !== null) {
|
|
764
|
+
value = [value];
|
|
765
|
+
}
|
|
766
|
+
}
|
|
725
767
|
const keys = key.split('.');
|
|
726
768
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
727
769
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -841,35 +883,62 @@ export default class SalesOrders extends Command {
|
|
|
841
883
|
this.log('');
|
|
842
884
|
this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
|
|
843
885
|
}
|
|
886
|
+
parseFilterValue(value) {
|
|
887
|
+
// Recognises the nestjs-paginate operator set (with or without the leading $)
|
|
888
|
+
// and recursively unwraps chained operators like 'not:null' -> '$not:$null'
|
|
889
|
+
// or 'not:like:John' -> '$not:$like:John'. Falls back to '$eq:<value>' when
|
|
890
|
+
// no operator prefix is present.
|
|
891
|
+
const opRe = /^\$?(and|or|not|eq|gt|gte|in|null|lt|lte|btw|ne|ilike|like|sw|contains)(?::|$)/;
|
|
892
|
+
const m = value.match(opRe);
|
|
893
|
+
if (!m)
|
|
894
|
+
return `\$eq:${value}`;
|
|
895
|
+
const op = m[1];
|
|
896
|
+
const rest = value.slice(m[0].length);
|
|
897
|
+
if (rest === '')
|
|
898
|
+
return `\$${op}`;
|
|
899
|
+
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
900
|
+
}
|
|
844
901
|
filterFields(data, fields) {
|
|
845
902
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
846
903
|
const pick = (obj, keys) => {
|
|
847
|
-
|
|
904
|
+
// Group keys by their first segment so sibling sub-paths
|
|
905
|
+
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
906
|
+
// and don't clobber each other on merge.
|
|
907
|
+
const directKeys = new Set();
|
|
908
|
+
const nestedKeys = new Map();
|
|
848
909
|
for (const key of keys) {
|
|
849
910
|
if (key.split('.').some(isUnsafeKey))
|
|
850
911
|
continue;
|
|
851
912
|
if (key.includes('.')) {
|
|
852
|
-
// Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
|
|
853
913
|
const [first, ...rest] = key.split('.');
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
...pick(item, [restKey]),
|
|
863
|
-
}));
|
|
864
|
-
}
|
|
865
|
-
else {
|
|
866
|
-
const nested = pick(val, [restKey]);
|
|
867
|
-
result[first] = { ...(result[first] || {}), ...nested };
|
|
868
|
-
}
|
|
914
|
+
if (isUnsafeKey(first))
|
|
915
|
+
continue;
|
|
916
|
+
const list = nestedKeys.get(first);
|
|
917
|
+
if (list) {
|
|
918
|
+
list.push(rest.join('.'));
|
|
919
|
+
}
|
|
920
|
+
else {
|
|
921
|
+
nestedKeys.set(first, [rest.join('.')]);
|
|
869
922
|
}
|
|
870
923
|
}
|
|
871
|
-
else
|
|
924
|
+
else {
|
|
925
|
+
directKeys.add(key);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
const result = {};
|
|
929
|
+
for (const key of directKeys) {
|
|
930
|
+
if (key in obj)
|
|
872
931
|
result[key] = obj[key];
|
|
932
|
+
}
|
|
933
|
+
for (const [first, restKeys] of nestedKeys) {
|
|
934
|
+
const val = obj[first];
|
|
935
|
+
if (!val || typeof val !== 'object')
|
|
936
|
+
continue;
|
|
937
|
+
if (Array.isArray(val)) {
|
|
938
|
+
result[first] = val.map(item => pick(item, restKeys));
|
|
939
|
+
}
|
|
940
|
+
else {
|
|
941
|
+
result[first] = pick(val, restKeys);
|
|
873
942
|
}
|
|
874
943
|
}
|
|
875
944
|
return result;
|
package/dist/commands/switch.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { select } from '@inquirer/prompts';
|
|
1
|
+
import { confirm, select } from '@inquirer/prompts';
|
|
2
2
|
import { Command } from '@oclif/core';
|
|
3
|
-
import {
|
|
3
|
+
import { loginAndSwitchTenant } from '../lib/auth.js';
|
|
4
|
+
import { getContext, getCurrentContext, listContexts, setCurrentContext } from '../lib/config.js';
|
|
4
5
|
export default class Switch extends Command {
|
|
5
6
|
static description = 'Switch context and tenant interactively';
|
|
6
7
|
static examples = ['<%= config.bin %> switch'];
|
|
@@ -34,8 +35,62 @@ export default class Switch extends Command {
|
|
|
34
35
|
setCurrentContext(selectedContext);
|
|
35
36
|
this.log(`Switched to context: ${selectedContext}`);
|
|
36
37
|
}
|
|
37
|
-
// Step 2: Select tenant within chosen context
|
|
38
|
+
// Step 2: Select tenant within chosen context.
|
|
39
|
+
// If the session is expired, prompt the user to re-login and then retry.
|
|
38
40
|
const { default: AuthTenant } = await import('./auth/tenant.js');
|
|
41
|
+
try {
|
|
42
|
+
await AuthTenant.run([`--context=${selectedContext}`]);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (!isSessionExpired(error))
|
|
46
|
+
throw error;
|
|
47
|
+
await this.handleSessionExpired(selectedContext);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async handleSessionExpired(selectedContext) {
|
|
51
|
+
const shouldLogin = await confirm({
|
|
52
|
+
default: true,
|
|
53
|
+
message: 'Session expired. Do you want to login again?',
|
|
54
|
+
});
|
|
55
|
+
if (!shouldLogin)
|
|
56
|
+
return;
|
|
57
|
+
// If we have a cached tenant list from a previous session, let the user pick
|
|
58
|
+
// tenant FIRST. Then run the combined login + tenant-switch flow which uses
|
|
59
|
+
// a single browser tab (chained PKCE rounds with a meta-refresh redirect).
|
|
60
|
+
const ctx = getContext(selectedContext);
|
|
61
|
+
const cached = ctx?.cachedTenants ?? [];
|
|
62
|
+
if (cached.length > 1) {
|
|
63
|
+
const sorted = [...cached].sort((a, b) => {
|
|
64
|
+
if (a.tenantId === ctx?.tenant_id)
|
|
65
|
+
return -1;
|
|
66
|
+
if (b.tenantId === ctx?.tenant_id)
|
|
67
|
+
return 1;
|
|
68
|
+
return a.name.localeCompare(b.name);
|
|
69
|
+
});
|
|
70
|
+
const selectedTenant = await select({
|
|
71
|
+
choices: sorted.map((t) => ({
|
|
72
|
+
name: t.tenantId === ctx?.tenant_id ? `${t.name} (previous)` : t.name,
|
|
73
|
+
value: t.tenantId,
|
|
74
|
+
})),
|
|
75
|
+
message: 'Select a tenant:',
|
|
76
|
+
});
|
|
77
|
+
const selectedName = cached.find((t) => t.tenantId === selectedTenant)?.name;
|
|
78
|
+
this.log(`Logging in and switching to ${selectedName ?? selectedTenant}…`);
|
|
79
|
+
if (ctx) {
|
|
80
|
+
await loginAndSwitchTenant(ctx, selectedTenant);
|
|
81
|
+
}
|
|
82
|
+
this.log(`Successfully switched to tenant ${selectedName ? `${selectedName} (${selectedTenant})` : selectedTenant}`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// No cached tenant list (or only one tenant) — fall back to the two-step
|
|
86
|
+
// flow: login first, then run the normal tenant selector.
|
|
87
|
+
const { default: AuthLogin } = await import('./auth/login.js');
|
|
88
|
+
const { default: AuthTenant } = await import('./auth/tenant.js');
|
|
89
|
+
await AuthLogin.run([`--context=${selectedContext}`]);
|
|
39
90
|
await AuthTenant.run([`--context=${selectedContext}`]);
|
|
40
91
|
}
|
|
41
92
|
}
|
|
93
|
+
function isSessionExpired(error) {
|
|
94
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
95
|
+
return /session expired/i.test(message) || /not logged in/i.test(message);
|
|
96
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
export default class
|
|
2
|
+
export default class Tags extends Command {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static args: {
|
|
@@ -30,6 +30,7 @@ export default class ProcessingLogs extends Command {
|
|
|
30
30
|
private runExport;
|
|
31
31
|
private runCamt053Import;
|
|
32
32
|
private runNestedImport;
|
|
33
|
+
private parseFilterValue;
|
|
33
34
|
private filterFields;
|
|
34
35
|
private sanitizeOutput;
|
|
35
36
|
private getColumns;
|