@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
@@ -46,6 +46,7 @@ export default class PaymentTerms 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 PaymentTerms 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 PaymentTerms 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 PaymentTerms 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 Payments 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 Payments extends Command {
31
32
  private runCamt053Import;
32
33
  private runNestedImport;
33
34
  private parseFilterValue;
35
+ /** The page line under a table: which page, how many, and any sums the API returned. */
36
+ private printPageSummary;
37
+ /**
38
+ * The API caps how many records a page may hold. Asking for more and getting
39
+ * fewer looks like "that is everything" unless it is said out loud.
40
+ */
41
+ private warnShortPage;
34
42
  private sanitizeOutput;
35
43
  private getColumns;
36
44
  private getValue;
@@ -47,6 +47,7 @@ export default class Payments extends Command {
47
47
  query: Flags.string({ description: 'Additional query params as JSON' }),
48
48
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
49
49
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
50
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
50
51
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
51
52
  };
52
53
  async run() {
@@ -339,9 +340,15 @@ export default class Payments extends Command {
339
340
  }
340
341
  catch { /* keep as string */ }
341
342
  }
342
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
343
+ // A paginated answer is { data, meta, links, totals }. The records are
344
+ // unwrapped for output, but what the API said about the page is kept:
345
+ // dropping it silently let a script read one page as the whole result
346
+ // (AderisERP/product#61).
347
+ let envelope;
343
348
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
344
- output = output.data;
349
+ const page = output;
350
+ envelope = page;
351
+ output = page.data;
345
352
  }
346
353
  // For table/csv on non-array responses, auto-extract the primary array
347
354
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -358,12 +365,24 @@ export default class Payments extends Command {
358
365
  output = this.sanitizeOutput(output);
359
366
  }
360
367
  const fieldList = flags.fields?.split(',').map(f => f.trim());
361
- if (flags.csv || flags.output === 'csv')
368
+ if (envelope)
369
+ this.warnShortPage(envelope, flags.limit);
370
+ if (flags.csv || flags.output === 'csv') {
362
371
  this.printCsv(output, fieldList);
363
- else if (flags.table || flags.output === 'table')
372
+ }
373
+ else if (flags.table || flags.output === 'table') {
364
374
  this.printTable(output, fieldList);
365
- else
375
+ if (envelope)
376
+ this.printPageSummary(envelope);
377
+ }
378
+ else if (envelope && flags.meta) {
379
+ // The whole envelope, for a caller that asked for it; the default stays
380
+ // a bare array so existing scripts are unaffected.
381
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
382
+ }
383
+ else {
366
384
  this.log(JSON.stringify(output, null, 2));
385
+ }
367
386
  }
368
387
  catch (error) {
369
388
  if (error instanceof ApiError) {
@@ -992,6 +1011,34 @@ export default class Payments extends Command {
992
1011
  return `\$${op}`;
993
1012
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
994
1013
  }
1014
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1015
+ printPageSummary(envelope) {
1016
+ const meta = envelope.meta;
1017
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1018
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1019
+ this.log(`
1020
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1021
+ }
1022
+ for (const total of envelope.totals ?? []) {
1023
+ const { currency, ...sums } = total;
1024
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1025
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1026
+ }
1027
+ }
1028
+ /**
1029
+ * The API caps how many records a page may hold. Asking for more and getting
1030
+ * fewer looks like "that is everything" unless it is said out loud.
1031
+ */
1032
+ warnShortPage(envelope, requestedLimit) {
1033
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1034
+ const perPage = envelope.meta?.itemsPerPage;
1035
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1036
+ return;
1037
+ const pages = envelope.meta?.totalPages;
1038
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1039
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1040
+ `);
1041
+ }
995
1042
  sanitizeOutput(data) {
996
1043
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
997
1044
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -27,6 +27,7 @@ export default class PayrollRuns 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 PayrollRuns 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;
@@ -79,6 +79,7 @@ export default class PayrollRuns extends Command {
79
79
  query: Flags.string({ description: 'Additional query params as JSON' }),
80
80
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
81
81
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
82
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
82
83
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
83
84
  };
84
85
  async run() {
@@ -371,9 +372,15 @@ export default class PayrollRuns extends Command {
371
372
  }
372
373
  catch { /* keep as string */ }
373
374
  }
374
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
375
+ // A paginated answer is { data, meta, links, totals }. The records are
376
+ // unwrapped for output, but what the API said about the page is kept:
377
+ // dropping it silently let a script read one page as the whole result
378
+ // (AderisERP/product#61).
379
+ let envelope;
375
380
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
376
- output = output.data;
381
+ const page = output;
382
+ envelope = page;
383
+ output = page.data;
377
384
  }
378
385
  // For table/csv on non-array responses, auto-extract the primary array
379
386
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -390,12 +397,24 @@ export default class PayrollRuns extends Command {
390
397
  output = this.sanitizeOutput(output);
391
398
  }
392
399
  const fieldList = flags.fields?.split(',').map(f => f.trim());
393
- if (flags.csv || flags.output === 'csv')
400
+ if (envelope)
401
+ this.warnShortPage(envelope, flags.limit);
402
+ if (flags.csv || flags.output === 'csv') {
394
403
  this.printCsv(output, fieldList);
395
- else if (flags.table || flags.output === 'table')
404
+ }
405
+ else if (flags.table || flags.output === 'table') {
396
406
  this.printTable(output, fieldList);
397
- else
407
+ if (envelope)
408
+ this.printPageSummary(envelope);
409
+ }
410
+ else if (envelope && flags.meta) {
411
+ // The whole envelope, for a caller that asked for it; the default stays
412
+ // a bare array so existing scripts are unaffected.
413
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
414
+ }
415
+ else {
398
416
  this.log(JSON.stringify(output, null, 2));
417
+ }
399
418
  }
400
419
  catch (error) {
401
420
  if (error instanceof ApiError) {
@@ -1024,6 +1043,34 @@ export default class PayrollRuns extends Command {
1024
1043
  return `\$${op}`;
1025
1044
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
1026
1045
  }
1046
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1047
+ printPageSummary(envelope) {
1048
+ const meta = envelope.meta;
1049
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1050
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1051
+ this.log(`
1052
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1053
+ }
1054
+ for (const total of envelope.totals ?? []) {
1055
+ const { currency, ...sums } = total;
1056
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1057
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1058
+ }
1059
+ }
1060
+ /**
1061
+ * The API caps how many records a page may hold. Asking for more and getting
1062
+ * fewer looks like "that is everything" unless it is said out loud.
1063
+ */
1064
+ warnShortPage(envelope, requestedLimit) {
1065
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1066
+ const perPage = envelope.meta?.itemsPerPage;
1067
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1068
+ return;
1069
+ const pages = envelope.meta?.totalPages;
1070
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1071
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1072
+ `);
1073
+ }
1027
1074
  sanitizeOutput(data) {
1028
1075
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1029
1076
  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 Permissions 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 Permissions 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;
@@ -37,6 +37,7 @@ export default class Permissions extends Command {
37
37
  query: Flags.string({ description: 'Additional query params as JSON' }),
38
38
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
39
39
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
40
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
40
41
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
41
42
  };
42
43
  async run() {
@@ -329,9 +330,15 @@ export default class Permissions extends Command {
329
330
  }
330
331
  catch { /* keep as string */ }
331
332
  }
332
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
333
+ // A paginated answer is { data, meta, links, totals }. The records are
334
+ // unwrapped for output, but what the API said about the page is kept:
335
+ // dropping it silently let a script read one page as the whole result
336
+ // (AderisERP/product#61).
337
+ let envelope;
333
338
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
334
- output = output.data;
339
+ const page = output;
340
+ envelope = page;
341
+ output = page.data;
335
342
  }
336
343
  // For table/csv on non-array responses, auto-extract the primary array
337
344
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -348,12 +355,24 @@ export default class Permissions extends Command {
348
355
  output = this.sanitizeOutput(output);
349
356
  }
350
357
  const fieldList = flags.fields?.split(',').map(f => f.trim());
351
- if (flags.csv || flags.output === 'csv')
358
+ if (envelope)
359
+ this.warnShortPage(envelope, flags.limit);
360
+ if (flags.csv || flags.output === 'csv') {
352
361
  this.printCsv(output, fieldList);
353
- else if (flags.table || flags.output === 'table')
362
+ }
363
+ else if (flags.table || flags.output === 'table') {
354
364
  this.printTable(output, fieldList);
355
- else
365
+ if (envelope)
366
+ this.printPageSummary(envelope);
367
+ }
368
+ else if (envelope && flags.meta) {
369
+ // The whole envelope, for a caller that asked for it; the default stays
370
+ // a bare array so existing scripts are unaffected.
371
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
372
+ }
373
+ else {
356
374
  this.log(JSON.stringify(output, null, 2));
375
+ }
357
376
  }
358
377
  catch (error) {
359
378
  if (error instanceof ApiError) {
@@ -982,6 +1001,34 @@ export default class Permissions extends Command {
982
1001
  return `\$${op}`;
983
1002
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
984
1003
  }
1004
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1005
+ printPageSummary(envelope) {
1006
+ const meta = envelope.meta;
1007
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1008
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1009
+ this.log(`
1010
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1011
+ }
1012
+ for (const total of envelope.totals ?? []) {
1013
+ const { currency, ...sums } = total;
1014
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1015
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1016
+ }
1017
+ }
1018
+ /**
1019
+ * The API caps how many records a page may hold. Asking for more and getting
1020
+ * fewer looks like "that is everything" unless it is said out loud.
1021
+ */
1022
+ warnShortPage(envelope, requestedLimit) {
1023
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1024
+ const perPage = envelope.meta?.itemsPerPage;
1025
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1026
+ return;
1027
+ const pages = envelope.meta?.totalPages;
1028
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1029
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1030
+ `);
1031
+ }
985
1032
  sanitizeOutput(data) {
986
1033
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
987
1034
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -24,6 +24,7 @@ export default class PriceLists extends Command {
24
24
  query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
25
25
  fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
26
26
  sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
27
+ meta: import("@oclif/core/interfaces").BooleanFlag<boolean>;
27
28
  'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
28
29
  };
29
30
  run(): Promise<void>;
@@ -32,6 +33,13 @@ export default class PriceLists extends Command {
32
33
  private runCamt053Import;
33
34
  private runNestedImport;
34
35
  private parseFilterValue;
36
+ /** The page line under a table: which page, how many, and any sums the API returned. */
37
+ private printPageSummary;
38
+ /**
39
+ * The API caps how many records a page may hold. Asking for more and getting
40
+ * fewer looks like "that is everything" unless it is said out loud.
41
+ */
42
+ private warnShortPage;
35
43
  private sanitizeOutput;
36
44
  private getColumns;
37
45
  private getValue;
@@ -51,6 +51,7 @@ export default class PriceLists extends Command {
51
51
  query: Flags.string({ description: 'Additional query params as JSON' }),
52
52
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
53
53
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
54
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
54
55
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
55
56
  };
56
57
  async run() {
@@ -343,9 +344,15 @@ export default class PriceLists extends Command {
343
344
  }
344
345
  catch { /* keep as string */ }
345
346
  }
346
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
347
+ // A paginated answer is { data, meta, links, totals }. The records are
348
+ // unwrapped for output, but what the API said about the page is kept:
349
+ // dropping it silently let a script read one page as the whole result
350
+ // (AderisERP/product#61).
351
+ let envelope;
347
352
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
348
- output = output.data;
353
+ const page = output;
354
+ envelope = page;
355
+ output = page.data;
349
356
  }
350
357
  // For table/csv on non-array responses, auto-extract the primary array
351
358
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -362,12 +369,24 @@ export default class PriceLists extends Command {
362
369
  output = this.sanitizeOutput(output);
363
370
  }
364
371
  const fieldList = flags.fields?.split(',').map(f => f.trim());
365
- if (flags.csv || flags.output === 'csv')
372
+ if (envelope)
373
+ this.warnShortPage(envelope, flags.limit);
374
+ if (flags.csv || flags.output === 'csv') {
366
375
  this.printCsv(output, fieldList);
367
- else if (flags.table || flags.output === 'table')
376
+ }
377
+ else if (flags.table || flags.output === 'table') {
368
378
  this.printTable(output, fieldList);
369
- else
379
+ if (envelope)
380
+ this.printPageSummary(envelope);
381
+ }
382
+ else if (envelope && flags.meta) {
383
+ // The whole envelope, for a caller that asked for it; the default stays
384
+ // a bare array so existing scripts are unaffected.
385
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
386
+ }
387
+ else {
370
388
  this.log(JSON.stringify(output, null, 2));
389
+ }
371
390
  }
372
391
  catch (error) {
373
392
  if (error instanceof ApiError) {
@@ -996,6 +1015,34 @@ export default class PriceLists extends Command {
996
1015
  return `\$${op}`;
997
1016
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
998
1017
  }
1018
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1019
+ printPageSummary(envelope) {
1020
+ const meta = envelope.meta;
1021
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1022
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1023
+ this.log(`
1024
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1025
+ }
1026
+ for (const total of envelope.totals ?? []) {
1027
+ const { currency, ...sums } = total;
1028
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1029
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1030
+ }
1031
+ }
1032
+ /**
1033
+ * The API caps how many records a page may hold. Asking for more and getting
1034
+ * fewer looks like "that is everything" unless it is said out loud.
1035
+ */
1036
+ warnShortPage(envelope, requestedLimit) {
1037
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1038
+ const perPage = envelope.meta?.itemsPerPage;
1039
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1040
+ return;
1041
+ const pages = envelope.meta?.totalPages;
1042
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1043
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1044
+ `);
1045
+ }
999
1046
  sanitizeOutput(data) {
1000
1047
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1001
1048
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -23,6 +23,7 @@ export default class ProcessorMappings 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 ProcessorMappings 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 ProcessorMappings 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 ProcessorMappings 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 ProcessorMappings 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 ProcessorMappings 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;