@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.
Files changed (134) hide show
  1. package/dist/commands/access-groups.d.ts +8 -0
  2. package/dist/commands/access-groups.js +54 -6
  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 +64 -7
  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 +52 -5
  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 +52 -5
  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/tags.d.ts +8 -0
  112. package/dist/commands/tags.js +52 -5
  113. package/dist/commands/tax-codes.d.ts +8 -0
  114. package/dist/commands/tax-codes.js +52 -5
  115. package/dist/commands/tax-matrix.d.ts +8 -0
  116. package/dist/commands/tax-matrix.js +52 -5
  117. package/dist/commands/templates.d.ts +8 -0
  118. package/dist/commands/templates.js +52 -5
  119. package/dist/commands/tenants.d.ts +8 -0
  120. package/dist/commands/tenants.js +52 -5
  121. package/dist/commands/tools.d.ts +8 -0
  122. package/dist/commands/tools.js +52 -5
  123. package/dist/commands/users.d.ts +8 -0
  124. package/dist/commands/users.js +52 -5
  125. package/dist/commands/vat-declarations.d.ts +8 -0
  126. package/dist/commands/vat-declarations.js +52 -5
  127. package/dist/commands/vendor-invoices.d.ts +9 -0
  128. package/dist/commands/vendor-invoices.js +60 -8
  129. package/dist/commands/webhooks.d.ts +8 -0
  130. package/dist/commands/webhooks.js +52 -5
  131. package/dist/commands/workflows.d.ts +8 -0
  132. package/dist/commands/workflows.js +52 -5
  133. package/oclif.manifest.json +433 -10
  134. package/package.json +1 -1
@@ -45,6 +45,7 @@ export default class DunningConfigs 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 DunningConfigs 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 DunningConfigs 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 DunningConfigs 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;
@@ -23,6 +23,7 @@ export default class DunningReminders 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 DunningReminders 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;
@@ -46,6 +46,7 @@ export default class DunningReminders 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 DunningReminders 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 DunningReminders 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 DunningReminders 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 DunningRuns 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 DunningRuns 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;
@@ -34,6 +34,7 @@ export default class DunningRuns extends Command {
34
34
  query: Flags.string({ description: 'Additional query params as JSON' }),
35
35
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
36
36
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
37
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
37
38
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
38
39
  };
39
40
  async run() {
@@ -326,9 +327,15 @@ export default class DunningRuns extends Command {
326
327
  }
327
328
  catch { /* keep as string */ }
328
329
  }
329
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
330
+ // A paginated answer is { data, meta, links, totals }. The records are
331
+ // unwrapped for output, but what the API said about the page is kept:
332
+ // dropping it silently let a script read one page as the whole result
333
+ // (AderisERP/product#61).
334
+ let envelope;
330
335
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
331
- output = output.data;
336
+ const page = output;
337
+ envelope = page;
338
+ output = page.data;
332
339
  }
333
340
  // For table/csv on non-array responses, auto-extract the primary array
334
341
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -345,12 +352,24 @@ export default class DunningRuns extends Command {
345
352
  output = this.sanitizeOutput(output);
346
353
  }
347
354
  const fieldList = flags.fields?.split(',').map(f => f.trim());
348
- if (flags.csv || flags.output === 'csv')
355
+ if (envelope)
356
+ this.warnShortPage(envelope, flags.limit);
357
+ if (flags.csv || flags.output === 'csv') {
349
358
  this.printCsv(output, fieldList);
350
- else if (flags.table || flags.output === 'table')
359
+ }
360
+ else if (flags.table || flags.output === 'table') {
351
361
  this.printTable(output, fieldList);
352
- else
362
+ if (envelope)
363
+ this.printPageSummary(envelope);
364
+ }
365
+ else if (envelope && flags.meta) {
366
+ // The whole envelope, for a caller that asked for it; the default stays
367
+ // a bare array so existing scripts are unaffected.
368
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
369
+ }
370
+ else {
353
371
  this.log(JSON.stringify(output, null, 2));
372
+ }
354
373
  }
355
374
  catch (error) {
356
375
  if (error instanceof ApiError) {
@@ -979,6 +998,34 @@ export default class DunningRuns extends Command {
979
998
  return `\$${op}`;
980
999
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
981
1000
  }
1001
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1002
+ printPageSummary(envelope) {
1003
+ const meta = envelope.meta;
1004
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1005
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1006
+ this.log(`
1007
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1008
+ }
1009
+ for (const total of envelope.totals ?? []) {
1010
+ const { currency, ...sums } = total;
1011
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1012
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1013
+ }
1014
+ }
1015
+ /**
1016
+ * The API caps how many records a page may hold. Asking for more and getting
1017
+ * fewer looks like "that is everything" unless it is said out loud.
1018
+ */
1019
+ warnShortPage(envelope, requestedLimit) {
1020
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1021
+ const perPage = envelope.meta?.itemsPerPage;
1022
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1023
+ return;
1024
+ const pages = envelope.meta?.totalPages;
1025
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1026
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1027
+ `);
1028
+ }
982
1029
  sanitizeOutput(data) {
983
1030
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
984
1031
  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 EmailTransactions 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 EmailTransactions 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;
@@ -40,6 +40,7 @@ export default class EmailTransactions extends Command {
40
40
  query: Flags.string({ description: 'Additional query params as JSON' }),
41
41
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
42
42
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
43
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
43
44
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
44
45
  };
45
46
  async run() {
@@ -332,9 +333,15 @@ export default class EmailTransactions extends Command {
332
333
  }
333
334
  catch { /* keep as string */ }
334
335
  }
335
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
336
+ // A paginated answer is { data, meta, links, totals }. The records are
337
+ // unwrapped for output, but what the API said about the page is kept:
338
+ // dropping it silently let a script read one page as the whole result
339
+ // (AderisERP/product#61).
340
+ let envelope;
336
341
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
337
- output = output.data;
342
+ const page = output;
343
+ envelope = page;
344
+ output = page.data;
338
345
  }
339
346
  // For table/csv on non-array responses, auto-extract the primary array
340
347
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -351,12 +358,24 @@ export default class EmailTransactions extends Command {
351
358
  output = this.sanitizeOutput(output);
352
359
  }
353
360
  const fieldList = flags.fields?.split(',').map(f => f.trim());
354
- if (flags.csv || flags.output === 'csv')
361
+ if (envelope)
362
+ this.warnShortPage(envelope, flags.limit);
363
+ if (flags.csv || flags.output === 'csv') {
355
364
  this.printCsv(output, fieldList);
356
- else if (flags.table || flags.output === 'table')
365
+ }
366
+ else if (flags.table || flags.output === 'table') {
357
367
  this.printTable(output, fieldList);
358
- else
368
+ if (envelope)
369
+ this.printPageSummary(envelope);
370
+ }
371
+ else if (envelope && flags.meta) {
372
+ // The whole envelope, for a caller that asked for it; the default stays
373
+ // a bare array so existing scripts are unaffected.
374
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
375
+ }
376
+ else {
359
377
  this.log(JSON.stringify(output, null, 2));
378
+ }
360
379
  }
361
380
  catch (error) {
362
381
  if (error instanceof ApiError) {
@@ -985,6 +1004,34 @@ export default class EmailTransactions extends Command {
985
1004
  return `\$${op}`;
986
1005
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
987
1006
  }
1007
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1008
+ printPageSummary(envelope) {
1009
+ const meta = envelope.meta;
1010
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1011
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1012
+ this.log(`
1013
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1014
+ }
1015
+ for (const total of envelope.totals ?? []) {
1016
+ const { currency, ...sums } = total;
1017
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1018
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1019
+ }
1020
+ }
1021
+ /**
1022
+ * The API caps how many records a page may hold. Asking for more and getting
1023
+ * fewer looks like "that is everything" unless it is said out loud.
1024
+ */
1025
+ warnShortPage(envelope, requestedLimit) {
1026
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1027
+ const perPage = envelope.meta?.itemsPerPage;
1028
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1029
+ return;
1030
+ const pages = envelope.meta?.totalPages;
1031
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1032
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1033
+ `);
1034
+ }
988
1035
  sanitizeOutput(data) {
989
1036
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
990
1037
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -27,6 +27,7 @@ export default class Employees extends Command {
27
27
  query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
28
28
  fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
29
29
  sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
30
+ meta: import("@oclif/core/interfaces").BooleanFlag<boolean>;
30
31
  'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
31
32
  };
32
33
  run(): Promise<void>;
@@ -35,6 +36,13 @@ export default class Employees extends Command {
35
36
  private runCamt053Import;
36
37
  private runNestedImport;
37
38
  private parseFilterValue;
39
+ /** The page line under a table: which page, how many, and any sums the API returned. */
40
+ private printPageSummary;
41
+ /**
42
+ * The API caps how many records a page may hold. Asking for more and getting
43
+ * fewer looks like "that is everything" unless it is said out loud.
44
+ */
45
+ private warnShortPage;
38
46
  private sanitizeOutput;
39
47
  private getColumns;
40
48
  private getValue;
@@ -76,6 +76,7 @@ export default class Employees 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 Employees 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 Employees 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 Employees 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 EntityConfigurations 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 EntityConfigurations 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 EntityConfigurations 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 EntityConfigurations 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 EntityConfigurations 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 EntityConfigurations 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;