@aderis-erp/cli 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/commands/access-groups.js +36 -22
- package/dist/commands/account-transactions.js +30 -18
- package/dist/commands/accounts.js +30 -18
- package/dist/commands/agent-runs.js +33 -21
- package/dist/commands/{processing-logs.d.ts → async-endpoints-internal.d.ts} +2 -2
- package/dist/commands/{processing-logs.js → async-endpoints-internal.js} +39 -28
- package/dist/commands/audit-log.js +30 -18
- package/dist/commands/auth/tenant.d.ts +1 -0
- package/dist/commands/auth/tenant.js +23 -7
- package/dist/commands/bank-accounts.js +30 -18
- package/dist/commands/bank-integrations.d.ts +2 -0
- package/dist/commands/bank-integrations.js +41 -19
- package/dist/commands/business-partners---contacts.js +30 -18
- package/dist/commands/business-partners.js +30 -18
- package/dist/commands/credit-card-accounts.js +30 -18
- package/dist/commands/documents.js +36 -22
- package/dist/commands/dunning-configs.js +30 -18
- package/dist/commands/dunning-reminders.js +30 -18
- package/dist/commands/dunning-runs.js +30 -18
- package/dist/commands/employees.js +30 -18
- package/dist/commands/event-processor-mappings.js +30 -18
- package/dist/commands/files-internal.js +30 -18
- package/dist/commands/health.js +30 -18
- package/dist/commands/inbox.js +34 -20
- package/dist/commands/integrations.js +30 -18
- package/dist/commands/item-categories.js +30 -18
- package/dist/commands/items.js +30 -18
- package/dist/commands/journal-lines.js +30 -18
- package/dist/commands/journals.js +30 -18
- package/dist/commands/ledgers.js +30 -18
- package/dist/commands/office-locations.js +30 -18
- package/dist/commands/payment-method-ledger-mappings.js +30 -18
- package/dist/commands/payment-methods.js +30 -18
- package/dist/commands/payment-runs.js +30 -18
- package/dist/commands/payment-terms.js +30 -18
- package/dist/commands/price-lists.js +30 -18
- package/dist/commands/processor-mappings.js +30 -18
- package/dist/commands/processors.js +31 -19
- package/dist/commands/reconciliation.js +30 -18
- package/dist/commands/reports.js +30 -18
- package/dist/commands/salary-types.js +30 -18
- package/dist/commands/sales-invoices.js +32 -19
- package/dist/commands/sales-orders.js +33 -19
- package/dist/commands/switch.d.ts +1 -0
- package/dist/commands/switch.js +58 -3
- package/dist/commands/tags.js +30 -18
- package/dist/commands/tax-codes.js +30 -18
- package/dist/commands/tax-matrix.js +30 -18
- package/dist/commands/tenants.js +30 -18
- package/dist/commands/text-templates.js +30 -18
- package/dist/commands/tools.js +30 -18
- package/dist/commands/vendor-invoices.js +33 -19
- package/dist/commands/webhooks.js +30 -18
- package/dist/commands/workflows.js +30 -18
- package/dist/index 2.js +1 -0
- package/dist/index.d 2.ts +1 -0
- package/dist/lib/auth.d.ts +16 -1
- package/dist/lib/auth.js +167 -50
- package/dist/lib/config.d.ts +7 -0
- package/dist/lib/config.js +6 -0
- package/oclif.manifest.json +65 -385
- package/package.json +2 -2
- package/dist/commands/account-statement-runs.d.ts +0 -40
- package/dist/commands/account-statement-runs.js +0 -1004
- package/dist/commands/bank-statements.d.ts +0 -40
- package/dist/commands/bank-statements.js +0 -998
|
@@ -819,32 +819,44 @@ export default class SalaryTypes extends Command {
|
|
|
819
819
|
filterFields(data, fields) {
|
|
820
820
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
821
821
|
const pick = (obj, keys) => {
|
|
822
|
-
|
|
822
|
+
// Group keys by their first segment so sibling sub-paths
|
|
823
|
+
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
824
|
+
// and don't clobber each other on merge.
|
|
825
|
+
const directKeys = new Set();
|
|
826
|
+
const nestedKeys = new Map();
|
|
823
827
|
for (const key of keys) {
|
|
824
828
|
if (key.split('.').some(isUnsafeKey))
|
|
825
829
|
continue;
|
|
826
830
|
if (key.includes('.')) {
|
|
827
|
-
// Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
|
|
828
831
|
const [first, ...rest] = key.split('.');
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
const existing = Array.isArray(result[first]) ? result[first] : [];
|
|
835
|
-
result[first] = val.map((item, i) => ({
|
|
836
|
-
...(existing[i] || {}),
|
|
837
|
-
...pick(item, [restKey]),
|
|
838
|
-
}));
|
|
839
|
-
}
|
|
840
|
-
else {
|
|
841
|
-
const nested = pick(val, [restKey]);
|
|
842
|
-
result[first] = { ...(result[first] || {}), ...nested };
|
|
843
|
-
}
|
|
832
|
+
if (isUnsafeKey(first))
|
|
833
|
+
continue;
|
|
834
|
+
const list = nestedKeys.get(first);
|
|
835
|
+
if (list) {
|
|
836
|
+
list.push(rest.join('.'));
|
|
844
837
|
}
|
|
838
|
+
else {
|
|
839
|
+
nestedKeys.set(first, [rest.join('.')]);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
directKeys.add(key);
|
|
845
844
|
}
|
|
846
|
-
|
|
845
|
+
}
|
|
846
|
+
const result = {};
|
|
847
|
+
for (const key of directKeys) {
|
|
848
|
+
if (key in obj)
|
|
847
849
|
result[key] = obj[key];
|
|
850
|
+
}
|
|
851
|
+
for (const [first, restKeys] of nestedKeys) {
|
|
852
|
+
const val = obj[first];
|
|
853
|
+
if (!val || typeof val !== 'object')
|
|
854
|
+
continue;
|
|
855
|
+
if (Array.isArray(val)) {
|
|
856
|
+
result[first] = val.map(item => pick(item, restKeys));
|
|
857
|
+
}
|
|
858
|
+
else {
|
|
859
|
+
result[first] = pick(val, restKeys);
|
|
848
860
|
}
|
|
849
861
|
}
|
|
850
862
|
return result;
|
|
@@ -6,7 +6,7 @@ const HAS_IMPORT = true;
|
|
|
6
6
|
const HAS_EXPORT = true;
|
|
7
7
|
const HAS_CAMT053 = false;
|
|
8
8
|
const NESTED_IMPORTS = ["line-items", "notes", "files"];
|
|
9
|
-
const SUBCOMMANDS = [...["list", "create", "create-migrated-invoice", "pdf", "get", "update", "delete", "approve", "validate", "request-approval", "send", "send-resend", "cancel", "copy", "replace", "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}`)];
|
|
9
|
+
const SUBCOMMANDS = [...["list", "create", "create-migrated-invoice", "pdf", "get", "update", "delete", "approve", "validate", "request-approval", "send", "send-resend", "cancel", "copy", "replace", "create-line-items", "update-line-items-reorder", "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/sales-invoices', summary: 'Get all sales invoices with pagination', pathParams: [], hasBody: false, isMultipart: false },
|
|
12
12
|
'create': { method: 'POST', path: '/v1/sales-invoices', summary: 'Create a new sales invoice', pathParams: [], hasBody: true, isMultipart: false },
|
|
@@ -24,6 +24,7 @@ const ENDPOINTS = {
|
|
|
24
24
|
'copy': { method: 'POST', path: '/v1/sales-invoices/{id}/copy', summary: 'Create a copy of a cancelled invoice', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
25
25
|
'replace': { method: 'POST', path: '/v1/sales-invoices/{id}/replace', summary: 'Replace invoice with corrected version (creates new CORRECTED_INVOICE)', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
26
26
|
'create-line-items': { method: 'POST', path: '/v1/sales-invoices/{invoiceId}/line-items', summary: 'Add a line item to a sales invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false },
|
|
27
|
+
'update-line-items-reorder': { method: 'PATCH', path: '/v1/sales-invoices/{invoiceId}/line-items/reorder', summary: 'Bulk reorder line items (drag-and-drop)', pathParams: ['invoiceId'], hasBody: true, isMultipart: false },
|
|
27
28
|
'update-line-items': { method: 'PATCH', path: '/v1/sales-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Update a line item on a sales invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: true, isMultipart: false },
|
|
28
29
|
'delete-line-items': { method: 'DELETE', path: '/v1/sales-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Remove a line item from a sales invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: false, isMultipart: false },
|
|
29
30
|
'list-notes': { method: 'GET', path: '/v1/sales-invoices/{salesInvoiceId}/notes', summary: 'Get all notes for a sales invoice', pathParams: ['salesInvoiceId'], hasBody: false, isMultipart: false },
|
|
@@ -846,32 +847,44 @@ export default class SalesInvoices extends Command {
|
|
|
846
847
|
filterFields(data, fields) {
|
|
847
848
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
848
849
|
const pick = (obj, keys) => {
|
|
849
|
-
|
|
850
|
+
// Group keys by their first segment so sibling sub-paths
|
|
851
|
+
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
852
|
+
// and don't clobber each other on merge.
|
|
853
|
+
const directKeys = new Set();
|
|
854
|
+
const nestedKeys = new Map();
|
|
850
855
|
for (const key of keys) {
|
|
851
856
|
if (key.split('.').some(isUnsafeKey))
|
|
852
857
|
continue;
|
|
853
858
|
if (key.includes('.')) {
|
|
854
|
-
// Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
|
|
855
859
|
const [first, ...rest] = key.split('.');
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
const existing = Array.isArray(result[first]) ? result[first] : [];
|
|
862
|
-
result[first] = val.map((item, i) => ({
|
|
863
|
-
...(existing[i] || {}),
|
|
864
|
-
...pick(item, [restKey]),
|
|
865
|
-
}));
|
|
866
|
-
}
|
|
867
|
-
else {
|
|
868
|
-
const nested = pick(val, [restKey]);
|
|
869
|
-
result[first] = { ...(result[first] || {}), ...nested };
|
|
870
|
-
}
|
|
860
|
+
if (isUnsafeKey(first))
|
|
861
|
+
continue;
|
|
862
|
+
const list = nestedKeys.get(first);
|
|
863
|
+
if (list) {
|
|
864
|
+
list.push(rest.join('.'));
|
|
871
865
|
}
|
|
866
|
+
else {
|
|
867
|
+
nestedKeys.set(first, [rest.join('.')]);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
else {
|
|
871
|
+
directKeys.add(key);
|
|
872
872
|
}
|
|
873
|
-
|
|
873
|
+
}
|
|
874
|
+
const result = {};
|
|
875
|
+
for (const key of directKeys) {
|
|
876
|
+
if (key in obj)
|
|
874
877
|
result[key] = obj[key];
|
|
878
|
+
}
|
|
879
|
+
for (const [first, restKeys] of nestedKeys) {
|
|
880
|
+
const val = obj[first];
|
|
881
|
+
if (!val || typeof val !== 'object')
|
|
882
|
+
continue;
|
|
883
|
+
if (Array.isArray(val)) {
|
|
884
|
+
result[first] = val.map(item => pick(item, restKeys));
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
result[first] = pick(val, restKeys);
|
|
875
888
|
}
|
|
876
889
|
}
|
|
877
890
|
return result;
|
|
@@ -6,13 +6,14 @@ const HAS_IMPORT = true;
|
|
|
6
6
|
const HAS_EXPORT = true;
|
|
7
7
|
const HAS_CAMT053 = false;
|
|
8
8
|
const NESTED_IMPORTS = ["line-items", "notes", "files"];
|
|
9
|
-
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "submit", "approve", "accept", "terminate", "cancel", "replace", "create-invoice", "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}`)];
|
|
9
|
+
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "pdf", "submit", "approve", "accept", "terminate", "cancel", "replace", "create-invoice", "create-line-items", "update-line-items-reorder", "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/sales-orders', summary: 'Get all sales orders with pagination', pathParams: [], hasBody: false, isMultipart: false },
|
|
12
12
|
'create': { method: 'POST', path: '/v1/sales-orders', summary: 'Create a new sales order', pathParams: [], hasBody: true, isMultipart: false },
|
|
13
13
|
'get': { method: 'GET', path: '/v1/sales-orders/{id}', summary: 'Get a sales order by ID', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
14
14
|
'update': { method: 'PATCH', path: '/v1/sales-orders/{id}', summary: 'Update a sales order', pathParams: ['id'], hasBody: true, isMultipart: false },
|
|
15
15
|
'delete': { method: 'DELETE', path: '/v1/sales-orders/{id}', summary: 'Delete a sales order', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
16
|
+
'pdf': { method: 'GET', path: '/v1/sales-orders/{id}/pdf', summary: 'Generate a PDF preview (delivery slip) of a sales order', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
16
17
|
'submit': { method: 'POST', path: '/v1/sales-orders/{id}/submit', summary: 'Submit order for approval (DRAFT → PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
17
18
|
'approve': { method: 'POST', path: '/v1/sales-orders/{id}/approve', summary: 'Approve order (PENDING_APPROVAL → APPROVED). Generates order number.', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
18
19
|
'accept': { method: 'POST', path: '/v1/sales-orders/{id}/accept', summary: 'Accept order (APPROVED → ACCEPTED). Adjusts billing start date.', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
@@ -21,6 +22,7 @@ const ENDPOINTS = {
|
|
|
21
22
|
'replace': { method: 'POST', path: '/v1/sales-orders/{id}/replace', summary: 'Replace order with corrected version (APPROVED → creates new DRAFT)', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
22
23
|
'create-invoice': { method: 'POST', path: '/v1/sales-orders/{id}/create-invoice', summary: 'Create invoice from sales order (ACCEPTED → creates DRAFT invoice)', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
23
24
|
'create-line-items': { method: 'POST', path: '/v1/sales-orders/{orderId}/line-items', summary: 'Add a line item to a sales order', pathParams: ['orderId'], hasBody: true, isMultipart: false },
|
|
25
|
+
'update-line-items-reorder': { method: 'PATCH', path: '/v1/sales-orders/{orderId}/line-items/reorder', summary: 'Bulk reorder line items (drag-and-drop)', pathParams: ['orderId'], hasBody: true, isMultipart: false },
|
|
24
26
|
'update-line-items': { method: 'PATCH', path: '/v1/sales-orders/{orderId}/line-items/{lineItemId}', summary: 'Update a line item on a sales order', pathParams: ['orderId', 'lineItemId'], hasBody: true, isMultipart: false },
|
|
25
27
|
'delete-line-items': { method: 'DELETE', path: '/v1/sales-orders/{orderId}/line-items/{lineItemId}', summary: 'Remove a line item from a sales order', pathParams: ['orderId', 'lineItemId'], hasBody: false, isMultipart: false },
|
|
26
28
|
'list-notes': { method: 'GET', path: '/v1/sales-orders/{salesOrderId}/notes', summary: 'Get all notes for a sales order', pathParams: ['salesOrderId'], hasBody: false, isMultipart: false },
|
|
@@ -843,32 +845,44 @@ export default class SalesOrders extends Command {
|
|
|
843
845
|
filterFields(data, fields) {
|
|
844
846
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
845
847
|
const pick = (obj, keys) => {
|
|
846
|
-
|
|
848
|
+
// Group keys by their first segment so sibling sub-paths
|
|
849
|
+
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
850
|
+
// and don't clobber each other on merge.
|
|
851
|
+
const directKeys = new Set();
|
|
852
|
+
const nestedKeys = new Map();
|
|
847
853
|
for (const key of keys) {
|
|
848
854
|
if (key.split('.').some(isUnsafeKey))
|
|
849
855
|
continue;
|
|
850
856
|
if (key.includes('.')) {
|
|
851
|
-
// Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
|
|
852
857
|
const [first, ...rest] = key.split('.');
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
const existing = Array.isArray(result[first]) ? result[first] : [];
|
|
859
|
-
result[first] = val.map((item, i) => ({
|
|
860
|
-
...(existing[i] || {}),
|
|
861
|
-
...pick(item, [restKey]),
|
|
862
|
-
}));
|
|
863
|
-
}
|
|
864
|
-
else {
|
|
865
|
-
const nested = pick(val, [restKey]);
|
|
866
|
-
result[first] = { ...(result[first] || {}), ...nested };
|
|
867
|
-
}
|
|
858
|
+
if (isUnsafeKey(first))
|
|
859
|
+
continue;
|
|
860
|
+
const list = nestedKeys.get(first);
|
|
861
|
+
if (list) {
|
|
862
|
+
list.push(rest.join('.'));
|
|
868
863
|
}
|
|
864
|
+
else {
|
|
865
|
+
nestedKeys.set(first, [rest.join('.')]);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
directKeys.add(key);
|
|
869
870
|
}
|
|
870
|
-
|
|
871
|
+
}
|
|
872
|
+
const result = {};
|
|
873
|
+
for (const key of directKeys) {
|
|
874
|
+
if (key in obj)
|
|
871
875
|
result[key] = obj[key];
|
|
876
|
+
}
|
|
877
|
+
for (const [first, restKeys] of nestedKeys) {
|
|
878
|
+
const val = obj[first];
|
|
879
|
+
if (!val || typeof val !== 'object')
|
|
880
|
+
continue;
|
|
881
|
+
if (Array.isArray(val)) {
|
|
882
|
+
result[first] = val.map(item => pick(item, restKeys));
|
|
883
|
+
}
|
|
884
|
+
else {
|
|
885
|
+
result[first] = pick(val, restKeys);
|
|
872
886
|
}
|
|
873
887
|
}
|
|
874
888
|
return result;
|
package/dist/commands/switch.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { select } from '@inquirer/prompts';
|
|
1
|
+
import { confirm, select } from '@inquirer/prompts';
|
|
2
2
|
import { Command } from '@oclif/core';
|
|
3
|
-
import {
|
|
3
|
+
import { loginAndSwitchTenant } from '../lib/auth.js';
|
|
4
|
+
import { getContext, getCurrentContext, listContexts, setCurrentContext } from '../lib/config.js';
|
|
4
5
|
export default class Switch extends Command {
|
|
5
6
|
static description = 'Switch context and tenant interactively';
|
|
6
7
|
static examples = ['<%= config.bin %> switch'];
|
|
@@ -34,8 +35,62 @@ export default class Switch extends Command {
|
|
|
34
35
|
setCurrentContext(selectedContext);
|
|
35
36
|
this.log(`Switched to context: ${selectedContext}`);
|
|
36
37
|
}
|
|
37
|
-
// Step 2: Select tenant within chosen context
|
|
38
|
+
// Step 2: Select tenant within chosen context.
|
|
39
|
+
// If the session is expired, prompt the user to re-login and then retry.
|
|
38
40
|
const { default: AuthTenant } = await import('./auth/tenant.js');
|
|
41
|
+
try {
|
|
42
|
+
await AuthTenant.run([`--context=${selectedContext}`]);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (!isSessionExpired(error))
|
|
46
|
+
throw error;
|
|
47
|
+
await this.handleSessionExpired(selectedContext);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async handleSessionExpired(selectedContext) {
|
|
51
|
+
const shouldLogin = await confirm({
|
|
52
|
+
default: true,
|
|
53
|
+
message: 'Session expired. Do you want to login again?',
|
|
54
|
+
});
|
|
55
|
+
if (!shouldLogin)
|
|
56
|
+
return;
|
|
57
|
+
// If we have a cached tenant list from a previous session, let the user pick
|
|
58
|
+
// tenant FIRST. Then run the combined login + tenant-switch flow which uses
|
|
59
|
+
// a single browser tab (chained PKCE rounds with a meta-refresh redirect).
|
|
60
|
+
const ctx = getContext(selectedContext);
|
|
61
|
+
const cached = ctx?.cachedTenants ?? [];
|
|
62
|
+
if (cached.length > 1) {
|
|
63
|
+
const sorted = [...cached].sort((a, b) => {
|
|
64
|
+
if (a.tenantId === ctx?.tenant_id)
|
|
65
|
+
return -1;
|
|
66
|
+
if (b.tenantId === ctx?.tenant_id)
|
|
67
|
+
return 1;
|
|
68
|
+
return a.name.localeCompare(b.name);
|
|
69
|
+
});
|
|
70
|
+
const selectedTenant = await select({
|
|
71
|
+
choices: sorted.map((t) => ({
|
|
72
|
+
name: t.tenantId === ctx?.tenant_id ? `${t.name} (previous)` : t.name,
|
|
73
|
+
value: t.tenantId,
|
|
74
|
+
})),
|
|
75
|
+
message: 'Select a tenant:',
|
|
76
|
+
});
|
|
77
|
+
const selectedName = cached.find((t) => t.tenantId === selectedTenant)?.name;
|
|
78
|
+
this.log(`Logging in and switching to ${selectedName ?? selectedTenant}…`);
|
|
79
|
+
if (ctx) {
|
|
80
|
+
await loginAndSwitchTenant(ctx, selectedTenant);
|
|
81
|
+
}
|
|
82
|
+
this.log(`Successfully switched to tenant ${selectedName ? `${selectedName} (${selectedTenant})` : selectedTenant}`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// No cached tenant list (or only one tenant) — fall back to the two-step
|
|
86
|
+
// flow: login first, then run the normal tenant selector.
|
|
87
|
+
const { default: AuthLogin } = await import('./auth/login.js');
|
|
88
|
+
const { default: AuthTenant } = await import('./auth/tenant.js');
|
|
89
|
+
await AuthLogin.run([`--context=${selectedContext}`]);
|
|
39
90
|
await AuthTenant.run([`--context=${selectedContext}`]);
|
|
40
91
|
}
|
|
41
92
|
}
|
|
93
|
+
function isSessionExpired(error) {
|
|
94
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
95
|
+
return /session expired/i.test(message) || /not logged in/i.test(message);
|
|
96
|
+
}
|
package/dist/commands/tags.js
CHANGED
|
@@ -819,32 +819,44 @@ export default class Tags extends Command {
|
|
|
819
819
|
filterFields(data, fields) {
|
|
820
820
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
821
821
|
const pick = (obj, keys) => {
|
|
822
|
-
|
|
822
|
+
// Group keys by their first segment so sibling sub-paths
|
|
823
|
+
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
824
|
+
// and don't clobber each other on merge.
|
|
825
|
+
const directKeys = new Set();
|
|
826
|
+
const nestedKeys = new Map();
|
|
823
827
|
for (const key of keys) {
|
|
824
828
|
if (key.split('.').some(isUnsafeKey))
|
|
825
829
|
continue;
|
|
826
830
|
if (key.includes('.')) {
|
|
827
|
-
// Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
|
|
828
831
|
const [first, ...rest] = key.split('.');
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
const existing = Array.isArray(result[first]) ? result[first] : [];
|
|
835
|
-
result[first] = val.map((item, i) => ({
|
|
836
|
-
...(existing[i] || {}),
|
|
837
|
-
...pick(item, [restKey]),
|
|
838
|
-
}));
|
|
839
|
-
}
|
|
840
|
-
else {
|
|
841
|
-
const nested = pick(val, [restKey]);
|
|
842
|
-
result[first] = { ...(result[first] || {}), ...nested };
|
|
843
|
-
}
|
|
832
|
+
if (isUnsafeKey(first))
|
|
833
|
+
continue;
|
|
834
|
+
const list = nestedKeys.get(first);
|
|
835
|
+
if (list) {
|
|
836
|
+
list.push(rest.join('.'));
|
|
844
837
|
}
|
|
838
|
+
else {
|
|
839
|
+
nestedKeys.set(first, [rest.join('.')]);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
directKeys.add(key);
|
|
845
844
|
}
|
|
846
|
-
|
|
845
|
+
}
|
|
846
|
+
const result = {};
|
|
847
|
+
for (const key of directKeys) {
|
|
848
|
+
if (key in obj)
|
|
847
849
|
result[key] = obj[key];
|
|
850
|
+
}
|
|
851
|
+
for (const [first, restKeys] of nestedKeys) {
|
|
852
|
+
const val = obj[first];
|
|
853
|
+
if (!val || typeof val !== 'object')
|
|
854
|
+
continue;
|
|
855
|
+
if (Array.isArray(val)) {
|
|
856
|
+
result[first] = val.map(item => pick(item, restKeys));
|
|
857
|
+
}
|
|
858
|
+
else {
|
|
859
|
+
result[first] = pick(val, restKeys);
|
|
848
860
|
}
|
|
849
861
|
}
|
|
850
862
|
return result;
|
|
@@ -822,32 +822,44 @@ export default class TaxCodes extends Command {
|
|
|
822
822
|
filterFields(data, fields) {
|
|
823
823
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
824
824
|
const pick = (obj, keys) => {
|
|
825
|
-
|
|
825
|
+
// Group keys by their first segment so sibling sub-paths
|
|
826
|
+
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
827
|
+
// and don't clobber each other on merge.
|
|
828
|
+
const directKeys = new Set();
|
|
829
|
+
const nestedKeys = new Map();
|
|
826
830
|
for (const key of keys) {
|
|
827
831
|
if (key.split('.').some(isUnsafeKey))
|
|
828
832
|
continue;
|
|
829
833
|
if (key.includes('.')) {
|
|
830
|
-
// Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
|
|
831
834
|
const [first, ...rest] = key.split('.');
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
const existing = Array.isArray(result[first]) ? result[first] : [];
|
|
838
|
-
result[first] = val.map((item, i) => ({
|
|
839
|
-
...(existing[i] || {}),
|
|
840
|
-
...pick(item, [restKey]),
|
|
841
|
-
}));
|
|
842
|
-
}
|
|
843
|
-
else {
|
|
844
|
-
const nested = pick(val, [restKey]);
|
|
845
|
-
result[first] = { ...(result[first] || {}), ...nested };
|
|
846
|
-
}
|
|
835
|
+
if (isUnsafeKey(first))
|
|
836
|
+
continue;
|
|
837
|
+
const list = nestedKeys.get(first);
|
|
838
|
+
if (list) {
|
|
839
|
+
list.push(rest.join('.'));
|
|
847
840
|
}
|
|
841
|
+
else {
|
|
842
|
+
nestedKeys.set(first, [rest.join('.')]);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
else {
|
|
846
|
+
directKeys.add(key);
|
|
848
847
|
}
|
|
849
|
-
|
|
848
|
+
}
|
|
849
|
+
const result = {};
|
|
850
|
+
for (const key of directKeys) {
|
|
851
|
+
if (key in obj)
|
|
850
852
|
result[key] = obj[key];
|
|
853
|
+
}
|
|
854
|
+
for (const [first, restKeys] of nestedKeys) {
|
|
855
|
+
const val = obj[first];
|
|
856
|
+
if (!val || typeof val !== 'object')
|
|
857
|
+
continue;
|
|
858
|
+
if (Array.isArray(val)) {
|
|
859
|
+
result[first] = val.map(item => pick(item, restKeys));
|
|
860
|
+
}
|
|
861
|
+
else {
|
|
862
|
+
result[first] = pick(val, restKeys);
|
|
851
863
|
}
|
|
852
864
|
}
|
|
853
865
|
return result;
|
|
@@ -819,32 +819,44 @@ export default class TaxMatrix extends Command {
|
|
|
819
819
|
filterFields(data, fields) {
|
|
820
820
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
821
821
|
const pick = (obj, keys) => {
|
|
822
|
-
|
|
822
|
+
// Group keys by their first segment so sibling sub-paths
|
|
823
|
+
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
824
|
+
// and don't clobber each other on merge.
|
|
825
|
+
const directKeys = new Set();
|
|
826
|
+
const nestedKeys = new Map();
|
|
823
827
|
for (const key of keys) {
|
|
824
828
|
if (key.split('.').some(isUnsafeKey))
|
|
825
829
|
continue;
|
|
826
830
|
if (key.includes('.')) {
|
|
827
|
-
// Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
|
|
828
831
|
const [first, ...rest] = key.split('.');
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
const existing = Array.isArray(result[first]) ? result[first] : [];
|
|
835
|
-
result[first] = val.map((item, i) => ({
|
|
836
|
-
...(existing[i] || {}),
|
|
837
|
-
...pick(item, [restKey]),
|
|
838
|
-
}));
|
|
839
|
-
}
|
|
840
|
-
else {
|
|
841
|
-
const nested = pick(val, [restKey]);
|
|
842
|
-
result[first] = { ...(result[first] || {}), ...nested };
|
|
843
|
-
}
|
|
832
|
+
if (isUnsafeKey(first))
|
|
833
|
+
continue;
|
|
834
|
+
const list = nestedKeys.get(first);
|
|
835
|
+
if (list) {
|
|
836
|
+
list.push(rest.join('.'));
|
|
844
837
|
}
|
|
838
|
+
else {
|
|
839
|
+
nestedKeys.set(first, [rest.join('.')]);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
directKeys.add(key);
|
|
845
844
|
}
|
|
846
|
-
|
|
845
|
+
}
|
|
846
|
+
const result = {};
|
|
847
|
+
for (const key of directKeys) {
|
|
848
|
+
if (key in obj)
|
|
847
849
|
result[key] = obj[key];
|
|
850
|
+
}
|
|
851
|
+
for (const [first, restKeys] of nestedKeys) {
|
|
852
|
+
const val = obj[first];
|
|
853
|
+
if (!val || typeof val !== 'object')
|
|
854
|
+
continue;
|
|
855
|
+
if (Array.isArray(val)) {
|
|
856
|
+
result[first] = val.map(item => pick(item, restKeys));
|
|
857
|
+
}
|
|
858
|
+
else {
|
|
859
|
+
result[first] = pick(val, restKeys);
|
|
848
860
|
}
|
|
849
861
|
}
|
|
850
862
|
return result;
|
package/dist/commands/tenants.js
CHANGED
|
@@ -819,32 +819,44 @@ export default class Tenants extends Command {
|
|
|
819
819
|
filterFields(data, fields) {
|
|
820
820
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
821
821
|
const pick = (obj, keys) => {
|
|
822
|
-
|
|
822
|
+
// Group keys by their first segment so sibling sub-paths
|
|
823
|
+
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
824
|
+
// and don't clobber each other on merge.
|
|
825
|
+
const directKeys = new Set();
|
|
826
|
+
const nestedKeys = new Map();
|
|
823
827
|
for (const key of keys) {
|
|
824
828
|
if (key.split('.').some(isUnsafeKey))
|
|
825
829
|
continue;
|
|
826
830
|
if (key.includes('.')) {
|
|
827
|
-
// Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
|
|
828
831
|
const [first, ...rest] = key.split('.');
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
const existing = Array.isArray(result[first]) ? result[first] : [];
|
|
835
|
-
result[first] = val.map((item, i) => ({
|
|
836
|
-
...(existing[i] || {}),
|
|
837
|
-
...pick(item, [restKey]),
|
|
838
|
-
}));
|
|
839
|
-
}
|
|
840
|
-
else {
|
|
841
|
-
const nested = pick(val, [restKey]);
|
|
842
|
-
result[first] = { ...(result[first] || {}), ...nested };
|
|
843
|
-
}
|
|
832
|
+
if (isUnsafeKey(first))
|
|
833
|
+
continue;
|
|
834
|
+
const list = nestedKeys.get(first);
|
|
835
|
+
if (list) {
|
|
836
|
+
list.push(rest.join('.'));
|
|
844
837
|
}
|
|
838
|
+
else {
|
|
839
|
+
nestedKeys.set(first, [rest.join('.')]);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
directKeys.add(key);
|
|
845
844
|
}
|
|
846
|
-
|
|
845
|
+
}
|
|
846
|
+
const result = {};
|
|
847
|
+
for (const key of directKeys) {
|
|
848
|
+
if (key in obj)
|
|
847
849
|
result[key] = obj[key];
|
|
850
|
+
}
|
|
851
|
+
for (const [first, restKeys] of nestedKeys) {
|
|
852
|
+
const val = obj[first];
|
|
853
|
+
if (!val || typeof val !== 'object')
|
|
854
|
+
continue;
|
|
855
|
+
if (Array.isArray(val)) {
|
|
856
|
+
result[first] = val.map(item => pick(item, restKeys));
|
|
857
|
+
}
|
|
858
|
+
else {
|
|
859
|
+
result[first] = pick(val, restKeys);
|
|
848
860
|
}
|
|
849
861
|
}
|
|
850
862
|
return result;
|
|
@@ -819,32 +819,44 @@ export default class TextTemplates extends Command {
|
|
|
819
819
|
filterFields(data, fields) {
|
|
820
820
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
821
821
|
const pick = (obj, keys) => {
|
|
822
|
-
|
|
822
|
+
// Group keys by their first segment so sibling sub-paths
|
|
823
|
+
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
824
|
+
// and don't clobber each other on merge.
|
|
825
|
+
const directKeys = new Set();
|
|
826
|
+
const nestedKeys = new Map();
|
|
823
827
|
for (const key of keys) {
|
|
824
828
|
if (key.split('.').some(isUnsafeKey))
|
|
825
829
|
continue;
|
|
826
830
|
if (key.includes('.')) {
|
|
827
|
-
// Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
|
|
828
831
|
const [first, ...rest] = key.split('.');
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
const existing = Array.isArray(result[first]) ? result[first] : [];
|
|
835
|
-
result[first] = val.map((item, i) => ({
|
|
836
|
-
...(existing[i] || {}),
|
|
837
|
-
...pick(item, [restKey]),
|
|
838
|
-
}));
|
|
839
|
-
}
|
|
840
|
-
else {
|
|
841
|
-
const nested = pick(val, [restKey]);
|
|
842
|
-
result[first] = { ...(result[first] || {}), ...nested };
|
|
843
|
-
}
|
|
832
|
+
if (isUnsafeKey(first))
|
|
833
|
+
continue;
|
|
834
|
+
const list = nestedKeys.get(first);
|
|
835
|
+
if (list) {
|
|
836
|
+
list.push(rest.join('.'));
|
|
844
837
|
}
|
|
838
|
+
else {
|
|
839
|
+
nestedKeys.set(first, [rest.join('.')]);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
directKeys.add(key);
|
|
845
844
|
}
|
|
846
|
-
|
|
845
|
+
}
|
|
846
|
+
const result = {};
|
|
847
|
+
for (const key of directKeys) {
|
|
848
|
+
if (key in obj)
|
|
847
849
|
result[key] = obj[key];
|
|
850
|
+
}
|
|
851
|
+
for (const [first, restKeys] of nestedKeys) {
|
|
852
|
+
const val = obj[first];
|
|
853
|
+
if (!val || typeof val !== 'object')
|
|
854
|
+
continue;
|
|
855
|
+
if (Array.isArray(val)) {
|
|
856
|
+
result[first] = val.map(item => pick(item, restKeys));
|
|
857
|
+
}
|
|
858
|
+
else {
|
|
859
|
+
result[first] = pick(val, restKeys);
|
|
848
860
|
}
|
|
849
861
|
}
|
|
850
862
|
return result;
|