@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
@@ -27,6 +27,7 @@ export default class Ledgers 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 Ledgers 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;
@@ -91,6 +91,7 @@ export default class Ledgers extends Command {
91
91
  query: Flags.string({ description: 'Additional query params as JSON' }),
92
92
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
93
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' }),
94
95
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
95
96
  };
96
97
  async run() {
@@ -383,9 +384,15 @@ export default class Ledgers extends Command {
383
384
  }
384
385
  catch { /* keep as string */ }
385
386
  }
386
- // 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;
387
392
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
388
- output = output.data;
393
+ const page = output;
394
+ envelope = page;
395
+ output = page.data;
389
396
  }
390
397
  // For table/csv on non-array responses, auto-extract the primary array
391
398
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -402,12 +409,24 @@ export default class Ledgers extends Command {
402
409
  output = this.sanitizeOutput(output);
403
410
  }
404
411
  const fieldList = flags.fields?.split(',').map(f => f.trim());
405
- if (flags.csv || flags.output === 'csv')
412
+ if (envelope)
413
+ this.warnShortPage(envelope, flags.limit);
414
+ if (flags.csv || flags.output === 'csv') {
406
415
  this.printCsv(output, fieldList);
407
- else if (flags.table || flags.output === 'table')
416
+ }
417
+ else if (flags.table || flags.output === 'table') {
408
418
  this.printTable(output, fieldList);
409
- 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 {
410
428
  this.log(JSON.stringify(output, null, 2));
429
+ }
411
430
  }
412
431
  catch (error) {
413
432
  if (error instanceof ApiError) {
@@ -1036,6 +1055,34 @@ export default class Ledgers extends Command {
1036
1055
  return `\$${op}`;
1037
1056
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
1038
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
+ }
1039
1086
  sanitizeOutput(data) {
1040
1087
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1041
1088
  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 MyAccount 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 MyAccount 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;
@@ -51,6 +51,7 @@ export default class MyAccount 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 MyAccount 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 MyAccount 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 MyAccount 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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 OfficeLocations 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;
@@ -24,6 +24,7 @@ export default class PaymentMethods 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 PaymentMethods 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 PaymentMethods 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 PaymentMethods 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 PaymentMethods 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 PaymentMethods 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;
@@ -25,6 +25,7 @@ export default class PaymentRuns extends Command {
25
25
  query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
26
26
  fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
27
27
  sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
28
+ meta: import("@oclif/core/interfaces").BooleanFlag<boolean>;
28
29
  'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
29
30
  };
30
31
  run(): Promise<void>;
@@ -33,6 +34,13 @@ export default class PaymentRuns extends Command {
33
34
  private runCamt053Import;
34
35
  private runNestedImport;
35
36
  private parseFilterValue;
37
+ /** The page line under a table: which page, how many, and any sums the API returned. */
38
+ private printPageSummary;
39
+ /**
40
+ * The API caps how many records a page may hold. Asking for more and getting
41
+ * fewer looks like "that is everything" unless it is said out loud.
42
+ */
43
+ private warnShortPage;
36
44
  private sanitizeOutput;
37
45
  private getColumns;
38
46
  private getValue;
@@ -6,7 +6,7 @@ const HAS_IMPORT = true;
6
6
  const HAS_EXPORT = true;
7
7
  const HAS_CAMT053 = false;
8
8
  const NESTED_IMPORTS = [];
9
- const SUBCOMMANDS = [...["open-submissions", "eligible-payments", "list", "create", "preview", "schedule", "status", "get", "update", "delete", "create-payments", "delete-payments", "send", "painfile", "sync-status", "sync-status-xml", "sync-all"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
9
+ const SUBCOMMANDS = [...["open-submissions", "eligible-payments", "list", "create", "preview", "schedule", "status", "get", "update", "delete", "create-payments", "delete-payments", "send", "painfile", "beneficiary-check", "sync-status", "sync-status-xml", "sync-all"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
11
  'open-submissions': { method: 'GET', path: '/v1/payment-runs/bank-accounts/{bankAccountId}/open-submissions', summary: 'Get open submission IDs for a bank account', pathParams: ['bankAccountId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
12
12
  'eligible-payments': { method: 'GET', path: '/v1/payment-runs/eligible-payments/{bankAccountId}', summary: 'Show what a payment run from this bank account would pay', pathParams: ['bankAccountId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
@@ -22,6 +22,7 @@ const ENDPOINTS = {
22
22
  'delete-payments': { method: 'DELETE', path: '/v1/payment-runs/{id}/payments/{paymentId}', summary: 'Remove an item from a DRAFT payment run', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
23
23
  'send': { method: 'POST', path: '/v1/payment-runs/{id}/send', summary: 'Execute a DRAFT payment run (with dryRun support)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
24
24
  'painfile': { method: 'GET', path: '/v1/payment-runs/{id}/painfile', summary: 'Download or preview PAIN XML for a payment run', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
25
+ 'beneficiary-check': { method: 'GET', path: '/v1/payment-runs/{id}/beneficiary-check', summary: 'Check a payment run\'s beneficiary accounts without sending it', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
25
26
  'sync-status': { method: 'POST', path: '/v1/payment-runs/update/status/{submissionId}', summary: 'Sync payment status for a specific submission', pathParams: ['submissionId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
26
27
  'sync-status-xml': { method: 'POST', path: '/v1/payment-runs/update/status/{submissionId}/xml', summary: 'Sync payment status from pain.002 XML for a specific submission', pathParams: ['submissionId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
27
28
  'sync-all': { method: 'POST', path: '/v1/payment-runs/status', summary: 'Sync payment status for all pending submissions', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
@@ -59,6 +60,7 @@ export default class PaymentRuns extends Command {
59
60
  query: Flags.string({ description: 'Additional query params as JSON' }),
60
61
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
61
62
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
63
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
62
64
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
63
65
  };
64
66
  async run() {
@@ -351,9 +353,15 @@ export default class PaymentRuns extends Command {
351
353
  }
352
354
  catch { /* keep as string */ }
353
355
  }
354
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
356
+ // A paginated answer is { data, meta, links, totals }. The records are
357
+ // unwrapped for output, but what the API said about the page is kept:
358
+ // dropping it silently let a script read one page as the whole result
359
+ // (AderisERP/product#61).
360
+ let envelope;
355
361
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
356
- output = output.data;
362
+ const page = output;
363
+ envelope = page;
364
+ output = page.data;
357
365
  }
358
366
  // For table/csv on non-array responses, auto-extract the primary array
359
367
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -370,12 +378,24 @@ export default class PaymentRuns extends Command {
370
378
  output = this.sanitizeOutput(output);
371
379
  }
372
380
  const fieldList = flags.fields?.split(',').map(f => f.trim());
373
- if (flags.csv || flags.output === 'csv')
381
+ if (envelope)
382
+ this.warnShortPage(envelope, flags.limit);
383
+ if (flags.csv || flags.output === 'csv') {
374
384
  this.printCsv(output, fieldList);
375
- else if (flags.table || flags.output === 'table')
385
+ }
386
+ else if (flags.table || flags.output === 'table') {
376
387
  this.printTable(output, fieldList);
377
- else
388
+ if (envelope)
389
+ this.printPageSummary(envelope);
390
+ }
391
+ else if (envelope && flags.meta) {
392
+ // The whole envelope, for a caller that asked for it; the default stays
393
+ // a bare array so existing scripts are unaffected.
394
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
395
+ }
396
+ else {
378
397
  this.log(JSON.stringify(output, null, 2));
398
+ }
379
399
  }
380
400
  catch (error) {
381
401
  if (error instanceof ApiError) {
@@ -1004,6 +1024,34 @@ export default class PaymentRuns extends Command {
1004
1024
  return `\$${op}`;
1005
1025
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
1006
1026
  }
1027
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1028
+ printPageSummary(envelope) {
1029
+ const meta = envelope.meta;
1030
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1031
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1032
+ this.log(`
1033
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1034
+ }
1035
+ for (const total of envelope.totals ?? []) {
1036
+ const { currency, ...sums } = total;
1037
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1038
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1039
+ }
1040
+ }
1041
+ /**
1042
+ * The API caps how many records a page may hold. Asking for more and getting
1043
+ * fewer looks like "that is everything" unless it is said out loud.
1044
+ */
1045
+ warnShortPage(envelope, requestedLimit) {
1046
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1047
+ const perPage = envelope.meta?.itemsPerPage;
1048
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1049
+ return;
1050
+ const pages = envelope.meta?.totalPages;
1051
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1052
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1053
+ `);
1054
+ }
1007
1055
  sanitizeOutput(data) {
1008
1056
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1009
1057
  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 PaymentTerms 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 PaymentTerms 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;