@aderis-erp/cli 1.0.3 → 1.0.4

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 (169) hide show
  1. package/README.md +6 -0
  2. package/dist/commands/access-groups 3.d.ts +40 -0
  3. package/dist/commands/access-groups 3.js +1072 -0
  4. package/dist/commands/access-groups.d.ts +1 -0
  5. package/dist/commands/access-groups.js +79 -23
  6. package/dist/commands/account-transactions.d.ts +1 -0
  7. package/dist/commands/account-transactions.js +91 -35
  8. package/dist/commands/accounts 2.d.ts +41 -0
  9. package/dist/commands/accounts 2.js +1078 -0
  10. package/dist/commands/accounts.d.ts +1 -0
  11. package/dist/commands/accounts.js +84 -28
  12. package/dist/commands/agent-runs.d.ts +1 -0
  13. package/dist/commands/agent-runs.js +80 -22
  14. package/dist/commands/async-endpoints-internal 3.d.ts +40 -0
  15. package/dist/commands/async-endpoints-internal 3.js +1062 -0
  16. package/dist/commands/async-endpoints-internal.d.ts +1 -0
  17. package/dist/commands/async-endpoints-internal.js +77 -21
  18. package/dist/commands/audit-log 3.d.ts +40 -0
  19. package/dist/commands/audit-log 3.js +1066 -0
  20. package/dist/commands/audit-log.d.ts +1 -0
  21. package/dist/commands/audit-log.js +77 -21
  22. package/dist/commands/bank-accounts.d.ts +1 -0
  23. package/dist/commands/bank-accounts.js +91 -35
  24. package/dist/commands/bank-integrations.d.ts +1 -0
  25. package/dist/commands/bank-integrations.js +92 -36
  26. package/dist/commands/business-partners 3.d.ts +41 -0
  27. package/dist/commands/business-partners 3.js +1078 -0
  28. package/dist/commands/business-partners---contacts 3.d.ts +42 -0
  29. package/dist/commands/business-partners---contacts 3.js +1072 -0
  30. package/dist/commands/business-partners---contacts.d.ts +1 -0
  31. package/dist/commands/business-partners---contacts.js +84 -28
  32. package/dist/commands/business-partners.d.ts +1 -0
  33. package/dist/commands/business-partners.js +84 -28
  34. package/dist/commands/credit-card-accounts 3.d.ts +42 -0
  35. package/dist/commands/credit-card-accounts 3.js +1084 -0
  36. package/dist/commands/credit-card-accounts.d.ts +1 -0
  37. package/dist/commands/credit-card-accounts.js +89 -33
  38. package/dist/commands/documents.d.ts +1 -0
  39. package/dist/commands/documents.js +80 -24
  40. package/dist/commands/dunning-configs 3.d.ts +40 -0
  41. package/dist/commands/dunning-configs 3.js +1072 -0
  42. package/dist/commands/dunning-configs.d.ts +1 -0
  43. package/dist/commands/dunning-configs.js +79 -23
  44. package/dist/commands/dunning-reminders.d.ts +1 -0
  45. package/dist/commands/dunning-reminders.js +82 -26
  46. package/dist/commands/dunning-runs 3.d.ts +40 -0
  47. package/dist/commands/dunning-runs 3.js +1061 -0
  48. package/dist/commands/dunning-runs.d.ts +1 -0
  49. package/dist/commands/dunning-runs.js +75 -19
  50. package/dist/commands/employees.d.ts +1 -0
  51. package/dist/commands/employees.js +106 -50
  52. package/dist/commands/event-processor-mappings 3.d.ts +40 -0
  53. package/dist/commands/event-processor-mappings 3.js +1071 -0
  54. package/dist/commands/event-processor-mappings.d.ts +1 -0
  55. package/dist/commands/event-processor-mappings.js +79 -23
  56. package/dist/commands/files-internal 3.d.ts +40 -0
  57. package/dist/commands/files-internal 3.js +1073 -0
  58. package/dist/commands/files-internal.d.ts +1 -0
  59. package/dist/commands/files-internal.js +83 -27
  60. package/dist/commands/health 3.d.ts +40 -0
  61. package/dist/commands/health 3.js +1067 -0
  62. package/dist/commands/health.d.ts +1 -0
  63. package/dist/commands/health.js +79 -23
  64. package/dist/commands/inbox 3.d.ts +40 -0
  65. package/dist/commands/inbox 3.js +1065 -0
  66. package/dist/commands/inbox.d.ts +1 -0
  67. package/dist/commands/inbox.js +79 -23
  68. package/dist/commands/integrations 3.d.ts +41 -0
  69. package/dist/commands/integrations 3.js +1067 -0
  70. package/dist/commands/integrations.d.ts +1 -0
  71. package/dist/commands/integrations.js +77 -21
  72. package/dist/commands/item-categories 3.d.ts +40 -0
  73. package/dist/commands/item-categories 3.js +1072 -0
  74. package/dist/commands/item-categories.d.ts +1 -0
  75. package/dist/commands/item-categories.js +79 -23
  76. package/dist/commands/items 3.d.ts +40 -0
  77. package/dist/commands/items 3.js +1072 -0
  78. package/dist/commands/items.d.ts +1 -0
  79. package/dist/commands/items.js +79 -23
  80. package/dist/commands/journal-lines.d.ts +1 -0
  81. package/dist/commands/journal-lines.js +75 -19
  82. package/dist/commands/journals 3.d.ts +42 -0
  83. package/dist/commands/journals 3.js +1088 -0
  84. package/dist/commands/journals.d.ts +1 -0
  85. package/dist/commands/journals.js +93 -37
  86. package/dist/commands/ledgers.d.ts +1 -0
  87. package/dist/commands/ledgers.js +90 -34
  88. package/dist/commands/office-locations 3.d.ts +40 -0
  89. package/dist/commands/office-locations 3.js +1072 -0
  90. package/dist/commands/office-locations.d.ts +1 -0
  91. package/dist/commands/office-locations.js +79 -23
  92. package/dist/commands/payment-method-ledger-mappings 3.d.ts +41 -0
  93. package/dist/commands/payment-method-ledger-mappings 3.js +1066 -0
  94. package/dist/commands/payment-method-ledger-mappings.d.ts +1 -0
  95. package/dist/commands/payment-method-ledger-mappings.js +79 -23
  96. package/dist/commands/payment-methods 3.d.ts +40 -0
  97. package/dist/commands/payment-methods 3.js +1072 -0
  98. package/dist/commands/payment-methods.d.ts +1 -0
  99. package/dist/commands/payment-methods.js +79 -23
  100. package/dist/commands/payment-runs 3.d.ts +42 -0
  101. package/dist/commands/payment-runs 3.js +1087 -0
  102. package/dist/commands/payment-runs.d.ts +1 -0
  103. package/dist/commands/payment-runs.js +92 -36
  104. package/dist/commands/payment-terms 3.d.ts +40 -0
  105. package/dist/commands/payment-terms 3.js +1073 -0
  106. package/dist/commands/payment-terms.d.ts +1 -0
  107. package/dist/commands/payment-terms.js +80 -24
  108. package/dist/commands/price-lists 3.d.ts +41 -0
  109. package/dist/commands/price-lists 3.js +1078 -0
  110. package/dist/commands/price-lists.d.ts +1 -0
  111. package/dist/commands/price-lists.js +84 -28
  112. package/dist/commands/processor-mappings 2.d.ts +40 -0
  113. package/dist/commands/processor-mappings 2.js +1070 -0
  114. package/dist/commands/processor-mappings.d.ts +1 -0
  115. package/dist/commands/processor-mappings.js +78 -22
  116. package/dist/commands/processors.d.ts +1 -0
  117. package/dist/commands/processors.js +83 -27
  118. package/dist/commands/reconciliation 3.d.ts +40 -0
  119. package/dist/commands/reconciliation 3.js +1069 -0
  120. package/dist/commands/reconciliation.d.ts +1 -0
  121. package/dist/commands/reconciliation.js +80 -24
  122. package/dist/commands/reports.d.ts +1 -0
  123. package/dist/commands/reports.js +80 -21
  124. package/dist/commands/salary-types 3.d.ts +40 -0
  125. package/dist/commands/salary-types 3.js +1072 -0
  126. package/dist/commands/salary-types.d.ts +1 -0
  127. package/dist/commands/salary-types.js +79 -23
  128. package/dist/commands/sales-invoices 3.d.ts +43 -0
  129. package/dist/commands/sales-invoices 3.js +1100 -0
  130. package/dist/commands/sales-invoices.d.ts +1 -0
  131. package/dist/commands/sales-invoices.js +104 -48
  132. package/dist/commands/sales-orders 3.d.ts +43 -0
  133. package/dist/commands/sales-orders 3.js +1098 -0
  134. package/dist/commands/sales-orders.d.ts +1 -0
  135. package/dist/commands/sales-orders.js +102 -46
  136. package/dist/commands/tags 3.d.ts +40 -0
  137. package/dist/commands/tags 3.js +1072 -0
  138. package/dist/commands/tags.d.ts +1 -0
  139. package/dist/commands/tags.js +79 -23
  140. package/dist/commands/tax-codes 3.d.ts +40 -0
  141. package/dist/commands/tax-codes 3.js +1075 -0
  142. package/dist/commands/tax-codes.d.ts +1 -0
  143. package/dist/commands/tax-codes.js +82 -26
  144. package/dist/commands/tax-matrix.d.ts +1 -0
  145. package/dist/commands/tax-matrix.js +79 -23
  146. package/dist/commands/tenants.d.ts +1 -0
  147. package/dist/commands/tenants.js +79 -23
  148. package/dist/commands/text-templates 3.d.ts +40 -0
  149. package/dist/commands/text-templates 3.js +1072 -0
  150. package/dist/commands/text-templates.d.ts +1 -0
  151. package/dist/commands/text-templates.js +79 -23
  152. package/dist/commands/tools.d.ts +1 -0
  153. package/dist/commands/tools.js +76 -20
  154. package/dist/commands/vendor-invoices.d.ts +1 -0
  155. package/dist/commands/vendor-invoices.js +108 -52
  156. package/dist/commands/webhooks 3.d.ts +40 -0
  157. package/dist/commands/webhooks 3.js +1074 -0
  158. package/dist/commands/webhooks.d.ts +1 -0
  159. package/dist/commands/webhooks.js +81 -25
  160. package/dist/commands/workflows.d.ts +1 -0
  161. package/dist/commands/workflows.js +77 -21
  162. package/dist/lib/api-client.d.ts +5 -1
  163. package/dist/lib/api-client.js +19 -5
  164. package/dist/lib/auth.js +17 -0
  165. package/dist/lib/config.js +1 -1
  166. package/oclif.manifest.json +6099 -711
  167. package/package.json +1 -1
  168. package/dist/index 2.js +0 -1
  169. package/dist/index.d 2.ts +0 -1
@@ -30,6 +30,7 @@ export default class DunningRuns extends Command {
30
30
  private runExport;
31
31
  private runCamt053Import;
32
32
  private runNestedImport;
33
+ private parseFilterValue;
33
34
  private filterFields;
34
35
  private sanitizeOutput;
35
36
  private getColumns;
@@ -8,7 +8,7 @@ const HAS_CAMT053 = false;
8
8
  const NESTED_IMPORTS = [];
9
9
  const SUBCOMMANDS = [...["trigger"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
- 'trigger': { method: 'POST', path: '/v1/dunning-runs/trigger', summary: 'Manually trigger a dunning run', pathParams: [], hasBody: true, isMultipart: false },
11
+ 'trigger': { method: 'POST', path: '/v1/dunning-runs/trigger', summary: 'Manually trigger a dunning run', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
12
12
  };
13
13
  export default class DunningRuns extends Command {
14
14
  static description = 'Manage dunning runs';
@@ -119,15 +119,7 @@ export default class DunningRuns extends Command {
119
119
  if (eqIndex === -1)
120
120
  this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
121
121
  const key = f.slice(0, eqIndex);
122
- let value = f.slice(eqIndex + 1);
123
- // Check for operator prefix (with or without $)
124
- const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
125
- if (opMatch) {
126
- value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
127
- }
128
- else {
129
- value = `\$eq:${value}`;
130
- }
122
+ const value = this.parseFilterValue(f.slice(eqIndex + 1));
131
123
  const filterKey = `filter.${key}`;
132
124
  if (filterKey in queryParams) {
133
125
  // Multiple filters on same field: collect as array
@@ -160,7 +152,9 @@ export default class DunningRuns extends Command {
160
152
  data = form;
161
153
  }
162
154
  else if (endpoint.hasBody) {
163
- if (flags.file) {
155
+ // For binary-response endpoints, --file is the output path, not a JSON
156
+ // input file — skip the read-as-body branch.
157
+ if (flags.file && !endpoint.isBinaryResponse) {
164
158
  const fsModule = await import('node:fs');
165
159
  try {
166
160
  data = JSON.parse(fsModule.readFileSync(flags.file, 'utf-8'));
@@ -182,6 +176,7 @@ export default class DunningRuns extends Command {
182
176
  if (!data)
183
177
  data = {};
184
178
  const body = data;
179
+ const arrayFields = new Set(endpoint.bodyArrayFields);
185
180
  for (const pair of flags.set) {
186
181
  const eqIndex = pair.indexOf('=');
187
182
  if (eqIndex === -1)
@@ -193,6 +188,19 @@ export default class DunningRuns extends Command {
193
188
  value = JSON.parse(value);
194
189
  }
195
190
  catch { /* keep as string */ }
191
+ // For top-level keys typed as array in the API schema, split bare
192
+ // comma-separated input (e.g. "1171,1170" -> ["1171","1170"]) and
193
+ // wrap a lone scalar in a single-element array. Skips when --set
194
+ // already provided a JSON array.
195
+ const isArrayField = !key.includes('.') && arrayFields.has(key);
196
+ if (isArrayField && !Array.isArray(value)) {
197
+ if (typeof value === 'string') {
198
+ value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
199
+ }
200
+ else if (value !== undefined && value !== null) {
201
+ value = [value];
202
+ }
203
+ }
196
204
  // Support nested keys like "address.city"
197
205
  const keys = key.split('.');
198
206
  if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
@@ -243,6 +251,26 @@ export default class DunningRuns extends Command {
243
251
  process.stderr.write('\n');
244
252
  }
245
253
  try {
254
+ // Binary response: download to file
255
+ if (endpoint.isBinaryResponse) {
256
+ if (!flags.file) {
257
+ this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx).`);
258
+ }
259
+ const binResponse = await apiRequest(endpoint.method, apiPath, {
260
+ params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
261
+ data,
262
+ contextName: flags.context,
263
+ binary: true,
264
+ });
265
+ const fsModule = await import('node:fs');
266
+ const binData = binResponse.data;
267
+ const buffer = Buffer.isBuffer(binData)
268
+ ? binData
269
+ : Buffer.from(binData);
270
+ fsModule.writeFileSync(flags.file, buffer);
271
+ this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
272
+ return;
273
+ }
246
274
  const response = await apiRequest(endpoint.method, apiPath, {
247
275
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
248
276
  data,
@@ -333,6 +361,7 @@ export default class DunningRuns extends Command {
333
361
  }
334
362
  else if (flags.set && flags.set.length > 0) {
335
363
  const item = {};
364
+ const arrayFields = new Set(ENDPOINTS['create']?.bodyArrayFields ?? []);
336
365
  for (const pair of flags.set) {
337
366
  const eqIndex = pair.indexOf('=');
338
367
  if (eqIndex === -1)
@@ -343,6 +372,15 @@ export default class DunningRuns extends Command {
343
372
  value = JSON.parse(value);
344
373
  }
345
374
  catch { /* keep as string */ }
375
+ const isArrayField = !key.includes('.') && arrayFields.has(key);
376
+ if (isArrayField && !Array.isArray(value)) {
377
+ if (typeof value === 'string') {
378
+ value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
379
+ }
380
+ else if (value !== undefined && value !== null) {
381
+ value = [value];
382
+ }
383
+ }
346
384
  const keys = key.split('.');
347
385
  if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
348
386
  this.error(`Invalid --set key: ${key}`);
@@ -492,14 +530,7 @@ export default class DunningRuns extends Command {
492
530
  if (eqIndex === -1)
493
531
  this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
494
532
  const key = f.slice(0, eqIndex);
495
- let value = f.slice(eqIndex + 1);
496
- const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
497
- if (opMatch) {
498
- value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
499
- }
500
- else {
501
- value = `\$eq:${value}`;
502
- }
533
+ const value = this.parseFilterValue(f.slice(eqIndex + 1));
503
534
  const filterKey = `filter.${key}`;
504
535
  if (filterKey in baseParams) {
505
536
  const existing = baseParams[filterKey];
@@ -676,6 +707,7 @@ export default class DunningRuns extends Command {
676
707
  }
677
708
  else if (flags.set && flags.set.length > 0) {
678
709
  const item = {};
710
+ const arrayFields = new Set(ENDPOINTS[`create-${suffix}`]?.bodyArrayFields ?? []);
679
711
  for (const pair of flags.set) {
680
712
  const eqIndex = pair.indexOf('=');
681
713
  if (eqIndex === -1)
@@ -686,6 +718,15 @@ export default class DunningRuns extends Command {
686
718
  value = JSON.parse(value);
687
719
  }
688
720
  catch { /* keep as string */ }
721
+ const isArrayField = !key.includes('.') && arrayFields.has(key);
722
+ if (isArrayField && !Array.isArray(value)) {
723
+ if (typeof value === 'string') {
724
+ value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
725
+ }
726
+ else if (value !== undefined && value !== null) {
727
+ value = [value];
728
+ }
729
+ }
689
730
  const keys = key.split('.');
690
731
  if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
691
732
  this.error(`Invalid --set key: ${key}`);
@@ -805,6 +846,21 @@ export default class DunningRuns extends Command {
805
846
  this.log('');
806
847
  this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
807
848
  }
849
+ parseFilterValue(value) {
850
+ // Recognises the nestjs-paginate operator set (with or without the leading $)
851
+ // and recursively unwraps chained operators like 'not:null' -> '$not:$null'
852
+ // or 'not:like:John' -> '$not:$like:John'. Falls back to '$eq:<value>' when
853
+ // no operator prefix is present.
854
+ const opRe = /^\$?(and|or|not|eq|gt|gte|in|null|lt|lte|btw|ne|ilike|like|sw|contains)(?::|$)/;
855
+ const m = value.match(opRe);
856
+ if (!m)
857
+ return `\$eq:${value}`;
858
+ const op = m[1];
859
+ const rest = value.slice(m[0].length);
860
+ if (rest === '')
861
+ return `\$${op}`;
862
+ return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
863
+ }
808
864
  filterFields(data, fields) {
809
865
  const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
810
866
  const pick = (obj, keys) => {
@@ -34,6 +34,7 @@ export default class Employees extends Command {
34
34
  private runExport;
35
35
  private runCamt053Import;
36
36
  private runNestedImport;
37
+ private parseFilterValue;
37
38
  private filterFields;
38
39
  private sanitizeOutput;
39
40
  private getColumns;
@@ -8,38 +8,38 @@ const HAS_CAMT053 = false;
8
8
  const NESTED_IMPORTS = ["salaries", "payments", "notes", "files", "salary-payment-files"];
9
9
  const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "get-salaries", "create-salaries", "get-salaries-salaryId", "update-salaries", "delete-salaries", "get-payments", "create-payments", "get-payments-paymentId", "update-payments", "delete-payments", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download", "list-salary-payment-files", "create-salary-payment-files", "get-salary-payment-files", "update-salary-payment-files", "delete-salary-payment-files", "download-salary-payment-files"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
- 'list': { method: 'GET', path: '/v1/employees', summary: 'Get all employees with pagination', pathParams: [], hasBody: false, isMultipart: false },
12
- 'create': { method: 'POST', path: '/v1/employees', summary: 'Create a new employee', pathParams: [], hasBody: true, isMultipart: false },
13
- 'get': { method: 'GET', path: '/v1/employees/{id}', summary: 'Get an employee by ID', pathParams: ['id'], hasBody: false, isMultipart: false },
14
- 'update': { method: 'PATCH', path: '/v1/employees/{id}', summary: 'Update an employee', pathParams: ['id'], hasBody: true, isMultipart: false },
15
- 'delete': { method: 'DELETE', path: '/v1/employees/{id}', summary: 'Delete an employee', pathParams: ['id'], hasBody: false, isMultipart: false },
16
- 'get-salaries': { method: 'GET', path: '/v1/employees/{id}/salaries', summary: 'Get all salaries for an employee', pathParams: ['id'], hasBody: false, isMultipart: false },
17
- 'create-salaries': { method: 'POST', path: '/v1/employees/{id}/salaries', summary: 'Add a salary configuration to an employee', pathParams: ['id'], hasBody: true, isMultipart: false },
18
- 'get-salaries-salaryId': { method: 'GET', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Get a specific salary by ID', pathParams: ['id', 'salaryId'], hasBody: false, isMultipart: false },
19
- 'update-salaries': { method: 'PATCH', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Update a salary configuration', pathParams: ['id', 'salaryId'], hasBody: true, isMultipart: false },
20
- 'delete-salaries': { method: 'DELETE', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Delete a salary configuration', pathParams: ['id', 'salaryId'], hasBody: false, isMultipart: false },
21
- 'get-payments': { method: 'GET', path: '/v1/employees/{id}/payments', summary: 'Get all payments for an employee', pathParams: ['id'], hasBody: false, isMultipart: false },
22
- 'create-payments': { method: 'POST', path: '/v1/employees/{id}/payments', summary: 'Add a payment to an employee', pathParams: ['id'], hasBody: true, isMultipart: false },
23
- 'get-payments-paymentId': { method: 'GET', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Get a specific payment by ID', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false },
24
- 'update-payments': { method: 'PATCH', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Update a payment', pathParams: ['id', 'paymentId'], hasBody: true, isMultipart: false },
25
- 'delete-payments': { method: 'DELETE', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Delete a payment', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false },
26
- 'list-notes': { method: 'GET', path: '/v1/employees/{employeeId}/notes', summary: 'Get all notes for an employee', pathParams: ['employeeId'], hasBody: false, isMultipart: false },
27
- 'create-notes': { method: 'POST', path: '/v1/employees/{employeeId}/notes', summary: 'Create a note for an employee', pathParams: ['employeeId'], hasBody: true, isMultipart: false },
28
- 'get-notes': { method: 'GET', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Get a specific note for an employee', pathParams: ['employeeId', 'noteId'], hasBody: false, isMultipart: false },
29
- 'update-notes': { method: 'PATCH', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Update a note for an employee', pathParams: ['employeeId', 'noteId'], hasBody: true, isMultipart: false },
30
- 'delete-notes': { method: 'DELETE', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Delete a note from an employee', pathParams: ['employeeId', 'noteId'], hasBody: false, isMultipart: false },
31
- 'list-files': { method: 'GET', path: '/v1/employees/{parentId}/files', summary: 'List file attachments for a employee', pathParams: ['parentId'], hasBody: false, isMultipart: false },
32
- 'create-files': { method: 'POST', path: '/v1/employees/{parentId}/files', summary: 'Upload a file attachment to a employee', pathParams: ['parentId'], hasBody: false, isMultipart: true },
33
- 'get-files': { method: 'GET', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Get a file attachment for a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false },
34
- 'update-files': { method: 'PATCH', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a employee', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false },
35
- 'delete-files': { method: 'DELETE', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false },
36
- 'files-download': { method: 'GET', path: '/v1/employees/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false },
37
- 'list-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files', summary: 'List file attachments for a salary payment', pathParams: ['parentId'], hasBody: false, isMultipart: false },
38
- 'create-salary-payment-files': { method: 'POST', path: '/v1/salary-payments/{parentId}/files', summary: 'Upload a file attachment to a salary payment', pathParams: ['parentId'], hasBody: false, isMultipart: true },
39
- 'get-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Get a file attachment for a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false },
40
- 'update-salary-payment-files': { method: 'PATCH', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a salary payment', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false },
41
- 'delete-salary-payment-files': { method: 'DELETE', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false },
42
- 'download-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false },
11
+ 'list': { method: 'GET', path: '/v1/employees', summary: 'Get all employees with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
12
+ 'create': { method: 'POST', path: '/v1/employees', summary: 'Create a new employee', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['children', 'salaries', 'files', 'notes'] },
13
+ 'get': { method: 'GET', path: '/v1/employees/{id}', summary: 'Get an employee by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
14
+ 'update': { method: 'PATCH', path: '/v1/employees/{id}', summary: 'Update an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['children', 'salaries', 'files', 'notes'] },
15
+ 'delete': { method: 'DELETE', path: '/v1/employees/{id}', summary: 'Delete an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
16
+ 'get-salaries': { method: 'GET', path: '/v1/employees/{id}/salaries', summary: 'Get all salaries for an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
17
+ 'create-salaries': { method: 'POST', path: '/v1/employees/{id}/salaries', summary: 'Add a salary configuration to an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
18
+ 'get-salaries-salaryId': { method: 'GET', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Get a specific salary by ID', pathParams: ['id', 'salaryId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
19
+ 'update-salaries': { method: 'PATCH', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Update a salary configuration', pathParams: ['id', 'salaryId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
20
+ 'delete-salaries': { method: 'DELETE', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Delete a salary configuration', pathParams: ['id', 'salaryId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
21
+ 'get-payments': { method: 'GET', path: '/v1/employees/{id}/payments', summary: 'Get all payments for an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
22
+ 'create-payments': { method: 'POST', path: '/v1/employees/{id}/payments', summary: 'Add a payment to an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['salaryComponents', 'files'] },
23
+ 'get-payments-paymentId': { method: 'GET', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Get a specific payment by ID', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
24
+ 'update-payments': { method: 'PATCH', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Update a payment', pathParams: ['id', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['salaryComponents', 'files'] },
25
+ 'delete-payments': { method: 'DELETE', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Delete a payment', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
26
+ 'list-notes': { method: 'GET', path: '/v1/employees/{employeeId}/notes', summary: 'Get all notes for an employee', pathParams: ['employeeId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
27
+ 'create-notes': { method: 'POST', path: '/v1/employees/{employeeId}/notes', summary: 'Create a note for an employee', pathParams: ['employeeId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
28
+ 'get-notes': { method: 'GET', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Get a specific note for an employee', pathParams: ['employeeId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
29
+ 'update-notes': { method: 'PATCH', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Update a note for an employee', pathParams: ['employeeId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
30
+ 'delete-notes': { method: 'DELETE', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Delete a note from an employee', pathParams: ['employeeId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
31
+ 'list-files': { method: 'GET', path: '/v1/employees/{parentId}/files', summary: 'List file attachments for a employee', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
32
+ 'create-files': { method: 'POST', path: '/v1/employees/{parentId}/files', summary: 'Upload a file attachment to a employee', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, bodyArrayFields: [] },
33
+ 'get-files': { method: 'GET', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Get a file attachment for a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
34
+ 'update-files': { method: 'PATCH', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a employee', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
35
+ 'delete-files': { method: 'DELETE', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
36
+ 'files-download': { method: 'GET', path: '/v1/employees/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
37
+ 'list-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files', summary: 'List file attachments for a salary payment', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
38
+ 'create-salary-payment-files': { method: 'POST', path: '/v1/salary-payments/{parentId}/files', summary: 'Upload a file attachment to a salary payment', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, bodyArrayFields: [] },
39
+ 'get-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Get a file attachment for a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
40
+ 'update-salary-payment-files': { method: 'PATCH', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a salary payment', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
41
+ 'delete-salary-payment-files': { method: 'DELETE', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
42
+ 'download-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
43
43
  };
44
44
  export default class Employees extends Command {
45
45
  static description = 'Manage employees';
@@ -161,15 +161,7 @@ export default class Employees extends Command {
161
161
  if (eqIndex === -1)
162
162
  this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
163
163
  const key = f.slice(0, eqIndex);
164
- let value = f.slice(eqIndex + 1);
165
- // Check for operator prefix (with or without $)
166
- const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
167
- if (opMatch) {
168
- value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
169
- }
170
- else {
171
- value = `\$eq:${value}`;
172
- }
164
+ const value = this.parseFilterValue(f.slice(eqIndex + 1));
173
165
  const filterKey = `filter.${key}`;
174
166
  if (filterKey in queryParams) {
175
167
  // Multiple filters on same field: collect as array
@@ -202,7 +194,9 @@ export default class Employees extends Command {
202
194
  data = form;
203
195
  }
204
196
  else if (endpoint.hasBody) {
205
- if (flags.file) {
197
+ // For binary-response endpoints, --file is the output path, not a JSON
198
+ // input file — skip the read-as-body branch.
199
+ if (flags.file && !endpoint.isBinaryResponse) {
206
200
  const fsModule = await import('node:fs');
207
201
  try {
208
202
  data = JSON.parse(fsModule.readFileSync(flags.file, 'utf-8'));
@@ -224,6 +218,7 @@ export default class Employees extends Command {
224
218
  if (!data)
225
219
  data = {};
226
220
  const body = data;
221
+ const arrayFields = new Set(endpoint.bodyArrayFields);
227
222
  for (const pair of flags.set) {
228
223
  const eqIndex = pair.indexOf('=');
229
224
  if (eqIndex === -1)
@@ -235,6 +230,19 @@ export default class Employees extends Command {
235
230
  value = JSON.parse(value);
236
231
  }
237
232
  catch { /* keep as string */ }
233
+ // For top-level keys typed as array in the API schema, split bare
234
+ // comma-separated input (e.g. "1171,1170" -> ["1171","1170"]) and
235
+ // wrap a lone scalar in a single-element array. Skips when --set
236
+ // already provided a JSON array.
237
+ const isArrayField = !key.includes('.') && arrayFields.has(key);
238
+ if (isArrayField && !Array.isArray(value)) {
239
+ if (typeof value === 'string') {
240
+ value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
241
+ }
242
+ else if (value !== undefined && value !== null) {
243
+ value = [value];
244
+ }
245
+ }
238
246
  // Support nested keys like "address.city"
239
247
  const keys = key.split('.');
240
248
  if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
@@ -285,6 +293,26 @@ export default class Employees extends Command {
285
293
  process.stderr.write('\n');
286
294
  }
287
295
  try {
296
+ // Binary response: download to file
297
+ if (endpoint.isBinaryResponse) {
298
+ if (!flags.file) {
299
+ this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx).`);
300
+ }
301
+ const binResponse = await apiRequest(endpoint.method, apiPath, {
302
+ params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
303
+ data,
304
+ contextName: flags.context,
305
+ binary: true,
306
+ });
307
+ const fsModule = await import('node:fs');
308
+ const binData = binResponse.data;
309
+ const buffer = Buffer.isBuffer(binData)
310
+ ? binData
311
+ : Buffer.from(binData);
312
+ fsModule.writeFileSync(flags.file, buffer);
313
+ this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
314
+ return;
315
+ }
288
316
  const response = await apiRequest(endpoint.method, apiPath, {
289
317
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
290
318
  data,
@@ -375,6 +403,7 @@ export default class Employees extends Command {
375
403
  }
376
404
  else if (flags.set && flags.set.length > 0) {
377
405
  const item = {};
406
+ const arrayFields = new Set(ENDPOINTS['create']?.bodyArrayFields ?? []);
378
407
  for (const pair of flags.set) {
379
408
  const eqIndex = pair.indexOf('=');
380
409
  if (eqIndex === -1)
@@ -385,6 +414,15 @@ export default class Employees extends Command {
385
414
  value = JSON.parse(value);
386
415
  }
387
416
  catch { /* keep as string */ }
417
+ const isArrayField = !key.includes('.') && arrayFields.has(key);
418
+ if (isArrayField && !Array.isArray(value)) {
419
+ if (typeof value === 'string') {
420
+ value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
421
+ }
422
+ else if (value !== undefined && value !== null) {
423
+ value = [value];
424
+ }
425
+ }
388
426
  const keys = key.split('.');
389
427
  if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
390
428
  this.error(`Invalid --set key: ${key}`);
@@ -534,14 +572,7 @@ export default class Employees extends Command {
534
572
  if (eqIndex === -1)
535
573
  this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
536
574
  const key = f.slice(0, eqIndex);
537
- let value = f.slice(eqIndex + 1);
538
- const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
539
- if (opMatch) {
540
- value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
541
- }
542
- else {
543
- value = `\$eq:${value}`;
544
- }
575
+ const value = this.parseFilterValue(f.slice(eqIndex + 1));
545
576
  const filterKey = `filter.${key}`;
546
577
  if (filterKey in baseParams) {
547
578
  const existing = baseParams[filterKey];
@@ -718,6 +749,7 @@ export default class Employees extends Command {
718
749
  }
719
750
  else if (flags.set && flags.set.length > 0) {
720
751
  const item = {};
752
+ const arrayFields = new Set(ENDPOINTS[`create-${suffix}`]?.bodyArrayFields ?? []);
721
753
  for (const pair of flags.set) {
722
754
  const eqIndex = pair.indexOf('=');
723
755
  if (eqIndex === -1)
@@ -728,6 +760,15 @@ export default class Employees extends Command {
728
760
  value = JSON.parse(value);
729
761
  }
730
762
  catch { /* keep as string */ }
763
+ const isArrayField = !key.includes('.') && arrayFields.has(key);
764
+ if (isArrayField && !Array.isArray(value)) {
765
+ if (typeof value === 'string') {
766
+ value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
767
+ }
768
+ else if (value !== undefined && value !== null) {
769
+ value = [value];
770
+ }
771
+ }
731
772
  const keys = key.split('.');
732
773
  if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
733
774
  this.error(`Invalid --set key: ${key}`);
@@ -847,6 +888,21 @@ export default class Employees extends Command {
847
888
  this.log('');
848
889
  this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
849
890
  }
891
+ parseFilterValue(value) {
892
+ // Recognises the nestjs-paginate operator set (with or without the leading $)
893
+ // and recursively unwraps chained operators like 'not:null' -> '$not:$null'
894
+ // or 'not:like:John' -> '$not:$like:John'. Falls back to '$eq:<value>' when
895
+ // no operator prefix is present.
896
+ const opRe = /^\$?(and|or|not|eq|gt|gte|in|null|lt|lte|btw|ne|ilike|like|sw|contains)(?::|$)/;
897
+ const m = value.match(opRe);
898
+ if (!m)
899
+ return `\$eq:${value}`;
900
+ const op = m[1];
901
+ const rest = value.slice(m[0].length);
902
+ if (rest === '')
903
+ return `\$${op}`;
904
+ return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
905
+ }
850
906
  filterFields(data, fields) {
851
907
  const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
852
908
  const pick = (obj, keys) => {
@@ -0,0 +1,40 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class EventProcessorMappings extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ action: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
8
+ };
9
+ static flags: {
10
+ context: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ output: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ table: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ data: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ set: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ page: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ limit: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
+ include: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
+ search: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
+ 'sort-by': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
+ filter: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
23
+ query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
24
+ fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
25
+ sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
26
+ 'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
27
+ };
28
+ run(): Promise<void>;
29
+ private runImport;
30
+ private runExport;
31
+ private runCamt053Import;
32
+ private runNestedImport;
33
+ private parseFilterValue;
34
+ private filterFields;
35
+ private sanitizeOutput;
36
+ private getColumns;
37
+ private getValue;
38
+ private printTable;
39
+ private printCsv;
40
+ }