@aderis-erp/cli 1.0.14 → 1.0.17

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 (167) hide show
  1. package/README.md +30 -8
  2. package/dist/commands/access-groups.d.ts +0 -1
  3. package/dist/commands/access-groups.js +22 -78
  4. package/dist/commands/account-transaction-allocations.d.ts +0 -1
  5. package/dist/commands/account-transaction-allocations.js +22 -78
  6. package/dist/commands/account-transactions.d.ts +0 -1
  7. package/dist/commands/account-transactions.js +28 -82
  8. package/dist/commands/accounts.d.ts +0 -1
  9. package/dist/commands/accounts.js +24 -80
  10. package/dist/commands/agent-platform.d.ts +0 -1
  11. package/dist/commands/agent-platform.js +24 -79
  12. package/dist/commands/analytics.d.ts +39 -0
  13. package/dist/commands/analytics.js +1113 -0
  14. package/dist/commands/{documents.d.ts → api-keys.d.ts} +1 -2
  15. package/dist/commands/api-keys.js +1117 -0
  16. package/dist/commands/audit-log.d.ts +0 -1
  17. package/dist/commands/audit-log.js +22 -78
  18. package/dist/commands/auth/login.d.ts +9 -0
  19. package/dist/commands/auth/login.js +83 -21
  20. package/dist/commands/auth/logout.d.ts +6 -0
  21. package/dist/commands/auth/logout.js +23 -6
  22. package/dist/commands/auth/status.js +46 -23
  23. package/dist/commands/auth/tenant.js +15 -9
  24. package/dist/commands/auth.d.ts +0 -1
  25. package/dist/commands/auth.js +37 -51
  26. package/dist/commands/bank-accounts.d.ts +0 -1
  27. package/dist/commands/bank-accounts.js +22 -78
  28. package/dist/commands/bank-integrations.d.ts +0 -1
  29. package/dist/commands/bank-integrations.js +22 -78
  30. package/dist/commands/business-partners.d.ts +1 -1
  31. package/dist/commands/business-partners.js +38 -84
  32. package/dist/commands/chat.d.ts +0 -1
  33. package/dist/commands/chat.js +22 -78
  34. package/dist/commands/config/add.d.ts +12 -0
  35. package/dist/commands/config/add.js +97 -17
  36. package/dist/commands/config/current.js +12 -4
  37. package/dist/commands/config/list.js +12 -4
  38. package/dist/commands/config/set-api-key.d.ts +10 -0
  39. package/dist/commands/config/set-api-key.js +49 -0
  40. package/dist/commands/config/use.js +3 -4
  41. package/dist/commands/credit-card-accounts.d.ts +0 -1
  42. package/dist/commands/credit-card-accounts.js +22 -78
  43. package/dist/commands/delivery-notes.d.ts +0 -1
  44. package/dist/commands/delivery-notes.js +29 -84
  45. package/dist/commands/dunning-configs.d.ts +0 -1
  46. package/dist/commands/dunning-configs.js +22 -78
  47. package/dist/commands/dunning-reminders.d.ts +0 -1
  48. package/dist/commands/dunning-reminders.js +22 -78
  49. package/dist/commands/dunning-runs.d.ts +0 -1
  50. package/dist/commands/dunning-runs.js +22 -78
  51. package/dist/commands/email-transactions.d.ts +0 -1
  52. package/dist/commands/email-transactions.js +22 -78
  53. package/dist/commands/employees.d.ts +0 -1
  54. package/dist/commands/employees.js +24 -80
  55. package/dist/commands/entity-configurations.d.ts +0 -1
  56. package/dist/commands/entity-configurations.js +22 -78
  57. package/dist/commands/event-processor-mappings.d.ts +0 -1
  58. package/dist/commands/event-processor-mappings.js +22 -78
  59. package/dist/commands/{files-internal.d.ts → files.d.ts} +1 -2
  60. package/dist/commands/{files-internal.js → files.js} +40 -90
  61. package/dist/commands/health.d.ts +0 -1
  62. package/dist/commands/health.js +22 -78
  63. package/dist/commands/inbox.d.ts +0 -1
  64. package/dist/commands/inbox.js +22 -78
  65. package/dist/commands/integrations.d.ts +0 -1
  66. package/dist/commands/integrations.js +22 -78
  67. package/dist/commands/item-categories.d.ts +0 -1
  68. package/dist/commands/item-categories.js +22 -78
  69. package/dist/commands/items.d.ts +0 -1
  70. package/dist/commands/items.js +24 -80
  71. package/dist/commands/journal-lines.d.ts +0 -1
  72. package/dist/commands/journal-lines.js +22 -78
  73. package/dist/commands/journals.d.ts +1 -1
  74. package/dist/commands/journals.js +29 -81
  75. package/dist/commands/ledgers.d.ts +0 -1
  76. package/dist/commands/ledgers.js +26 -79
  77. package/dist/commands/nango-integrations.d.ts +39 -0
  78. package/dist/commands/nango-integrations.js +1115 -0
  79. package/dist/commands/office-locations.d.ts +0 -1
  80. package/dist/commands/office-locations.js +22 -78
  81. package/dist/commands/payment-methods.d.ts +0 -1
  82. package/dist/commands/payment-methods.js +22 -78
  83. package/dist/commands/payment-runs.d.ts +0 -1
  84. package/dist/commands/payment-runs.js +24 -79
  85. package/dist/commands/payment-terms.d.ts +0 -1
  86. package/dist/commands/payment-terms.js +22 -78
  87. package/dist/commands/payments.d.ts +0 -1
  88. package/dist/commands/payments.js +22 -78
  89. package/dist/commands/price-lists.d.ts +0 -1
  90. package/dist/commands/price-lists.js +22 -78
  91. package/dist/commands/processor-mappings.d.ts +0 -1
  92. package/dist/commands/processor-mappings.js +22 -78
  93. package/dist/commands/processors.d.ts +0 -1
  94. package/dist/commands/processors.js +22 -78
  95. package/dist/commands/purchase-order-types.d.ts +0 -1
  96. package/dist/commands/purchase-order-types.js +22 -78
  97. package/dist/commands/purchase-orders.d.ts +1 -1
  98. package/dist/commands/purchase-orders.js +35 -84
  99. package/dist/commands/reports.d.ts +0 -1
  100. package/dist/commands/reports.js +23 -79
  101. package/dist/commands/salary-types.d.ts +0 -1
  102. package/dist/commands/salary-types.js +22 -78
  103. package/dist/commands/sales-invoices.d.ts +0 -1
  104. package/dist/commands/sales-invoices.js +38 -89
  105. package/dist/commands/sales-orders.d.ts +0 -1
  106. package/dist/commands/sales-orders.js +26 -82
  107. package/dist/commands/saved-views.d.ts +0 -1
  108. package/dist/commands/saved-views.js +22 -78
  109. package/dist/commands/scheduled-processor-mappings.d.ts +0 -1
  110. package/dist/commands/scheduled-processor-mappings.js +22 -78
  111. package/dist/commands/schedules.d.ts +0 -1
  112. package/dist/commands/schedules.js +22 -78
  113. package/dist/commands/switch.js +14 -7
  114. package/dist/commands/tag-configurations.d.ts +39 -0
  115. package/dist/commands/{documents.js → tag-configurations.js} +40 -96
  116. package/dist/commands/tags.d.ts +0 -1
  117. package/dist/commands/tags.js +22 -78
  118. package/dist/commands/tax-codes.d.ts +0 -1
  119. package/dist/commands/tax-codes.js +22 -78
  120. package/dist/commands/tax-matrix.d.ts +0 -1
  121. package/dist/commands/tax-matrix.js +22 -78
  122. package/dist/commands/tenants.d.ts +0 -1
  123. package/dist/commands/tenants.js +24 -79
  124. package/dist/commands/text-templates.d.ts +0 -1
  125. package/dist/commands/text-templates.js +22 -78
  126. package/dist/commands/tools.d.ts +0 -1
  127. package/dist/commands/tools.js +22 -78
  128. package/dist/commands/vat-declarations.d.ts +1 -1
  129. package/dist/commands/vat-declarations.js +28 -79
  130. package/dist/commands/vendor-invoices.d.ts +0 -1
  131. package/dist/commands/vendor-invoices.js +31 -86
  132. package/dist/commands/webhooks.d.ts +0 -1
  133. package/dist/commands/webhooks.js +22 -78
  134. package/dist/commands/workflows.d.ts +0 -1
  135. package/dist/commands/workflows.js +22 -78
  136. package/dist/lib/api-client.js +8 -6
  137. package/dist/lib/auth/constants.d.ts +11 -0
  138. package/dist/lib/auth/constants.js +11 -0
  139. package/dist/lib/auth/errors.d.ts +17 -0
  140. package/dist/lib/auth/errors.js +22 -0
  141. package/dist/lib/auth/flow.d.ts +27 -0
  142. package/dist/lib/auth/flow.js +163 -0
  143. package/dist/lib/auth/guards.d.ts +5 -0
  144. package/dist/lib/auth/guards.js +14 -0
  145. package/dist/lib/auth/http.d.ts +16 -0
  146. package/dist/lib/auth/http.js +49 -0
  147. package/dist/lib/auth/index.d.ts +49 -0
  148. package/dist/lib/auth/index.js +217 -0
  149. package/dist/lib/auth/keycloak-provider.d.ts +75 -0
  150. package/dist/lib/auth/keycloak-provider.js +215 -0
  151. package/dist/lib/auth/pages.d.ts +25 -0
  152. package/dist/lib/auth/pages.js +123 -0
  153. package/dist/lib/auth/pkce.d.ts +11 -0
  154. package/dist/lib/auth/pkce.js +17 -0
  155. package/dist/lib/auth/tokens.d.ts +54 -0
  156. package/dist/lib/auth/tokens.js +104 -0
  157. package/dist/lib/auth/types.d.ts +57 -0
  158. package/dist/lib/auth/types.js +1 -0
  159. package/dist/lib/auth/validated-url.d.ts +6 -0
  160. package/dist/lib/auth/validated-url.js +53 -0
  161. package/dist/lib/base-api-command.js +6 -4
  162. package/dist/lib/config.d.ts +44 -4
  163. package/dist/lib/config.js +55 -19
  164. package/oclif.manifest.json +1181 -330
  165. package/package.json +3 -2
  166. package/dist/lib/auth.d.ts +0 -35
  167. package/dist/lib/auth.js +0 -566
@@ -1,30 +1,36 @@
1
1
  import { Command, Args, Flags } from '@oclif/core';
2
2
  import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
3
- import { getCurrentContext, getContext, getBaseUrls } from '../lib/config.js';
3
+ import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
4
4
  // Add 'import' action if both create and update exist
5
5
  const HAS_IMPORT = true;
6
6
  const HAS_EXPORT = true;
7
7
  const HAS_CAMT053 = false;
8
- const NESTED_IMPORTS = ["line-items", "notes"];
9
- const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "submit", "approve", "decline", "cancel", "create-line-items", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
8
+ const NESTED_IMPORTS = ["line-items", "notes", "files"];
9
+ const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "submit", "approve", "decline", "cancel", "create-line-items", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
11
  'list': { method: 'GET', path: '/v1/purchase-orders', summary: 'Get all purchase orders with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
12
- 'create': { method: 'POST', path: '/v1/purchase-orders', summary: 'Create a new purchase order', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'notes', 'discounts', 'lineItems'] },
12
+ 'create': { method: 'POST', path: '/v1/purchase-orders', summary: 'Create a new purchase order', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes', 'files', 'notes', 'discounts', 'lineItems'] },
13
13
  'get': { method: 'GET', path: '/v1/purchase-orders/{id}', summary: 'Get a purchase order by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
14
- 'update': { method: 'PATCH', path: '/v1/purchase-orders/{id}', summary: 'Update a purchase order', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'notes', 'discounts', 'lineItems'] },
14
+ 'update': { method: 'PATCH', path: '/v1/purchase-orders/{id}', summary: 'Update a purchase order', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes', 'files', 'notes', 'discounts', 'lineItems', 'removeFileIds'] },
15
15
  'delete': { method: 'DELETE', path: '/v1/purchase-orders/{id}', summary: 'Delete a purchase order', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
16
16
  'submit': { method: 'POST', path: '/v1/purchase-orders/{id}/submit', summary: 'Submit order for approval (DRAFT → PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
17
17
  'approve': { method: 'POST', path: '/v1/purchase-orders/{id}/approve', summary: 'Approve order (PENDING_APPROVAL → APPROVED). Generates order number if missing.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
18
18
  'decline': { method: 'POST', path: '/v1/purchase-orders/{id}/decline', summary: 'Decline order (PENDING_APPROVAL → DECLINED)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
19
19
  'cancel': { method: 'POST', path: '/v1/purchase-orders/{id}/cancel', summary: 'Cancel order (DRAFT, PENDING_APPROVAL or APPROVED → CANCELLED)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
20
- 'create-line-items': { method: 'POST', path: '/v1/purchase-orders/{orderId}/line-items', summary: 'Add a line item to a purchase order', pathParams: ['orderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts'] },
21
- 'update-line-items': { method: 'PATCH', path: '/v1/purchase-orders/{orderId}/line-items/{lineItemId}', summary: 'Update a line item on a purchase order', pathParams: ['orderId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts'] },
20
+ 'create-line-items': { method: 'POST', path: '/v1/purchase-orders/{orderId}/line-items', summary: 'Add a line item to a purchase order', pathParams: ['orderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts', 'tagIds', 'tagCodes'] },
21
+ 'update-line-items': { method: 'PATCH', path: '/v1/purchase-orders/{orderId}/line-items/{lineItemId}', summary: 'Update a line item on a purchase order', pathParams: ['orderId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts', 'tagIds', 'tagCodes'] },
22
22
  'delete-line-items': { method: 'DELETE', path: '/v1/purchase-orders/{orderId}/line-items/{lineItemId}', summary: 'Remove a line item from a purchase order', pathParams: ['orderId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
23
23
  'list-notes': { method: 'GET', path: '/v1/purchase-orders/{purchaseOrderId}/notes', summary: 'Get all notes for a purchase order', pathParams: ['purchaseOrderId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
24
24
  'create-notes': { method: 'POST', path: '/v1/purchase-orders/{purchaseOrderId}/notes', summary: 'Create a note for a purchase order', pathParams: ['purchaseOrderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
25
25
  'get-notes': { method: 'GET', path: '/v1/purchase-orders/{purchaseOrderId}/notes/{noteId}', summary: 'Get a specific note for a purchase order', pathParams: ['purchaseOrderId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
26
26
  'update-notes': { method: 'PATCH', path: '/v1/purchase-orders/{purchaseOrderId}/notes/{noteId}', summary: 'Update a note for a purchase order', pathParams: ['purchaseOrderId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
27
27
  'delete-notes': { method: 'DELETE', path: '/v1/purchase-orders/{purchaseOrderId}/notes/{noteId}', summary: 'Delete a note from a purchase order', pathParams: ['purchaseOrderId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
28
+ 'list-files': { method: 'GET', path: '/v1/purchase-orders/{parentId}/files', summary: 'List file attachments for a purchase order', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
29
+ 'create-files': { method: 'POST', path: '/v1/purchase-orders/{parentId}/files', summary: 'Upload a file attachment to a purchase order', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, bodyArrayFields: [] },
30
+ 'get-files': { method: 'GET', path: '/v1/purchase-orders/{parentId}/files/{fileId}', summary: 'Get a file attachment for a purchase order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
31
+ 'update-files': { method: 'PATCH', path: '/v1/purchase-orders/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a purchase order', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
32
+ 'delete-files': { method: 'DELETE', path: '/v1/purchase-orders/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a purchase order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
33
+ 'files-download': { method: 'GET', path: '/v1/purchase-orders/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a purchase order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
28
34
  };
29
35
  export default class PurchaseOrders extends Command {
30
36
  static description = 'Manage purchase orders';
@@ -41,7 +47,7 @@ export default class PurchaseOrders extends Command {
41
47
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
42
48
  };
43
49
  static flags = {
44
- context: Flags.string({ char: 'c', description: 'Context to use' }),
50
+ context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
45
51
  output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
46
52
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
47
53
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
@@ -50,6 +56,7 @@ export default class PurchaseOrders extends Command {
50
56
  set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
51
57
  'line-item-id': Flags.string({ description: 'Path param: lineItemId' }),
52
58
  'note-id': Flags.string({ description: 'Path param: noteId' }),
59
+ 'file-id': Flags.string({ description: 'Path param: fileId' }),
53
60
  page: Flags.string({ description: 'Page number' }),
54
61
  limit: Flags.string({ description: 'Items per page' }),
55
62
  include: Flags.string({ description: 'Relations to include' }),
@@ -57,7 +64,7 @@ export default class PurchaseOrders extends Command {
57
64
  'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
58
65
  filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
59
66
  query: Flags.string({ description: 'Additional query params as JSON' }),
60
- fields: Flags.string({ description: 'Comma-separated list of fields to show (e.g. --fields=id,name,status)' }),
67
+ fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
61
68
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
62
69
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
63
70
  };
@@ -115,9 +122,9 @@ export default class PurchaseOrders extends Command {
115
122
  }
116
123
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
117
124
  if (!context)
118
- this.error('No context configured. Run: aderis auth login --new');
119
- if (!context.tokens)
120
- this.error('Not logged in. Run: aderis auth login');
125
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
126
+ if (!hasCredentials(context))
127
+ this.error(missingCredentialsMessage(context));
121
128
  const queryParams = {};
122
129
  if (flags.page)
123
130
  queryParams.page = flags.page;
@@ -129,6 +136,8 @@ export default class PurchaseOrders extends Command {
129
136
  queryParams.search = flags.search;
130
137
  if (flags['sort-by'])
131
138
  queryParams.sortBy = normalizeSortBy(flags['sort-by']);
139
+ if (flags.fields)
140
+ queryParams.fields = flags.fields;
132
141
  if (flags.query) {
133
142
  try {
134
143
  Object.assign(queryParams, JSON.parse(flags.query));
@@ -331,11 +340,6 @@ export default class PurchaseOrders extends Command {
331
340
  output = obj[arrayKeys[0]];
332
341
  }
333
342
  }
334
- // Filter fields if --fields flag is provided
335
- if (flags.fields) {
336
- const fieldList = flags.fields.split(',').map(f => f.trim());
337
- output = this.filterFields(output, fieldList);
338
- }
339
343
  // Sanitize output if requested
340
344
  if (flags.sanitize) {
341
345
  output = this.sanitizeOutput(output);
@@ -360,9 +364,9 @@ export default class PurchaseOrders extends Command {
360
364
  async runImport(flags) {
361
365
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
362
366
  if (!context)
363
- this.error('No context configured. Run: aderis auth login --new');
364
- if (!context.tokens)
365
- this.error('Not logged in. Run: aderis auth login');
367
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
368
+ if (!hasCredentials(context))
369
+ this.error(missingCredentialsMessage(context));
366
370
  // Get data from file, --data, or --set flags
367
371
  let items = [];
368
372
  if (flags.file) {
@@ -572,9 +576,9 @@ export default class PurchaseOrders extends Command {
572
576
  async runExport(flags) {
573
577
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
574
578
  if (!context)
575
- this.error('No context configured. Run: aderis auth login --new');
576
- if (!context.tokens)
577
- this.error('Not logged in. Run: aderis auth login');
579
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
580
+ if (!hasCredentials(context))
581
+ this.error(missingCredentialsMessage(context));
578
582
  const listEndpoint = ENDPOINTS['list'];
579
583
  if (!listEndpoint)
580
584
  this.error('Export requires a list endpoint');
@@ -590,6 +594,8 @@ export default class PurchaseOrders extends Command {
590
594
  baseParams.sortBy = normalizeSortBy(flags['sort-by']);
591
595
  if (flags.include)
592
596
  baseParams.include = flags.include;
597
+ if (flags.fields)
598
+ baseParams.fields = flags.fields;
593
599
  if (flags.query) {
594
600
  try {
595
601
  Object.assign(baseParams, JSON.parse(flags.query));
@@ -644,12 +650,7 @@ export default class PurchaseOrders extends Command {
644
650
  }
645
651
  }
646
652
  process.stderr.write(' done\n');
647
- // Filter fields if specified
648
653
  let output = allItems;
649
- if (flags.fields) {
650
- const fieldList = flags.fields.split(',').map(f => f.trim());
651
- output = this.filterFields(allItems, fieldList);
652
- }
653
654
  // Sanitize output if requested
654
655
  if (flags.sanitize) {
655
656
  output = this.sanitizeOutput(output);
@@ -666,9 +667,9 @@ export default class PurchaseOrders extends Command {
666
667
  async runCamt053Import(flags) {
667
668
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
668
669
  if (!context)
669
- this.error('No context configured. Run: aderis auth login --new');
670
- if (!context.tokens)
671
- this.error('Not logged in. Run: aderis auth login');
670
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
671
+ if (!hasCredentials(context))
672
+ this.error(missingCredentialsMessage(context));
672
673
  if (!flags.file) {
673
674
  this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
674
675
  }
@@ -756,9 +757,9 @@ export default class PurchaseOrders extends Command {
756
757
  }
757
758
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
758
759
  if (!context)
759
- this.error('No context configured. Run: aderis auth login --new');
760
- if (!context.tokens)
761
- this.error('Not logged in. Run: aderis auth login');
760
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
761
+ if (!hasCredentials(context))
762
+ this.error(missingCredentialsMessage(context));
762
763
  // Get data from file, --data, or --set flags
763
764
  let items = [];
764
765
  if (flags.file) {
@@ -978,56 +979,6 @@ export default class PurchaseOrders extends Command {
978
979
  return `\$${op}`;
979
980
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
980
981
  }
981
- filterFields(data, fields) {
982
- const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
983
- const pick = (obj, keys) => {
984
- // Group keys by their first segment so sibling sub-paths
985
- // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
986
- // and don't clobber each other on merge.
987
- const directKeys = new Set();
988
- const nestedKeys = new Map();
989
- for (const key of keys) {
990
- if (key.split('.').some(isUnsafeKey))
991
- continue;
992
- if (key.includes('.')) {
993
- const [first, ...rest] = key.split('.');
994
- if (isUnsafeKey(first))
995
- continue;
996
- const list = nestedKeys.get(first);
997
- if (list) {
998
- list.push(rest.join('.'));
999
- }
1000
- else {
1001
- nestedKeys.set(first, [rest.join('.')]);
1002
- }
1003
- }
1004
- else {
1005
- directKeys.add(key);
1006
- }
1007
- }
1008
- const result = {};
1009
- for (const key of directKeys) {
1010
- if (key in obj)
1011
- result[key] = obj[key];
1012
- }
1013
- for (const [first, restKeys] of nestedKeys) {
1014
- const val = obj[first];
1015
- if (!val || typeof val !== 'object')
1016
- continue;
1017
- if (Array.isArray(val)) {
1018
- result[first] = val.map(item => pick(item, restKeys));
1019
- }
1020
- else {
1021
- result[first] = pick(val, restKeys);
1022
- }
1023
- }
1024
- return result;
1025
- };
1026
- if (Array.isArray(data)) {
1027
- return data.map(item => pick(item, fields));
1028
- }
1029
- return pick(data, fields);
1030
- }
1031
982
  sanitizeOutput(data) {
1032
983
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1033
984
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -31,7 +31,6 @@ export default class Reports extends Command {
31
31
  private runCamt053Import;
32
32
  private runNestedImport;
33
33
  private parseFilterValue;
34
- private filterFields;
35
34
  private sanitizeOutput;
36
35
  private getColumns;
37
36
  private getValue;
@@ -1,6 +1,6 @@
1
1
  import { Command, Args, Flags } from '@oclif/core';
2
2
  import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
3
- import { getCurrentContext, getContext, getBaseUrls } from '../lib/config.js';
3
+ import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
4
4
  // Add 'import' action if both create and update exist
5
5
  const HAS_IMPORT = false;
6
6
  const HAS_EXPORT = false;
@@ -8,7 +8,7 @@ const HAS_CAMT053 = false;
8
8
  const NESTED_IMPORTS = [];
9
9
  const SUBCOMMANDS = [...["profit-and-loss", "balance-sheet", "gl-detail", "gl-detail-export-xlsx", "gl-detail-export-pdf"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
- 'profit-and-loss': { method: 'GET', path: '/v1/reports/profit-and-loss', summary: 'Get Profit & Loss report', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
11
+ 'profit-and-loss': { method: 'POST', path: '/v1/reports/profit-and-loss', summary: 'Get Profit & Loss report', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['ledgerIds', 'periods'] },
12
12
  'balance-sheet': { method: 'GET', path: '/v1/reports/balance-sheet', summary: 'Generate Balance Sheet report', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
13
13
  'gl-detail': { method: 'POST', path: '/v1/reports/gl-detail', summary: 'Get GL Detail report', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['accountIds', 'accountCodes'] },
14
14
  'gl-detail-export-xlsx': { method: 'POST', path: '/v1/reports/gl-detail/export/xlsx', summary: 'Export GL Detail report as Excel (one sheet per account)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: true, bodyArrayFields: ['accountIds', 'accountCodes'] },
@@ -22,7 +22,7 @@ export default class Reports extends Command {
22
22
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
23
23
  };
24
24
  static flags = {
25
- context: Flags.string({ char: 'c', description: 'Context to use' }),
25
+ context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
26
26
  output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
27
27
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
28
28
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
@@ -36,7 +36,7 @@ export default class Reports extends Command {
36
36
  'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
37
37
  filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
38
38
  query: Flags.string({ description: 'Additional query params as JSON' }),
39
- fields: Flags.string({ description: 'Comma-separated list of fields to show (e.g. --fields=id,name,status)' }),
39
+ fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
40
40
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
41
41
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
42
42
  };
@@ -94,9 +94,9 @@ export default class Reports extends Command {
94
94
  }
95
95
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
96
96
  if (!context)
97
- this.error('No context configured. Run: aderis auth login --new');
98
- if (!context.tokens)
99
- this.error('Not logged in. Run: aderis auth login');
97
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
98
+ if (!hasCredentials(context))
99
+ this.error(missingCredentialsMessage(context));
100
100
  const queryParams = {};
101
101
  if (flags.page)
102
102
  queryParams.page = flags.page;
@@ -108,6 +108,8 @@ export default class Reports extends Command {
108
108
  queryParams.search = flags.search;
109
109
  if (flags['sort-by'])
110
110
  queryParams.sortBy = normalizeSortBy(flags['sort-by']);
111
+ if (flags.fields)
112
+ queryParams.fields = flags.fields;
111
113
  if (flags.query) {
112
114
  try {
113
115
  Object.assign(queryParams, JSON.parse(flags.query));
@@ -310,11 +312,6 @@ export default class Reports extends Command {
310
312
  output = obj[arrayKeys[0]];
311
313
  }
312
314
  }
313
- // Filter fields if --fields flag is provided
314
- if (flags.fields) {
315
- const fieldList = flags.fields.split(',').map(f => f.trim());
316
- output = this.filterFields(output, fieldList);
317
- }
318
315
  // Sanitize output if requested
319
316
  if (flags.sanitize) {
320
317
  output = this.sanitizeOutput(output);
@@ -339,9 +336,9 @@ export default class Reports extends Command {
339
336
  async runImport(flags) {
340
337
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
341
338
  if (!context)
342
- this.error('No context configured. Run: aderis auth login --new');
343
- if (!context.tokens)
344
- this.error('Not logged in. Run: aderis auth login');
339
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
340
+ if (!hasCredentials(context))
341
+ this.error(missingCredentialsMessage(context));
345
342
  // Get data from file, --data, or --set flags
346
343
  let items = [];
347
344
  if (flags.file) {
@@ -551,9 +548,9 @@ export default class Reports extends Command {
551
548
  async runExport(flags) {
552
549
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
553
550
  if (!context)
554
- this.error('No context configured. Run: aderis auth login --new');
555
- if (!context.tokens)
556
- this.error('Not logged in. Run: aderis auth login');
551
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
552
+ if (!hasCredentials(context))
553
+ this.error(missingCredentialsMessage(context));
557
554
  const listEndpoint = ENDPOINTS['list'];
558
555
  if (!listEndpoint)
559
556
  this.error('Export requires a list endpoint');
@@ -569,6 +566,8 @@ export default class Reports extends Command {
569
566
  baseParams.sortBy = normalizeSortBy(flags['sort-by']);
570
567
  if (flags.include)
571
568
  baseParams.include = flags.include;
569
+ if (flags.fields)
570
+ baseParams.fields = flags.fields;
572
571
  if (flags.query) {
573
572
  try {
574
573
  Object.assign(baseParams, JSON.parse(flags.query));
@@ -623,12 +622,7 @@ export default class Reports extends Command {
623
622
  }
624
623
  }
625
624
  process.stderr.write(' done\n');
626
- // Filter fields if specified
627
625
  let output = allItems;
628
- if (flags.fields) {
629
- const fieldList = flags.fields.split(',').map(f => f.trim());
630
- output = this.filterFields(allItems, fieldList);
631
- }
632
626
  // Sanitize output if requested
633
627
  if (flags.sanitize) {
634
628
  output = this.sanitizeOutput(output);
@@ -645,9 +639,9 @@ export default class Reports extends Command {
645
639
  async runCamt053Import(flags) {
646
640
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
647
641
  if (!context)
648
- this.error('No context configured. Run: aderis auth login --new');
649
- if (!context.tokens)
650
- this.error('Not logged in. Run: aderis auth login');
642
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
643
+ if (!hasCredentials(context))
644
+ this.error(missingCredentialsMessage(context));
651
645
  if (!flags.file) {
652
646
  this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
653
647
  }
@@ -735,9 +729,9 @@ export default class Reports extends Command {
735
729
  }
736
730
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
737
731
  if (!context)
738
- this.error('No context configured. Run: aderis auth login --new');
739
- if (!context.tokens)
740
- this.error('Not logged in. Run: aderis auth login');
732
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
733
+ if (!hasCredentials(context))
734
+ this.error(missingCredentialsMessage(context));
741
735
  // Get data from file, --data, or --set flags
742
736
  let items = [];
743
737
  if (flags.file) {
@@ -957,56 +951,6 @@ export default class Reports extends Command {
957
951
  return `\$${op}`;
958
952
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
959
953
  }
960
- filterFields(data, fields) {
961
- const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
962
- const pick = (obj, keys) => {
963
- // Group keys by their first segment so sibling sub-paths
964
- // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
965
- // and don't clobber each other on merge.
966
- const directKeys = new Set();
967
- const nestedKeys = new Map();
968
- for (const key of keys) {
969
- if (key.split('.').some(isUnsafeKey))
970
- continue;
971
- if (key.includes('.')) {
972
- const [first, ...rest] = key.split('.');
973
- if (isUnsafeKey(first))
974
- continue;
975
- const list = nestedKeys.get(first);
976
- if (list) {
977
- list.push(rest.join('.'));
978
- }
979
- else {
980
- nestedKeys.set(first, [rest.join('.')]);
981
- }
982
- }
983
- else {
984
- directKeys.add(key);
985
- }
986
- }
987
- const result = {};
988
- for (const key of directKeys) {
989
- if (key in obj)
990
- result[key] = obj[key];
991
- }
992
- for (const [first, restKeys] of nestedKeys) {
993
- const val = obj[first];
994
- if (!val || typeof val !== 'object')
995
- continue;
996
- if (Array.isArray(val)) {
997
- result[first] = val.map(item => pick(item, restKeys));
998
- }
999
- else {
1000
- result[first] = pick(val, restKeys);
1001
- }
1002
- }
1003
- return result;
1004
- };
1005
- if (Array.isArray(data)) {
1006
- return data.map(item => pick(item, fields));
1007
- }
1008
- return pick(data, fields);
1009
- }
1010
954
  sanitizeOutput(data) {
1011
955
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1012
956
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -31,7 +31,6 @@ export default class SalaryTypes extends Command {
31
31
  private runCamt053Import;
32
32
  private runNestedImport;
33
33
  private parseFilterValue;
34
- private filterFields;
35
34
  private sanitizeOutput;
36
35
  private getColumns;
37
36
  private getValue;
@@ -1,6 +1,6 @@
1
1
  import { Command, Args, Flags } from '@oclif/core';
2
2
  import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
3
- import { getCurrentContext, getContext, getBaseUrls } from '../lib/config.js';
3
+ import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
4
4
  // Add 'import' action if both create and update exist
5
5
  const HAS_IMPORT = true;
6
6
  const HAS_EXPORT = true;
@@ -29,7 +29,7 @@ export default class SalaryTypes extends Command {
29
29
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
30
30
  };
31
31
  static flags = {
32
- context: Flags.string({ char: 'c', description: 'Context to use' }),
32
+ context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
33
33
  output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
34
34
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
35
35
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
@@ -43,7 +43,7 @@ export default class SalaryTypes extends Command {
43
43
  'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
44
44
  filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
45
45
  query: Flags.string({ description: 'Additional query params as JSON' }),
46
- fields: Flags.string({ description: 'Comma-separated list of fields to show (e.g. --fields=id,name,status)' }),
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
48
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
49
49
  };
@@ -101,9 +101,9 @@ export default class SalaryTypes extends Command {
101
101
  }
102
102
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
103
103
  if (!context)
104
- this.error('No context configured. Run: aderis auth login --new');
105
- if (!context.tokens)
106
- this.error('Not logged in. Run: aderis auth login');
104
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
105
+ if (!hasCredentials(context))
106
+ this.error(missingCredentialsMessage(context));
107
107
  const queryParams = {};
108
108
  if (flags.page)
109
109
  queryParams.page = flags.page;
@@ -115,6 +115,8 @@ export default class SalaryTypes extends Command {
115
115
  queryParams.search = flags.search;
116
116
  if (flags['sort-by'])
117
117
  queryParams.sortBy = normalizeSortBy(flags['sort-by']);
118
+ if (flags.fields)
119
+ queryParams.fields = flags.fields;
118
120
  if (flags.query) {
119
121
  try {
120
122
  Object.assign(queryParams, JSON.parse(flags.query));
@@ -317,11 +319,6 @@ export default class SalaryTypes extends Command {
317
319
  output = obj[arrayKeys[0]];
318
320
  }
319
321
  }
320
- // Filter fields if --fields flag is provided
321
- if (flags.fields) {
322
- const fieldList = flags.fields.split(',').map(f => f.trim());
323
- output = this.filterFields(output, fieldList);
324
- }
325
322
  // Sanitize output if requested
326
323
  if (flags.sanitize) {
327
324
  output = this.sanitizeOutput(output);
@@ -346,9 +343,9 @@ export default class SalaryTypes extends Command {
346
343
  async runImport(flags) {
347
344
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
348
345
  if (!context)
349
- this.error('No context configured. Run: aderis auth login --new');
350
- if (!context.tokens)
351
- this.error('Not logged in. Run: aderis auth login');
346
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
347
+ if (!hasCredentials(context))
348
+ this.error(missingCredentialsMessage(context));
352
349
  // Get data from file, --data, or --set flags
353
350
  let items = [];
354
351
  if (flags.file) {
@@ -558,9 +555,9 @@ export default class SalaryTypes extends Command {
558
555
  async runExport(flags) {
559
556
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
560
557
  if (!context)
561
- this.error('No context configured. Run: aderis auth login --new');
562
- if (!context.tokens)
563
- this.error('Not logged in. Run: aderis auth login');
558
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
559
+ if (!hasCredentials(context))
560
+ this.error(missingCredentialsMessage(context));
564
561
  const listEndpoint = ENDPOINTS['list'];
565
562
  if (!listEndpoint)
566
563
  this.error('Export requires a list endpoint');
@@ -576,6 +573,8 @@ export default class SalaryTypes extends Command {
576
573
  baseParams.sortBy = normalizeSortBy(flags['sort-by']);
577
574
  if (flags.include)
578
575
  baseParams.include = flags.include;
576
+ if (flags.fields)
577
+ baseParams.fields = flags.fields;
579
578
  if (flags.query) {
580
579
  try {
581
580
  Object.assign(baseParams, JSON.parse(flags.query));
@@ -630,12 +629,7 @@ export default class SalaryTypes extends Command {
630
629
  }
631
630
  }
632
631
  process.stderr.write(' done\n');
633
- // Filter fields if specified
634
632
  let output = allItems;
635
- if (flags.fields) {
636
- const fieldList = flags.fields.split(',').map(f => f.trim());
637
- output = this.filterFields(allItems, fieldList);
638
- }
639
633
  // Sanitize output if requested
640
634
  if (flags.sanitize) {
641
635
  output = this.sanitizeOutput(output);
@@ -652,9 +646,9 @@ export default class SalaryTypes extends Command {
652
646
  async runCamt053Import(flags) {
653
647
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
654
648
  if (!context)
655
- this.error('No context configured. Run: aderis auth login --new');
656
- if (!context.tokens)
657
- this.error('Not logged in. Run: aderis auth login');
649
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
650
+ if (!hasCredentials(context))
651
+ this.error(missingCredentialsMessage(context));
658
652
  if (!flags.file) {
659
653
  this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
660
654
  }
@@ -742,9 +736,9 @@ export default class SalaryTypes extends Command {
742
736
  }
743
737
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
744
738
  if (!context)
745
- this.error('No context configured. Run: aderis auth login --new');
746
- if (!context.tokens)
747
- this.error('Not logged in. Run: aderis auth login');
739
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
740
+ if (!hasCredentials(context))
741
+ this.error(missingCredentialsMessage(context));
748
742
  // Get data from file, --data, or --set flags
749
743
  let items = [];
750
744
  if (flags.file) {
@@ -964,56 +958,6 @@ export default class SalaryTypes extends Command {
964
958
  return `\$${op}`;
965
959
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
966
960
  }
967
- filterFields(data, fields) {
968
- const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
969
- const pick = (obj, keys) => {
970
- // Group keys by their first segment so sibling sub-paths
971
- // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
972
- // and don't clobber each other on merge.
973
- const directKeys = new Set();
974
- const nestedKeys = new Map();
975
- for (const key of keys) {
976
- if (key.split('.').some(isUnsafeKey))
977
- continue;
978
- if (key.includes('.')) {
979
- const [first, ...rest] = key.split('.');
980
- if (isUnsafeKey(first))
981
- continue;
982
- const list = nestedKeys.get(first);
983
- if (list) {
984
- list.push(rest.join('.'));
985
- }
986
- else {
987
- nestedKeys.set(first, [rest.join('.')]);
988
- }
989
- }
990
- else {
991
- directKeys.add(key);
992
- }
993
- }
994
- const result = {};
995
- for (const key of directKeys) {
996
- if (key in obj)
997
- result[key] = obj[key];
998
- }
999
- for (const [first, restKeys] of nestedKeys) {
1000
- const val = obj[first];
1001
- if (!val || typeof val !== 'object')
1002
- continue;
1003
- if (Array.isArray(val)) {
1004
- result[first] = val.map(item => pick(item, restKeys));
1005
- }
1006
- else {
1007
- result[first] = pick(val, restKeys);
1008
- }
1009
- }
1010
- return result;
1011
- };
1012
- if (Array.isArray(data)) {
1013
- return data.map(item => pick(item, fields));
1014
- }
1015
- return pick(data, fields);
1016
- }
1017
961
  sanitizeOutput(data) {
1018
962
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1019
963
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;