@aderis-erp/cli 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/commands/access-groups.d.ts +8 -0
  2. package/dist/commands/access-groups.js +56 -8
  3. package/dist/commands/account-transaction-allocations.d.ts +8 -0
  4. package/dist/commands/account-transaction-allocations.js +52 -5
  5. package/dist/commands/account-transactions.d.ts +8 -0
  6. package/dist/commands/account-transactions.js +55 -7
  7. package/dist/commands/accounts.d.ts +8 -0
  8. package/dist/commands/accounts.js +52 -5
  9. package/dist/commands/agent-platform.d.ts +8 -0
  10. package/dist/commands/agent-platform.js +52 -5
  11. package/dist/commands/agent-runs.d.ts +8 -0
  12. package/dist/commands/agent-runs.js +52 -5
  13. package/dist/commands/analytics.d.ts +8 -0
  14. package/dist/commands/analytics.js +52 -5
  15. package/dist/commands/api-keys.d.ts +8 -0
  16. package/dist/commands/api-keys.js +52 -5
  17. package/dist/commands/async-jobs.d.ts +8 -0
  18. package/dist/commands/async-jobs.js +52 -5
  19. package/dist/commands/audit-log.d.ts +8 -0
  20. package/dist/commands/audit-log.js +52 -5
  21. package/dist/commands/bank-accounts.d.ts +8 -0
  22. package/dist/commands/bank-accounts.js +52 -5
  23. package/dist/commands/bank-integrations.d.ts +8 -0
  24. package/dist/commands/bank-integrations.js +52 -5
  25. package/dist/commands/business-partners.d.ts +9 -0
  26. package/dist/commands/business-partners.js +66 -9
  27. package/dist/commands/chat.d.ts +8 -0
  28. package/dist/commands/chat.js +52 -5
  29. package/dist/commands/credit-card-accounts.d.ts +8 -0
  30. package/dist/commands/credit-card-accounts.js +52 -5
  31. package/dist/commands/delivery-notes.d.ts +8 -0
  32. package/dist/commands/delivery-notes.js +52 -5
  33. package/dist/commands/dunning-configs.d.ts +8 -0
  34. package/dist/commands/dunning-configs.js +52 -5
  35. package/dist/commands/dunning-reminders.d.ts +8 -0
  36. package/dist/commands/dunning-reminders.js +52 -5
  37. package/dist/commands/dunning-runs.d.ts +8 -0
  38. package/dist/commands/dunning-runs.js +52 -5
  39. package/dist/commands/email-transactions.d.ts +8 -0
  40. package/dist/commands/email-transactions.js +52 -5
  41. package/dist/commands/employees.d.ts +8 -0
  42. package/dist/commands/employees.js +52 -5
  43. package/dist/commands/entity-configurations.d.ts +8 -0
  44. package/dist/commands/entity-configurations.js +52 -5
  45. package/dist/commands/event-processor-mappings.d.ts +8 -0
  46. package/dist/commands/event-processor-mappings.js +52 -5
  47. package/dist/commands/files.d.ts +8 -0
  48. package/dist/commands/files.js +53 -6
  49. package/dist/commands/health.d.ts +8 -0
  50. package/dist/commands/health.js +52 -5
  51. package/dist/commands/imports.d.ts +8 -0
  52. package/dist/commands/imports.js +53 -8
  53. package/dist/commands/inbox.d.ts +8 -0
  54. package/dist/commands/inbox.js +53 -7
  55. package/dist/commands/integration-identities.d.ts +8 -0
  56. package/dist/commands/integration-identities.js +52 -5
  57. package/dist/commands/integrations.d.ts +8 -0
  58. package/dist/commands/integrations.js +52 -5
  59. package/dist/commands/item-categories.d.ts +8 -0
  60. package/dist/commands/item-categories.js +52 -5
  61. package/dist/commands/items.d.ts +8 -0
  62. package/dist/commands/items.js +52 -5
  63. package/dist/commands/journal-lines.d.ts +8 -0
  64. package/dist/commands/journal-lines.js +52 -5
  65. package/dist/commands/journals.d.ts +8 -0
  66. package/dist/commands/journals.js +52 -5
  67. package/dist/commands/ledgers.d.ts +8 -0
  68. package/dist/commands/ledgers.js +57 -9
  69. package/dist/commands/my-account.d.ts +8 -0
  70. package/dist/commands/my-account.js +52 -5
  71. package/dist/commands/office-locations.d.ts +8 -0
  72. package/dist/commands/office-locations.js +52 -5
  73. package/dist/commands/payment-methods.d.ts +8 -0
  74. package/dist/commands/payment-methods.js +52 -5
  75. package/dist/commands/payment-runs.d.ts +8 -0
  76. package/dist/commands/payment-runs.js +54 -6
  77. package/dist/commands/payment-terms.d.ts +8 -0
  78. package/dist/commands/payment-terms.js +52 -5
  79. package/dist/commands/payments.d.ts +8 -0
  80. package/dist/commands/payments.js +52 -5
  81. package/dist/commands/payroll-runs.d.ts +8 -0
  82. package/dist/commands/payroll-runs.js +52 -5
  83. package/dist/commands/permissions.d.ts +8 -0
  84. package/dist/commands/permissions.js +52 -5
  85. package/dist/commands/price-lists.d.ts +8 -0
  86. package/dist/commands/price-lists.js +52 -5
  87. package/dist/commands/processor-mappings.d.ts +8 -0
  88. package/dist/commands/processor-mappings.js +52 -5
  89. package/dist/commands/processors.d.ts +8 -0
  90. package/dist/commands/processors.js +52 -5
  91. package/dist/commands/purchase-order-types.d.ts +8 -0
  92. package/dist/commands/purchase-order-types.js +52 -5
  93. package/dist/commands/purchase-orders.d.ts +8 -0
  94. package/dist/commands/purchase-orders.js +52 -5
  95. package/dist/commands/reports.d.ts +8 -0
  96. package/dist/commands/reports.js +52 -5
  97. package/dist/commands/roles.d.ts +8 -0
  98. package/dist/commands/roles.js +52 -5
  99. package/dist/commands/salary-types.d.ts +8 -0
  100. package/dist/commands/salary-types.js +52 -5
  101. package/dist/commands/sales-invoices.d.ts +10 -0
  102. package/dist/commands/sales-invoices.js +62 -6
  103. package/dist/commands/sales-orders.d.ts +8 -0
  104. package/dist/commands/sales-orders.js +52 -5
  105. package/dist/commands/saved-views.d.ts +8 -0
  106. package/dist/commands/saved-views.js +52 -5
  107. package/dist/commands/scheduled-processor-mappings.d.ts +8 -0
  108. package/dist/commands/scheduled-processor-mappings.js +52 -5
  109. package/dist/commands/schedules.d.ts +8 -0
  110. package/dist/commands/schedules.js +52 -5
  111. package/dist/commands/support/end.d.ts +9 -0
  112. package/dist/commands/support/end.js +29 -0
  113. package/dist/commands/support/start.d.ts +14 -0
  114. package/dist/commands/support/start.js +50 -0
  115. package/dist/commands/support/status.d.ts +10 -0
  116. package/dist/commands/support/status.js +45 -0
  117. package/dist/commands/support/tenants.d.ts +10 -0
  118. package/dist/commands/support/tenants.js +39 -0
  119. package/dist/commands/tags.d.ts +8 -0
  120. package/dist/commands/tags.js +56 -8
  121. package/dist/commands/tax-codes.d.ts +8 -0
  122. package/dist/commands/tax-codes.js +52 -5
  123. package/dist/commands/tax-matrix.d.ts +8 -0
  124. package/dist/commands/tax-matrix.js +52 -5
  125. package/dist/commands/templates.d.ts +8 -0
  126. package/dist/commands/templates.js +52 -5
  127. package/dist/commands/tenants.d.ts +8 -0
  128. package/dist/commands/tenants.js +52 -5
  129. package/dist/commands/tools.d.ts +8 -0
  130. package/dist/commands/tools.js +52 -5
  131. package/dist/commands/users.d.ts +8 -0
  132. package/dist/commands/users.js +52 -5
  133. package/dist/commands/vat-declarations.d.ts +8 -0
  134. package/dist/commands/vat-declarations.js +52 -5
  135. package/dist/commands/vendor-invoices.d.ts +9 -0
  136. package/dist/commands/vendor-invoices.js +60 -8
  137. package/dist/commands/webhooks.d.ts +8 -0
  138. package/dist/commands/webhooks.js +52 -5
  139. package/dist/commands/workflows.d.ts +8 -0
  140. package/dist/commands/workflows.js +52 -5
  141. package/dist/lib/auth/constants.d.ts +12 -0
  142. package/dist/lib/auth/constants.js +12 -0
  143. package/dist/lib/auth/index.d.ts +11 -1
  144. package/dist/lib/auth/index.js +90 -2
  145. package/dist/lib/auth/keycloak-provider.d.ts +22 -0
  146. package/dist/lib/auth/keycloak-provider.js +108 -3
  147. package/dist/lib/config.d.ts +18 -0
  148. package/dist/lib/config.js +10 -0
  149. package/oclif.manifest.json +640 -216
  150. package/package.json +4 -1
  151. package/dist/commands/tag-configurations.d.ts +0 -39
  152. package/dist/commands/tag-configurations.js +0 -1155
@@ -6,13 +6,12 @@ const HAS_IMPORT = false;
6
6
  const HAS_EXPORT = false;
7
7
  const HAS_CAMT053 = false;
8
8
  const NESTED_IMPORTS = [];
9
- const SUBCOMMANDS = [...["create-upload", "email-webhook", "document-types", "uploads-files-content", "import", "list-processing-logs", "processing-logs"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
9
+ const SUBCOMMANDS = [...["create-upload", "email-webhook", "document-types", "uploads-files-content", "list-processing-logs", "processing-logs"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
11
  'create-upload': { method: 'POST', path: '/v1/inbox/upload', summary: 'Upload document(s) to inbox for processing', pathParams: [], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
12
12
  'email-webhook': { method: 'POST', path: '/v1/inbox/email-webhook/{secret}', summary: 'Email webhook for document ingestion (SendGrid inbound-parse)', pathParams: ['secret'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
13
13
  'document-types': { method: 'GET', path: '/v1/inbox/document-types', summary: 'Get available document types and per-parent allowed types', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
14
14
  'uploads-files-content': { method: 'GET', path: '/v1/inbox/uploads/{processingId}/files/{fileId}/content', summary: 'Stream the original bytes of an uploaded inbox file', pathParams: ['fileId', 'processingId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
- 'import': { method: 'POST', path: '/v1/inbox/import', summary: 'Import a provider-identified document into the inbox, refusing duplicates', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
16
15
  'list-processing-logs': { method: 'GET', path: '/v1/inbox/processing-logs', summary: 'List recent inbox processing logs (newest first)', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
17
16
  'processing-logs': { method: 'GET', path: '/v1/inbox/processing-logs/{processingId}', summary: 'Get a processing run by its processing identifier', pathParams: ['processingId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
18
17
  };
@@ -41,6 +40,7 @@ export default class Inbox extends Command {
41
40
  query: Flags.string({ description: 'Additional query params as JSON' }),
42
41
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
43
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' }),
44
44
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
45
45
  };
46
46
  async run() {
@@ -333,9 +333,15 @@ export default class Inbox extends Command {
333
333
  }
334
334
  catch { /* keep as string */ }
335
335
  }
336
- // 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;
337
341
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
338
- output = output.data;
342
+ const page = output;
343
+ envelope = page;
344
+ output = page.data;
339
345
  }
340
346
  // For table/csv on non-array responses, auto-extract the primary array
341
347
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -352,12 +358,24 @@ export default class Inbox extends Command {
352
358
  output = this.sanitizeOutput(output);
353
359
  }
354
360
  const fieldList = flags.fields?.split(',').map(f => f.trim());
355
- if (flags.csv || flags.output === 'csv')
361
+ if (envelope)
362
+ this.warnShortPage(envelope, flags.limit);
363
+ if (flags.csv || flags.output === 'csv') {
356
364
  this.printCsv(output, fieldList);
357
- else if (flags.table || flags.output === 'table')
365
+ }
366
+ else if (flags.table || flags.output === 'table') {
358
367
  this.printTable(output, fieldList);
359
- 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 {
360
377
  this.log(JSON.stringify(output, null, 2));
378
+ }
361
379
  }
362
380
  catch (error) {
363
381
  if (error instanceof ApiError) {
@@ -986,6 +1004,34 @@ export default class Inbox extends Command {
986
1004
  return `\$${op}`;
987
1005
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
988
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
+ }
989
1035
  sanitizeOutput(data) {
990
1036
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
991
1037
  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 IntegrationIdentities 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 IntegrationIdentities 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;
@@ -42,6 +42,7 @@ export default class IntegrationIdentities extends Command {
42
42
  query: Flags.string({ description: 'Additional query params as JSON' }),
43
43
  fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
44
44
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
45
+ meta: Flags.boolean({ description: 'Keep the API envelope in JSON output: data plus page, totals and sums' }),
45
46
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
46
47
  };
47
48
  async run() {
@@ -334,9 +335,15 @@ export default class IntegrationIdentities extends Command {
334
335
  }
335
336
  catch { /* keep as string */ }
336
337
  }
337
- // Extract data array from paginated wrapper (e.g., { data: [...], total: 3 })
338
+ // A paginated answer is { data, meta, links, totals }. The records are
339
+ // unwrapped for output, but what the API said about the page is kept:
340
+ // dropping it silently let a script read one page as the whole result
341
+ // (AderisERP/product#61).
342
+ let envelope;
338
343
  if (output && typeof output === 'object' && !Array.isArray(output) && 'data' in output && Array.isArray(output.data)) {
339
- output = output.data;
344
+ const page = output;
345
+ envelope = page;
346
+ output = page.data;
340
347
  }
341
348
  // For table/csv on non-array responses, auto-extract the primary array
342
349
  // e.g., { payments: [...], total: 3 } → extract payments array
@@ -353,12 +360,24 @@ export default class IntegrationIdentities extends Command {
353
360
  output = this.sanitizeOutput(output);
354
361
  }
355
362
  const fieldList = flags.fields?.split(',').map(f => f.trim());
356
- if (flags.csv || flags.output === 'csv')
363
+ if (envelope)
364
+ this.warnShortPage(envelope, flags.limit);
365
+ if (flags.csv || flags.output === 'csv') {
357
366
  this.printCsv(output, fieldList);
358
- else if (flags.table || flags.output === 'table')
367
+ }
368
+ else if (flags.table || flags.output === 'table') {
359
369
  this.printTable(output, fieldList);
360
- else
370
+ if (envelope)
371
+ this.printPageSummary(envelope);
372
+ }
373
+ else if (envelope && flags.meta) {
374
+ // The whole envelope, for a caller that asked for it; the default stays
375
+ // a bare array so existing scripts are unaffected.
376
+ this.log(JSON.stringify({ ...envelope, data: output }, null, 2));
377
+ }
378
+ else {
361
379
  this.log(JSON.stringify(output, null, 2));
380
+ }
362
381
  }
363
382
  catch (error) {
364
383
  if (error instanceof ApiError) {
@@ -987,6 +1006,34 @@ export default class IntegrationIdentities extends Command {
987
1006
  return `\$${op}`;
988
1007
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
989
1008
  }
1009
+ /** The page line under a table: which page, how many, and any sums the API returned. */
1010
+ printPageSummary(envelope) {
1011
+ const meta = envelope.meta;
1012
+ if (meta && typeof meta.currentPage === 'number' && typeof meta.totalPages === 'number') {
1013
+ const shown = Array.isArray(envelope.data) ? envelope.data.length : 0;
1014
+ this.log(`
1015
+ Page ${meta.currentPage}/${meta.totalPages} — ${shown} of ${meta.totalItems ?? '?'} records`);
1016
+ }
1017
+ for (const total of envelope.totals ?? []) {
1018
+ const { currency, ...sums } = total;
1019
+ const parts = Object.entries(sums).map(([column, value]) => `${column} ${value}`).join(', ');
1020
+ this.log(`Total ${currency ?? ''}: ${parts}`.trim());
1021
+ }
1022
+ }
1023
+ /**
1024
+ * The API caps how many records a page may hold. Asking for more and getting
1025
+ * fewer looks like "that is everything" unless it is said out loud.
1026
+ */
1027
+ warnShortPage(envelope, requestedLimit) {
1028
+ const asked = requestedLimit === undefined ? undefined : Number(requestedLimit);
1029
+ const perPage = envelope.meta?.itemsPerPage;
1030
+ if (asked === undefined || Number.isNaN(asked) || typeof perPage !== 'number' || perPage >= asked)
1031
+ return;
1032
+ const pages = envelope.meta?.totalPages;
1033
+ const more = typeof pages === 'number' && pages > 1 ? ` Use --page, or the export action for every page.` : '';
1034
+ process.stderr.write(`! Asked for ${asked} records per page; the API returned at most ${perPage}.${more}
1035
+ `);
1036
+ }
990
1037
  sanitizeOutput(data) {
991
1038
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
992
1039
  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 Integrations 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 Integrations 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;
@@ -40,6 +40,7 @@ export default class Integrations 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 Integrations 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 Integrations 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 Integrations 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;
@@ -23,6 +23,7 @@ export default class ItemCategories 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 ItemCategories 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 ItemCategories 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 ItemCategories 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 ItemCategories 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 ItemCategories 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 Items 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 Items 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 Items 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 Items 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 Items 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 Items 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 JournalLines 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 JournalLines 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;