@aderis-erp/cli 1.0.3 → 1.0.7
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.d.ts +1 -0
- package/dist/commands/access-groups.js +84 -28
- package/dist/commands/{async-endpoints-internal.d.ts → account-transaction-allocations.d.ts} +3 -2
- package/dist/commands/{async-endpoints-internal.js → account-transaction-allocations.js} +93 -33
- package/dist/commands/account-transactions.d.ts +1 -0
- package/dist/commands/account-transactions.js +96 -40
- package/dist/commands/accounts.d.ts +1 -0
- package/dist/commands/accounts.js +89 -33
- package/dist/commands/agent-runs.d.ts +1 -0
- package/dist/commands/agent-runs.js +85 -27
- package/dist/commands/audit-log.d.ts +1 -0
- package/dist/commands/audit-log.js +82 -26
- package/dist/commands/auth/login.js +1 -2
- package/dist/commands/auth/tenant.d.ts +1 -1
- package/dist/commands/auth/tenant.js +11 -11
- package/dist/commands/auth.js +1 -1
- package/dist/commands/bank-accounts.d.ts +1 -0
- package/dist/commands/bank-accounts.js +96 -40
- package/dist/commands/bank-integrations.d.ts +1 -0
- package/dist/commands/bank-integrations.js +97 -41
- package/dist/commands/business-partners---contacts.d.ts +1 -0
- package/dist/commands/business-partners---contacts.js +89 -33
- package/dist/commands/business-partners.d.ts +1 -0
- package/dist/commands/business-partners.js +89 -33
- package/dist/commands/credit-card-accounts.d.ts +1 -0
- package/dist/commands/credit-card-accounts.js +94 -38
- package/dist/commands/documents.d.ts +1 -0
- package/dist/commands/documents.js +85 -29
- package/dist/commands/dunning-configs.d.ts +1 -0
- package/dist/commands/dunning-configs.js +84 -28
- package/dist/commands/dunning-reminders.d.ts +1 -0
- package/dist/commands/dunning-reminders.js +87 -31
- package/dist/commands/dunning-runs.d.ts +1 -0
- package/dist/commands/dunning-runs.js +80 -24
- package/dist/commands/employees.d.ts +1 -0
- package/dist/commands/employees.js +111 -55
- package/dist/commands/event-processor-mappings.d.ts +1 -0
- package/dist/commands/event-processor-mappings.js +84 -28
- package/dist/commands/files-internal.d.ts +1 -0
- package/dist/commands/files-internal.js +88 -32
- package/dist/commands/health.d.ts +1 -0
- package/dist/commands/health.js +84 -28
- package/dist/commands/inbox.d.ts +1 -0
- package/dist/commands/inbox.js +84 -28
- package/dist/commands/integrations.d.ts +1 -0
- package/dist/commands/integrations.js +82 -26
- package/dist/commands/item-categories.d.ts +1 -0
- package/dist/commands/item-categories.js +84 -28
- package/dist/commands/items.d.ts +1 -0
- package/dist/commands/items.js +84 -28
- package/dist/commands/journal-lines.d.ts +1 -0
- package/dist/commands/journal-lines.js +80 -24
- package/dist/commands/journals.d.ts +1 -0
- package/dist/commands/journals.js +98 -42
- package/dist/commands/ledgers.d.ts +1 -0
- package/dist/commands/ledgers.js +95 -39
- package/dist/commands/office-locations.d.ts +1 -0
- package/dist/commands/office-locations.js +84 -28
- package/dist/commands/payment-method-ledger-mappings.d.ts +1 -0
- package/dist/commands/payment-method-ledger-mappings.js +84 -28
- package/dist/commands/payment-methods.d.ts +1 -0
- package/dist/commands/payment-methods.js +84 -28
- package/dist/commands/payment-runs.d.ts +1 -0
- package/dist/commands/payment-runs.js +97 -41
- package/dist/commands/payment-terms.d.ts +1 -0
- package/dist/commands/payment-terms.js +85 -29
- package/dist/commands/price-lists.d.ts +1 -0
- package/dist/commands/price-lists.js +89 -33
- package/dist/commands/processor-mappings.d.ts +1 -0
- package/dist/commands/processor-mappings.js +83 -27
- package/dist/commands/processors.d.ts +1 -0
- package/dist/commands/processors.js +88 -32
- package/dist/commands/reconciliation.d.ts +1 -0
- package/dist/commands/reconciliation.js +85 -29
- package/dist/commands/reports.d.ts +1 -0
- package/dist/commands/reports.js +85 -26
- package/dist/commands/salary-types.d.ts +1 -0
- package/dist/commands/salary-types.js +84 -28
- package/dist/commands/sales-invoices.d.ts +1 -0
- package/dist/commands/sales-invoices.js +109 -53
- package/dist/commands/sales-orders.d.ts +1 -0
- package/dist/commands/sales-orders.js +107 -51
- package/dist/commands/tags.d.ts +1 -0
- package/dist/commands/tags.js +84 -28
- package/dist/commands/tax-codes.d.ts +1 -0
- package/dist/commands/tax-codes.js +87 -31
- package/dist/commands/tax-matrix.d.ts +1 -0
- package/dist/commands/tax-matrix.js +84 -28
- package/dist/commands/tenants.d.ts +1 -0
- package/dist/commands/tenants.js +84 -28
- package/dist/commands/text-templates.d.ts +1 -0
- package/dist/commands/text-templates.js +84 -28
- package/dist/commands/tools.d.ts +1 -0
- package/dist/commands/tools.js +81 -25
- package/dist/commands/vat-declaration.d.ts +41 -0
- package/dist/commands/vat-declaration.js +1079 -0
- package/dist/commands/vendor-invoices.d.ts +1 -0
- package/dist/commands/vendor-invoices.js +113 -57
- package/dist/commands/webhooks.d.ts +1 -0
- package/dist/commands/webhooks.js +86 -30
- package/dist/commands/workflows.d.ts +1 -0
- package/dist/commands/workflows.js +82 -26
- package/dist/lib/api-client.d.ts +6 -1
- package/dist/lib/api-client.js +23 -5
- package/dist/lib/auth.js +18 -0
- package/dist/lib/config.js +1 -1
- package/oclif.manifest.json +229 -52
- package/package.json +15 -13
- package/dist/index 2.js +0 -1
- package/dist/index.d 2.ts +0 -1
|
@@ -8,34 +8,34 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = ["line-items", "notes", "files"];
|
|
9
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
|
-
'pdf': { method: 'GET', path: '/v1/sales-orders/{id}/pdf', summary: '
|
|
17
|
-
'submit': { method: 'POST', path: '/v1/sales-orders/{id}/submit', summary: 'Submit order for approval (DRAFT → PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
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 },
|
|
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 },
|
|
20
|
-
'terminate': { method: 'POST', path: '/v1/sales-orders/{id}/terminate', summary: 'Terminate order (ACCEPTED → TERMINATED). Requires billingEndDate.', pathParams: ['id'], hasBody: true, isMultipart: false },
|
|
21
|
-
'cancel': { method: 'POST', path: '/v1/sales-orders/{id}/cancel', summary: 'Cancel order (any pre-active status → CANCELLED)', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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: [] },
|
|
39
39
|
};
|
|
40
40
|
export default class SalesOrders extends Command {
|
|
41
41
|
static description = 'Manage sales orders';
|
|
@@ -107,11 +107,6 @@ export default class SalesOrders extends Command {
|
|
|
107
107
|
const endpoint = ENDPOINTS[args.action];
|
|
108
108
|
if (!endpoint)
|
|
109
109
|
this.error(`Unknown action: ${args.action}. Available: ${SUBCOMMANDS.join(', ')}`);
|
|
110
|
-
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
111
|
-
if (!context)
|
|
112
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
113
|
-
if (!context.tokens)
|
|
114
|
-
this.error('Not logged in. Run: aderis auth login');
|
|
115
110
|
let apiPath = endpoint.path;
|
|
116
111
|
for (let i = 0; i < endpoint.pathParams.length; i++) {
|
|
117
112
|
const param = endpoint.pathParams[i];
|
|
@@ -130,6 +125,11 @@ export default class SalesOrders extends Command {
|
|
|
130
125
|
}
|
|
131
126
|
apiPath = apiPath.replace(`{${param}}`, String(value));
|
|
132
127
|
}
|
|
128
|
+
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
129
|
+
if (!context)
|
|
130
|
+
this.error('No context configured. Run: aderis auth login --new');
|
|
131
|
+
if (!context.tokens)
|
|
132
|
+
this.error('Not logged in. Run: aderis auth login');
|
|
133
133
|
const queryParams = {};
|
|
134
134
|
if (flags.page)
|
|
135
135
|
queryParams.page = flags.page;
|
|
@@ -156,15 +156,7 @@ export default class SalesOrders extends Command {
|
|
|
156
156
|
if (eqIndex === -1)
|
|
157
157
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
158
158
|
const key = f.slice(0, eqIndex);
|
|
159
|
-
|
|
160
|
-
// Check for operator prefix (with or without $)
|
|
161
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
162
|
-
if (opMatch) {
|
|
163
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
value = `\$eq:${value}`;
|
|
167
|
-
}
|
|
159
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
168
160
|
const filterKey = `filter.${key}`;
|
|
169
161
|
if (filterKey in queryParams) {
|
|
170
162
|
// Multiple filters on same field: collect as array
|
|
@@ -197,7 +189,9 @@ export default class SalesOrders extends Command {
|
|
|
197
189
|
data = form;
|
|
198
190
|
}
|
|
199
191
|
else if (endpoint.hasBody) {
|
|
200
|
-
|
|
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) {
|
|
201
195
|
const fsModule = await import('node:fs');
|
|
202
196
|
try {
|
|
203
197
|
data = JSON.parse(fsModule.readFileSync(flags.file, 'utf-8'));
|
|
@@ -219,6 +213,7 @@ export default class SalesOrders extends Command {
|
|
|
219
213
|
if (!data)
|
|
220
214
|
data = {};
|
|
221
215
|
const body = data;
|
|
216
|
+
const arrayFields = new Set(endpoint.bodyArrayFields);
|
|
222
217
|
for (const pair of flags.set) {
|
|
223
218
|
const eqIndex = pair.indexOf('=');
|
|
224
219
|
if (eqIndex === -1)
|
|
@@ -230,6 +225,19 @@ export default class SalesOrders extends Command {
|
|
|
230
225
|
value = JSON.parse(value);
|
|
231
226
|
}
|
|
232
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
|
+
}
|
|
233
241
|
// Support nested keys like "address.city"
|
|
234
242
|
const keys = key.split('.');
|
|
235
243
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
@@ -280,6 +288,26 @@ export default class SalesOrders extends Command {
|
|
|
280
288
|
process.stderr.write('\n');
|
|
281
289
|
}
|
|
282
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
|
+
}
|
|
283
311
|
const response = await apiRequest(endpoint.method, apiPath, {
|
|
284
312
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
285
313
|
data,
|
|
@@ -370,6 +398,7 @@ export default class SalesOrders extends Command {
|
|
|
370
398
|
}
|
|
371
399
|
else if (flags.set && flags.set.length > 0) {
|
|
372
400
|
const item = {};
|
|
401
|
+
const arrayFields = new Set(ENDPOINTS['create']?.bodyArrayFields ?? []);
|
|
373
402
|
for (const pair of flags.set) {
|
|
374
403
|
const eqIndex = pair.indexOf('=');
|
|
375
404
|
if (eqIndex === -1)
|
|
@@ -380,6 +409,15 @@ export default class SalesOrders extends Command {
|
|
|
380
409
|
value = JSON.parse(value);
|
|
381
410
|
}
|
|
382
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
|
+
}
|
|
383
421
|
const keys = key.split('.');
|
|
384
422
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
385
423
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -529,14 +567,7 @@ export default class SalesOrders extends Command {
|
|
|
529
567
|
if (eqIndex === -1)
|
|
530
568
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
531
569
|
const key = f.slice(0, eqIndex);
|
|
532
|
-
|
|
533
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
534
|
-
if (opMatch) {
|
|
535
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
536
|
-
}
|
|
537
|
-
else {
|
|
538
|
-
value = `\$eq:${value}`;
|
|
539
|
-
}
|
|
570
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
540
571
|
const filterKey = `filter.${key}`;
|
|
541
572
|
if (filterKey in baseParams) {
|
|
542
573
|
const existing = baseParams[filterKey];
|
|
@@ -713,6 +744,7 @@ export default class SalesOrders extends Command {
|
|
|
713
744
|
}
|
|
714
745
|
else if (flags.set && flags.set.length > 0) {
|
|
715
746
|
const item = {};
|
|
747
|
+
const arrayFields = new Set(ENDPOINTS[`create-${suffix}`]?.bodyArrayFields ?? []);
|
|
716
748
|
for (const pair of flags.set) {
|
|
717
749
|
const eqIndex = pair.indexOf('=');
|
|
718
750
|
if (eqIndex === -1)
|
|
@@ -723,6 +755,15 @@ export default class SalesOrders extends Command {
|
|
|
723
755
|
value = JSON.parse(value);
|
|
724
756
|
}
|
|
725
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
|
+
}
|
|
726
767
|
const keys = key.split('.');
|
|
727
768
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
728
769
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -842,6 +883,21 @@ export default class SalesOrders extends Command {
|
|
|
842
883
|
this.log('');
|
|
843
884
|
this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
|
|
844
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
|
+
}
|
|
845
901
|
filterFields(data, fields) {
|
|
846
902
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
847
903
|
const pick = (obj, keys) => {
|
package/dist/commands/tags.d.ts
CHANGED
package/dist/commands/tags.js
CHANGED
|
@@ -8,11 +8,11 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
9
|
const SUBCOMMANDS = [...["list", "create", "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/tags', summary: 'Get all tags with pagination', pathParams: [], hasBody: false, isMultipart: false },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/tags', summary: 'Create a new tag', pathParams: [], hasBody: true, isMultipart: false },
|
|
13
|
-
'get': { method: 'GET', path: '/v1/tags/{id}', summary: 'Get a tag by ID', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/tags/{id}', summary: 'Update a tag', pathParams: ['id'], hasBody: true, isMultipart: false },
|
|
15
|
-
'delete': { method: 'DELETE', path: '/v1/tags/{id}', summary: 'Delete a tag', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/tags', summary: 'Get all tags with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/tags', summary: 'Create a new tag', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
13
|
+
'get': { method: 'GET', path: '/v1/tags/{id}', summary: 'Get a tag by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
+
'update': { method: 'PATCH', path: '/v1/tags/{id}', summary: 'Update a tag', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
+
'delete': { method: 'DELETE', path: '/v1/tags/{id}', summary: 'Delete a tag', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
16
|
};
|
|
17
17
|
export default class Tags extends Command {
|
|
18
18
|
static description = 'Manage tags';
|
|
@@ -81,11 +81,6 @@ export default class Tags extends Command {
|
|
|
81
81
|
const endpoint = ENDPOINTS[args.action];
|
|
82
82
|
if (!endpoint)
|
|
83
83
|
this.error(`Unknown action: ${args.action}. Available: ${SUBCOMMANDS.join(', ')}`);
|
|
84
|
-
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
85
|
-
if (!context)
|
|
86
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
87
|
-
if (!context.tokens)
|
|
88
|
-
this.error('Not logged in. Run: aderis auth login');
|
|
89
84
|
let apiPath = endpoint.path;
|
|
90
85
|
for (let i = 0; i < endpoint.pathParams.length; i++) {
|
|
91
86
|
const param = endpoint.pathParams[i];
|
|
@@ -104,6 +99,11 @@ export default class Tags extends Command {
|
|
|
104
99
|
}
|
|
105
100
|
apiPath = apiPath.replace(`{${param}}`, String(value));
|
|
106
101
|
}
|
|
102
|
+
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
103
|
+
if (!context)
|
|
104
|
+
this.error('No context configured. Run: aderis auth login --new');
|
|
105
|
+
if (!context.tokens)
|
|
106
|
+
this.error('Not logged in. Run: aderis auth login');
|
|
107
107
|
const queryParams = {};
|
|
108
108
|
if (flags.page)
|
|
109
109
|
queryParams.page = flags.page;
|
|
@@ -130,15 +130,7 @@ export default class Tags extends Command {
|
|
|
130
130
|
if (eqIndex === -1)
|
|
131
131
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
132
132
|
const key = f.slice(0, eqIndex);
|
|
133
|
-
|
|
134
|
-
// Check for operator prefix (with or without $)
|
|
135
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
136
|
-
if (opMatch) {
|
|
137
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
value = `\$eq:${value}`;
|
|
141
|
-
}
|
|
133
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
142
134
|
const filterKey = `filter.${key}`;
|
|
143
135
|
if (filterKey in queryParams) {
|
|
144
136
|
// Multiple filters on same field: collect as array
|
|
@@ -171,7 +163,9 @@ export default class Tags extends Command {
|
|
|
171
163
|
data = form;
|
|
172
164
|
}
|
|
173
165
|
else if (endpoint.hasBody) {
|
|
174
|
-
|
|
166
|
+
// For binary-response endpoints, --file is the output path, not a JSON
|
|
167
|
+
// input file — skip the read-as-body branch.
|
|
168
|
+
if (flags.file && !endpoint.isBinaryResponse) {
|
|
175
169
|
const fsModule = await import('node:fs');
|
|
176
170
|
try {
|
|
177
171
|
data = JSON.parse(fsModule.readFileSync(flags.file, 'utf-8'));
|
|
@@ -193,6 +187,7 @@ export default class Tags extends Command {
|
|
|
193
187
|
if (!data)
|
|
194
188
|
data = {};
|
|
195
189
|
const body = data;
|
|
190
|
+
const arrayFields = new Set(endpoint.bodyArrayFields);
|
|
196
191
|
for (const pair of flags.set) {
|
|
197
192
|
const eqIndex = pair.indexOf('=');
|
|
198
193
|
if (eqIndex === -1)
|
|
@@ -204,6 +199,19 @@ export default class Tags extends Command {
|
|
|
204
199
|
value = JSON.parse(value);
|
|
205
200
|
}
|
|
206
201
|
catch { /* keep as string */ }
|
|
202
|
+
// For top-level keys typed as array in the API schema, split bare
|
|
203
|
+
// comma-separated input (e.g. "1171,1170" -> ["1171","1170"]) and
|
|
204
|
+
// wrap a lone scalar in a single-element array. Skips when --set
|
|
205
|
+
// already provided a JSON array.
|
|
206
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
207
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
208
|
+
if (typeof value === 'string') {
|
|
209
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
210
|
+
}
|
|
211
|
+
else if (value !== undefined && value !== null) {
|
|
212
|
+
value = [value];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
207
215
|
// Support nested keys like "address.city"
|
|
208
216
|
const keys = key.split('.');
|
|
209
217
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
@@ -254,6 +262,26 @@ export default class Tags extends Command {
|
|
|
254
262
|
process.stderr.write('\n');
|
|
255
263
|
}
|
|
256
264
|
try {
|
|
265
|
+
// Binary response: download to file
|
|
266
|
+
if (endpoint.isBinaryResponse) {
|
|
267
|
+
if (!flags.file) {
|
|
268
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx).`);
|
|
269
|
+
}
|
|
270
|
+
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
271
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
272
|
+
data,
|
|
273
|
+
contextName: flags.context,
|
|
274
|
+
binary: true,
|
|
275
|
+
});
|
|
276
|
+
const fsModule = await import('node:fs');
|
|
277
|
+
const binData = binResponse.data;
|
|
278
|
+
const buffer = Buffer.isBuffer(binData)
|
|
279
|
+
? binData
|
|
280
|
+
: Buffer.from(binData);
|
|
281
|
+
fsModule.writeFileSync(flags.file, buffer);
|
|
282
|
+
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
257
285
|
const response = await apiRequest(endpoint.method, apiPath, {
|
|
258
286
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
259
287
|
data,
|
|
@@ -344,6 +372,7 @@ export default class Tags extends Command {
|
|
|
344
372
|
}
|
|
345
373
|
else if (flags.set && flags.set.length > 0) {
|
|
346
374
|
const item = {};
|
|
375
|
+
const arrayFields = new Set(ENDPOINTS['create']?.bodyArrayFields ?? []);
|
|
347
376
|
for (const pair of flags.set) {
|
|
348
377
|
const eqIndex = pair.indexOf('=');
|
|
349
378
|
if (eqIndex === -1)
|
|
@@ -354,6 +383,15 @@ export default class Tags extends Command {
|
|
|
354
383
|
value = JSON.parse(value);
|
|
355
384
|
}
|
|
356
385
|
catch { /* keep as string */ }
|
|
386
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
387
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
388
|
+
if (typeof value === 'string') {
|
|
389
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
390
|
+
}
|
|
391
|
+
else if (value !== undefined && value !== null) {
|
|
392
|
+
value = [value];
|
|
393
|
+
}
|
|
394
|
+
}
|
|
357
395
|
const keys = key.split('.');
|
|
358
396
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
359
397
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -503,14 +541,7 @@ export default class Tags extends Command {
|
|
|
503
541
|
if (eqIndex === -1)
|
|
504
542
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
505
543
|
const key = f.slice(0, eqIndex);
|
|
506
|
-
|
|
507
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
508
|
-
if (opMatch) {
|
|
509
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
510
|
-
}
|
|
511
|
-
else {
|
|
512
|
-
value = `\$eq:${value}`;
|
|
513
|
-
}
|
|
544
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
514
545
|
const filterKey = `filter.${key}`;
|
|
515
546
|
if (filterKey in baseParams) {
|
|
516
547
|
const existing = baseParams[filterKey];
|
|
@@ -687,6 +718,7 @@ export default class Tags extends Command {
|
|
|
687
718
|
}
|
|
688
719
|
else if (flags.set && flags.set.length > 0) {
|
|
689
720
|
const item = {};
|
|
721
|
+
const arrayFields = new Set(ENDPOINTS[`create-${suffix}`]?.bodyArrayFields ?? []);
|
|
690
722
|
for (const pair of flags.set) {
|
|
691
723
|
const eqIndex = pair.indexOf('=');
|
|
692
724
|
if (eqIndex === -1)
|
|
@@ -697,6 +729,15 @@ export default class Tags extends Command {
|
|
|
697
729
|
value = JSON.parse(value);
|
|
698
730
|
}
|
|
699
731
|
catch { /* keep as string */ }
|
|
732
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
733
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
734
|
+
if (typeof value === 'string') {
|
|
735
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
736
|
+
}
|
|
737
|
+
else if (value !== undefined && value !== null) {
|
|
738
|
+
value = [value];
|
|
739
|
+
}
|
|
740
|
+
}
|
|
700
741
|
const keys = key.split('.');
|
|
701
742
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
702
743
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -816,6 +857,21 @@ export default class Tags extends Command {
|
|
|
816
857
|
this.log('');
|
|
817
858
|
this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
|
|
818
859
|
}
|
|
860
|
+
parseFilterValue(value) {
|
|
861
|
+
// Recognises the nestjs-paginate operator set (with or without the leading $)
|
|
862
|
+
// and recursively unwraps chained operators like 'not:null' -> '$not:$null'
|
|
863
|
+
// or 'not:like:John' -> '$not:$like:John'. Falls back to '$eq:<value>' when
|
|
864
|
+
// no operator prefix is present.
|
|
865
|
+
const opRe = /^\$?(and|or|not|eq|gt|gte|in|null|lt|lte|btw|ne|ilike|like|sw|contains)(?::|$)/;
|
|
866
|
+
const m = value.match(opRe);
|
|
867
|
+
if (!m)
|
|
868
|
+
return `\$eq:${value}`;
|
|
869
|
+
const op = m[1];
|
|
870
|
+
const rest = value.slice(m[0].length);
|
|
871
|
+
if (rest === '')
|
|
872
|
+
return `\$${op}`;
|
|
873
|
+
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
874
|
+
}
|
|
819
875
|
filterFields(data, fields) {
|
|
820
876
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
821
877
|
const pick = (obj, keys) => {
|