@aderis-erp/cli 1.1.1 → 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 +54 -6
- 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 +64 -7
- 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 +52 -5
- 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 +52 -5
- 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/tags.d.ts +8 -0
- package/dist/commands/tags.js +52 -5
- 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/oclif.manifest.json +433 -10
- package/package.json +1 -1
package/dist/commands/tools.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export default class Tools 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 Tools 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;
|
package/dist/commands/tools.js
CHANGED
|
@@ -38,6 +38,7 @@ export default class Tools extends Command {
|
|
|
38
38
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
39
39
|
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
40
40
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
41
|
+
meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
|
|
41
42
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
42
43
|
};
|
|
43
44
|
async run() {
|
|
@@ -330,9 +331,15 @@ export default class Tools extends Command {
|
|
|
330
331
|
}
|
|
331
332
|
catch { /* keep as string */ }
|
|
332
333
|
}
|
|
333
|
-
//
|
|
334
|
+
// A paginated answer is { data, meta, links, totals }. The records are
|
|
335
|
+
// unwrapped for output, but what the API said about the page is kept:
|
|
336
|
+
// dropping it silently let a script read one page as the whole result
|
|
337
|
+
// (AderisERP/product#61).
|
|
338
|
+
let envelope;
|
|
334
339
|
if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
|
|
335
|
-
|
|
340
|
+
const page = output;
|
|
341
|
+
envelope = page;
|
|
342
|
+
output = page.data;
|
|
336
343
|
}
|
|
337
344
|
// For table/csv on non-array responses, auto-extract the primary array
|
|
338
345
|
// e.g., { payments: [...], total: 3 } → extract payments array
|
|
@@ -349,12 +356,24 @@ export default class Tools extends Command {
|
|
|
349
356
|
output = this.sanitizeOutput(output);
|
|
350
357
|
}
|
|
351
358
|
const fieldList = flags.fields?.split(',').map(f => f.trim());
|
|
352
|
-
if (
|
|
359
|
+
if (envelope)
|
|
360
|
+
this.warnShortPage(envelope, flags.limit);
|
|
361
|
+
if (flags.csv || flags.output === 'csv') {
|
|
353
362
|
this.printCsv(output, fieldList);
|
|
354
|
-
|
|
363
|
+
}
|
|
364
|
+
else if (flags.table || flags.output === 'table') {
|
|
355
365
|
this.printTable(output, fieldList);
|
|
356
|
-
|
|
366
|
+
if (envelope)
|
|
367
|
+
this.printPageSummary(envelope);
|
|
368
|
+
}
|
|
369
|
+
else if (envelope && flags.meta) {
|
|
370
|
+
// The whole envelope, for a caller that asked for it; the default stays
|
|
371
|
+
// a bare array so existing scripts are unaffected.
|
|
372
|
+
this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
357
375
|
this.log(JSON.stringify(output, null, 2));
|
|
376
|
+
}
|
|
358
377
|
}
|
|
359
378
|
catch (error) {
|
|
360
379
|
if (error instanceof ApiError) {
|
|
@@ -983,6 +1002,34 @@ export default class Tools extends Command {
|
|
|
983
1002
|
return `\$${op}`;
|
|
984
1003
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
985
1004
|
}
|
|
1005
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
1006
|
+
printPageSummary(envelope) {
|
|
1007
|
+
const meta = envelope.meta;
|
|
1008
|
+
if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
|
|
1009
|
+
const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
|
|
1010
|
+
this.log(`
|
|
1011
|
+
Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
|
|
1012
|
+
}
|
|
1013
|
+
for (const total of envelope.totals ?? []) {
|
|
1014
|
+
const { currency, ...sums } = total;
|
|
1015
|
+
const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
|
|
1016
|
+
this.log(`Total ${currency ?? ''}: ${parts}`.trim());
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
1021
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
1022
|
+
*/
|
|
1023
|
+
warnShortPage(envelope, requestedLimit) {
|
|
1024
|
+
const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
|
|
1025
|
+
const perPage = envelope.meta?.itemsPerPage;
|
|
1026
|
+
if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
|
|
1027
|
+
return;
|
|
1028
|
+
const pages = envelope.meta?.totalPages;
|
|
1029
|
+
const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
|
|
1030
|
+
process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
|
|
1031
|
+
`);
|
|
1032
|
+
}
|
|
986
1033
|
sanitizeOutput(data) {
|
|
987
1034
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
988
1035
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
package/dist/commands/users.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export default class Users 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 Users 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;
|
package/dist/commands/users.js
CHANGED
|
@@ -65,6 +65,7 @@ export default class Users extends Command {
|
|
|
65
65
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
66
66
|
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
67
67
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
68
|
+
meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
|
|
68
69
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
69
70
|
};
|
|
70
71
|
async run() {
|
|
@@ -357,9 +358,15 @@ export default class Users extends Command {
|
|
|
357
358
|
}
|
|
358
359
|
catch { /* keep as string */ }
|
|
359
360
|
}
|
|
360
|
-
//
|
|
361
|
+
// A paginated answer is { data, meta, links, totals }. The records are
|
|
362
|
+
// unwrapped for output, but what the API said about the page is kept:
|
|
363
|
+
// dropping it silently let a script read one page as the whole result
|
|
364
|
+
// (AderisERP/product#61).
|
|
365
|
+
let envelope;
|
|
361
366
|
if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
|
|
362
|
-
|
|
367
|
+
const page = output;
|
|
368
|
+
envelope = page;
|
|
369
|
+
output = page.data;
|
|
363
370
|
}
|
|
364
371
|
// For table/csv on non-array responses, auto-extract the primary array
|
|
365
372
|
// e.g., { payments: [...], total: 3 } → extract payments array
|
|
@@ -376,12 +383,24 @@ export default class Users extends Command {
|
|
|
376
383
|
output = this.sanitizeOutput(output);
|
|
377
384
|
}
|
|
378
385
|
const fieldList = flags.fields?.split(',').map(f => f.trim());
|
|
379
|
-
if (
|
|
386
|
+
if (envelope)
|
|
387
|
+
this.warnShortPage(envelope, flags.limit);
|
|
388
|
+
if (flags.csv || flags.output === 'csv') {
|
|
380
389
|
this.printCsv(output, fieldList);
|
|
381
|
-
|
|
390
|
+
}
|
|
391
|
+
else if (flags.table || flags.output === 'table') {
|
|
382
392
|
this.printTable(output, fieldList);
|
|
383
|
-
|
|
393
|
+
if (envelope)
|
|
394
|
+
this.printPageSummary(envelope);
|
|
395
|
+
}
|
|
396
|
+
else if (envelope && flags.meta) {
|
|
397
|
+
// The whole envelope, for a caller that asked for it; the default stays
|
|
398
|
+
// a bare array so existing scripts are unaffected.
|
|
399
|
+
this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
384
402
|
this.log(JSON.stringify(output, null, 2));
|
|
403
|
+
}
|
|
385
404
|
}
|
|
386
405
|
catch (error) {
|
|
387
406
|
if (error instanceof ApiError) {
|
|
@@ -1010,6 +1029,34 @@ export default class Users extends Command {
|
|
|
1010
1029
|
return `\$${op}`;
|
|
1011
1030
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
1012
1031
|
}
|
|
1032
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
1033
|
+
printPageSummary(envelope) {
|
|
1034
|
+
const meta = envelope.meta;
|
|
1035
|
+
if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
|
|
1036
|
+
const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
|
|
1037
|
+
this.log(`
|
|
1038
|
+
Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
|
|
1039
|
+
}
|
|
1040
|
+
for (const total of envelope.totals ?? []) {
|
|
1041
|
+
const { currency, ...sums } = total;
|
|
1042
|
+
const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
|
|
1043
|
+
this.log(`Total ${currency ?? ''}: ${parts}`.trim());
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
1048
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
1049
|
+
*/
|
|
1050
|
+
warnShortPage(envelope, requestedLimit) {
|
|
1051
|
+
const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
|
|
1052
|
+
const perPage = envelope.meta?.itemsPerPage;
|
|
1053
|
+
if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
|
|
1054
|
+
return;
|
|
1055
|
+
const pages = envelope.meta?.totalPages;
|
|
1056
|
+
const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
|
|
1057
|
+
process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
|
|
1058
|
+
`);
|
|
1059
|
+
}
|
|
1013
1060
|
sanitizeOutput(data) {
|
|
1014
1061
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1015
1062
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -28,6 +28,7 @@ export default class VatDeclarations extends Command {
|
|
|
28
28
|
query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
29
29
|
fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
30
30
|
sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
31
|
+
meta: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
31
32
|
'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
32
33
|
};
|
|
33
34
|
run(): Promise<void>;
|
|
@@ -36,6 +37,13 @@ export default class VatDeclarations extends Command {
|
|
|
36
37
|
private runCamt053Import;
|
|
37
38
|
private runNestedImport;
|
|
38
39
|
private parseFilterValue;
|
|
40
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
41
|
+
private printPageSummary;
|
|
42
|
+
/**
|
|
43
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
44
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
45
|
+
*/
|
|
46
|
+
private warnShortPage;
|
|
39
47
|
private sanitizeOutput;
|
|
40
48
|
private getColumns;
|
|
41
49
|
private getValue;
|
|
@@ -70,6 +70,7 @@ export default class VatDeclarations extends Command {
|
|
|
70
70
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
71
71
|
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
72
72
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
73
|
+
meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
|
|
73
74
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
74
75
|
};
|
|
75
76
|
async run() {
|
|
@@ -362,9 +363,15 @@ export default class VatDeclarations extends Command {
|
|
|
362
363
|
}
|
|
363
364
|
catch { /* keep as string */ }
|
|
364
365
|
}
|
|
365
|
-
//
|
|
366
|
+
// A paginated answer is { data, meta, links, totals }. The records are
|
|
367
|
+
// unwrapped for output, but what the API said about the page is kept:
|
|
368
|
+
// dropping it silently let a script read one page as the whole result
|
|
369
|
+
// (AderisERP/product#61).
|
|
370
|
+
let envelope;
|
|
366
371
|
if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
|
|
367
|
-
|
|
372
|
+
const page = output;
|
|
373
|
+
envelope = page;
|
|
374
|
+
output = page.data;
|
|
368
375
|
}
|
|
369
376
|
// For table/csv on non-array responses, auto-extract the primary array
|
|
370
377
|
// e.g., { payments: [...], total: 3 } → extract payments array
|
|
@@ -381,12 +388,24 @@ export default class VatDeclarations extends Command {
|
|
|
381
388
|
output = this.sanitizeOutput(output);
|
|
382
389
|
}
|
|
383
390
|
const fieldList = flags.fields?.split(',').map(f => f.trim());
|
|
384
|
-
if (
|
|
391
|
+
if (envelope)
|
|
392
|
+
this.warnShortPage(envelope, flags.limit);
|
|
393
|
+
if (flags.csv || flags.output === 'csv') {
|
|
385
394
|
this.printCsv(output, fieldList);
|
|
386
|
-
|
|
395
|
+
}
|
|
396
|
+
else if (flags.table || flags.output === 'table') {
|
|
387
397
|
this.printTable(output, fieldList);
|
|
388
|
-
|
|
398
|
+
if (envelope)
|
|
399
|
+
this.printPageSummary(envelope);
|
|
400
|
+
}
|
|
401
|
+
else if (envelope && flags.meta) {
|
|
402
|
+
// The whole envelope, for a caller that asked for it; the default stays
|
|
403
|
+
// a bare array so existing scripts are unaffected.
|
|
404
|
+
this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
389
407
|
this.log(JSON.stringify(output, null, 2));
|
|
408
|
+
}
|
|
390
409
|
}
|
|
391
410
|
catch (error) {
|
|
392
411
|
if (error instanceof ApiError) {
|
|
@@ -1015,6 +1034,34 @@ export default class VatDeclarations extends Command {
|
|
|
1015
1034
|
return `\$${op}`;
|
|
1016
1035
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
1017
1036
|
}
|
|
1037
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
1038
|
+
printPageSummary(envelope) {
|
|
1039
|
+
const meta = envelope.meta;
|
|
1040
|
+
if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
|
|
1041
|
+
const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
|
|
1042
|
+
this.log(`
|
|
1043
|
+
Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
|
|
1044
|
+
}
|
|
1045
|
+
for (const total of envelope.totals ?? []) {
|
|
1046
|
+
const { currency, ...sums } = total;
|
|
1047
|
+
const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
|
|
1048
|
+
this.log(`Total ${currency ?? ''}: ${parts}`.trim());
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
1053
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
1054
|
+
*/
|
|
1055
|
+
warnShortPage(envelope, requestedLimit) {
|
|
1056
|
+
const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
|
|
1057
|
+
const perPage = envelope.meta?.itemsPerPage;
|
|
1058
|
+
if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
|
|
1059
|
+
return;
|
|
1060
|
+
const pages = envelope.meta?.totalPages;
|
|
1061
|
+
const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
|
|
1062
|
+
process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
|
|
1063
|
+
`);
|
|
1064
|
+
}
|
|
1018
1065
|
sanitizeOutput(data) {
|
|
1019
1066
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1020
1067
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -18,6 +18,7 @@ export default class VendorInvoices extends Command {
|
|
|
18
18
|
'note-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
19
|
'file-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
20
|
'payment-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
'offset-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
22
|
page: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
23
|
limit: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
23
24
|
include: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -27,6 +28,7 @@ export default class VendorInvoices extends Command {
|
|
|
27
28
|
query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
28
29
|
fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
29
30
|
sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
31
|
+
meta: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
30
32
|
'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
31
33
|
};
|
|
32
34
|
run(): Promise<void>;
|
|
@@ -35,6 +37,13 @@ export default class VendorInvoices extends Command {
|
|
|
35
37
|
private runCamt053Import;
|
|
36
38
|
private runNestedImport;
|
|
37
39
|
private parseFilterValue;
|
|
40
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
41
|
+
private printPageSummary;
|
|
42
|
+
/**
|
|
43
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
44
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
45
|
+
*/
|
|
46
|
+
private warnShortPage;
|
|
38
47
|
private sanitizeOutput;
|
|
39
48
|
private getColumns;
|
|
40
49
|
private getValue;
|
|
@@ -6,7 +6,7 @@ const HAS_IMPORT = true;
|
|
|
6
6
|
const HAS_EXPORT = true;
|
|
7
7
|
const HAS_CAMT053 = false;
|
|
8
8
|
const NESTED_IMPORTS = ["line-items", "notes", "files", "payments"];
|
|
9
|
-
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "payment-derivation", "submit", "approve", "decline", "book", "rebuild-journal", "cancel", "change-vendor", "recalculate-payment-status", "validate", "create-line-items", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download", "list-payments", "create-payments", "get-payments", "update-payments", "delete-payments", "payments-approve", "payments-reopen", "payments-cancel"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
9
|
+
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "payment-derivation", "beneficiary-check", "submit", "approve", "decline", "book", "rebuild-journal", "cancel", "change-vendor", "recalculate-payment-status", "validate", "create-line-items", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download", "list-payments", "create-payments", "get-payments", "update-payments", "delete-payments", "payments-approve", "payments-reopen", "payments-cancel", "create-offsets", "delete-offsets", "offset-candidates"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
11
|
'list': { method: 'GET', path: '/v1/vendor-invoices', summary: 'Get all vendor invoices with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
12
|
'create': { method: 'POST', path: '/v1/vendor-invoices', summary: 'Create a new vendor invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'payments'] },
|
|
@@ -14,6 +14,7 @@ const ENDPOINTS = {
|
|
|
14
14
|
'update': { method: 'PATCH', path: '/v1/vendor-invoices/{id}', summary: 'Update a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
|
|
15
15
|
'delete': { method: 'DELETE', path: '/v1/vendor-invoices/{id}', summary: 'Delete a vendor invoice', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
16
|
'payment-derivation': { method: 'GET', path: '/v1/vendor-invoices/{id}/payment-derivation', summary: 'Explain how a vendor invoice\'s paid amount is derived', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
+
'beneficiary-check': { method: 'GET', path: '/v1/vendor-invoices/{id}/beneficiary-check', summary: 'Check a vendor invoice\'s beneficiary account', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
18
|
'submit': { method: 'POST', path: '/v1/vendor-invoices/{id}/submit', summary: 'Submit a vendor invoice for approval (transitions DRAFT to PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
19
|
'approve': { method: 'POST', path: '/v1/vendor-invoices/{id}/approve', summary: 'Approve a vendor invoice (transitions DRAFT/PENDING_APPROVAL to APPROVED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
|
|
19
20
|
'decline': { method: 'POST', path: '/v1/vendor-invoices/{id}/decline', summary: 'Decline a vendor invoice (transitions PENDING_APPROVAL to DECLINED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
@@ -23,8 +24,8 @@ const ENDPOINTS = {
|
|
|
23
24
|
'change-vendor': { method: 'POST', path: '/v1/vendor-invoices/{id}/change-vendor', summary: 'Change the vendor on a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
24
25
|
'recalculate-payment-status': { method: 'POST', path: '/v1/vendor-invoices/{id}/recalculate-payment-status', summary: 'Recalculate a vendor invoice\'s open amount and payment status', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
25
26
|
'validate': { method: 'POST', path: '/v1/vendor-invoices/{id}/validate', summary: 'Validate a vendor invoice update without persisting changes', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
|
|
26
|
-
'create-line-items': { method: 'POST', path: '/v1/vendor-invoices/{invoiceId}/line-items', summary: 'Add a line item to a vendor invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['tagIds', 'tagCodes', 'purchaseOrderLineItems'] },
|
|
27
|
-
'update-line-items': { method: 'PATCH', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Update a line item on a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['tagIds', 'tagCodes', 'purchaseOrderLineItems'] },
|
|
27
|
+
'create-line-items': { method: 'POST', path: '/v1/vendor-invoices/{invoiceId}/line-items', summary: 'Add a line item to a vendor invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['tagIds', 'tagCodes', 'purchaseOrderLineItems', 'creditedVendorInvoiceLineItems'] },
|
|
28
|
+
'update-line-items': { method: 'PATCH', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Update a line item on a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['tagIds', 'tagCodes', 'purchaseOrderLineItems', 'creditedVendorInvoiceLineItems'] },
|
|
28
29
|
'delete-line-items': { method: 'DELETE', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Remove a line item from a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
29
30
|
'list-notes': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes', summary: 'Get all notes for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
30
31
|
'create-notes': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes', summary: 'Create a note for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
@@ -45,6 +46,9 @@ const ENDPOINTS = {
|
|
|
45
46
|
'payments-approve': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/approve', summary: 'Approve a payment entry (OPEN → APPROVED)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
46
47
|
'payments-reopen': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/reopen', summary: 'Reopen an approved payment entry (APPROVED → OPEN)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
47
48
|
'payments-cancel': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/cancel', summary: 'Cancel a sent payment entry (SENT -> ERROR)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
49
|
+
'create-offsets': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/offsets', summary: 'Offset a prepayment or credit note against a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
50
|
+
'delete-offsets': { method: 'DELETE', path: '/v1/vendor-invoices/{vendorInvoiceId}/offsets/{offsetId}', summary: 'Remove an offset of a prepayment or credit note from a vendor invoice', pathParams: ['vendorInvoiceId', 'offsetId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
51
|
+
'offset-candidates': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/offset-candidates', summary: 'List the prepayments and credit notes that can be offset against a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
48
52
|
};
|
|
49
53
|
export default class VendorInvoices extends Command {
|
|
50
54
|
static description = 'Manage vendor invoices';
|
|
@@ -72,6 +76,7 @@ export default class VendorInvoices extends Command {
|
|
|
72
76
|
'note-id': Flags.string({ description: 'Path param: noteId' }),
|
|
73
77
|
'file-id': Flags.string({ description: 'Path param: fileId' }),
|
|
74
78
|
'payment-id': Flags.string({ description: 'Path param: paymentId' }),
|
|
79
|
+
'offset-id': Flags.string({ description: 'Path param: offsetId' }),
|
|
75
80
|
page: Flags.string({ description: 'Page number' }),
|
|
76
81
|
limit: Flags.string({ description: 'Items per page' }),
|
|
77
82
|
include: Flags.string({ description: 'Relations to include' }),
|
|
@@ -81,6 +86,7 @@ export default class VendorInvoices extends Command {
|
|
|
81
86
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
82
87
|
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
83
88
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
89
|
+
meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
|
|
84
90
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
85
91
|
};
|
|
86
92
|
async run() {
|
|
@@ -373,9 +379,15 @@ export default class VendorInvoices extends Command {
|
|
|
373
379
|
}
|
|
374
380
|
catch { /* keep as string */ }
|
|
375
381
|
}
|
|
376
|
-
//
|
|
382
|
+
// A paginated answer is { data, meta, links, totals }. The records are
|
|
383
|
+
// unwrapped for output, but what the API said about the page is kept:
|
|
384
|
+
// dropping it silently let a script read one page as the whole result
|
|
385
|
+
// (AderisERP/product#61).
|
|
386
|
+
let envelope;
|
|
377
387
|
if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
|
|
378
|
-
|
|
388
|
+
const page = output;
|
|
389
|
+
envelope = page;
|
|
390
|
+
output = page.data;
|
|
379
391
|
}
|
|
380
392
|
// For table/csv on non-array responses, auto-extract the primary array
|
|
381
393
|
// e.g., { payments: [...], total: 3 } → extract payments array
|
|
@@ -392,12 +404,24 @@ export default class VendorInvoices extends Command {
|
|
|
392
404
|
output = this.sanitizeOutput(output);
|
|
393
405
|
}
|
|
394
406
|
const fieldList = flags.fields?.split(',').map(f => f.trim());
|
|
395
|
-
if (
|
|
407
|
+
if (envelope)
|
|
408
|
+
this.warnShortPage(envelope, flags.limit);
|
|
409
|
+
if (flags.csv || flags.output === 'csv') {
|
|
396
410
|
this.printCsv(output, fieldList);
|
|
397
|
-
|
|
411
|
+
}
|
|
412
|
+
else if (flags.table || flags.output === 'table') {
|
|
398
413
|
this.printTable(output, fieldList);
|
|
399
|
-
|
|
414
|
+
if (envelope)
|
|
415
|
+
this.printPageSummary(envelope);
|
|
416
|
+
}
|
|
417
|
+
else if (envelope && flags.meta) {
|
|
418
|
+
// The whole envelope, for a caller that asked for it; the default stays
|
|
419
|
+
// a bare array so existing scripts are unaffected.
|
|
420
|
+
this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
400
423
|
this.log(JSON.stringify(output, null, 2));
|
|
424
|
+
}
|
|
401
425
|
}
|
|
402
426
|
catch (error) {
|
|
403
427
|
if (error instanceof ApiError) {
|
|
@@ -1026,6 +1050,34 @@ export default class VendorInvoices extends Command {
|
|
|
1026
1050
|
return `\$${op}`;
|
|
1027
1051
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
1028
1052
|
}
|
|
1053
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
1054
|
+
printPageSummary(envelope) {
|
|
1055
|
+
const meta = envelope.meta;
|
|
1056
|
+
if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
|
|
1057
|
+
const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
|
|
1058
|
+
this.log(`
|
|
1059
|
+
Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
|
|
1060
|
+
}
|
|
1061
|
+
for (const total of envelope.totals ?? []) {
|
|
1062
|
+
const { currency, ...sums } = total;
|
|
1063
|
+
const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
|
|
1064
|
+
this.log(`Total ${currency ?? ''}: ${parts}`.trim());
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
1069
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
1070
|
+
*/
|
|
1071
|
+
warnShortPage(envelope, requestedLimit) {
|
|
1072
|
+
const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
|
|
1073
|
+
const perPage = envelope.meta?.itemsPerPage;
|
|
1074
|
+
if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
|
|
1075
|
+
return;
|
|
1076
|
+
const pages = envelope.meta?.totalPages;
|
|
1077
|
+
const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
|
|
1078
|
+
process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
|
|
1079
|
+
`);
|
|
1080
|
+
}
|
|
1029
1081
|
sanitizeOutput(data) {
|
|
1030
1082
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1031
1083
|
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 Webhooks 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 Webhooks 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;
|
|
@@ -47,6 +47,7 @@ export default class Webhooks extends Command {
|
|
|
47
47
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
48
48
|
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
49
49
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
50
|
+
meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
|
|
50
51
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
51
52
|
};
|
|
52
53
|
async run() {
|
|
@@ -339,9 +340,15 @@ export default class Webhooks extends Command {
|
|
|
339
340
|
}
|
|
340
341
|
catch { /* keep as string */ }
|
|
341
342
|
}
|
|
342
|
-
//
|
|
343
|
+
// A paginated answer is { data, meta, links, totals }. The records are
|
|
344
|
+
// unwrapped for output, but what the API said about the page is kept:
|
|
345
|
+
// dropping it silently let a script read one page as the whole result
|
|
346
|
+
// (AderisERP/product#61).
|
|
347
|
+
let envelope;
|
|
343
348
|
if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
|
|
344
|
-
|
|
349
|
+
const page = output;
|
|
350
|
+
envelope = page;
|
|
351
|
+
output = page.data;
|
|
345
352
|
}
|
|
346
353
|
// For table/csv on non-array responses, auto-extract the primary array
|
|
347
354
|
// e.g., { payments: [...], total: 3 } → extract payments array
|
|
@@ -358,12 +365,24 @@ export default class Webhooks extends Command {
|
|
|
358
365
|
output = this.sanitizeOutput(output);
|
|
359
366
|
}
|
|
360
367
|
const fieldList = flags.fields?.split(',').map(f => f.trim());
|
|
361
|
-
if (
|
|
368
|
+
if (envelope)
|
|
369
|
+
this.warnShortPage(envelope, flags.limit);
|
|
370
|
+
if (flags.csv || flags.output === 'csv') {
|
|
362
371
|
this.printCsv(output, fieldList);
|
|
363
|
-
|
|
372
|
+
}
|
|
373
|
+
else if (flags.table || flags.output === 'table') {
|
|
364
374
|
this.printTable(output, fieldList);
|
|
365
|
-
|
|
375
|
+
if (envelope)
|
|
376
|
+
this.printPageSummary(envelope);
|
|
377
|
+
}
|
|
378
|
+
else if (envelope && flags.meta) {
|
|
379
|
+
// The whole envelope, for a caller that asked for it; the default stays
|
|
380
|
+
// a bare array so existing scripts are unaffected.
|
|
381
|
+
this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
366
384
|
this.log(JSON.stringify(output, null, 2));
|
|
385
|
+
}
|
|
367
386
|
}
|
|
368
387
|
catch (error) {
|
|
369
388
|
if (error instanceof ApiError) {
|
|
@@ -992,6 +1011,34 @@ export default class Webhooks extends Command {
|
|
|
992
1011
|
return `\$${op}`;
|
|
993
1012
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
994
1013
|
}
|
|
1014
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
1015
|
+
printPageSummary(envelope) {
|
|
1016
|
+
const meta = envelope.meta;
|
|
1017
|
+
if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
|
|
1018
|
+
const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
|
|
1019
|
+
this.log(`
|
|
1020
|
+
Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
|
|
1021
|
+
}
|
|
1022
|
+
for (const total of envelope.totals ?? []) {
|
|
1023
|
+
const { currency, ...sums } = total;
|
|
1024
|
+
const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
|
|
1025
|
+
this.log(`Total ${currency ?? ''}: ${parts}`.trim());
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
1030
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
1031
|
+
*/
|
|
1032
|
+
warnShortPage(envelope, requestedLimit) {
|
|
1033
|
+
const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
|
|
1034
|
+
const perPage = envelope.meta?.itemsPerPage;
|
|
1035
|
+
if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
|
|
1036
|
+
return;
|
|
1037
|
+
const pages = envelope.meta?.totalPages;
|
|
1038
|
+
const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
|
|
1039
|
+
process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
|
|
1040
|
+
`);
|
|
1041
|
+
}
|
|
995
1042
|
sanitizeOutput(data) {
|
|
996
1043
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
997
1044
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|