@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
@@ -23,6 +23,7 @@ export default class Reports 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 Reports 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;
@@ -38,6 +38,7 @@ export default class Reports 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 Reports extends Command {
330
331
  }
331
332
  catch { /* keep as string */ }
332
333
  }
333
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
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
- output = output.data;
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 Reports extends Command {
349
356
  output = this.sanitizeOutput(output);
350
357
  }
351
358
  const fieldList = flags.fields?.split(',').map(f => f.trim());
352
- if (flags.csv || flags.output === 'csv')
359
+ if (envelope)
360
+ this.warnShortPage(envelope, flags.limit);
361
+ if (flags.csv || flags.output === 'csv') {
353
362
  this.printCsv(output, fieldList);
354
- else if (flags.table || flags.output === 'table')
363
+ }
364
+ else if (flags.table || flags.output === 'table') {
355
365
  this.printTable(output, fieldList);
356
- else
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 Reports 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;
@@ -23,6 +23,7 @@ export default class Roles 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 Roles 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;
@@ -43,6 +43,7 @@ export default class Roles extends Command {
43
43
  query: Flags.string({ description: 'Additional query params as JSON' }),
44
44
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
45
45
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
46
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
46
47
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
47
48
  };
48
49
  async run() {
@@ -335,9 +336,15 @@ export default class Roles extends Command {
335
336
  }
336
337
  catch { /* keep as string */ }
337
338
  }
338
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
339
+ // A paginated answer is { data, meta, links, totals }. The records are
340
+ // unwrapped for output, but what the API said about the page is kept:
341
+ // dropping it silently let a script read one page as the whole result
342
+ // (AderisERP/product#61).
343
+ let envelope;
339
344
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
340
- output = output.data;
345
+ const page = output;
346
+ envelope = page;
347
+ output = page.data;
341
348
  }
342
349
  // For table/csv on non-array responses, auto-extract the primary array
343
350
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -354,12 +361,24 @@ export default class Roles extends Command {
354
361
  output = this.sanitizeOutput(output);
355
362
  }
356
363
  const fieldList = flags.fields?.split(',').map(f => f.trim());
357
- if (flags.csv || flags.output === 'csv')
364
+ if (envelope)
365
+ this.warnShortPage(envelope, flags.limit);
366
+ if (flags.csv || flags.output === 'csv') {
358
367
  this.printCsv(output, fieldList);
359
- else if (flags.table || flags.output === 'table')
368
+ }
369
+ else if (flags.table || flags.output === 'table') {
360
370
  this.printTable(output, fieldList);
361
- else
371
+ if (envelope)
372
+ this.printPageSummary(envelope);
373
+ }
374
+ else if (envelope && flags.meta) {
375
+ // The whole envelope, for a caller that asked for it; the default stays
376
+ // a bare array so existing scripts are unaffected.
377
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
378
+ }
379
+ else {
362
380
  this.log(JSON.stringify(output, null, 2));
381
+ }
363
382
  }
364
383
  catch (error) {
365
384
  if (error instanceof ApiError) {
@@ -988,6 +1007,34 @@ export default class Roles extends Command {
988
1007
  return `\$${op}`;
989
1008
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
990
1009
  }
1010
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1011
+ printPageSummary(envelope) {
1012
+ const meta = envelope.meta;
1013
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1014
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1015
+ this.log(`
1016
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1017
+ }
1018
+ for (const total of envelope.totals ?? []) {
1019
+ const { currency, ...sums } = total;
1020
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1021
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1022
+ }
1023
+ }
1024
+ /**
1025
+ * The API caps how many records a page may hold. Asking for more and getting
1026
+ * fewer looks like "that is everything" unless it is said out loud.
1027
+ */
1028
+ warnShortPage(envelope, requestedLimit) {
1029
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1030
+ const perPage = envelope.meta?.itemsPerPage;
1031
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1032
+ return;
1033
+ const pages = envelope.meta?.totalPages;
1034
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1035
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1036
+ `);
1037
+ }
991
1038
  sanitizeOutput(data) {
992
1039
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
993
1040
  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 SalaryTypes 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 SalaryTypes 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 SalaryTypes 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 SalaryTypes extends Command {
337
338
  }
338
339
  catch { /* keep as string */ }
339
340
  }
340
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
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
- output = output.data;
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 SalaryTypes extends Command {
356
363
  output = this.sanitizeOutput(output);
357
364
  }
358
365
  const fieldList = flags.fields?.split(',').map(f => f.trim());
359
- if (flags.csv || flags.output === 'csv')
366
+ if (envelope)
367
+ this.warnShortPage(envelope, flags.limit);
368
+ if (flags.csv || flags.output === 'csv') {
360
369
  this.printCsv(output, fieldList);
361
- else if (flags.table || flags.output === 'table')
370
+ }
371
+ else if (flags.table || flags.output === 'table') {
362
372
  this.printTable(output, fieldList);
363
- else
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 SalaryTypes 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;
@@ -14,9 +14,11 @@ export default class SalesInvoices extends Command {
14
14
  data: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
15
  file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
16
  set: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ 'offset-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
18
  'line-item-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
19
  'note-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
20
  'file-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
+ 'payment-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
22
  page: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
23
  limit: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
24
  include: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -26,6 +28,7 @@ export default class SalesInvoices extends Command {
26
28
  query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
27
29
  fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
28
30
  sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
31
+ meta: import("@oclif/core/interfaces").BooleanFlag<boolean>;
29
32
  'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
30
33
  };
31
34
  run(): Promise<void>;
@@ -34,6 +37,13 @@ export default class SalesInvoices extends Command {
34
37
  private runCamt053Import;
35
38
  private runNestedImport;
36
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;
37
47
  private sanitizeOutput;
38
48
  private getColumns;
39
49
  private getValue;
@@ -6,8 +6,11 @@ const HAS_IMPORT = true;
6
6
  const HAS_EXPORT = true;
7
7
  const HAS_CAMT053 = false;
8
8
  const NESTED_IMPORTS = ["migrated-invoice", "line-items", "notes", "files"];
9
- const SUBCOMMANDS = [...["list", "create", "create-migrated-invoice", "get-pdf", "create-pdf", "get", "update", "delete", "payment-derivation", "update-migrated-invoice", "approve", "match", "validate", "request-approval", "send", "send-resend", "cancel", "copy", "replace", "rebuild-journal", "recalculate-payment-status", "merge", "create-line-items", "line-items-from-sales-order", "line-items-from-delivery-note", "update-line-items-reorder", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
9
+ const SUBCOMMANDS = [...["create-offsets", "delete-offsets", "offset-candidates", "list", "create", "create-migrated-invoice", "get-pdf", "create-pdf", "get", "update", "delete", "payment-derivation", "update-migrated-invoice", "approve", "match", "validate", "request-approval", "send", "send-resend", "cancel", "copy", "replace", "rebuild-journal", "recalculate-payment-status", "merge", "create-line-items", "line-items-from-sales-order", "line-items-from-delivery-note", "update-line-items-reorder", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download", "line-items-from-sales-invoice", "credit-notes", "list-refund-account-options", "update-refund-payments-beneficiary-account"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
+ 'create-offsets': { method: 'POST', path: '/v1/sales-invoices/{salesInvoiceId}/offsets', summary: 'Deduct a prepayment or credit note from a sales invoice', pathParams: ['salesInvoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
12
+ 'delete-offsets': { method: 'DELETE', path: '/v1/sales-invoices/{salesInvoiceId}/offsets/{offsetId}', summary: 'Remove a deduction of a prepayment or credit note from a sales invoice', pathParams: ['salesInvoiceId', 'offsetId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
13
+ 'offset-candidates': { method: 'GET', path: '/v1/sales-invoices/{salesInvoiceId}/offset-candidates', summary: 'List the prepayments and credit notes that can be deducted from a sales invoice', pathParams: ['salesInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
11
14
  'list': { method: 'GET', path: '/v1/sales-invoices', summary: 'Get all sales invoices with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
12
15
  'create': { method: 'POST', path: '/v1/sales-invoices', summary: 'Create a new sales invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'lineItems', 'discounts', 'files', 'notes', 'totalAmountByTaxRate', 'tagIds', 'tagCodes', 'invoiceEmailCc'] },
13
16
  'create-migrated-invoice': { method: 'POST', path: '/v1/sales-invoices/create-migrated-invoice', summary: 'Create a sales invoice with explicit tax codes on line items (for data migration)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['lineItems', 'files', 'notes', 'totalAmountByTaxRate', 'tagIds', 'tagCodes', 'invoiceEmailCc'] },
@@ -47,6 +50,10 @@ const ENDPOINTS = {
47
50
  'update-files': { method: 'PATCH', path: '/v1/sales-invoices/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a sales invoice', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
48
51
  'delete-files': { method: 'DELETE', path: '/v1/sales-invoices/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a sales invoice', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
49
52
  'files-download': { method: 'GET', path: '/v1/sales-invoices/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a sales invoice', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
53
+ 'line-items-from-sales-invoice': { method: 'POST', path: '/v1/sales-invoices/{invoiceId}/line-items/from-sales-invoice', summary: 'Add line items to a sales credit note from a sales invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['lines'] },
54
+ 'credit-notes': { method: 'POST', path: '/v1/sales-invoices/{invoiceId}/credit-notes', summary: 'Create a sales credit note from a sales invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['lines'] },
55
+ 'list-refund-account-options': { method: 'GET', path: '/v1/sales-invoices/{salesInvoiceId}/refund-account-options', summary: 'List the accounts the refund of a sales credit note can be paid to', pathParams: ['salesInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
56
+ 'update-refund-payments-beneficiary-account': { method: 'PUT', path: '/v1/sales-invoices/{salesInvoiceId}/refund-payments/{paymentId}/beneficiary-account', summary: 'Choose the account a refund payment pays to', pathParams: ['salesInvoiceId', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
50
57
  };
51
58
  export default class SalesInvoices extends Command {
52
59
  static description = 'Manage sales invoices';
@@ -70,9 +77,11 @@ export default class SalesInvoices extends Command {
70
77
  data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
71
78
  file: Flags.string({ char: 'f', description: 'Read request body from file' }),
72
79
  set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
80
+ 'offset-id': Flags.string({ description: 'Path param: offsetId' }),
73
81
  'line-item-id': Flags.string({ description: 'Path param: lineItemId' }),
74
82
  'note-id': Flags.string({ description: 'Path param: noteId' }),
75
83
  'file-id': Flags.string({ description: 'Path param: fileId' }),
84
+ 'payment-id': Flags.string({ description: 'Path param: paymentId' }),
76
85
  page: Flags.string({ description: 'Page number' }),
77
86
  limit: Flags.string({ description: 'Items per page' }),
78
87
  include: Flags.string({ description: 'Relations to include' }),
@@ -82,6 +91,7 @@ export default class SalesInvoices extends Command {
82
91
  query: Flags.string({ description: 'Additional query params as JSON' }),
83
92
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
84
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' }),
85
95
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
86
96
  };
87
97
  async run() {
@@ -374,9 +384,15 @@ export default class SalesInvoices extends Command {
374
384
  }
375
385
  catch { /* keep as string */ }
376
386
  }
377
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
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;
378
392
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
379
- output = output.data;
393
+ const page = output;
394
+ envelope = page;
395
+ output = page.data;
380
396
  }
381
397
  // For table/csv on non-array responses, auto-extract the primary array
382
398
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -393,12 +409,24 @@ export default class SalesInvoices extends Command {
393
409
  output = this.sanitizeOutput(output);
394
410
  }
395
411
  const fieldList = flags.fields?.split(',').map(f => f.trim());
396
- if (flags.csv || flags.output === 'csv')
412
+ if (envelope)
413
+ this.warnShortPage(envelope, flags.limit);
414
+ if (flags.csv || flags.output === 'csv') {
397
415
  this.printCsv(output, fieldList);
398
- else if (flags.table || flags.output === 'table')
416
+ }
417
+ else if (flags.table || flags.output === 'table') {
399
418
  this.printTable(output, fieldList);
400
- else
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 {
401
428
  this.log(JSON.stringify(output, null, 2));
429
+ }
402
430
  }
403
431
  catch (error) {
404
432
  if (error instanceof ApiError) {
@@ -1027,6 +1055,34 @@ export default class SalesInvoices extends Command {
1027
1055
  return `\$${op}`;
1028
1056
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
1029
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
+ }
1030
1086
  sanitizeOutput(data) {
1031
1087
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1032
1088
  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 SalesOrders 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 SalesOrders 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;
@@ -76,6 +76,7 @@ export default class SalesOrders extends Command {
76
76
  query: Flags.string({ description: 'Additional query params as JSON' }),
77
77
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
78
78
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
79
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
79
80
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
80
81
  };
81
82
  async run() {
@@ -368,9 +369,15 @@ export default class SalesOrders extends Command {
368
369
  }
369
370
  catch { /* keep as string */ }
370
371
  }
371
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
372
+ // A paginated answer is { data, meta, links, totals }. The records are
373
+ // unwrapped for output, but what the API said about the page is kept:
374
+ // dropping it silently let a script read one page as the whole result
375
+ // (AderisERP/product#61).
376
+ let envelope;
372
377
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
373
- output = output.data;
378
+ const page = output;
379
+ envelope = page;
380
+ output = page.data;
374
381
  }
375
382
  // For table/csv on non-array responses, auto-extract the primary array
376
383
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -387,12 +394,24 @@ export default class SalesOrders extends Command {
387
394
  output = this.sanitizeOutput(output);
388
395
  }
389
396
  const fieldList = flags.fields?.split(',').map(f => f.trim());
390
- if (flags.csv || flags.output === 'csv')
397
+ if (envelope)
398
+ this.warnShortPage(envelope, flags.limit);
399
+ if (flags.csv || flags.output === 'csv') {
391
400
  this.printCsv(output, fieldList);
392
- else if (flags.table || flags.output === 'table')
401
+ }
402
+ else if (flags.table || flags.output === 'table') {
393
403
  this.printTable(output, fieldList);
394
- else
404
+ if (envelope)
405
+ this.printPageSummary(envelope);
406
+ }
407
+ else if (envelope && flags.meta) {
408
+ // The whole envelope, for a caller that asked for it; the default stays
409
+ // a bare array so existing scripts are unaffected.
410
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
411
+ }
412
+ else {
395
413
  this.log(JSON.stringify(output, null, 2));
414
+ }
396
415
  }
397
416
  catch (error) {
398
417
  if (error instanceof ApiError) {
@@ -1021,6 +1040,34 @@ export default class SalesOrders extends Command {
1021
1040
  return `\$${op}`;
1022
1041
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
1023
1042
  }
1043
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1044
+ printPageSummary(envelope) {
1045
+ const meta = envelope.meta;
1046
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1047
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1048
+ this.log(`
1049
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1050
+ }
1051
+ for (const total of envelope.totals ?? []) {
1052
+ const { currency, ...sums } = total;
1053
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1054
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1055
+ }
1056
+ }
1057
+ /**
1058
+ * The API caps how many records a page may hold. Asking for more and getting
1059
+ * fewer looks like "that is everything" unless it is said out loud.
1060
+ */
1061
+ warnShortPage(envelope, requestedLimit) {
1062
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1063
+ const perPage = envelope.meta?.itemsPerPage;
1064
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1065
+ return;
1066
+ const pages = envelope.meta?.totalPages;
1067
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1068
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1069
+ `);
1070
+ }
1024
1071
  sanitizeOutput(data) {
1025
1072
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1026
1073
  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 SavedViews 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 SavedViews 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;