mage-remote-run 0.16.0 → 0.18.0
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/lib/command-registry.js +2 -0
- package/lib/commands/company.js +435 -0
- package/lib/commands/console.js +8 -6
- package/lib/commands/purchase-order-cart.js +63 -0
- package/lib/utils.js +27 -1
- package/package.json +1 -1
package/lib/command-registry.js
CHANGED
|
@@ -10,6 +10,7 @@ import { registerTaxCommands } from './commands/tax.js';
|
|
|
10
10
|
import { registerInventoryCommands } from './commands/inventory.js';
|
|
11
11
|
import { registerAdobeIoEventsCommands } from './commands/adobe-io-events.js';
|
|
12
12
|
import { registerWebhooksCommands } from './commands/webhooks.js';
|
|
13
|
+
import { registerPurchaseOrderCartCommands } from './commands/purchase-order-cart.js';
|
|
13
14
|
import { registerConsoleCommand } from './commands/console.js';
|
|
14
15
|
|
|
15
16
|
export { registerConnectionCommands, registerConsoleCommand };
|
|
@@ -29,6 +30,7 @@ export function registerCoreCommands(program) {
|
|
|
29
30
|
export function registerCloudCommands(program) {
|
|
30
31
|
registerAdobeIoEventsCommands(program);
|
|
31
32
|
registerCompanyCommands(program);
|
|
33
|
+
registerPurchaseOrderCartCommands(program);
|
|
32
34
|
registerWebhooksCommands(program);
|
|
33
35
|
}
|
|
34
36
|
|
package/lib/commands/company.js
CHANGED
|
@@ -132,4 +132,439 @@ Examples:
|
|
|
132
132
|
|
|
133
133
|
} catch (e) { handleError(e); }
|
|
134
134
|
});
|
|
135
|
+
|
|
136
|
+
//-------------------------------------------------------
|
|
137
|
+
// "company create" Command
|
|
138
|
+
//-------------------------------------------------------
|
|
139
|
+
company.command('create')
|
|
140
|
+
.description('Create a new company')
|
|
141
|
+
.addHelpText('after', `
|
|
142
|
+
Examples:
|
|
143
|
+
$ mage-remote-run company create
|
|
144
|
+
`)
|
|
145
|
+
.action(async () => {
|
|
146
|
+
try {
|
|
147
|
+
const client = await createClient();
|
|
148
|
+
const { default: inquirer } = await import('inquirer');
|
|
149
|
+
|
|
150
|
+
// Prompt for basic info
|
|
151
|
+
const answers = await inquirer.prompt([
|
|
152
|
+
{ name: 'company_name', message: 'Company Name:' },
|
|
153
|
+
{ name: 'company_email', message: 'Company Email:' },
|
|
154
|
+
{ name: 'legal_name', message: 'Legal Name (optional):' },
|
|
155
|
+
{ name: 'vat_tax_id', message: 'VAT/Tax ID (optional):' },
|
|
156
|
+
{ name: 'reseller_id', message: 'Reseller ID (optional):' },
|
|
157
|
+
{ name: 'customer_group_id', message: 'Customer Group ID:', default: '1' },
|
|
158
|
+
{ name: 'sales_representative_id', message: 'Sales Representative ID (Admin User ID):', default: '1' },
|
|
159
|
+
{ name: 'email', message: 'Super Admin Email:' },
|
|
160
|
+
{ name: 'firstname', message: 'Super Admin First Name:' },
|
|
161
|
+
{ name: 'lastname', message: 'Super Admin Last Name:' }
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
// Address is required usually
|
|
165
|
+
const address = await inquirer.prompt([
|
|
166
|
+
{ name: 'street', message: 'Street:' },
|
|
167
|
+
{ name: 'city', message: 'City:' },
|
|
168
|
+
{ name: 'country_id', message: 'Country ID:', default: 'US' },
|
|
169
|
+
{ name: 'region', message: 'Region/State:' },
|
|
170
|
+
{ name: 'postcode', message: 'Postcode:' },
|
|
171
|
+
{ name: 'telephone', message: 'Telephone:' }
|
|
172
|
+
]);
|
|
173
|
+
|
|
174
|
+
const payload = {
|
|
175
|
+
company: {
|
|
176
|
+
company_name: answers.company_name,
|
|
177
|
+
company_email: answers.company_email,
|
|
178
|
+
legal_name: answers.legal_name,
|
|
179
|
+
vat_tax_id: answers.vat_tax_id,
|
|
180
|
+
reseller_id: answers.reseller_id,
|
|
181
|
+
customer_group_id: answers.customer_group_id,
|
|
182
|
+
sales_representative_id: answers.sales_representative_id,
|
|
183
|
+
super_user_id: 0, // 0 for new user
|
|
184
|
+
street: [address.street],
|
|
185
|
+
city: address.city,
|
|
186
|
+
country_id: address.country_id,
|
|
187
|
+
region: { region: address.region }, // Simplified
|
|
188
|
+
postcode: address.postcode,
|
|
189
|
+
telephone: address.telephone
|
|
190
|
+
},
|
|
191
|
+
company_admin: {
|
|
192
|
+
email: answers.email,
|
|
193
|
+
firstname: answers.firstname,
|
|
194
|
+
lastname: answers.lastname
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// POST /V1/company/
|
|
199
|
+
const data = await client.post('V1/company', payload);
|
|
200
|
+
console.log(chalk.green(`✅ Company created with ID: ${data.id}`));
|
|
201
|
+
} catch (e) { handleError(e); }
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
//-------------------------------------------------------
|
|
205
|
+
// "company update" Command
|
|
206
|
+
//-------------------------------------------------------
|
|
207
|
+
company.command('update <companyId>')
|
|
208
|
+
.description('Update company details')
|
|
209
|
+
.addHelpText('after', `
|
|
210
|
+
Examples:
|
|
211
|
+
$ mage-remote-run company update 123
|
|
212
|
+
`)
|
|
213
|
+
.action(async (companyId) => {
|
|
214
|
+
try {
|
|
215
|
+
const client = await createClient();
|
|
216
|
+
const { default: inquirer } = await import('inquirer');
|
|
217
|
+
|
|
218
|
+
// First fetch data
|
|
219
|
+
let current;
|
|
220
|
+
try {
|
|
221
|
+
current = await client.get(`V1/company/${companyId}`);
|
|
222
|
+
} catch (e) {
|
|
223
|
+
throw new Error(`Company ${companyId} not found.`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const answers = await inquirer.prompt([
|
|
227
|
+
{ name: 'company_name', message: 'Company Name:', default: current.company_name },
|
|
228
|
+
{ name: 'company_email', message: 'Company Email:', default: current.company_email },
|
|
229
|
+
{ name: 'sales_representative_id', message: 'Sales Rep ID:', default: String(current.sales_representative_id) },
|
|
230
|
+
{ name: 'customer_group_id', message: 'Customer Group ID:', default: String(current.customer_group_id) }
|
|
231
|
+
]);
|
|
232
|
+
|
|
233
|
+
// Merge
|
|
234
|
+
const payload = {
|
|
235
|
+
company: {
|
|
236
|
+
...current,
|
|
237
|
+
...answers
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
await client.put(`V1/company/${companyId}`, payload);
|
|
242
|
+
console.log(chalk.green(`✅ Company ${companyId} updated.`));
|
|
243
|
+
} catch (e) { handleError(e); }
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
//-------------------------------------------------------
|
|
247
|
+
// "company delete" Command
|
|
248
|
+
//-------------------------------------------------------
|
|
249
|
+
company.command('delete <companyId>')
|
|
250
|
+
.description('Delete a company')
|
|
251
|
+
.option('--force', 'Force delete without confirmation')
|
|
252
|
+
.action(async (companyId, options) => {
|
|
253
|
+
try {
|
|
254
|
+
const client = await createClient();
|
|
255
|
+
const { default: inquirer } = await import('inquirer');
|
|
256
|
+
|
|
257
|
+
if (!options.force) {
|
|
258
|
+
const { confirm } = await inquirer.prompt([{
|
|
259
|
+
type: 'confirm',
|
|
260
|
+
name: 'confirm',
|
|
261
|
+
message: `Delete company ${companyId}?`,
|
|
262
|
+
default: false
|
|
263
|
+
}]);
|
|
264
|
+
if (!confirm) return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
await client.delete(`V1/company/${companyId}`);
|
|
268
|
+
console.log(chalk.green(`✅ Company ${companyId} deleted.`));
|
|
269
|
+
} catch (e) { handleError(e); }
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
//-------------------------------------------------------
|
|
273
|
+
// "company structure" Command
|
|
274
|
+
//-------------------------------------------------------
|
|
275
|
+
company.command('structure <companyId>')
|
|
276
|
+
.description('Show company structure (hierarchy)')
|
|
277
|
+
.action(async (companyId) => {
|
|
278
|
+
try {
|
|
279
|
+
const client = await createClient();
|
|
280
|
+
// GET /V1/company/relations usually returns the user structure.
|
|
281
|
+
// However, without a specific hierarchy endpoint that is easy to visualize, we might just dump data.
|
|
282
|
+
const data = await client.get(`V1/company/${companyId}`);
|
|
283
|
+
console.log(chalk.bold(`Company Structure for ${data.company_name} (${data.id})`));
|
|
284
|
+
console.log('Hierarchy visualization requires more complex data. Showing basic info.');
|
|
285
|
+
console.log(`Parent ID: ${data.parent_id || 'None'}`);
|
|
286
|
+
console.log(`Legal Name: ${data.legal_name || '-'}`);
|
|
287
|
+
} catch (e) { handleError(e); }
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
//-------------------------------------------------------
|
|
291
|
+
// "company role" Group
|
|
292
|
+
//-------------------------------------------------------
|
|
293
|
+
const role = company.command('role').description('Manage company roles');
|
|
294
|
+
|
|
295
|
+
role.command('list')
|
|
296
|
+
.description('List roles')
|
|
297
|
+
.action(async () => {
|
|
298
|
+
try {
|
|
299
|
+
const client = await createClient();
|
|
300
|
+
// Assuming lists all roles visible to admin context
|
|
301
|
+
const data = await client.get('V1/company/role', { 'searchCriteria[pageSize]': 20 });
|
|
302
|
+
const items = data.items || [];
|
|
303
|
+
const rows = items.map(r => [r.role_id, r.role_name, r.company_id]);
|
|
304
|
+
console.log(chalk.bold(`Total Roles: ${data.total_count}`));
|
|
305
|
+
printTable(['ID', 'Name', 'Company ID'], rows);
|
|
306
|
+
} catch (e) { handleError(e); }
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
role.command('show <roleId>')
|
|
310
|
+
.description('Show role details')
|
|
311
|
+
.action(async (roleId) => {
|
|
312
|
+
try {
|
|
313
|
+
const client = await createClient();
|
|
314
|
+
const data = await client.get(`V1/company/role/${roleId}`);
|
|
315
|
+
console.log(chalk.bold(`\nRole: ${data.role_name} (ID: ${data.role_id})`));
|
|
316
|
+
if (data.permissions) {
|
|
317
|
+
console.log(chalk.bold('\nPermissions:'));
|
|
318
|
+
data.permissions.forEach(p => console.log(` - ${p.resource_id}: ${p.permission}`));
|
|
319
|
+
}
|
|
320
|
+
} catch (e) { handleError(e); }
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
//-------------------------------------------------------
|
|
324
|
+
// "company credit" Group
|
|
325
|
+
//-------------------------------------------------------
|
|
326
|
+
const credit = company.command('credit').description('Manage company credits');
|
|
327
|
+
|
|
328
|
+
credit.command('show <companyId>')
|
|
329
|
+
.description('Show credit for company')
|
|
330
|
+
.action(async (companyId) => {
|
|
331
|
+
try {
|
|
332
|
+
const client = await createClient();
|
|
333
|
+
const data = await client.get(`V1/companyCredits/company/${companyId}`);
|
|
334
|
+
console.log(chalk.bold(`\nCredit ID: ${data.id}`));
|
|
335
|
+
console.log(`Balance: ${data.balance} ${data.currency_code}`);
|
|
336
|
+
console.log(`Limit: ${data.credit_limit} ${data.currency_code}`);
|
|
337
|
+
console.log(`Avail: ${data.available_limit} ${data.currency_code}`);
|
|
338
|
+
} catch (e) { handleError(e); }
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
credit.command('history <companyId>')
|
|
342
|
+
.description('Show credit history')
|
|
343
|
+
.option('-p, --page <number>', 'Page number', '1')
|
|
344
|
+
.action(async (companyId, options) => {
|
|
345
|
+
try {
|
|
346
|
+
const client = await createClient();
|
|
347
|
+
|
|
348
|
+
// 1. Get Credit ID for the company
|
|
349
|
+
let creditData;
|
|
350
|
+
try {
|
|
351
|
+
creditData = await client.get(`V1/companyCredits/company/${companyId}`);
|
|
352
|
+
} catch (e) {
|
|
353
|
+
throw new Error(`Could not find credit profile for company ${companyId}`);
|
|
354
|
+
}
|
|
355
|
+
const creditId = creditData.id;
|
|
356
|
+
|
|
357
|
+
// 2. Search History by company_credit_id
|
|
358
|
+
const params = {
|
|
359
|
+
'searchCriteria[filterGroups][0][filters][0][field]': 'company_credit_id',
|
|
360
|
+
'searchCriteria[filterGroups][0][filters][0][value]': creditId,
|
|
361
|
+
'searchCriteria[currentPage]': options.page,
|
|
362
|
+
'searchCriteria[pageSize]': 20
|
|
363
|
+
};
|
|
364
|
+
const data = await client.get('V1/companyCredits/history', params);
|
|
365
|
+
|
|
366
|
+
if (data.total_count === 0) {
|
|
367
|
+
console.log(chalk.yellow('No history found.'));
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const typeMap = {
|
|
372
|
+
1: 'Allocate',
|
|
373
|
+
2: 'Reimburse',
|
|
374
|
+
3: 'Purchase',
|
|
375
|
+
4: 'Refund',
|
|
376
|
+
5: 'Revert',
|
|
377
|
+
6: 'Currency Update'
|
|
378
|
+
};
|
|
379
|
+
const rows = (data.items || []).map(h => [
|
|
380
|
+
h.datetime,
|
|
381
|
+
`${typeMap[h.type] || 'Unknown'} (${h.type})`,
|
|
382
|
+
h.amount,
|
|
383
|
+
h.balance,
|
|
384
|
+
h.comment
|
|
385
|
+
]);
|
|
386
|
+
console.log(chalk.bold(`Total Entries: ${data.total_count}`));
|
|
387
|
+
printTable(['Date', 'Type', 'Amount', 'Balance', 'Comment'], rows);
|
|
388
|
+
} catch (e) { handleError(e); }
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
credit.command('increase [creditId] [amount]')
|
|
392
|
+
.description('Increase balance')
|
|
393
|
+
.option('--currency <code >', 'Currency code')
|
|
394
|
+
.option('--type <number>', 'Operation Type (1=Allocate, 2=Reimburse, 4=Refund, 5=Revert)')
|
|
395
|
+
.option('--comment <text>', 'Comment', 'Manual increase')
|
|
396
|
+
.option('--po <number>', 'PO Number', '')
|
|
397
|
+
.action(async (creditId, amount, options) => {
|
|
398
|
+
try {
|
|
399
|
+
const client = await createClient();
|
|
400
|
+
const inquirer = (await import('inquirer')).default;
|
|
401
|
+
const { select } = await import('@inquirer/prompts');
|
|
402
|
+
|
|
403
|
+
let isInteractive = false;
|
|
404
|
+
if (!creditId) {
|
|
405
|
+
isInteractive = true;
|
|
406
|
+
const ans = await inquirer.prompt([{
|
|
407
|
+
type: 'input',
|
|
408
|
+
name: 'creditId',
|
|
409
|
+
message: 'Enter Credit ID:'
|
|
410
|
+
}]);
|
|
411
|
+
creditId = ans.creditId;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (!amount) {
|
|
415
|
+
isInteractive = true;
|
|
416
|
+
const ans = await inquirer.prompt([{
|
|
417
|
+
type: 'input',
|
|
418
|
+
name: 'amount',
|
|
419
|
+
message: 'Enter Amount:'
|
|
420
|
+
}]);
|
|
421
|
+
amount = ans.amount;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Operation Type Selection (if not provided)
|
|
425
|
+
let operationType = options.type;
|
|
426
|
+
if (!operationType) {
|
|
427
|
+
if (isInteractive) {
|
|
428
|
+
operationType = await select({
|
|
429
|
+
message: 'Select Operation Type:',
|
|
430
|
+
choices: [
|
|
431
|
+
{ name: 'Reimburse (2)', value: 2 },
|
|
432
|
+
{ name: 'Allocate (1)', value: 1 },
|
|
433
|
+
{ name: 'Refund (4)', value: 4 },
|
|
434
|
+
{ name: 'Revert (5)', value: 5 }
|
|
435
|
+
],
|
|
436
|
+
default: 2
|
|
437
|
+
});
|
|
438
|
+
} else {
|
|
439
|
+
operationType = 2;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// 1. Get current credit details to find currency
|
|
444
|
+
let currency = options.currency;
|
|
445
|
+
if (!currency) {
|
|
446
|
+
try {
|
|
447
|
+
const creditData = await client.get(`V1/companyCredits/${creditId}`);
|
|
448
|
+
currency = creditData.currency_code;
|
|
449
|
+
} catch (e) {
|
|
450
|
+
console.warn(chalk.yellow('Could not fetch credit details to determine currency. Using interactive prompt.'));
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (!currency) {
|
|
455
|
+
const ans = await inquirer.prompt([{
|
|
456
|
+
type: 'input',
|
|
457
|
+
name: 'currency',
|
|
458
|
+
message: 'Enter currency code:',
|
|
459
|
+
default: 'USD'
|
|
460
|
+
}]);
|
|
461
|
+
currency = ans.currency;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const payload = {
|
|
465
|
+
value: parseFloat(amount),
|
|
466
|
+
currency: currency,
|
|
467
|
+
operationType: parseInt(operationType), // 1=Allocate, 2=Reimburse, 4=Refund, 5=Revert
|
|
468
|
+
comment: options.comment,
|
|
469
|
+
options: {
|
|
470
|
+
purchase_order: options.po,
|
|
471
|
+
order_increment: '',
|
|
472
|
+
currency_display: currency,
|
|
473
|
+
currency_base: currency
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
await client.post(`V1/companyCredits/${creditId}/increaseBalance`, payload);
|
|
478
|
+
console.log(chalk.green(`✅ Credit ${creditId} increased by ${amount} ${currency}`));
|
|
479
|
+
} catch (e) { handleError(e); }
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
credit.command('decrease [creditId] [amount]')
|
|
483
|
+
.description('Decrease balance')
|
|
484
|
+
.option('--currency <code>', 'Currency code')
|
|
485
|
+
.option('--type <number>', 'Operation Type (3=Purchase)')
|
|
486
|
+
.option('--comment <text>', 'Comment', 'Manual decrease')
|
|
487
|
+
.option('--po <number>', 'PO Number', '')
|
|
488
|
+
.action(async (creditId, amount, options) => {
|
|
489
|
+
try {
|
|
490
|
+
const client = await createClient();
|
|
491
|
+
const inquirer = (await import('inquirer')).default;
|
|
492
|
+
const { select } = await import('@inquirer/prompts');
|
|
493
|
+
|
|
494
|
+
let isInteractive = false;
|
|
495
|
+
if (!creditId) {
|
|
496
|
+
isInteractive = true;
|
|
497
|
+
const ans = await inquirer.prompt([{
|
|
498
|
+
type: 'input',
|
|
499
|
+
name: 'creditId',
|
|
500
|
+
message: 'Enter Credit ID:'
|
|
501
|
+
}]);
|
|
502
|
+
creditId = ans.creditId;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (!amount) {
|
|
506
|
+
isInteractive = true;
|
|
507
|
+
const ans = await inquirer.prompt([{
|
|
508
|
+
type: 'input',
|
|
509
|
+
name: 'amount',
|
|
510
|
+
message: 'Enter Amount:'
|
|
511
|
+
}]);
|
|
512
|
+
amount = ans.amount;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Operation Type Selection (if not provided)
|
|
516
|
+
let operationType = options.type;
|
|
517
|
+
if (!operationType) {
|
|
518
|
+
if (isInteractive) {
|
|
519
|
+
operationType = await select({
|
|
520
|
+
message: 'Select Operation Type:',
|
|
521
|
+
choices: [
|
|
522
|
+
{ name: 'Purchase (3)', value: 3 },
|
|
523
|
+
{ name: 'Reimburse (2)', value: 2 }
|
|
524
|
+
],
|
|
525
|
+
default: 3
|
|
526
|
+
});
|
|
527
|
+
} else {
|
|
528
|
+
operationType = 3;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// 1. Get current credit details to find currency
|
|
533
|
+
let currency = options.currency;
|
|
534
|
+
if (!currency) {
|
|
535
|
+
try {
|
|
536
|
+
const creditData = await client.get(`V1/companyCredits/${creditId}`);
|
|
537
|
+
currency = creditData.currency_code;
|
|
538
|
+
} catch (e) {
|
|
539
|
+
console.warn(chalk.yellow('Could not fetch credit details to determine currency. Using interactive prompt.'));
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (!currency) {
|
|
544
|
+
const ans = await inquirer.prompt([{
|
|
545
|
+
type: 'input',
|
|
546
|
+
name: 'currency',
|
|
547
|
+
message: 'Enter currency code:',
|
|
548
|
+
default: 'USD'
|
|
549
|
+
}]);
|
|
550
|
+
currency = ans.currency;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const payload = {
|
|
554
|
+
value: parseFloat(amount),
|
|
555
|
+
currency: currency,
|
|
556
|
+
operationType: parseInt(operationType),
|
|
557
|
+
comment: options.comment,
|
|
558
|
+
options: {
|
|
559
|
+
purchase_order: options.po,
|
|
560
|
+
order_increment: '',
|
|
561
|
+
currency_display: currency,
|
|
562
|
+
currency_base: currency
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
await client.post(`V1/companyCredits/${creditId}/decreaseBalance`, payload);
|
|
567
|
+
console.log(chalk.green(`✅ Credit ${creditId} decreased by ${amount} ${currency}`));
|
|
568
|
+
} catch (e) { handleError(e); }
|
|
569
|
+
});
|
|
135
570
|
}
|
package/lib/commands/console.js
CHANGED
|
@@ -18,12 +18,14 @@ export function registerConsoleCommand(program) {
|
|
|
18
18
|
console.log(chalk.gray('Debug mode enabled'));
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
22
|
+
console.log(chalk.bold.blue('Mage Remote Run Interactive Console'));
|
|
23
|
+
console.log(chalk.gray('Type your commands directly or write JS code.'));
|
|
24
|
+
console.log(chalk.gray('Global variables available: client (async factory), config, chalk'));
|
|
25
|
+
console.log(chalk.gray('Example JS: await (await client()).get("V1/store/websites")'));
|
|
26
|
+
console.log(chalk.gray('Type "list" to see available commands.'));
|
|
27
|
+
console.log(chalk.gray('Type .exit to quit.\n'));
|
|
28
|
+
}
|
|
27
29
|
|
|
28
30
|
// State for the REPL
|
|
29
31
|
let localProgram;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createClient } from '../api/factory.js';
|
|
2
|
+
import { printTable, handleError } from '../utils.js';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
export function registerPurchaseOrderCartCommands(program) {
|
|
6
|
+
const poCart = program.command('po-cart').description('Manage Purchase Order Carts');
|
|
7
|
+
|
|
8
|
+
//-------------------------------------------------------
|
|
9
|
+
// "po-cart totals" Command
|
|
10
|
+
//-------------------------------------------------------
|
|
11
|
+
poCart.command('totals <cartId>')
|
|
12
|
+
.description('Get purchase order cart totals')
|
|
13
|
+
.action(async (cartId) => {
|
|
14
|
+
try {
|
|
15
|
+
const client = await createClient();
|
|
16
|
+
const data = await client.get(`V1/purchase-order-carts/${cartId}/totals`);
|
|
17
|
+
|
|
18
|
+
console.log(chalk.bold('Totals:'));
|
|
19
|
+
const rows = (data.total_segments || []).map(t => [t.title, t.value]);
|
|
20
|
+
printTable(['Title', 'Value'], rows);
|
|
21
|
+
|
|
22
|
+
console.log(chalk.bold(`\nGrand Total: ${data.grand_total} ${data.quote_currency_code}`));
|
|
23
|
+
} catch (e) { handleError(e); }
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
//-------------------------------------------------------
|
|
27
|
+
// "po-cart shipping-methods" Command
|
|
28
|
+
//-------------------------------------------------------
|
|
29
|
+
poCart.command('shipping-methods <cartId>')
|
|
30
|
+
.description('Estimate shipping methods (requires address ID)')
|
|
31
|
+
.requiredOption('--address-id <id>', 'Address ID to estimate for')
|
|
32
|
+
.action(async (cartId, options) => {
|
|
33
|
+
try {
|
|
34
|
+
const client = await createClient();
|
|
35
|
+
const payload = { addressId: options.addressId };
|
|
36
|
+
const data = await client.post(`V1/purchase-order-carts/${cartId}/estimate-shipping-methods-by-address-id`, payload);
|
|
37
|
+
|
|
38
|
+
console.log(chalk.bold('Shipping Methods:'));
|
|
39
|
+
const rows = data.map(m => [m.carrier_title, m.method_title, m.amount, m.price_excl_tax]);
|
|
40
|
+
printTable(['Carrier', 'Method', 'Amount', 'Price Excl Tax'], rows);
|
|
41
|
+
} catch (e) { handleError(e); }
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
//-------------------------------------------------------
|
|
45
|
+
// "po-cart payment-info" Command
|
|
46
|
+
//-------------------------------------------------------
|
|
47
|
+
poCart.command('payment-info <cartId>')
|
|
48
|
+
.description('Get payment information')
|
|
49
|
+
.action(async (cartId) => {
|
|
50
|
+
try {
|
|
51
|
+
const client = await createClient();
|
|
52
|
+
const data = await client.get(`V1/purchase-order-carts/${cartId}/payment-information`);
|
|
53
|
+
|
|
54
|
+
console.log(chalk.bold('Payment Methods:'));
|
|
55
|
+
const methods = data.payment_methods || [];
|
|
56
|
+
methods.forEach(m => console.log(`- ${m.title} (${m.code})`));
|
|
57
|
+
|
|
58
|
+
console.log(chalk.bold('\nTotals:'));
|
|
59
|
+
const totals = data.totals || {};
|
|
60
|
+
console.log(`Grand Total: ${totals.grand_total} ${totals.quote_currency_code}`);
|
|
61
|
+
} catch (e) { handleError(e); }
|
|
62
|
+
});
|
|
63
|
+
}
|
package/lib/utils.js
CHANGED
|
@@ -11,7 +11,33 @@ export function printTable(headers, data) {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function handleError(error) {
|
|
14
|
-
|
|
14
|
+
let message = error.message;
|
|
15
|
+
|
|
16
|
+
// specific handling for Magento API Errors which are often JSON stringified
|
|
17
|
+
// Format: "API Error 404: {...}"
|
|
18
|
+
const apiErrorMatch = message.match(/^API Error (\d+): (.+)$/);
|
|
19
|
+
if (apiErrorMatch) {
|
|
20
|
+
const statusCode = apiErrorMatch[1];
|
|
21
|
+
const jsonPart = apiErrorMatch[2];
|
|
22
|
+
try {
|
|
23
|
+
const parsed = JSON.parse(jsonPart);
|
|
24
|
+
if (parsed.message) {
|
|
25
|
+
let prettyMessage = parsed.message;
|
|
26
|
+
if (parsed.parameters) {
|
|
27
|
+
// Substitute %fieldName with values
|
|
28
|
+
Object.keys(parsed.parameters).forEach(key => {
|
|
29
|
+
prettyMessage = prettyMessage.replace(new RegExp(`%${key}`, 'g'), parsed.parameters[key]);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
message = `${prettyMessage}`;
|
|
33
|
+
// Optional: append status code if not 200? The user request just showed the clean message.
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {
|
|
36
|
+
// If parsing fails, keep original message
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.error(chalk.red('Error:'), message);
|
|
15
41
|
if (process.env.DEBUG) {
|
|
16
42
|
console.error(error);
|
|
17
43
|
}
|