@aderis-erp/cli 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/access-groups.d.ts +8 -0
- package/dist/commands/access-groups.js +56 -8
- package/dist/commands/account-transaction-allocations.d.ts +8 -0
- package/dist/commands/account-transaction-allocations.js +52 -5
- package/dist/commands/account-transactions.d.ts +8 -0
- package/dist/commands/account-transactions.js +55 -7
- package/dist/commands/accounts.d.ts +8 -0
- package/dist/commands/accounts.js +52 -5
- package/dist/commands/agent-platform.d.ts +8 -0
- package/dist/commands/agent-platform.js +52 -5
- package/dist/commands/agent-runs.d.ts +8 -0
- package/dist/commands/agent-runs.js +52 -5
- package/dist/commands/analytics.d.ts +8 -0
- package/dist/commands/analytics.js +52 -5
- package/dist/commands/api-keys.d.ts +8 -0
- package/dist/commands/api-keys.js +52 -5
- package/dist/commands/async-jobs.d.ts +8 -0
- package/dist/commands/async-jobs.js +52 -5
- package/dist/commands/audit-log.d.ts +8 -0
- package/dist/commands/audit-log.js +52 -5
- package/dist/commands/bank-accounts.d.ts +8 -0
- package/dist/commands/bank-accounts.js +52 -5
- package/dist/commands/bank-integrations.d.ts +8 -0
- package/dist/commands/bank-integrations.js +52 -5
- package/dist/commands/business-partners.d.ts +9 -0
- package/dist/commands/business-partners.js +66 -9
- package/dist/commands/chat.d.ts +8 -0
- package/dist/commands/chat.js +52 -5
- package/dist/commands/credit-card-accounts.d.ts +8 -0
- package/dist/commands/credit-card-accounts.js +52 -5
- package/dist/commands/delivery-notes.d.ts +8 -0
- package/dist/commands/delivery-notes.js +52 -5
- package/dist/commands/dunning-configs.d.ts +8 -0
- package/dist/commands/dunning-configs.js +52 -5
- package/dist/commands/dunning-reminders.d.ts +8 -0
- package/dist/commands/dunning-reminders.js +52 -5
- package/dist/commands/dunning-runs.d.ts +8 -0
- package/dist/commands/dunning-runs.js +52 -5
- package/dist/commands/email-transactions.d.ts +8 -0
- package/dist/commands/email-transactions.js +52 -5
- package/dist/commands/employees.d.ts +8 -0
- package/dist/commands/employees.js +52 -5
- package/dist/commands/entity-configurations.d.ts +8 -0
- package/dist/commands/entity-configurations.js +52 -5
- package/dist/commands/event-processor-mappings.d.ts +8 -0
- package/dist/commands/event-processor-mappings.js +52 -5
- package/dist/commands/files.d.ts +8 -0
- package/dist/commands/files.js +53 -6
- package/dist/commands/health.d.ts +8 -0
- package/dist/commands/health.js +52 -5
- package/dist/commands/imports.d.ts +8 -0
- package/dist/commands/imports.js +53 -8
- package/dist/commands/inbox.d.ts +8 -0
- package/dist/commands/inbox.js +53 -7
- package/dist/commands/integration-identities.d.ts +8 -0
- package/dist/commands/integration-identities.js +52 -5
- package/dist/commands/integrations.d.ts +8 -0
- package/dist/commands/integrations.js +52 -5
- package/dist/commands/item-categories.d.ts +8 -0
- package/dist/commands/item-categories.js +52 -5
- package/dist/commands/items.d.ts +8 -0
- package/dist/commands/items.js +52 -5
- package/dist/commands/journal-lines.d.ts +8 -0
- package/dist/commands/journal-lines.js +52 -5
- package/dist/commands/journals.d.ts +8 -0
- package/dist/commands/journals.js +52 -5
- package/dist/commands/ledgers.d.ts +8 -0
- package/dist/commands/ledgers.js +57 -9
- package/dist/commands/my-account.d.ts +8 -0
- package/dist/commands/my-account.js +52 -5
- package/dist/commands/office-locations.d.ts +8 -0
- package/dist/commands/office-locations.js +52 -5
- package/dist/commands/payment-methods.d.ts +8 -0
- package/dist/commands/payment-methods.js +52 -5
- package/dist/commands/payment-runs.d.ts +8 -0
- package/dist/commands/payment-runs.js +54 -6
- package/dist/commands/payment-terms.d.ts +8 -0
- package/dist/commands/payment-terms.js +52 -5
- package/dist/commands/payments.d.ts +8 -0
- package/dist/commands/payments.js +52 -5
- package/dist/commands/payroll-runs.d.ts +8 -0
- package/dist/commands/payroll-runs.js +52 -5
- package/dist/commands/permissions.d.ts +8 -0
- package/dist/commands/permissions.js +52 -5
- package/dist/commands/price-lists.d.ts +8 -0
- package/dist/commands/price-lists.js +52 -5
- package/dist/commands/processor-mappings.d.ts +8 -0
- package/dist/commands/processor-mappings.js +52 -5
- package/dist/commands/processors.d.ts +8 -0
- package/dist/commands/processors.js +52 -5
- package/dist/commands/purchase-order-types.d.ts +8 -0
- package/dist/commands/purchase-order-types.js +52 -5
- package/dist/commands/purchase-orders.d.ts +8 -0
- package/dist/commands/purchase-orders.js +52 -5
- package/dist/commands/reports.d.ts +8 -0
- package/dist/commands/reports.js +52 -5
- package/dist/commands/roles.d.ts +8 -0
- package/dist/commands/roles.js +52 -5
- package/dist/commands/salary-types.d.ts +8 -0
- package/dist/commands/salary-types.js +52 -5
- package/dist/commands/sales-invoices.d.ts +10 -0
- package/dist/commands/sales-invoices.js +62 -6
- package/dist/commands/sales-orders.d.ts +8 -0
- package/dist/commands/sales-orders.js +52 -5
- package/dist/commands/saved-views.d.ts +8 -0
- package/dist/commands/saved-views.js +52 -5
- package/dist/commands/scheduled-processor-mappings.d.ts +8 -0
- package/dist/commands/scheduled-processor-mappings.js +52 -5
- package/dist/commands/schedules.d.ts +8 -0
- package/dist/commands/schedules.js +52 -5
- package/dist/commands/support/end.d.ts +9 -0
- package/dist/commands/support/end.js +29 -0
- package/dist/commands/support/start.d.ts +14 -0
- package/dist/commands/support/start.js +50 -0
- package/dist/commands/support/status.d.ts +10 -0
- package/dist/commands/support/status.js +45 -0
- package/dist/commands/support/tenants.d.ts +10 -0
- package/dist/commands/support/tenants.js +39 -0
- package/dist/commands/tags.d.ts +8 -0
- package/dist/commands/tags.js +56 -8
- package/dist/commands/tax-codes.d.ts +8 -0
- package/dist/commands/tax-codes.js +52 -5
- package/dist/commands/tax-matrix.d.ts +8 -0
- package/dist/commands/tax-matrix.js +52 -5
- package/dist/commands/templates.d.ts +8 -0
- package/dist/commands/templates.js +52 -5
- package/dist/commands/tenants.d.ts +8 -0
- package/dist/commands/tenants.js +52 -5
- package/dist/commands/tools.d.ts +8 -0
- package/dist/commands/tools.js +52 -5
- package/dist/commands/users.d.ts +8 -0
- package/dist/commands/users.js +52 -5
- package/dist/commands/vat-declarations.d.ts +8 -0
- package/dist/commands/vat-declarations.js +52 -5
- package/dist/commands/vendor-invoices.d.ts +9 -0
- package/dist/commands/vendor-invoices.js +60 -8
- package/dist/commands/webhooks.d.ts +8 -0
- package/dist/commands/webhooks.js +52 -5
- package/dist/commands/workflows.d.ts +8 -0
- package/dist/commands/workflows.js +52 -5
- package/dist/lib/auth/constants.d.ts +12 -0
- package/dist/lib/auth/constants.js +12 -0
- package/dist/lib/auth/index.d.ts +11 -1
- package/dist/lib/auth/index.js +90 -2
- package/dist/lib/auth/keycloak-provider.d.ts +22 -0
- package/dist/lib/auth/keycloak-provider.js +108 -3
- package/dist/lib/config.d.ts +18 -0
- package/dist/lib/config.js +10 -0
- package/oclif.manifest.json +640 -216
- package/package.json +4 -1
- package/dist/commands/tag-configurations.d.ts +0 -39
- package/dist/commands/tag-configurations.js +0 -1155
|
@@ -37,6 +37,7 @@ export default class JournalLines extends Command {
|
|
|
37
37
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
38
38
|
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
39
39
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
40
|
+
meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
|
|
40
41
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
41
42
|
};
|
|
42
43
|
async run() {
|
|
@@ -329,9 +330,15 @@ export default class JournalLines extends Command {
|
|
|
329
330
|
}
|
|
330
331
|
catch { /* keep as string */ }
|
|
331
332
|
}
|
|
332
|
-
//
|
|
333
|
+
// A paginated answer is { data, meta, links, totals }. The records are
|
|
334
|
+
// unwrapped for output, but what the API said about the page is kept:
|
|
335
|
+
// dropping it silently let a script read one page as the whole result
|
|
336
|
+
// (AderisERP/product#61).
|
|
337
|
+
let envelope;
|
|
333
338
|
if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
|
|
334
|
-
|
|
339
|
+
const page = output;
|
|
340
|
+
envelope = page;
|
|
341
|
+
output = page.data;
|
|
335
342
|
}
|
|
336
343
|
// For table/csv on non-array responses, auto-extract the primary array
|
|
337
344
|
// e.g., { payments: [...], total: 3 } → extract payments array
|
|
@@ -348,12 +355,24 @@ export default class JournalLines extends Command {
|
|
|
348
355
|
output = this.sanitizeOutput(output);
|
|
349
356
|
}
|
|
350
357
|
const fieldList = flags.fields?.split(',').map(f => f.trim());
|
|
351
|
-
if (
|
|
358
|
+
if (envelope)
|
|
359
|
+
this.warnShortPage(envelope, flags.limit);
|
|
360
|
+
if (flags.csv || flags.output === 'csv') {
|
|
352
361
|
this.printCsv(output, fieldList);
|
|
353
|
-
|
|
362
|
+
}
|
|
363
|
+
else if (flags.table || flags.output === 'table') {
|
|
354
364
|
this.printTable(output, fieldList);
|
|
355
|
-
|
|
365
|
+
if (envelope)
|
|
366
|
+
this.printPageSummary(envelope);
|
|
367
|
+
}
|
|
368
|
+
else if (envelope && flags.meta) {
|
|
369
|
+
// The whole envelope, for a caller that asked for it; the default stays
|
|
370
|
+
// a bare array so existing scripts are unaffected.
|
|
371
|
+
this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
356
374
|
this.log(JSON.stringify(output, null, 2));
|
|
375
|
+
}
|
|
357
376
|
}
|
|
358
377
|
catch (error) {
|
|
359
378
|
if (error instanceof ApiError) {
|
|
@@ -982,6 +1001,34 @@ export default class JournalLines extends Command {
|
|
|
982
1001
|
return `\$${op}`;
|
|
983
1002
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
984
1003
|
}
|
|
1004
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
1005
|
+
printPageSummary(envelope) {
|
|
1006
|
+
const meta = envelope.meta;
|
|
1007
|
+
if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
|
|
1008
|
+
const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
|
|
1009
|
+
this.log(`
|
|
1010
|
+
Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
|
|
1011
|
+
}
|
|
1012
|
+
for (const total of envelope.totals ?? []) {
|
|
1013
|
+
const { currency, ...sums } = total;
|
|
1014
|
+
const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
|
|
1015
|
+
this.log(`Total ${currency ?? ''}: ${parts}`.trim());
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
1020
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
1021
|
+
*/
|
|
1022
|
+
warnShortPage(envelope, requestedLimit) {
|
|
1023
|
+
const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
|
|
1024
|
+
const perPage = envelope.meta?.itemsPerPage;
|
|
1025
|
+
if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
|
|
1026
|
+
return;
|
|
1027
|
+
const pages = envelope.meta?.totalPages;
|
|
1028
|
+
const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
|
|
1029
|
+
process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
|
|
1030
|
+
`);
|
|
1031
|
+
}
|
|
985
1032
|
sanitizeOutput(data) {
|
|
986
1033
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
987
1034
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -26,6 +26,7 @@ export default class Journals extends Command {
|
|
|
26
26
|
query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
27
27
|
fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
28
28
|
sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
29
|
+
meta: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
29
30
|
'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
30
31
|
};
|
|
31
32
|
run(): Promise<void>;
|
|
@@ -34,6 +35,13 @@ export default class Journals extends Command {
|
|
|
34
35
|
private runCamt053Import;
|
|
35
36
|
private runNestedImport;
|
|
36
37
|
private parseFilterValue;
|
|
38
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
39
|
+
private printPageSummary;
|
|
40
|
+
/**
|
|
41
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
42
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
43
|
+
*/
|
|
44
|
+
private warnShortPage;
|
|
37
45
|
private sanitizeOutput;
|
|
38
46
|
private getColumns;
|
|
39
47
|
private getValue;
|
|
@@ -67,6 +67,7 @@ export default class Journals extends Command {
|
|
|
67
67
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
68
68
|
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
69
69
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
70
|
+
meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
|
|
70
71
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
71
72
|
};
|
|
72
73
|
async run() {
|
|
@@ -359,9 +360,15 @@ export default class Journals extends Command {
|
|
|
359
360
|
}
|
|
360
361
|
catch { /* keep as string */ }
|
|
361
362
|
}
|
|
362
|
-
//
|
|
363
|
+
// A paginated answer is { data, meta, links, totals }. The records are
|
|
364
|
+
// unwrapped for output, but what the API said about the page is kept:
|
|
365
|
+
// dropping it silently let a script read one page as the whole result
|
|
366
|
+
// (AderisERP/product#61).
|
|
367
|
+
let envelope;
|
|
363
368
|
if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
|
|
364
|
-
|
|
369
|
+
const page = output;
|
|
370
|
+
envelope = page;
|
|
371
|
+
output = page.data;
|
|
365
372
|
}
|
|
366
373
|
// For table/csv on non-array responses, auto-extract the primary array
|
|
367
374
|
// e.g., { payments: [...], total: 3 } → extract payments array
|
|
@@ -378,12 +385,24 @@ export default class Journals extends Command {
|
|
|
378
385
|
output = this.sanitizeOutput(output);
|
|
379
386
|
}
|
|
380
387
|
const fieldList = flags.fields?.split(',').map(f => f.trim());
|
|
381
|
-
if (
|
|
388
|
+
if (envelope)
|
|
389
|
+
this.warnShortPage(envelope, flags.limit);
|
|
390
|
+
if (flags.csv || flags.output === 'csv') {
|
|
382
391
|
this.printCsv(output, fieldList);
|
|
383
|
-
|
|
392
|
+
}
|
|
393
|
+
else if (flags.table || flags.output === 'table') {
|
|
384
394
|
this.printTable(output, fieldList);
|
|
385
|
-
|
|
395
|
+
if (envelope)
|
|
396
|
+
this.printPageSummary(envelope);
|
|
397
|
+
}
|
|
398
|
+
else if (envelope && flags.meta) {
|
|
399
|
+
// The whole envelope, for a caller that asked for it; the default stays
|
|
400
|
+
// a bare array so existing scripts are unaffected.
|
|
401
|
+
this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
386
404
|
this.log(JSON.stringify(output, null, 2));
|
|
405
|
+
}
|
|
387
406
|
}
|
|
388
407
|
catch (error) {
|
|
389
408
|
if (error instanceof ApiError) {
|
|
@@ -1012,6 +1031,34 @@ export default class Journals extends Command {
|
|
|
1012
1031
|
return `\$${op}`;
|
|
1013
1032
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
1014
1033
|
}
|
|
1034
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
1035
|
+
printPageSummary(envelope) {
|
|
1036
|
+
const meta = envelope.meta;
|
|
1037
|
+
if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
|
|
1038
|
+
const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
|
|
1039
|
+
this.log(`
|
|
1040
|
+
Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
|
|
1041
|
+
}
|
|
1042
|
+
for (const total of envelope.totals ?? []) {
|
|
1043
|
+
const { currency, ...sums } = total;
|
|
1044
|
+
const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
|
|
1045
|
+
this.log(`Total ${currency ?? ''}: ${parts}`.trim());
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
1050
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
1051
|
+
*/
|
|
1052
|
+
warnShortPage(envelope, requestedLimit) {
|
|
1053
|
+
const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
|
|
1054
|
+
const perPage = envelope.meta?.itemsPerPage;
|
|
1055
|
+
if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
|
|
1056
|
+
return;
|
|
1057
|
+
const pages = envelope.meta?.totalPages;
|
|
1058
|
+
const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
|
|
1059
|
+
process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
|
|
1060
|
+
`);
|
|
1061
|
+
}
|
|
1015
1062
|
sanitizeOutput(data) {
|
|
1016
1063
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1017
1064
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -27,6 +27,7 @@ export default class Ledgers extends Command {
|
|
|
27
27
|
query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
28
28
|
fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
29
29
|
sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
30
|
+
meta: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
30
31
|
'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
31
32
|
};
|
|
32
33
|
run(): Promise<void>;
|
|
@@ -35,6 +36,13 @@ export default class Ledgers extends Command {
|
|
|
35
36
|
private runCamt053Import;
|
|
36
37
|
private runNestedImport;
|
|
37
38
|
private parseFilterValue;
|
|
39
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
40
|
+
private printPageSummary;
|
|
41
|
+
/**
|
|
42
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
43
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
44
|
+
*/
|
|
45
|
+
private warnShortPage;
|
|
38
46
|
private sanitizeOutput;
|
|
39
47
|
private getColumns;
|
|
40
48
|
private getValue;
|
package/dist/commands/ledgers.js
CHANGED
|
@@ -6,12 +6,12 @@ const HAS_IMPORT = true;
|
|
|
6
6
|
const HAS_EXPORT = true;
|
|
7
7
|
const HAS_CAMT053 = false;
|
|
8
8
|
const NESTED_IMPORTS = ["month-end-closings", "month-end-closings-accruals", "month-end-closings-notes", "notes", "closings"];
|
|
9
|
-
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "list-month-end-closings", "create-month-end-closings", "get-month-end-closings", "update-month-end-closings", "delete-month-end-closings", "month-end-closings-complete", "create-month-end-closings-provisional-booking", "delete-month-end-closings-provisional-booking", "month-end-closings-reopen", "list-month-end-closings-accruals", "create-month-end-closings-accruals", "update-month-end-closings-accruals", "delete-month-end-closings-accruals", "month-end-closings-accruals-accept", "month-end-closings-accruals-dismiss", "list-month-end-closings-accrual-accounts", "list-month-end-closings-findings", "update-month-end-closings-findings", "list-month-end-closings-notes", "create-month-end-closings-notes", "get-month-end-closings-notes", "update-month-end-closings-notes", "delete-month-end-closings-notes", "update-month-end-closings-agent-results", "month-end-closings-agent-run", "get-fx-rate", "replace-fx-rate-providers", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-closings", "create-closings", "get-closings", "update-closings", "closings-close", "closings-reopen", "list-posting-runs", "create-posting-runs", "posting-runs"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
9
|
+
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "list-month-end-closings", "create-month-end-closings", "get-month-end-closings", "update-month-end-closings", "delete-month-end-closings", "month-end-closings-complete", "create-month-end-closings-provisional-booking", "delete-month-end-closings-provisional-booking", "month-end-closings-reopen", "list-month-end-closings-accruals", "create-month-end-closings-accruals", "update-month-end-closings-accruals", "delete-month-end-closings-accruals", "month-end-closings-accruals-accept", "month-end-closings-accruals-dismiss", "list-month-end-closings-accrual-accounts", "list-month-end-closings-findings", "update-month-end-closings-findings", "list-month-end-closings-notes", "create-month-end-closings-notes", "get-month-end-closings-notes", "update-month-end-closings-notes", "delete-month-end-closings-notes", "update-month-end-closings-agent-results", "month-end-closings-agent-run", "get-fx-rate", "replace-fx-rate-providers", "replace-closing-fx-rate-providers", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-closings", "create-closings", "get-closings", "update-closings", "closings-close", "closings-reopen", "list-posting-runs", "create-posting-runs", "posting-runs"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
11
|
'list': { method: 'GET', path: '/v1/ledgers', summary: 'List all ledgers', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/ledgers', summary: 'Create a new ledger', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['notes', 'fxRateProviders'] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/ledgers', summary: 'Create a new ledger', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['notes', 'fxRateProviders', 'closingFxRateProviders'] },
|
|
13
13
|
'get': { method: 'GET', path: '/v1/ledgers/{id}', summary: 'Get a ledger by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/ledgers/{id}', summary: 'Update a ledger', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['fxRateProviders'] },
|
|
14
|
+
'update': { method: 'PATCH', path: '/v1/ledgers/{id}', summary: 'Update a ledger', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['fxRateProviders', 'closingFxRateProviders'] },
|
|
15
15
|
'delete': { method: 'DELETE', path: '/v1/ledgers/{id}', summary: 'Delete a ledger', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
16
|
'list-month-end-closings': { method: 'GET', path: '/v1/ledgers/{ledgerId}/month-end-closings', summary: 'List the month-end closings of a ledger, newest month first', pathParams: ['ledgerId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
17
|
'create-month-end-closings': { method: 'POST', path: '/v1/ledgers/{ledgerId}/month-end-closings', summary: 'Create a DRAFT month-end closing for one calendar month', pathParams: ['ledgerId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
@@ -38,8 +38,9 @@ const ENDPOINTS = {
|
|
|
38
38
|
'delete-month-end-closings-notes': { method: 'DELETE', path: '/v1/ledgers/{ledgerId}/month-end-closings/{id}/notes/{noteId}', summary: 'Delete a note of a month-end closing', pathParams: ['ledgerId', 'id', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
39
39
|
'update-month-end-closings-agent-results': { method: 'PUT', path: '/v1/ledgers/{ledgerId}/month-end-closings/{id}/agent-results', summary: 'Write back a month-end agent run\'s explanations and accrual proposals', pathParams: ['ledgerId', 'id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['accounts', 'accruals'] },
|
|
40
40
|
'month-end-closings-agent-run': { method: 'POST', path: '/v1/ledgers/{ledgerId}/month-end-closings/{id}/agent-run', summary: 'Compute the month\'s findings and start a month-end agent run', pathParams: ['ledgerId', 'id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
41
|
-
'get-fx-rate': { method: 'GET', path: '/v1/ledgers/{id}/fx-rate', summary: 'Resolve the FX rate this ledger would book for a currency on a day', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
41
|
+
'get-fx-rate': { method: 'GET', path: '/v1/ledgers/{id}/fx-rate', summary: 'Resolve the FX rate this ledger would book for a currency on a day, or with purpose=CLOSING the rate its month-end close values a balance at', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
42
42
|
'replace-fx-rate-providers': { method: 'PUT', path: '/v1/ledgers/{id}/fx-rate-providers', summary: 'Replace the list of FX rate providers this ledger asks, leaving the rest of the ledger untouched', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['fxRateProviders'] },
|
|
43
|
+
'replace-closing-fx-rate-providers': { method: 'PUT', path: '/v1/ledgers/{id}/closing-fx-rate-providers', summary: 'Replace the list of FX rate providers the month-end close asks, leaving the rest of the ledger untouched', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['closingFxRateProviders'] },
|
|
43
44
|
'list-notes': { method: 'GET', path: '/v1/ledgers/{ledgerId}/notes', summary: 'Get all notes for a ledger', pathParams: ['ledgerId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
44
45
|
'create-notes': { method: 'POST', path: '/v1/ledgers/{ledgerId}/notes', summary: 'Create a note for a ledger', pathParams: ['ledgerId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
45
46
|
'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, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
@@ -90,6 +91,7 @@ export default class Ledgers extends Command {
|
|
|
90
91
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
91
92
|
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
92
93
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
94
|
+
meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
|
|
93
95
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
94
96
|
};
|
|
95
97
|
async run() {
|
|
@@ -382,9 +384,15 @@ export default class Ledgers extends Command {
|
|
|
382
384
|
}
|
|
383
385
|
catch { /* keep as string */ }
|
|
384
386
|
}
|
|
385
|
-
//
|
|
387
|
+
// A paginated answer is { data, meta, links, totals }. The records are
|
|
388
|
+
// unwrapped for output, but what the API said about the page is kept:
|
|
389
|
+
// dropping it silently let a script read one page as the whole result
|
|
390
|
+
// (AderisERP/product#61).
|
|
391
|
+
let envelope;
|
|
386
392
|
if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
|
|
387
|
-
|
|
393
|
+
const page = output;
|
|
394
|
+
envelope = page;
|
|
395
|
+
output = page.data;
|
|
388
396
|
}
|
|
389
397
|
// For table/csv on non-array responses, auto-extract the primary array
|
|
390
398
|
// e.g., { payments: [...], total: 3 } → extract payments array
|
|
@@ -401,12 +409,24 @@ export default class Ledgers extends Command {
|
|
|
401
409
|
output = this.sanitizeOutput(output);
|
|
402
410
|
}
|
|
403
411
|
const fieldList = flags.fields?.split(',').map(f => f.trim());
|
|
404
|
-
if (
|
|
412
|
+
if (envelope)
|
|
413
|
+
this.warnShortPage(envelope, flags.limit);
|
|
414
|
+
if (flags.csv || flags.output === 'csv') {
|
|
405
415
|
this.printCsv(output, fieldList);
|
|
406
|
-
|
|
416
|
+
}
|
|
417
|
+
else if (flags.table || flags.output === 'table') {
|
|
407
418
|
this.printTable(output, fieldList);
|
|
408
|
-
|
|
419
|
+
if (envelope)
|
|
420
|
+
this.printPageSummary(envelope);
|
|
421
|
+
}
|
|
422
|
+
else if (envelope && flags.meta) {
|
|
423
|
+
// The whole envelope, for a caller that asked for it; the default stays
|
|
424
|
+
// a bare array so existing scripts are unaffected.
|
|
425
|
+
this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
409
428
|
this.log(JSON.stringify(output, null, 2));
|
|
429
|
+
}
|
|
410
430
|
}
|
|
411
431
|
catch (error) {
|
|
412
432
|
if (error instanceof ApiError) {
|
|
@@ -1035,6 +1055,34 @@ export default class Ledgers extends Command {
|
|
|
1035
1055
|
return `\$${op}`;
|
|
1036
1056
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
1037
1057
|
}
|
|
1058
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
1059
|
+
printPageSummary(envelope) {
|
|
1060
|
+
const meta = envelope.meta;
|
|
1061
|
+
if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
|
|
1062
|
+
const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
|
|
1063
|
+
this.log(`
|
|
1064
|
+
Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
|
|
1065
|
+
}
|
|
1066
|
+
for (const total of envelope.totals ?? []) {
|
|
1067
|
+
const { currency, ...sums } = total;
|
|
1068
|
+
const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
|
|
1069
|
+
this.log(`Total ${currency ?? ''}: ${parts}`.trim());
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
1074
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
1075
|
+
*/
|
|
1076
|
+
warnShortPage(envelope, requestedLimit) {
|
|
1077
|
+
const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
|
|
1078
|
+
const perPage = envelope.meta?.itemsPerPage;
|
|
1079
|
+
if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
|
|
1080
|
+
return;
|
|
1081
|
+
const pages = envelope.meta?.totalPages;
|
|
1082
|
+
const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
|
|
1083
|
+
process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
|
|
1084
|
+
`);
|
|
1085
|
+
}
|
|
1038
1086
|
sanitizeOutput(data) {
|
|
1039
1087
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1040
1088
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -23,6 +23,7 @@ export default class MyAccount extends Command {
|
|
|
23
23
|
query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
24
|
fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
25
25
|
sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
26
|
+
meta: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
26
27
|
'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
27
28
|
};
|
|
28
29
|
run(): Promise<void>;
|
|
@@ -31,6 +32,13 @@ export default class MyAccount extends Command {
|
|
|
31
32
|
private runCamt053Import;
|
|
32
33
|
private runNestedImport;
|
|
33
34
|
private parseFilterValue;
|
|
35
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
36
|
+
private printPageSummary;
|
|
37
|
+
/**
|
|
38
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
39
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
40
|
+
*/
|
|
41
|
+
private warnShortPage;
|
|
34
42
|
private sanitizeOutput;
|
|
35
43
|
private getColumns;
|
|
36
44
|
private getValue;
|
|
@@ -51,6 +51,7 @@ export default class MyAccount extends Command {
|
|
|
51
51
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
52
52
|
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
53
53
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
54
|
+
meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
|
|
54
55
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
55
56
|
};
|
|
56
57
|
async run() {
|
|
@@ -343,9 +344,15 @@ export default class MyAccount extends Command {
|
|
|
343
344
|
}
|
|
344
345
|
catch { /* keep as string */ }
|
|
345
346
|
}
|
|
346
|
-
//
|
|
347
|
+
// A paginated answer is { data, meta, links, totals }. The records are
|
|
348
|
+
// unwrapped for output, but what the API said about the page is kept:
|
|
349
|
+
// dropping it silently let a script read one page as the whole result
|
|
350
|
+
// (AderisERP/product#61).
|
|
351
|
+
let envelope;
|
|
347
352
|
if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
|
|
348
|
-
|
|
353
|
+
const page = output;
|
|
354
|
+
envelope = page;
|
|
355
|
+
output = page.data;
|
|
349
356
|
}
|
|
350
357
|
// For table/csv on non-array responses, auto-extract the primary array
|
|
351
358
|
// e.g., { payments: [...], total: 3 } → extract payments array
|
|
@@ -362,12 +369,24 @@ export default class MyAccount extends Command {
|
|
|
362
369
|
output = this.sanitizeOutput(output);
|
|
363
370
|
}
|
|
364
371
|
const fieldList = flags.fields?.split(',').map(f => f.trim());
|
|
365
|
-
if (
|
|
372
|
+
if (envelope)
|
|
373
|
+
this.warnShortPage(envelope, flags.limit);
|
|
374
|
+
if (flags.csv || flags.output === 'csv') {
|
|
366
375
|
this.printCsv(output, fieldList);
|
|
367
|
-
|
|
376
|
+
}
|
|
377
|
+
else if (flags.table || flags.output === 'table') {
|
|
368
378
|
this.printTable(output, fieldList);
|
|
369
|
-
|
|
379
|
+
if (envelope)
|
|
380
|
+
this.printPageSummary(envelope);
|
|
381
|
+
}
|
|
382
|
+
else if (envelope && flags.meta) {
|
|
383
|
+
// The whole envelope, for a caller that asked for it; the default stays
|
|
384
|
+
// a bare array so existing scripts are unaffected.
|
|
385
|
+
this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
370
388
|
this.log(JSON.stringify(output, null, 2));
|
|
389
|
+
}
|
|
371
390
|
}
|
|
372
391
|
catch (error) {
|
|
373
392
|
if (error instanceof ApiError) {
|
|
@@ -996,6 +1015,34 @@ export default class MyAccount extends Command {
|
|
|
996
1015
|
return `\$${op}`;
|
|
997
1016
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
998
1017
|
}
|
|
1018
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
1019
|
+
printPageSummary(envelope) {
|
|
1020
|
+
const meta = envelope.meta;
|
|
1021
|
+
if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
|
|
1022
|
+
const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
|
|
1023
|
+
this.log(`
|
|
1024
|
+
Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
|
|
1025
|
+
}
|
|
1026
|
+
for (const total of envelope.totals ?? []) {
|
|
1027
|
+
const { currency, ...sums } = total;
|
|
1028
|
+
const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
|
|
1029
|
+
this.log(`Total ${currency ?? ''}: ${parts}`.trim());
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
1034
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
1035
|
+
*/
|
|
1036
|
+
warnShortPage(envelope, requestedLimit) {
|
|
1037
|
+
const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
|
|
1038
|
+
const perPage = envelope.meta?.itemsPerPage;
|
|
1039
|
+
if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
|
|
1040
|
+
return;
|
|
1041
|
+
const pages = envelope.meta?.totalPages;
|
|
1042
|
+
const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
|
|
1043
|
+
process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
|
|
1044
|
+
`);
|
|
1045
|
+
}
|
|
999
1046
|
sanitizeOutput(data) {
|
|
1000
1047
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1001
1048
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -23,6 +23,7 @@ export default class OfficeLocations extends Command {
|
|
|
23
23
|
query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
24
|
fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
25
25
|
sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
26
|
+
meta: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
26
27
|
'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
27
28
|
};
|
|
28
29
|
run(): Promise<void>;
|
|
@@ -31,6 +32,13 @@ export default class OfficeLocations extends Command {
|
|
|
31
32
|
private runCamt053Import;
|
|
32
33
|
private runNestedImport;
|
|
33
34
|
private parseFilterValue;
|
|
35
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
36
|
+
private printPageSummary;
|
|
37
|
+
/**
|
|
38
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
39
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
40
|
+
*/
|
|
41
|
+
private warnShortPage;
|
|
34
42
|
private sanitizeOutput;
|
|
35
43
|
private getColumns;
|
|
36
44
|
private getValue;
|
|
@@ -45,6 +45,7 @@ export default class OfficeLocations extends Command {
|
|
|
45
45
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
46
46
|
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
47
47
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
48
|
+
meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
|
|
48
49
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
49
50
|
};
|
|
50
51
|
async run() {
|
|
@@ -337,9 +338,15 @@ export default class OfficeLocations extends Command {
|
|
|
337
338
|
}
|
|
338
339
|
catch { /* keep as string */ }
|
|
339
340
|
}
|
|
340
|
-
//
|
|
341
|
+
// A paginated answer is { data, meta, links, totals }. The records are
|
|
342
|
+
// unwrapped for output, but what the API said about the page is kept:
|
|
343
|
+
// dropping it silently let a script read one page as the whole result
|
|
344
|
+
// (AderisERP/product#61).
|
|
345
|
+
let envelope;
|
|
341
346
|
if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
|
|
342
|
-
|
|
347
|
+
const page = output;
|
|
348
|
+
envelope = page;
|
|
349
|
+
output = page.data;
|
|
343
350
|
}
|
|
344
351
|
// For table/csv on non-array responses, auto-extract the primary array
|
|
345
352
|
// e.g., { payments: [...], total: 3 } → extract payments array
|
|
@@ -356,12 +363,24 @@ export default class OfficeLocations extends Command {
|
|
|
356
363
|
output = this.sanitizeOutput(output);
|
|
357
364
|
}
|
|
358
365
|
const fieldList = flags.fields?.split(',').map(f => f.trim());
|
|
359
|
-
if (
|
|
366
|
+
if (envelope)
|
|
367
|
+
this.warnShortPage(envelope, flags.limit);
|
|
368
|
+
if (flags.csv || flags.output === 'csv') {
|
|
360
369
|
this.printCsv(output, fieldList);
|
|
361
|
-
|
|
370
|
+
}
|
|
371
|
+
else if (flags.table || flags.output === 'table') {
|
|
362
372
|
this.printTable(output, fieldList);
|
|
363
|
-
|
|
373
|
+
if (envelope)
|
|
374
|
+
this.printPageSummary(envelope);
|
|
375
|
+
}
|
|
376
|
+
else if (envelope && flags.meta) {
|
|
377
|
+
// The whole envelope, for a caller that asked for it; the default stays
|
|
378
|
+
// a bare array so existing scripts are unaffected.
|
|
379
|
+
this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
364
382
|
this.log(JSON.stringify(output, null, 2));
|
|
383
|
+
}
|
|
365
384
|
}
|
|
366
385
|
catch (error) {
|
|
367
386
|
if (error instanceof ApiError) {
|
|
@@ -990,6 +1009,34 @@ export default class OfficeLocations extends Command {
|
|
|
990
1009
|
return `\$${op}`;
|
|
991
1010
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
992
1011
|
}
|
|
1012
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
1013
|
+
printPageSummary(envelope) {
|
|
1014
|
+
const meta = envelope.meta;
|
|
1015
|
+
if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
|
|
1016
|
+
const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
|
|
1017
|
+
this.log(`
|
|
1018
|
+
Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
|
|
1019
|
+
}
|
|
1020
|
+
for (const total of envelope.totals ?? []) {
|
|
1021
|
+
const { currency, ...sums } = total;
|
|
1022
|
+
const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
|
|
1023
|
+
this.log(`Total ${currency ?? ''}: ${parts}`.trim());
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
1028
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
1029
|
+
*/
|
|
1030
|
+
warnShortPage(envelope, requestedLimit) {
|
|
1031
|
+
const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
|
|
1032
|
+
const perPage = envelope.meta?.itemsPerPage;
|
|
1033
|
+
if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
|
|
1034
|
+
return;
|
|
1035
|
+
const pages = envelope.meta?.totalPages;
|
|
1036
|
+
const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
|
|
1037
|
+
process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
|
|
1038
|
+
`);
|
|
1039
|
+
}
|
|
993
1040
|
sanitizeOutput(data) {
|
|
994
1041
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
995
1042
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -24,6 +24,7 @@ export default class PaymentMethods extends Command {
|
|
|
24
24
|
query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
25
25
|
fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
26
26
|
sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
27
|
+
meta: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
27
28
|
'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
28
29
|
};
|
|
29
30
|
run(): Promise<void>;
|
|
@@ -32,6 +33,13 @@ export default class PaymentMethods extends Command {
|
|
|
32
33
|
private runCamt053Import;
|
|
33
34
|
private runNestedImport;
|
|
34
35
|
private parseFilterValue;
|
|
36
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
37
|
+
private printPageSummary;
|
|
38
|
+
/**
|
|
39
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
40
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
41
|
+
*/
|
|
42
|
+
private warnShortPage;
|
|
35
43
|
private sanitizeOutput;
|
|
36
44
|
private getColumns;
|
|
37
45
|
private getValue;
|