@aderis-erp/cli 1.0.1 → 1.0.3

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 (67) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/access-groups.js +36 -22
  3. package/dist/commands/account-transactions.js +30 -18
  4. package/dist/commands/accounts.js +30 -18
  5. package/dist/commands/agent-runs.js +33 -21
  6. package/dist/commands/{processing-logs.d.ts → async-endpoints-internal.d.ts} +2 -2
  7. package/dist/commands/{processing-logs.js → async-endpoints-internal.js} +39 -28
  8. package/dist/commands/audit-log.js +30 -18
  9. package/dist/commands/auth/tenant.d.ts +1 -0
  10. package/dist/commands/auth/tenant.js +23 -7
  11. package/dist/commands/bank-accounts.js +30 -18
  12. package/dist/commands/bank-integrations.d.ts +2 -0
  13. package/dist/commands/bank-integrations.js +41 -19
  14. package/dist/commands/business-partners---contacts.js +30 -18
  15. package/dist/commands/business-partners.js +30 -18
  16. package/dist/commands/credit-card-accounts.js +30 -18
  17. package/dist/commands/documents.js +36 -22
  18. package/dist/commands/dunning-configs.js +30 -18
  19. package/dist/commands/dunning-reminders.js +30 -18
  20. package/dist/commands/dunning-runs.js +30 -18
  21. package/dist/commands/employees.js +30 -18
  22. package/dist/commands/event-processor-mappings.js +30 -18
  23. package/dist/commands/files-internal.js +30 -18
  24. package/dist/commands/health.js +30 -18
  25. package/dist/commands/inbox.js +34 -20
  26. package/dist/commands/integrations.js +30 -18
  27. package/dist/commands/item-categories.js +30 -18
  28. package/dist/commands/items.js +30 -18
  29. package/dist/commands/journal-lines.js +30 -18
  30. package/dist/commands/journals.js +30 -18
  31. package/dist/commands/ledgers.js +30 -18
  32. package/dist/commands/office-locations.js +30 -18
  33. package/dist/commands/payment-method-ledger-mappings.js +30 -18
  34. package/dist/commands/payment-methods.js +30 -18
  35. package/dist/commands/payment-runs.js +30 -18
  36. package/dist/commands/payment-terms.js +30 -18
  37. package/dist/commands/price-lists.js +30 -18
  38. package/dist/commands/processor-mappings.js +30 -18
  39. package/dist/commands/processors.js +31 -19
  40. package/dist/commands/reconciliation.js +30 -18
  41. package/dist/commands/reports.js +30 -18
  42. package/dist/commands/salary-types.js +30 -18
  43. package/dist/commands/sales-invoices.js +32 -19
  44. package/dist/commands/sales-orders.js +33 -19
  45. package/dist/commands/switch.d.ts +1 -0
  46. package/dist/commands/switch.js +58 -3
  47. package/dist/commands/tags.js +30 -18
  48. package/dist/commands/tax-codes.js +30 -18
  49. package/dist/commands/tax-matrix.js +30 -18
  50. package/dist/commands/tenants.js +30 -18
  51. package/dist/commands/text-templates.js +30 -18
  52. package/dist/commands/tools.js +30 -18
  53. package/dist/commands/vendor-invoices.js +33 -19
  54. package/dist/commands/webhooks.js +30 -18
  55. package/dist/commands/workflows.js +30 -18
  56. package/dist/index 2.js +1 -0
  57. package/dist/index.d 2.ts +1 -0
  58. package/dist/lib/auth.d.ts +16 -1
  59. package/dist/lib/auth.js +167 -50
  60. package/dist/lib/config.d.ts +7 -0
  61. package/dist/lib/config.js +6 -0
  62. package/oclif.manifest.json +65 -385
  63. package/package.json +2 -2
  64. package/dist/commands/account-statement-runs.d.ts +0 -40
  65. package/dist/commands/account-statement-runs.js +0 -1004
  66. package/dist/commands/bank-statements.d.ts +0 -40
  67. package/dist/commands/bank-statements.js +0 -998
package/README.md CHANGED
@@ -14,7 +14,7 @@ Command-line interface for Aderis Finance ERP. Manage your Aderis account, execu
14
14
  ## Installation
15
15
 
16
16
  ```bash
17
- npm install -g aderis-cli
17
+ npm install -g @aderis-erp/cli
18
18
  aderis --help
19
19
  ```
20
20
 
@@ -3,24 +3,26 @@ import { apiRequest, ApiError } from '../lib/api-client.js';
3
3
  import { getCurrentContext, getContext, getBaseUrls } from '../lib/config.js';
4
4
  // Add 'import' action if both create and update exist
5
5
  const HAS_IMPORT = true;
6
- const HAS_EXPORT = false;
6
+ const HAS_EXPORT = true;
7
7
  const HAS_CAMT053 = false;
8
8
  const NESTED_IMPORTS = [];
9
- const SUBCOMMANDS = [...["get", "create", "get-all", "update", "delete"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
9
+ const SUBCOMMANDS = [...["list", "create", "get", "update", "delete"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
- 'get': { method: 'GET', path: '/v1/access-groups', summary: 'List access groups (paginated)', pathParams: [], hasBody: false, isMultipart: false },
11
+ 'list': { method: 'GET', path: '/v1/access-groups', summary: 'List access groups (paginated)', pathParams: [], hasBody: false, isMultipart: false },
12
12
  'create': { method: 'POST', path: '/v1/access-groups', summary: 'Create a new access group', pathParams: [], hasBody: true, isMultipart: false },
13
- 'get-all': { method: 'GET', path: '/v1/access-groups/{id}', summary: 'Get a single access group by ID', pathParams: ['id'], hasBody: false, isMultipart: false },
13
+ 'get': { method: 'GET', path: '/v1/access-groups/{id}', summary: 'Get a single access group by ID', pathParams: ['id'], hasBody: false, isMultipart: false },
14
14
  'update': { method: 'PATCH', path: '/v1/access-groups/{id}', summary: 'Update an existing access group', pathParams: ['id'], hasBody: true, isMultipart: false },
15
15
  'delete': { method: 'DELETE', path: '/v1/access-groups/{id}', summary: 'Delete an access group', pathParams: ['id'], hasBody: false, isMultipart: false },
16
16
  };
17
17
  export default class AccessGroups extends Command {
18
18
  static description = 'Manage access groups';
19
19
  static examples = [
20
+ '<%= config.bin %> access-groups list',
20
21
  '<%= config.bin %> access-groups get <id>',
21
22
  '<%= config.bin %> access-groups create --data \'{...}\'',
22
23
  '<%= config.bin %> access-groups update <id> --set field=value',
23
24
  '<%= config.bin %> access-groups import --file data.json',
25
+ '<%= config.bin %> access-groups export --csv > access-groups.csv',
24
26
  ];
25
27
  static args = {
26
28
  action: Args.string({ description: 'Action: ' + SUBCOMMANDS.join(', '), required: true }),
@@ -817,32 +819,44 @@ export default class AccessGroups extends Command {
817
819
  filterFields(data, fields) {
818
820
  const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
819
821
  const pick = (obj, keys) => {
820
- const result = {};
822
+ // Group keys by their first segment so sibling sub-paths
823
+ // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
824
+ // and don't clobber each other on merge.
825
+ const directKeys = new Set();
826
+ const nestedKeys = new Map();
821
827
  for (const key of keys) {
822
828
  if (key.split('.').some(isUnsafeKey))
823
829
  continue;
824
830
  if (key.includes('.')) {
825
- // Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
826
831
  const [first, ...rest] = key.split('.');
827
- const val = obj[first];
828
- if (val && typeof val === 'object') {
829
- const restKey = rest.join('.');
830
- if (Array.isArray(val)) {
831
- // Map over array elements (e.g., allocations[].salesInvoiceId)
832
- const existing = Array.isArray(result[first]) ? result[first] : [];
833
- result[first] = val.map((item, i) => ({
834
- ...(existing[i] || {}),
835
- ...pick(item, [restKey]),
836
- }));
837
- }
838
- else {
839
- const nested = pick(val, [restKey]);
840
- result[first] = { ...(result[first] || {}), ...nested };
841
- }
832
+ if (isUnsafeKey(first))
833
+ continue;
834
+ const list = nestedKeys.get(first);
835
+ if (list) {
836
+ list.push(rest.join('.'));
842
837
  }
838
+ else {
839
+ nestedKeys.set(first, [rest.join('.')]);
840
+ }
841
+ }
842
+ else {
843
+ directKeys.add(key);
843
844
  }
844
- else if (key in obj) {
845
+ }
846
+ const result = {};
847
+ for (const key of directKeys) {
848
+ if (key in obj)
845
849
  result[key] = obj[key];
850
+ }
851
+ for (const [first, restKeys] of nestedKeys) {
852
+ const val = obj[first];
853
+ if (!val || typeof val !== 'object')
854
+ continue;
855
+ if (Array.isArray(val)) {
856
+ result[first] = val.map(item => pick(item, restKeys));
857
+ }
858
+ else {
859
+ result[first] = pick(val, restKeys);
846
860
  }
847
861
  }
848
862
  return result;
@@ -833,32 +833,44 @@ export default class AccountTransactions extends Command {
833
833
  filterFields(data, fields) {
834
834
  const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
835
835
  const pick = (obj, keys) => {
836
- const result = {};
836
+ // Group keys by their first segment so sibling sub-paths
837
+ // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
838
+ // and don't clobber each other on merge.
839
+ const directKeys = new Set();
840
+ const nestedKeys = new Map();
837
841
  for (const key of keys) {
838
842
  if (key.split('.').some(isUnsafeKey))
839
843
  continue;
840
844
  if (key.includes('.')) {
841
- // Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
842
845
  const [first, ...rest] = key.split('.');
843
- const val = obj[first];
844
- if (val && typeof val === 'object') {
845
- const restKey = rest.join('.');
846
- if (Array.isArray(val)) {
847
- // Map over array elements (e.g., allocations[].salesInvoiceId)
848
- const existing = Array.isArray(result[first]) ? result[first] : [];
849
- result[first] = val.map((item, i) => ({
850
- ...(existing[i] || {}),
851
- ...pick(item, [restKey]),
852
- }));
853
- }
854
- else {
855
- const nested = pick(val, [restKey]);
856
- result[first] = { ...(result[first] || {}), ...nested };
857
- }
846
+ if (isUnsafeKey(first))
847
+ continue;
848
+ const list = nestedKeys.get(first);
849
+ if (list) {
850
+ list.push(rest.join('.'));
858
851
  }
852
+ else {
853
+ nestedKeys.set(first, [rest.join('.')]);
854
+ }
855
+ }
856
+ else {
857
+ directKeys.add(key);
859
858
  }
860
- else if (key in obj) {
859
+ }
860
+ const result = {};
861
+ for (const key of directKeys) {
862
+ if (key in obj)
861
863
  result[key] = obj[key];
864
+ }
865
+ for (const [first, restKeys] of nestedKeys) {
866
+ const val = obj[first];
867
+ if (!val || typeof val !== 'object')
868
+ continue;
869
+ if (Array.isArray(val)) {
870
+ result[first] = val.map(item => pick(item, restKeys));
871
+ }
872
+ else {
873
+ result[first] = pick(val, restKeys);
862
874
  }
863
875
  }
864
876
  return result;
@@ -825,32 +825,44 @@ export default class Accounts extends Command {
825
825
  filterFields(data, fields) {
826
826
  const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
827
827
  const pick = (obj, keys) => {
828
- const result = {};
828
+ // Group keys by their first segment so sibling sub-paths
829
+ // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
830
+ // and don't clobber each other on merge.
831
+ const directKeys = new Set();
832
+ const nestedKeys = new Map();
829
833
  for (const key of keys) {
830
834
  if (key.split('.').some(isUnsafeKey))
831
835
  continue;
832
836
  if (key.includes('.')) {
833
- // Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
834
837
  const [first, ...rest] = key.split('.');
835
- const val = obj[first];
836
- if (val && typeof val === 'object') {
837
- const restKey = rest.join('.');
838
- if (Array.isArray(val)) {
839
- // Map over array elements (e.g., allocations[].salesInvoiceId)
840
- const existing = Array.isArray(result[first]) ? result[first] : [];
841
- result[first] = val.map((item, i) => ({
842
- ...(existing[i] || {}),
843
- ...pick(item, [restKey]),
844
- }));
845
- }
846
- else {
847
- const nested = pick(val, [restKey]);
848
- result[first] = { ...(result[first] || {}), ...nested };
849
- }
838
+ if (isUnsafeKey(first))
839
+ continue;
840
+ const list = nestedKeys.get(first);
841
+ if (list) {
842
+ list.push(rest.join('.'));
850
843
  }
844
+ else {
845
+ nestedKeys.set(first, [rest.join('.')]);
846
+ }
847
+ }
848
+ else {
849
+ directKeys.add(key);
851
850
  }
852
- else if (key in obj) {
851
+ }
852
+ const result = {};
853
+ for (const key of directKeys) {
854
+ if (key in obj)
853
855
  result[key] = obj[key];
856
+ }
857
+ for (const [first, restKeys] of nestedKeys) {
858
+ const val = obj[first];
859
+ if (!val || typeof val !== 'object')
860
+ continue;
861
+ if (Array.isArray(val)) {
862
+ result[first] = val.map(item => pick(item, restKeys));
863
+ }
864
+ else {
865
+ result[first] = pick(val, restKeys);
854
866
  }
855
867
  }
856
868
  return result;
@@ -8,9 +8,9 @@ const HAS_CAMT053 = false;
8
8
  const NESTED_IMPORTS = [];
9
9
  const SUBCOMMANDS = [...["list", "get", "retry"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
- 'list': { method: 'GET', path: '/v1/agent-runs', summary: 'List agent runs for the current tenant.', pathParams: [], hasBody: false, isMultipart: false },
12
- 'get': { method: 'GET', path: '/v1/agent-runs/{runId}', summary: 'Get an agent-run detail by id.', pathParams: ['runId'], hasBody: false, isMultipart: false },
13
- 'retry': { method: 'POST', path: '/v1/agent-runs/{runId}/retry', summary: 'Retry an agent run from NEEDS_REVIEW with a reviewer instruction.', pathParams: ['runId'], hasBody: true, isMultipart: false },
11
+ 'list': { method: 'GET', path: '/v1/agent-runs', summary: 'List agent runs for the current tenant', pathParams: [], hasBody: false, isMultipart: false },
12
+ 'get': { method: 'GET', path: '/v1/agent-runs/{runId}', summary: 'Get an agent-run detail by id', pathParams: ['runId'], hasBody: false, isMultipart: false },
13
+ 'retry': { method: 'POST', path: '/v1/agent-runs/{runId}/retry', summary: 'Retry an agent run from NEEDS_REVIEW with a reviewer instruction', pathParams: ['runId'], hasBody: true, isMultipart: false },
14
14
  };
15
15
  export default class AgentRuns extends Command {
16
16
  static description = 'Manage agent runs';
@@ -814,32 +814,44 @@ export default class AgentRuns extends Command {
814
814
  filterFields(data, fields) {
815
815
  const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
816
816
  const pick = (obj, keys) => {
817
- const result = {};
817
+ // Group keys by their first segment so sibling sub-paths
818
+ // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
819
+ // and don't clobber each other on merge.
820
+ const directKeys = new Set();
821
+ const nestedKeys = new Map();
818
822
  for (const key of keys) {
819
823
  if (key.split('.').some(isUnsafeKey))
820
824
  continue;
821
825
  if (key.includes('.')) {
822
- // Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
823
826
  const [first, ...rest] = key.split('.');
824
- const val = obj[first];
825
- if (val && typeof val === 'object') {
826
- const restKey = rest.join('.');
827
- if (Array.isArray(val)) {
828
- // Map over array elements (e.g., allocations[].salesInvoiceId)
829
- const existing = Array.isArray(result[first]) ? result[first] : [];
830
- result[first] = val.map((item, i) => ({
831
- ...(existing[i] || {}),
832
- ...pick(item, [restKey]),
833
- }));
834
- }
835
- else {
836
- const nested = pick(val, [restKey]);
837
- result[first] = { ...(result[first] || {}), ...nested };
838
- }
827
+ if (isUnsafeKey(first))
828
+ continue;
829
+ const list = nestedKeys.get(first);
830
+ if (list) {
831
+ list.push(rest.join('.'));
839
832
  }
833
+ else {
834
+ nestedKeys.set(first, [rest.join('.')]);
835
+ }
836
+ }
837
+ else {
838
+ directKeys.add(key);
840
839
  }
841
- else if (key in obj) {
840
+ }
841
+ const result = {};
842
+ for (const key of directKeys) {
843
+ if (key in obj)
842
844
  result[key] = obj[key];
845
+ }
846
+ for (const [first, restKeys] of nestedKeys) {
847
+ const val = obj[first];
848
+ if (!val || typeof val !== 'object')
849
+ continue;
850
+ if (Array.isArray(val)) {
851
+ result[first] = val.map(item => pick(item, restKeys));
852
+ }
853
+ else {
854
+ result[first] = pick(val, restKeys);
843
855
  }
844
856
  }
845
857
  return result;
@@ -1,7 +1,7 @@
1
1
  import { Command } from '@oclif/core';
2
- export default class ProcessingLogs extends Command {
2
+ export default class AsyncEndpointsInternal extends Command {
3
3
  static description: string;
4
- static examples: string[];
4
+ static examples: never[];
5
5
  static args: {
6
6
  action: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
7
  id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
@@ -6,15 +6,14 @@ const HAS_IMPORT = false;
6
6
  const HAS_EXPORT = false;
7
7
  const HAS_CAMT053 = false;
8
8
  const NESTED_IMPORTS = [];
9
- const SUBCOMMANDS = [...["get"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
9
+ const SUBCOMMANDS = [...["webhook-events", "processor-jobs"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
- 'get': { method: 'GET', path: '/v1/processing-logs/{processingId}', summary: 'Get a processing run by its processing identifier', pathParams: ['processingId'], hasBody: false, isMultipart: false },
11
+ 'webhook-events': { method: 'POST', path: '/v1/pubsub/webhook-events', summary: '[INTERNAL] Async push endpoint for webhook event delivery', pathParams: [], hasBody: false, isMultipart: false },
12
+ 'processor-jobs': { method: 'POST', path: '/v1/pubsub/processor-jobs', summary: '[INTERNAL] Async push endpoint for processor-job.v1 envelopes', pathParams: [], hasBody: false, isMultipart: false },
12
13
  };
13
- export default class ProcessingLogs extends Command {
14
- static description = 'Manage processing logs';
15
- static examples = [
16
- '<%= config.bin %> processing-logs get <id>',
17
- ];
14
+ export default class AsyncEndpointsInternal extends Command {
15
+ static description = 'Manage async endpoints internal';
16
+ static examples = [];
18
17
  static args = {
19
18
  action: Args.string({ description: 'Action: ' + SUBCOMMANDS.join(', '), required: true }),
20
19
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
@@ -39,7 +38,7 @@ export default class ProcessingLogs extends Command {
39
38
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
40
39
  };
41
40
  async run() {
42
- const { args, flags } = await this.parse(ProcessingLogs);
41
+ const { args, flags } = await this.parse(AsyncEndpointsInternal);
43
42
  // Handle import action specially
44
43
  if (args.action === 'import') {
45
44
  if (!HAS_IMPORT)
@@ -85,7 +84,7 @@ export default class ProcessingLogs extends Command {
85
84
  // First path param always comes from positional argument
86
85
  value = args.id;
87
86
  if (!value)
88
- this.error('Missing required argument: id. Usage: aderis processing-logs <action> <id>');
87
+ this.error('Missing required argument: id. Usage: aderis async-endpoints-internal <action> <id>');
89
88
  }
90
89
  else {
91
90
  const flagName = param.replace(/\./g, '-').replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
@@ -648,7 +647,7 @@ export default class ProcessingLogs extends Command {
648
647
  }
649
648
  async runNestedImport(suffix, parentId, flags) {
650
649
  if (!parentId) {
651
- this.error(`Missing required argument: id. Usage: aderis processing-logs import-${suffix} <parent-id> --file data.json`);
650
+ this.error(`Missing required argument: id. Usage: aderis async-endpoints-internal import-${suffix} <parent-id> --file data.json`);
652
651
  }
653
652
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
654
653
  if (!context)
@@ -810,32 +809,44 @@ export default class ProcessingLogs extends Command {
810
809
  filterFields(data, fields) {
811
810
  const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
812
811
  const pick = (obj, keys) => {
813
- const result = {};
812
+ // Group keys by their first segment so sibling sub-paths
813
+ // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
814
+ // and don't clobber each other on merge.
815
+ const directKeys = new Set();
816
+ const nestedKeys = new Map();
814
817
  for (const key of keys) {
815
818
  if (key.split('.').some(isUnsafeKey))
816
819
  continue;
817
820
  if (key.includes('.')) {
818
- // Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
819
821
  const [first, ...rest] = key.split('.');
820
- const val = obj[first];
821
- if (val && typeof val === 'object') {
822
- const restKey = rest.join('.');
823
- if (Array.isArray(val)) {
824
- // Map over array elements (e.g., allocations[].salesInvoiceId)
825
- const existing = Array.isArray(result[first]) ? result[first] : [];
826
- result[first] = val.map((item, i) => ({
827
- ...(existing[i] || {}),
828
- ...pick(item, [restKey]),
829
- }));
830
- }
831
- else {
832
- const nested = pick(val, [restKey]);
833
- result[first] = { ...(result[first] || {}), ...nested };
834
- }
822
+ if (isUnsafeKey(first))
823
+ continue;
824
+ const list = nestedKeys.get(first);
825
+ if (list) {
826
+ list.push(rest.join('.'));
835
827
  }
828
+ else {
829
+ nestedKeys.set(first, [rest.join('.')]);
830
+ }
831
+ }
832
+ else {
833
+ directKeys.add(key);
836
834
  }
837
- else if (key in obj) {
835
+ }
836
+ const result = {};
837
+ for (const key of directKeys) {
838
+ if (key in obj)
838
839
  result[key] = obj[key];
840
+ }
841
+ for (const [first, restKeys] of nestedKeys) {
842
+ const val = obj[first];
843
+ if (!val || typeof val !== 'object')
844
+ continue;
845
+ if (Array.isArray(val)) {
846
+ result[first] = val.map(item => pick(item, restKeys));
847
+ }
848
+ else {
849
+ result[first] = pick(val, restKeys);
839
850
  }
840
851
  }
841
852
  return result;
@@ -813,32 +813,44 @@ export default class AuditLog extends Command {
813
813
  filterFields(data, fields) {
814
814
  const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
815
815
  const pick = (obj, keys) => {
816
- const result = {};
816
+ // Group keys by their first segment so sibling sub-paths
817
+ // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
818
+ // and don't clobber each other on merge.
819
+ const directKeys = new Set();
820
+ const nestedKeys = new Map();
817
821
  for (const key of keys) {
818
822
  if (key.split('.').some(isUnsafeKey))
819
823
  continue;
820
824
  if (key.includes('.')) {
821
- // Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
822
825
  const [first, ...rest] = key.split('.');
823
- const val = obj[first];
824
- if (val && typeof val === 'object') {
825
- const restKey = rest.join('.');
826
- if (Array.isArray(val)) {
827
- // Map over array elements (e.g., allocations[].salesInvoiceId)
828
- const existing = Array.isArray(result[first]) ? result[first] : [];
829
- result[first] = val.map((item, i) => ({
830
- ...(existing[i] || {}),
831
- ...pick(item, [restKey]),
832
- }));
833
- }
834
- else {
835
- const nested = pick(val, [restKey]);
836
- result[first] = { ...(result[first] || {}), ...nested };
837
- }
826
+ if (isUnsafeKey(first))
827
+ continue;
828
+ const list = nestedKeys.get(first);
829
+ if (list) {
830
+ list.push(rest.join('.'));
838
831
  }
832
+ else {
833
+ nestedKeys.set(first, [rest.join('.')]);
834
+ }
835
+ }
836
+ else {
837
+ directKeys.add(key);
839
838
  }
840
- else if (key in obj) {
839
+ }
840
+ const result = {};
841
+ for (const key of directKeys) {
842
+ if (key in obj)
841
843
  result[key] = obj[key];
844
+ }
845
+ for (const [first, restKeys] of nestedKeys) {
846
+ const val = obj[first];
847
+ if (!val || typeof val !== 'object')
848
+ continue;
849
+ if (Array.isArray(val)) {
850
+ result[first] = val.map(item => pick(item, restKeys));
851
+ }
852
+ else {
853
+ result[first] = pick(val, restKeys);
842
854
  }
843
855
  }
844
856
  return result;
@@ -15,4 +15,5 @@ export default class AuthTenant extends Command {
15
15
  private listTenants;
16
16
  private showCurrentTenant;
17
17
  private switchTenant;
18
+ private resolveTenantName;
18
19
  }
@@ -74,10 +74,10 @@ export default class AuthTenant extends Command {
74
74
  const tenant = tenants[0];
75
75
  this.log(`Only one tenant available: ${tenant.name}`);
76
76
  if (tenant.tenantId === current?.tenantId) {
77
- this.log('Already using this tenant');
77
+ this.log(`Already using this tenant (${tenant.name}, ${tenant.tenantId})`);
78
78
  }
79
79
  else {
80
- await this.switchTenant(contextName, tenant.tenantId);
80
+ await this.switchTenant(contextName, tenant.tenantId, tenant.name);
81
81
  }
82
82
  return;
83
83
  }
@@ -106,11 +106,13 @@ export default class AuthTenant extends Command {
106
106
  choices,
107
107
  message: 'Select a tenant:',
108
108
  });
109
+ const selectedTenantName = tenants.find((t) => t.tenantId === selectedTenant)?.name;
109
110
  if (selectedTenant === current?.tenantId) {
110
- this.log('Already using this tenant');
111
+ const label = selectedTenantName ? `${selectedTenantName}, ${selectedTenant}` : selectedTenant;
112
+ this.log(`Already using this tenant (${label})`);
111
113
  return;
112
114
  }
113
- await this.switchTenant(contextName, selectedTenant);
115
+ await this.switchTenant(contextName, selectedTenant, selectedTenantName);
114
116
  }
115
117
  async listTenants(contextName, asJson) {
116
118
  const tenants = await listTenants(contextName);
@@ -152,11 +154,14 @@ export default class AuthTenant extends Command {
152
154
  this.log(`Tenant ID: ${current.tenantId}`);
153
155
  }
154
156
  }
155
- async switchTenant(contextName, tenantId) {
156
- this.log(`Switching to tenant ${tenantId}...`);
157
+ async switchTenant(contextName, tenantId, tenantName) {
158
+ const requested = tenantName ? `${tenantName} (${tenantId})` : tenantId;
159
+ this.log(`Switching to tenant ${requested}...`);
157
160
  try {
158
161
  const tokens = await switchTenant(contextName, tenantId);
159
- this.log(`Successfully switched to tenant ${tokens.tenant_id}`);
162
+ const resolvedName = tenantName ?? (await this.resolveTenantName(contextName, tokens.tenant_id));
163
+ const display = resolvedName ? `${resolvedName} (${tokens.tenant_id})` : tokens.tenant_id;
164
+ this.log(`Successfully switched to tenant ${display}`);
160
165
  }
161
166
  catch (error) {
162
167
  if (error instanceof Error) {
@@ -165,4 +170,15 @@ export default class AuthTenant extends Command {
165
170
  throw error;
166
171
  }
167
172
  }
173
+ async resolveTenantName(contextName, tenantId) {
174
+ if (!tenantId)
175
+ return undefined;
176
+ try {
177
+ const tenants = await listTenants(contextName);
178
+ return tenants.find((t) => t.tenantId === tenantId)?.name;
179
+ }
180
+ catch {
181
+ return undefined;
182
+ }
183
+ }
168
184
  }
@@ -833,32 +833,44 @@ export default class BankAccounts extends Command {
833
833
  filterFields(data, fields) {
834
834
  const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
835
835
  const pick = (obj, keys) => {
836
- const result = {};
836
+ // Group keys by their first segment so sibling sub-paths
837
+ // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
838
+ // and don't clobber each other on merge.
839
+ const directKeys = new Set();
840
+ const nestedKeys = new Map();
837
841
  for (const key of keys) {
838
842
  if (key.split('.').some(isUnsafeKey))
839
843
  continue;
840
844
  if (key.includes('.')) {
841
- // Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
842
845
  const [first, ...rest] = key.split('.');
843
- const val = obj[first];
844
- if (val && typeof val === 'object') {
845
- const restKey = rest.join('.');
846
- if (Array.isArray(val)) {
847
- // Map over array elements (e.g., allocations[].salesInvoiceId)
848
- const existing = Array.isArray(result[first]) ? result[first] : [];
849
- result[first] = val.map((item, i) => ({
850
- ...(existing[i] || {}),
851
- ...pick(item, [restKey]),
852
- }));
853
- }
854
- else {
855
- const nested = pick(val, [restKey]);
856
- result[first] = { ...(result[first] || {}), ...nested };
857
- }
846
+ if (isUnsafeKey(first))
847
+ continue;
848
+ const list = nestedKeys.get(first);
849
+ if (list) {
850
+ list.push(rest.join('.'));
858
851
  }
852
+ else {
853
+ nestedKeys.set(first, [rest.join('.')]);
854
+ }
855
+ }
856
+ else {
857
+ directKeys.add(key);
859
858
  }
860
- else if (key in obj) {
859
+ }
860
+ const result = {};
861
+ for (const key of directKeys) {
862
+ if (key in obj)
861
863
  result[key] = obj[key];
864
+ }
865
+ for (const [first, restKeys] of nestedKeys) {
866
+ const val = obj[first];
867
+ if (!val || typeof val !== 'object')
868
+ continue;
869
+ if (Array.isArray(val)) {
870
+ result[first] = val.map(item => pick(item, restKeys));
871
+ }
872
+ else {
873
+ result[first] = pick(val, restKeys);
862
874
  }
863
875
  }
864
876
  return result;
@@ -14,6 +14,8 @@ export default class BankIntegrations extends Command {
14
14
  data: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
15
  file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
16
  set: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ 'import-run-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ 'statement-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
19
  page: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
20
  limit: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
21
  include: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;