@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
package/dist/commands/ledgers.js
CHANGED
|
@@ -8,22 +8,22 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = ["notes", "closings"];
|
|
9
9
|
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-closings", "create-closings", "get-closings", "update-closings", "closings-close", "closings-reopen"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list': { method: 'GET', path: '/v1/ledgers', summary: 'List all ledgers', pathParams: [], hasBody: false, isMultipart: false },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/ledgers', summary: 'Create a new ledger', pathParams: [], hasBody: true, isMultipart: false },
|
|
13
|
-
'get': { method: 'GET', path: '/v1/ledgers/{id}', summary: 'Get a ledger by ID', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/ledgers/{id}', summary: 'Update a ledger', pathParams: ['id'], hasBody: true, isMultipart: false },
|
|
15
|
-
'delete': { method: 'DELETE', path: '/v1/ledgers/{id}', summary: 'Delete a ledger', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
16
|
-
'list-notes': { method: 'GET', path: '/v1/ledgers/{ledgerId}/notes', summary: 'Get all notes for a ledger', pathParams: ['ledgerId'], hasBody: false, isMultipart: false },
|
|
17
|
-
'create-notes': { method: 'POST', path: '/v1/ledgers/{ledgerId}/notes', summary: 'Create a note for a ledger', pathParams: ['ledgerId'], hasBody: true, isMultipart: false },
|
|
18
|
-
'get-notes': { method: 'GET', path: '/v1/ledgers/{ledgerId}/notes/{noteId}', summary: 'Get a specific note for a ledger', pathParams: ['ledgerId', 'noteId'], hasBody: false, isMultipart: false },
|
|
19
|
-
'update-notes': { method: 'PATCH', path: '/v1/ledgers/{ledgerId}/notes/{noteId}', summary: 'Update a note for a ledger', pathParams: ['ledgerId', 'noteId'], hasBody: true, isMultipart: false },
|
|
20
|
-
'delete-notes': { method: 'DELETE', path: '/v1/ledgers/{ledgerId}/notes/{noteId}', summary: 'Delete a note from a ledger', pathParams: ['ledgerId', 'noteId'], hasBody: false, isMultipart: false },
|
|
21
|
-
'list-closings': { method: 'GET', path: '/v1/ledgers/{ledgerId}/closings', summary: 'List all closings for a ledger, sorted by period end ascending', pathParams: ['ledgerId'], hasBody: false, isMultipart: false },
|
|
22
|
-
'create-closings': { method: 'POST', path: '/v1/ledgers/{ledgerId}/closings', summary: 'Create a new closing in DRAFT status', pathParams: ['ledgerId'], hasBody: true, isMultipart: false },
|
|
23
|
-
'get-closings': { method: 'GET', path: '/v1/ledgers/{ledgerId}/closings/{id}', summary: 'Get a single closing by ID', pathParams: ['ledgerId', 'id'], hasBody: false, isMultipart: false },
|
|
24
|
-
'update-closings': { method: 'PATCH', path: '/v1/ledgers/{ledgerId}/closings/{id}', summary: 'Update closing description', pathParams: ['ledgerId', 'id'], hasBody: true, isMultipart: false },
|
|
25
|
-
'closings-close': { method: 'POST', path: '/v1/ledgers/{ledgerId}/closings/{id}/close', summary: 'Transition closing to CLOSED. Computes netPnl from POSTED journals. Valid from DRAFT or REOPENED.', pathParams: ['ledgerId', 'id'], hasBody: true, isMultipart: false },
|
|
26
|
-
'closings-reopen': { method: 'POST', path: '/v1/ledgers/{ledgerId}/closings/{id}/reopen', summary: 'Transition closing to REOPENED. netPnl snapshot is preserved. Valid from CLOSED only.', pathParams: ['ledgerId', 'id'], hasBody: true, isMultipart: false },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/ledgers', summary: 'List all ledgers', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/ledgers', summary: 'Create a new ledger', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['notes'] },
|
|
13
|
+
'get': { method: 'GET', path: '/v1/ledgers/{id}', summary: 'Get a ledger by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
+
'update': { method: 'PATCH', path: '/v1/ledgers/{id}', summary: 'Update a ledger', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
+
'delete': { method: 'DELETE', path: '/v1/ledgers/{id}', summary: 'Delete a ledger', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
|
+
'list-notes': { method: 'GET', path: '/v1/ledgers/{ledgerId}/notes', summary: 'Get all notes for a ledger', pathParams: ['ledgerId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
|
+
'create-notes': { method: 'POST', path: '/v1/ledgers/{ledgerId}/notes', summary: 'Create a note for a ledger', pathParams: ['ledgerId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
18
|
+
'get-notes': { method: 'GET', path: '/v1/ledgers/{ledgerId}/notes/{noteId}', summary: 'Get a specific note for a ledger', pathParams: ['ledgerId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
19
|
+
'update-notes': { method: 'PATCH', path: '/v1/ledgers/{ledgerId}/notes/{noteId}', summary: 'Update a note for a ledger', pathParams: ['ledgerId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
|
+
'delete-notes': { method: 'DELETE', path: '/v1/ledgers/{ledgerId}/notes/{noteId}', summary: 'Delete a note from a ledger', pathParams: ['ledgerId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
21
|
+
'list-closings': { method: 'GET', path: '/v1/ledgers/{ledgerId}/closings', summary: 'List all closings for a ledger, sorted by period end ascending', pathParams: ['ledgerId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
22
|
+
'create-closings': { method: 'POST', path: '/v1/ledgers/{ledgerId}/closings', summary: 'Create a new closing in DRAFT status', pathParams: ['ledgerId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
23
|
+
'get-closings': { method: 'GET', path: '/v1/ledgers/{ledgerId}/closings/{id}', summary: 'Get a single closing by ID', pathParams: ['ledgerId', 'id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
24
|
+
'update-closings': { method: 'PATCH', path: '/v1/ledgers/{ledgerId}/closings/{id}', summary: 'Update closing description', pathParams: ['ledgerId', 'id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
25
|
+
'closings-close': { method: 'POST', path: '/v1/ledgers/{ledgerId}/closings/{id}/close', summary: 'Transition closing to CLOSED. Computes netPnl from POSTED journals. Valid from DRAFT or REOPENED.', pathParams: ['ledgerId', 'id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
26
|
+
'closings-reopen': { method: 'POST', path: '/v1/ledgers/{ledgerId}/closings/{id}/reopen', summary: 'Transition closing to REOPENED. netPnl snapshot is preserved. Valid from CLOSED only.', pathParams: ['ledgerId', 'id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
27
27
|
};
|
|
28
28
|
export default class Ledgers extends Command {
|
|
29
29
|
static description = 'Manage ledgers';
|
|
@@ -94,11 +94,6 @@ export default class Ledgers extends Command {
|
|
|
94
94
|
const endpoint = ENDPOINTS[args.action];
|
|
95
95
|
if (!endpoint)
|
|
96
96
|
this.error(`Unknown action: ${args.action}. Available: ${SUBCOMMANDS.join(', ')}`);
|
|
97
|
-
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
98
|
-
if (!context)
|
|
99
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
100
|
-
if (!context.tokens)
|
|
101
|
-
this.error('Not logged in. Run: aderis auth login');
|
|
102
97
|
let apiPath = endpoint.path;
|
|
103
98
|
for (let i = 0; i < endpoint.pathParams.length; i++) {
|
|
104
99
|
const param = endpoint.pathParams[i];
|
|
@@ -117,6 +112,11 @@ export default class Ledgers extends Command {
|
|
|
117
112
|
}
|
|
118
113
|
apiPath = apiPath.replace(`{${param}}`, String(value));
|
|
119
114
|
}
|
|
115
|
+
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
116
|
+
if (!context)
|
|
117
|
+
this.error('No context configured. Run: aderis auth login --new');
|
|
118
|
+
if (!context.tokens)
|
|
119
|
+
this.error('Not logged in. Run: aderis auth login');
|
|
120
120
|
const queryParams = {};
|
|
121
121
|
if (flags.page)
|
|
122
122
|
queryParams.page = flags.page;
|
|
@@ -143,15 +143,7 @@ export default class Ledgers extends Command {
|
|
|
143
143
|
if (eqIndex === -1)
|
|
144
144
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
145
145
|
const key = f.slice(0, eqIndex);
|
|
146
|
-
|
|
147
|
-
// Check for operator prefix (with or without $)
|
|
148
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
149
|
-
if (opMatch) {
|
|
150
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
value = `\$eq:${value}`;
|
|
154
|
-
}
|
|
146
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
155
147
|
const filterKey = `filter.${key}`;
|
|
156
148
|
if (filterKey in queryParams) {
|
|
157
149
|
// Multiple filters on same field: collect as array
|
|
@@ -184,7 +176,9 @@ export default class Ledgers extends Command {
|
|
|
184
176
|
data = form;
|
|
185
177
|
}
|
|
186
178
|
else if (endpoint.hasBody) {
|
|
187
|
-
|
|
179
|
+
// For binary-response endpoints, --file is the output path, not a JSON
|
|
180
|
+
// input file — skip the read-as-body branch.
|
|
181
|
+
if (flags.file && !endpoint.isBinaryResponse) {
|
|
188
182
|
const fsModule = await import('node:fs');
|
|
189
183
|
try {
|
|
190
184
|
data = JSON.parse(fsModule.readFileSync(flags.file, 'utf-8'));
|
|
@@ -206,6 +200,7 @@ export default class Ledgers extends Command {
|
|
|
206
200
|
if (!data)
|
|
207
201
|
data = {};
|
|
208
202
|
const body = data;
|
|
203
|
+
const arrayFields = new Set(endpoint.bodyArrayFields);
|
|
209
204
|
for (const pair of flags.set) {
|
|
210
205
|
const eqIndex = pair.indexOf('=');
|
|
211
206
|
if (eqIndex === -1)
|
|
@@ -217,6 +212,19 @@ export default class Ledgers extends Command {
|
|
|
217
212
|
value = JSON.parse(value);
|
|
218
213
|
}
|
|
219
214
|
catch { /* keep as string */ }
|
|
215
|
+
// For top-level keys typed as array in the API schema, split bare
|
|
216
|
+
// comma-separated input (e.g. "1171,1170" -> ["1171","1170"]) and
|
|
217
|
+
// wrap a lone scalar in a single-element array. Skips when --set
|
|
218
|
+
// already provided a JSON array.
|
|
219
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
220
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
221
|
+
if (typeof value === 'string') {
|
|
222
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
223
|
+
}
|
|
224
|
+
else if (value !== undefined && value !== null) {
|
|
225
|
+
value = [value];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
220
228
|
// Support nested keys like "address.city"
|
|
221
229
|
const keys = key.split('.');
|
|
222
230
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
@@ -267,6 +275,26 @@ export default class Ledgers extends Command {
|
|
|
267
275
|
process.stderr.write('\n');
|
|
268
276
|
}
|
|
269
277
|
try {
|
|
278
|
+
// Binary response: download to file
|
|
279
|
+
if (endpoint.isBinaryResponse) {
|
|
280
|
+
if (!flags.file) {
|
|
281
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx).`);
|
|
282
|
+
}
|
|
283
|
+
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
284
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
285
|
+
data,
|
|
286
|
+
contextName: flags.context,
|
|
287
|
+
binary: true,
|
|
288
|
+
});
|
|
289
|
+
const fsModule = await import('node:fs');
|
|
290
|
+
const binData = binResponse.data;
|
|
291
|
+
const buffer = Buffer.isBuffer(binData)
|
|
292
|
+
? binData
|
|
293
|
+
: Buffer.from(binData);
|
|
294
|
+
fsModule.writeFileSync(flags.file, buffer);
|
|
295
|
+
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
270
298
|
const response = await apiRequest(endpoint.method, apiPath, {
|
|
271
299
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
272
300
|
data,
|
|
@@ -357,6 +385,7 @@ export default class Ledgers extends Command {
|
|
|
357
385
|
}
|
|
358
386
|
else if (flags.set && flags.set.length > 0) {
|
|
359
387
|
const item = {};
|
|
388
|
+
const arrayFields = new Set(ENDPOINTS['create']?.bodyArrayFields ?? []);
|
|
360
389
|
for (const pair of flags.set) {
|
|
361
390
|
const eqIndex = pair.indexOf('=');
|
|
362
391
|
if (eqIndex === -1)
|
|
@@ -367,6 +396,15 @@ export default class Ledgers extends Command {
|
|
|
367
396
|
value = JSON.parse(value);
|
|
368
397
|
}
|
|
369
398
|
catch { /* keep as string */ }
|
|
399
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
400
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
401
|
+
if (typeof value === 'string') {
|
|
402
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
403
|
+
}
|
|
404
|
+
else if (value !== undefined && value !== null) {
|
|
405
|
+
value = [value];
|
|
406
|
+
}
|
|
407
|
+
}
|
|
370
408
|
const keys = key.split('.');
|
|
371
409
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
372
410
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -516,14 +554,7 @@ export default class Ledgers extends Command {
|
|
|
516
554
|
if (eqIndex === -1)
|
|
517
555
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
518
556
|
const key = f.slice(0, eqIndex);
|
|
519
|
-
|
|
520
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
521
|
-
if (opMatch) {
|
|
522
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
523
|
-
}
|
|
524
|
-
else {
|
|
525
|
-
value = `\$eq:${value}`;
|
|
526
|
-
}
|
|
557
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
527
558
|
const filterKey = `filter.${key}`;
|
|
528
559
|
if (filterKey in baseParams) {
|
|
529
560
|
const existing = baseParams[filterKey];
|
|
@@ -700,6 +731,7 @@ export default class Ledgers extends Command {
|
|
|
700
731
|
}
|
|
701
732
|
else if (flags.set && flags.set.length > 0) {
|
|
702
733
|
const item = {};
|
|
734
|
+
const arrayFields = new Set(ENDPOINTS[`create-${suffix}`]?.bodyArrayFields ?? []);
|
|
703
735
|
for (const pair of flags.set) {
|
|
704
736
|
const eqIndex = pair.indexOf('=');
|
|
705
737
|
if (eqIndex === -1)
|
|
@@ -710,6 +742,15 @@ export default class Ledgers extends Command {
|
|
|
710
742
|
value = JSON.parse(value);
|
|
711
743
|
}
|
|
712
744
|
catch { /* keep as string */ }
|
|
745
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
746
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
747
|
+
if (typeof value === 'string') {
|
|
748
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
749
|
+
}
|
|
750
|
+
else if (value !== undefined && value !== null) {
|
|
751
|
+
value = [value];
|
|
752
|
+
}
|
|
753
|
+
}
|
|
713
754
|
const keys = key.split('.');
|
|
714
755
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
715
756
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -829,6 +870,21 @@ export default class Ledgers extends Command {
|
|
|
829
870
|
this.log('');
|
|
830
871
|
this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
|
|
831
872
|
}
|
|
873
|
+
parseFilterValue(value) {
|
|
874
|
+
// Recognises the nestjs-paginate operator set (with or without the leading $)
|
|
875
|
+
// and recursively unwraps chained operators like 'not:null' -> '$not:$null'
|
|
876
|
+
// or 'not:like:John' -> '$not:$like:John'. Falls back to '$eq:<value>' when
|
|
877
|
+
// no operator prefix is present.
|
|
878
|
+
const opRe = /^\$?(and|or|not|eq|gt|gte|in|null|lt|lte|btw|ne|ilike|like|sw|contains)(?::|$)/;
|
|
879
|
+
const m = value.match(opRe);
|
|
880
|
+
if (!m)
|
|
881
|
+
return `\$eq:${value}`;
|
|
882
|
+
const op = m[1];
|
|
883
|
+
const rest = value.slice(m[0].length);
|
|
884
|
+
if (rest === '')
|
|
885
|
+
return `\$${op}`;
|
|
886
|
+
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
887
|
+
}
|
|
832
888
|
filterFields(data, fields) {
|
|
833
889
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
834
890
|
const pick = (obj, keys) => {
|
|
@@ -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/office-locations', summary: 'Get all office locations with pagination', pathParams: [], hasBody: false, isMultipart: false },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/office-locations', summary: 'Create a new office location', pathParams: [], hasBody: true, isMultipart: false },
|
|
13
|
-
'get': { method: 'GET', path: '/v1/office-locations/{id}', summary: 'Get an office location by ID', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/office-locations/{id}', summary: 'Update an office location', pathParams: ['id'], hasBody: true, isMultipart: false },
|
|
15
|
-
'delete': { method: 'DELETE', path: '/v1/office-locations/{id}', summary: 'Delete an office location', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/office-locations', summary: 'Get all office locations with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/office-locations', summary: 'Create a new office location', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
13
|
+
'get': { method: 'GET', path: '/v1/office-locations/{id}', summary: 'Get an office location by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
+
'update': { method: 'PATCH', path: '/v1/office-locations/{id}', summary: 'Update an office location', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
+
'delete': { method: 'DELETE', path: '/v1/office-locations/{id}', summary: 'Delete an office location', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
16
|
};
|
|
17
17
|
export default class OfficeLocations extends Command {
|
|
18
18
|
static description = 'Manage office locations';
|
|
@@ -81,11 +81,6 @@ export default class OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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) => {
|