@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.
Files changed (152) hide show
  1. package/dist/commands/access-groups.d.ts +8 -0
  2. package/dist/commands/access-groups.js +56 -8
  3. package/dist/commands/account-transaction-allocations.d.ts +8 -0
  4. package/dist/commands/account-transaction-allocations.js +52 -5
  5. package/dist/commands/account-transactions.d.ts +8 -0
  6. package/dist/commands/account-transactions.js +55 -7
  7. package/dist/commands/accounts.d.ts +8 -0
  8. package/dist/commands/accounts.js +52 -5
  9. package/dist/commands/agent-platform.d.ts +8 -0
  10. package/dist/commands/agent-platform.js +52 -5
  11. package/dist/commands/agent-runs.d.ts +8 -0
  12. package/dist/commands/agent-runs.js +52 -5
  13. package/dist/commands/analytics.d.ts +8 -0
  14. package/dist/commands/analytics.js +52 -5
  15. package/dist/commands/api-keys.d.ts +8 -0
  16. package/dist/commands/api-keys.js +52 -5
  17. package/dist/commands/async-jobs.d.ts +8 -0
  18. package/dist/commands/async-jobs.js +52 -5
  19. package/dist/commands/audit-log.d.ts +8 -0
  20. package/dist/commands/audit-log.js +52 -5
  21. package/dist/commands/bank-accounts.d.ts +8 -0
  22. package/dist/commands/bank-accounts.js +52 -5
  23. package/dist/commands/bank-integrations.d.ts +8 -0
  24. package/dist/commands/bank-integrations.js +52 -5
  25. package/dist/commands/business-partners.d.ts +9 -0
  26. package/dist/commands/business-partners.js +66 -9
  27. package/dist/commands/chat.d.ts +8 -0
  28. package/dist/commands/chat.js +52 -5
  29. package/dist/commands/credit-card-accounts.d.ts +8 -0
  30. package/dist/commands/credit-card-accounts.js +52 -5
  31. package/dist/commands/delivery-notes.d.ts +8 -0
  32. package/dist/commands/delivery-notes.js +52 -5
  33. package/dist/commands/dunning-configs.d.ts +8 -0
  34. package/dist/commands/dunning-configs.js +52 -5
  35. package/dist/commands/dunning-reminders.d.ts +8 -0
  36. package/dist/commands/dunning-reminders.js +52 -5
  37. package/dist/commands/dunning-runs.d.ts +8 -0
  38. package/dist/commands/dunning-runs.js +52 -5
  39. package/dist/commands/email-transactions.d.ts +8 -0
  40. package/dist/commands/email-transactions.js +52 -5
  41. package/dist/commands/employees.d.ts +8 -0
  42. package/dist/commands/employees.js +52 -5
  43. package/dist/commands/entity-configurations.d.ts +8 -0
  44. package/dist/commands/entity-configurations.js +52 -5
  45. package/dist/commands/event-processor-mappings.d.ts +8 -0
  46. package/dist/commands/event-processor-mappings.js +52 -5
  47. package/dist/commands/files.d.ts +8 -0
  48. package/dist/commands/files.js +53 -6
  49. package/dist/commands/health.d.ts +8 -0
  50. package/dist/commands/health.js +52 -5
  51. package/dist/commands/imports.d.ts +8 -0
  52. package/dist/commands/imports.js +53 -8
  53. package/dist/commands/inbox.d.ts +8 -0
  54. package/dist/commands/inbox.js +53 -7
  55. package/dist/commands/integration-identities.d.ts +8 -0
  56. package/dist/commands/integration-identities.js +52 -5
  57. package/dist/commands/integrations.d.ts +8 -0
  58. package/dist/commands/integrations.js +52 -5
  59. package/dist/commands/item-categories.d.ts +8 -0
  60. package/dist/commands/item-categories.js +52 -5
  61. package/dist/commands/items.d.ts +8 -0
  62. package/dist/commands/items.js +52 -5
  63. package/dist/commands/journal-lines.d.ts +8 -0
  64. package/dist/commands/journal-lines.js +52 -5
  65. package/dist/commands/journals.d.ts +8 -0
  66. package/dist/commands/journals.js +52 -5
  67. package/dist/commands/ledgers.d.ts +8 -0
  68. package/dist/commands/ledgers.js +57 -9
  69. package/dist/commands/my-account.d.ts +8 -0
  70. package/dist/commands/my-account.js +52 -5
  71. package/dist/commands/office-locations.d.ts +8 -0
  72. package/dist/commands/office-locations.js +52 -5
  73. package/dist/commands/payment-methods.d.ts +8 -0
  74. package/dist/commands/payment-methods.js +52 -5
  75. package/dist/commands/payment-runs.d.ts +8 -0
  76. package/dist/commands/payment-runs.js +54 -6
  77. package/dist/commands/payment-terms.d.ts +8 -0
  78. package/dist/commands/payment-terms.js +52 -5
  79. package/dist/commands/payments.d.ts +8 -0
  80. package/dist/commands/payments.js +52 -5
  81. package/dist/commands/payroll-runs.d.ts +8 -0
  82. package/dist/commands/payroll-runs.js +52 -5
  83. package/dist/commands/permissions.d.ts +8 -0
  84. package/dist/commands/permissions.js +52 -5
  85. package/dist/commands/price-lists.d.ts +8 -0
  86. package/dist/commands/price-lists.js +52 -5
  87. package/dist/commands/processor-mappings.d.ts +8 -0
  88. package/dist/commands/processor-mappings.js +52 -5
  89. package/dist/commands/processors.d.ts +8 -0
  90. package/dist/commands/processors.js +52 -5
  91. package/dist/commands/purchase-order-types.d.ts +8 -0
  92. package/dist/commands/purchase-order-types.js +52 -5
  93. package/dist/commands/purchase-orders.d.ts +8 -0
  94. package/dist/commands/purchase-orders.js +52 -5
  95. package/dist/commands/reports.d.ts +8 -0
  96. package/dist/commands/reports.js +52 -5
  97. package/dist/commands/roles.d.ts +8 -0
  98. package/dist/commands/roles.js +52 -5
  99. package/dist/commands/salary-types.d.ts +8 -0
  100. package/dist/commands/salary-types.js +52 -5
  101. package/dist/commands/sales-invoices.d.ts +10 -0
  102. package/dist/commands/sales-invoices.js +62 -6
  103. package/dist/commands/sales-orders.d.ts +8 -0
  104. package/dist/commands/sales-orders.js +52 -5
  105. package/dist/commands/saved-views.d.ts +8 -0
  106. package/dist/commands/saved-views.js +52 -5
  107. package/dist/commands/scheduled-processor-mappings.d.ts +8 -0
  108. package/dist/commands/scheduled-processor-mappings.js +52 -5
  109. package/dist/commands/schedules.d.ts +8 -0
  110. package/dist/commands/schedules.js +52 -5
  111. package/dist/commands/support/end.d.ts +9 -0
  112. package/dist/commands/support/end.js +29 -0
  113. package/dist/commands/support/start.d.ts +14 -0
  114. package/dist/commands/support/start.js +50 -0
  115. package/dist/commands/support/status.d.ts +10 -0
  116. package/dist/commands/support/status.js +45 -0
  117. package/dist/commands/support/tenants.d.ts +10 -0
  118. package/dist/commands/support/tenants.js +39 -0
  119. package/dist/commands/tags.d.ts +8 -0
  120. package/dist/commands/tags.js +56 -8
  121. package/dist/commands/tax-codes.d.ts +8 -0
  122. package/dist/commands/tax-codes.js +52 -5
  123. package/dist/commands/tax-matrix.d.ts +8 -0
  124. package/dist/commands/tax-matrix.js +52 -5
  125. package/dist/commands/templates.d.ts +8 -0
  126. package/dist/commands/templates.js +52 -5
  127. package/dist/commands/tenants.d.ts +8 -0
  128. package/dist/commands/tenants.js +52 -5
  129. package/dist/commands/tools.d.ts +8 -0
  130. package/dist/commands/tools.js +52 -5
  131. package/dist/commands/users.d.ts +8 -0
  132. package/dist/commands/users.js +52 -5
  133. package/dist/commands/vat-declarations.d.ts +8 -0
  134. package/dist/commands/vat-declarations.js +52 -5
  135. package/dist/commands/vendor-invoices.d.ts +9 -0
  136. package/dist/commands/vendor-invoices.js +60 -8
  137. package/dist/commands/webhooks.d.ts +8 -0
  138. package/dist/commands/webhooks.js +52 -5
  139. package/dist/commands/workflows.d.ts +8 -0
  140. package/dist/commands/workflows.js +52 -5
  141. package/dist/lib/auth/constants.d.ts +12 -0
  142. package/dist/lib/auth/constants.js +12 -0
  143. package/dist/lib/auth/index.d.ts +11 -1
  144. package/dist/lib/auth/index.js +90 -2
  145. package/dist/lib/auth/keycloak-provider.d.ts +22 -0
  146. package/dist/lib/auth/keycloak-provider.js +108 -3
  147. package/dist/lib/config.d.ts +18 -0
  148. package/dist/lib/config.js +10 -0
  149. package/oclif.manifest.json +640 -216
  150. package/package.json +4 -1
  151. package/dist/commands/tag-configurations.d.ts +0 -39
  152. package/dist/commands/tag-configurations.js +0 -1155
@@ -46,6 +46,7 @@ export default class AgentRuns extends Command {
46
46
  query: Flags.string({ description: 'Additional query params as JSON' }),
47
47
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
48
48
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
49
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
49
50
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
50
51
  };
51
52
  async run() {
@@ -338,9 +339,15 @@ export default class AgentRuns extends Command {
338
339
  }
339
340
  catch { /* keep as string */ }
340
341
  }
341
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
342
+ // A paginated answer is { data, meta, links, totals }. The records are
343
+ // unwrapped for output, but what the API said about the page is kept:
344
+ // dropping it silently let a script read one page as the whole result
345
+ // (AderisERP/product#61).
346
+ let envelope;
342
347
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
343
- output = output.data;
348
+ const page = output;
349
+ envelope = page;
350
+ output = page.data;
344
351
  }
345
352
  // For table/csv on non-array responses, auto-extract the primary array
346
353
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -357,12 +364,24 @@ export default class AgentRuns extends Command {
357
364
  output = this.sanitizeOutput(output);
358
365
  }
359
366
  const fieldList = flags.fields?.split(',').map(f => f.trim());
360
- if (flags.csv || flags.output === 'csv')
367
+ if (envelope)
368
+ this.warnShortPage(envelope, flags.limit);
369
+ if (flags.csv || flags.output === 'csv') {
361
370
  this.printCsv(output, fieldList);
362
- else if (flags.table || flags.output === 'table')
371
+ }
372
+ else if (flags.table || flags.output === 'table') {
363
373
  this.printTable(output, fieldList);
364
- else
374
+ if (envelope)
375
+ this.printPageSummary(envelope);
376
+ }
377
+ else if (envelope && flags.meta) {
378
+ // The whole envelope, for a caller that asked for it; the default stays
379
+ // a bare array so existing scripts are unaffected.
380
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
381
+ }
382
+ else {
365
383
  this.log(JSON.stringify(output, null, 2));
384
+ }
366
385
  }
367
386
  catch (error) {
368
387
  if (error instanceof ApiError) {
@@ -991,6 +1010,34 @@ export default class AgentRuns extends Command {
991
1010
  return `\$${op}`;
992
1011
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
993
1012
  }
1013
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1014
+ printPageSummary(envelope) {
1015
+ const meta = envelope.meta;
1016
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1017
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1018
+ this.log(`
1019
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1020
+ }
1021
+ for (const total of envelope.totals ?? []) {
1022
+ const { currency, ...sums } = total;
1023
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1024
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1025
+ }
1026
+ }
1027
+ /**
1028
+ * The API caps how many records a page may hold. Asking for more and getting
1029
+ * fewer looks like "that is everything" unless it is said out loud.
1030
+ */
1031
+ warnShortPage(envelope, requestedLimit) {
1032
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1033
+ const perPage = envelope.meta?.itemsPerPage;
1034
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1035
+ return;
1036
+ const pages = envelope.meta?.totalPages;
1037
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1038
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1039
+ `);
1040
+ }
994
1041
  sanitizeOutput(data) {
995
1042
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
996
1043
  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 Analytics 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 Analytics 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;
@@ -36,6 +36,7 @@ export default class Analytics extends Command {
36
36
  query: Flags.string({ description: 'Additional query params as JSON' }),
37
37
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
38
38
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
39
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
39
40
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
40
41
  };
41
42
  async run() {
@@ -328,9 +329,15 @@ export default class Analytics extends Command {
328
329
  }
329
330
  catch { /* keep as string */ }
330
331
  }
331
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
332
+ // A paginated answer is { data, meta, links, totals }. The records are
333
+ // unwrapped for output, but what the API said about the page is kept:
334
+ // dropping it silently let a script read one page as the whole result
335
+ // (AderisERP/product#61).
336
+ let envelope;
332
337
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
333
- output = output.data;
338
+ const page = output;
339
+ envelope = page;
340
+ output = page.data;
334
341
  }
335
342
  // For table/csv on non-array responses, auto-extract the primary array
336
343
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -347,12 +354,24 @@ export default class Analytics extends Command {
347
354
  output = this.sanitizeOutput(output);
348
355
  }
349
356
  const fieldList = flags.fields?.split(',').map(f => f.trim());
350
- if (flags.csv || flags.output === 'csv')
357
+ if (envelope)
358
+ this.warnShortPage(envelope, flags.limit);
359
+ if (flags.csv || flags.output === 'csv') {
351
360
  this.printCsv(output, fieldList);
352
- else if (flags.table || flags.output === 'table')
361
+ }
362
+ else if (flags.table || flags.output === 'table') {
353
363
  this.printTable(output, fieldList);
354
- else
364
+ if (envelope)
365
+ this.printPageSummary(envelope);
366
+ }
367
+ else if (envelope && flags.meta) {
368
+ // The whole envelope, for a caller that asked for it; the default stays
369
+ // a bare array so existing scripts are unaffected.
370
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
371
+ }
372
+ else {
355
373
  this.log(JSON.stringify(output, null, 2));
374
+ }
356
375
  }
357
376
  catch (error) {
358
377
  if (error instanceof ApiError) {
@@ -981,6 +1000,34 @@ export default class Analytics extends Command {
981
1000
  return `\$${op}`;
982
1001
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
983
1002
  }
1003
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1004
+ printPageSummary(envelope) {
1005
+ const meta = envelope.meta;
1006
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1007
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1008
+ this.log(`
1009
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1010
+ }
1011
+ for (const total of envelope.totals ?? []) {
1012
+ const { currency, ...sums } = total;
1013
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1014
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1015
+ }
1016
+ }
1017
+ /**
1018
+ * The API caps how many records a page may hold. Asking for more and getting
1019
+ * fewer looks like "that is everything" unless it is said out loud.
1020
+ */
1021
+ warnShortPage(envelope, requestedLimit) {
1022
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1023
+ const perPage = envelope.meta?.itemsPerPage;
1024
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1025
+ return;
1026
+ const pages = envelope.meta?.totalPages;
1027
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1028
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1029
+ `);
1030
+ }
984
1031
  sanitizeOutput(data) {
985
1032
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
986
1033
  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 ApiKeys 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 ApiKeys 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;
@@ -41,6 +41,7 @@ export default class ApiKeys extends Command {
41
41
  query: Flags.string({ description: 'Additional query params as JSON' }),
42
42
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
43
43
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
44
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
44
45
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
45
46
  };
46
47
  async run() {
@@ -333,9 +334,15 @@ export default class ApiKeys extends Command {
333
334
  }
334
335
  catch { /* keep as string */ }
335
336
  }
336
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
337
+ // A paginated answer is { data, meta, links, totals }. The records are
338
+ // unwrapped for output, but what the API said about the page is kept:
339
+ // dropping it silently let a script read one page as the whole result
340
+ // (AderisERP/product#61).
341
+ let envelope;
337
342
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
338
- output = output.data;
343
+ const page = output;
344
+ envelope = page;
345
+ output = page.data;
339
346
  }
340
347
  // For table/csv on non-array responses, auto-extract the primary array
341
348
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -352,12 +359,24 @@ export default class ApiKeys extends Command {
352
359
  output = this.sanitizeOutput(output);
353
360
  }
354
361
  const fieldList = flags.fields?.split(',').map(f => f.trim());
355
- if (flags.csv || flags.output === 'csv')
362
+ if (envelope)
363
+ this.warnShortPage(envelope, flags.limit);
364
+ if (flags.csv || flags.output === 'csv') {
356
365
  this.printCsv(output, fieldList);
357
- else if (flags.table || flags.output === 'table')
366
+ }
367
+ else if (flags.table || flags.output === 'table') {
358
368
  this.printTable(output, fieldList);
359
- else
369
+ if (envelope)
370
+ this.printPageSummary(envelope);
371
+ }
372
+ else if (envelope && flags.meta) {
373
+ // The whole envelope, for a caller that asked for it; the default stays
374
+ // a bare array so existing scripts are unaffected.
375
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
376
+ }
377
+ else {
360
378
  this.log(JSON.stringify(output, null, 2));
379
+ }
361
380
  }
362
381
  catch (error) {
363
382
  if (error instanceof ApiError) {
@@ -986,6 +1005,34 @@ export default class ApiKeys extends Command {
986
1005
  return `\$${op}`;
987
1006
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
988
1007
  }
1008
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1009
+ printPageSummary(envelope) {
1010
+ const meta = envelope.meta;
1011
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1012
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1013
+ this.log(`
1014
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1015
+ }
1016
+ for (const total of envelope.totals ?? []) {
1017
+ const { currency, ...sums } = total;
1018
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1019
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1020
+ }
1021
+ }
1022
+ /**
1023
+ * The API caps how many records a page may hold. Asking for more and getting
1024
+ * fewer looks like "that is everything" unless it is said out loud.
1025
+ */
1026
+ warnShortPage(envelope, requestedLimit) {
1027
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1028
+ const perPage = envelope.meta?.itemsPerPage;
1029
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1030
+ return;
1031
+ const pages = envelope.meta?.totalPages;
1032
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1033
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1034
+ `);
1035
+ }
989
1036
  sanitizeOutput(data) {
990
1037
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
991
1038
  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 AsyncJobs 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 AsyncJobs 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;
@@ -49,6 +49,7 @@ export default class AsyncJobs extends Command {
49
49
  query: Flags.string({ description: 'Additional query params as JSON' }),
50
50
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
51
51
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
52
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
52
53
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
53
54
  };
54
55
  async run() {
@@ -341,9 +342,15 @@ export default class AsyncJobs extends Command {
341
342
  }
342
343
  catch { /* keep as string */ }
343
344
  }
344
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
345
+ // A paginated answer is { data, meta, links, totals }. The records are
346
+ // unwrapped for output, but what the API said about the page is kept:
347
+ // dropping it silently let a script read one page as the whole result
348
+ // (AderisERP/product#61).
349
+ let envelope;
345
350
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
346
- output = output.data;
351
+ const page = output;
352
+ envelope = page;
353
+ output = page.data;
347
354
  }
348
355
  // For table/csv on non-array responses, auto-extract the primary array
349
356
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -360,12 +367,24 @@ export default class AsyncJobs extends Command {
360
367
  output = this.sanitizeOutput(output);
361
368
  }
362
369
  const fieldList = flags.fields?.split(',').map(f => f.trim());
363
- if (flags.csv || flags.output === 'csv')
370
+ if (envelope)
371
+ this.warnShortPage(envelope, flags.limit);
372
+ if (flags.csv || flags.output === 'csv') {
364
373
  this.printCsv(output, fieldList);
365
- else if (flags.table || flags.output === 'table')
374
+ }
375
+ else if (flags.table || flags.output === 'table') {
366
376
  this.printTable(output, fieldList);
367
- else
377
+ if (envelope)
378
+ this.printPageSummary(envelope);
379
+ }
380
+ else if (envelope && flags.meta) {
381
+ // The whole envelope, for a caller that asked for it; the default stays
382
+ // a bare array so existing scripts are unaffected.
383
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
384
+ }
385
+ else {
368
386
  this.log(JSON.stringify(output, null, 2));
387
+ }
369
388
  }
370
389
  catch (error) {
371
390
  if (error instanceof ApiError) {
@@ -994,6 +1013,34 @@ export default class AsyncJobs extends Command {
994
1013
  return `\$${op}`;
995
1014
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
996
1015
  }
1016
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1017
+ printPageSummary(envelope) {
1018
+ const meta = envelope.meta;
1019
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1020
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1021
+ this.log(`
1022
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1023
+ }
1024
+ for (const total of envelope.totals ?? []) {
1025
+ const { currency, ...sums } = total;
1026
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1027
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1028
+ }
1029
+ }
1030
+ /**
1031
+ * The API caps how many records a page may hold. Asking for more and getting
1032
+ * fewer looks like "that is everything" unless it is said out loud.
1033
+ */
1034
+ warnShortPage(envelope, requestedLimit) {
1035
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1036
+ const perPage = envelope.meta?.itemsPerPage;
1037
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1038
+ return;
1039
+ const pages = envelope.meta?.totalPages;
1040
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1041
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1042
+ `);
1043
+ }
997
1044
  sanitizeOutput(data) {
998
1045
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
999
1046
  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 AuditLog 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 AuditLog 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;
@@ -39,6 +39,7 @@ export default class AuditLog extends Command {
39
39
  query: Flags.string({ description: 'Additional query params as JSON' }),
40
40
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
41
41
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
42
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
42
43
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
43
44
  };
44
45
  async run() {
@@ -331,9 +332,15 @@ export default class AuditLog extends Command {
331
332
  }
332
333
  catch { /* keep as string */ }
333
334
  }
334
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
335
+ // A paginated answer is { data, meta, links, totals }. The records are
336
+ // unwrapped for output, but what the API said about the page is kept:
337
+ // dropping it silently let a script read one page as the whole result
338
+ // (AderisERP/product#61).
339
+ let envelope;
335
340
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
336
- output = output.data;
341
+ const page = output;
342
+ envelope = page;
343
+ output = page.data;
337
344
  }
338
345
  // For table/csv on non-array responses, auto-extract the primary array
339
346
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -350,12 +357,24 @@ export default class AuditLog extends Command {
350
357
  output = this.sanitizeOutput(output);
351
358
  }
352
359
  const fieldList = flags.fields?.split(',').map(f => f.trim());
353
- if (flags.csv || flags.output === 'csv')
360
+ if (envelope)
361
+ this.warnShortPage(envelope, flags.limit);
362
+ if (flags.csv || flags.output === 'csv') {
354
363
  this.printCsv(output, fieldList);
355
- else if (flags.table || flags.output === 'table')
364
+ }
365
+ else if (flags.table || flags.output === 'table') {
356
366
  this.printTable(output, fieldList);
357
- else
367
+ if (envelope)
368
+ this.printPageSummary(envelope);
369
+ }
370
+ else if (envelope && flags.meta) {
371
+ // The whole envelope, for a caller that asked for it; the default stays
372
+ // a bare array so existing scripts are unaffected.
373
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
374
+ }
375
+ else {
358
376
  this.log(JSON.stringify(output, null, 2));
377
+ }
359
378
  }
360
379
  catch (error) {
361
380
  if (error instanceof ApiError) {
@@ -984,6 +1003,34 @@ export default class AuditLog extends Command {
984
1003
  return `\$${op}`;
985
1004
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
986
1005
  }
1006
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1007
+ printPageSummary(envelope) {
1008
+ const meta = envelope.meta;
1009
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1010
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1011
+ this.log(`
1012
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1013
+ }
1014
+ for (const total of envelope.totals ?? []) {
1015
+ const { currency, ...sums } = total;
1016
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1017
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1018
+ }
1019
+ }
1020
+ /**
1021
+ * The API caps how many records a page may hold. Asking for more and getting
1022
+ * fewer looks like "that is everything" unless it is said out loud.
1023
+ */
1024
+ warnShortPage(envelope, requestedLimit) {
1025
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1026
+ const perPage = envelope.meta?.itemsPerPage;
1027
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1028
+ return;
1029
+ const pages = envelope.meta?.totalPages;
1030
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1031
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1032
+ `);
1033
+ }
987
1034
  sanitizeOutput(data) {
988
1035
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
989
1036
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -25,6 +25,7 @@ export default class BankAccounts extends Command {
25
25
  query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
26
26
  fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
27
27
  sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
28
+ meta: import("@oclif/core/interfaces").BooleanFlag<boolean>;
28
29
  'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
29
30
  };
30
31
  run(): Promise<void>;
@@ -33,6 +34,13 @@ export default class BankAccounts extends Command {
33
34
  private runCamt053Import;
34
35
  private runNestedImport;
35
36
  private parseFilterValue;
37
+ /** The page line under a table: which page, how many, and any sums the API returned. */
38
+ private printPageSummary;
39
+ /**
40
+ * The API caps how many records a page may hold. Asking for more and getting
41
+ * fewer looks like "that is everything" unless it is said out loud.
42
+ */
43
+ private warnShortPage;
36
44
  private sanitizeOutput;
37
45
  private getColumns;
38
46
  private getValue;
@@ -60,6 +60,7 @@ export default class BankAccounts extends Command {
60
60
  query: Flags.string({ description: 'Additional query params as JSON' }),
61
61
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
62
62
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
63
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
63
64
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
64
65
  };
65
66
  async run() {
@@ -352,9 +353,15 @@ export default class BankAccounts extends Command {
352
353
  }
353
354
  catch { /* keep as string */ }
354
355
  }
355
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
356
+ // A paginated answer is { data, meta, links, totals }. The records are
357
+ // unwrapped for output, but what the API said about the page is kept:
358
+ // dropping it silently let a script read one page as the whole result
359
+ // (AderisERP/product#61).
360
+ let envelope;
356
361
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
357
- output = output.data;
362
+ const page = output;
363
+ envelope = page;
364
+ output = page.data;
358
365
  }
359
366
  // For table/csv on non-array responses, auto-extract the primary array
360
367
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -371,12 +378,24 @@ export default class BankAccounts extends Command {
371
378
  output = this.sanitizeOutput(output);
372
379
  }
373
380
  const fieldList = flags.fields?.split(',').map(f => f.trim());
374
- if (flags.csv || flags.output === 'csv')
381
+ if (envelope)
382
+ this.warnShortPage(envelope, flags.limit);
383
+ if (flags.csv || flags.output === 'csv') {
375
384
  this.printCsv(output, fieldList);
376
- else if (flags.table || flags.output === 'table')
385
+ }
386
+ else if (flags.table || flags.output === 'table') {
377
387
  this.printTable(output, fieldList);
378
- else
388
+ if (envelope)
389
+ this.printPageSummary(envelope);
390
+ }
391
+ else if (envelope && flags.meta) {
392
+ // The whole envelope, for a caller that asked for it; the default stays
393
+ // a bare array so existing scripts are unaffected.
394
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
395
+ }
396
+ else {
379
397
  this.log(JSON.stringify(output, null, 2));
398
+ }
380
399
  }
381
400
  catch (error) {
382
401
  if (error instanceof ApiError) {
@@ -1005,6 +1024,34 @@ export default class BankAccounts extends Command {
1005
1024
  return `\$${op}`;
1006
1025
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
1007
1026
  }
1027
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1028
+ printPageSummary(envelope) {
1029
+ const meta = envelope.meta;
1030
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1031
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1032
+ this.log(`
1033
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1034
+ }
1035
+ for (const total of envelope.totals ?? []) {
1036
+ const { currency, ...sums } = total;
1037
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1038
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1039
+ }
1040
+ }
1041
+ /**
1042
+ * The API caps how many records a page may hold. Asking for more and getting
1043
+ * fewer looks like "that is everything" unless it is said out loud.
1044
+ */
1045
+ warnShortPage(envelope, requestedLimit) {
1046
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1047
+ const perPage = envelope.meta?.itemsPerPage;
1048
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1049
+ return;
1050
+ const pages = envelope.meta?.totalPages;
1051
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1052
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1053
+ `);
1054
+ }
1008
1055
  sanitizeOutput(data) {
1009
1056
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1010
1057
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;