vps-work 2.5.0 → 2.5.1
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/dist/packages/vps-work/src/bin/vps-work.js +271 -0
- package/dist/packages/vps-work/src/commands/agent/index.js +264 -0
- package/dist/packages/vps-work/src/commands/approval/index.js +7 -0
- package/dist/packages/vps-work/src/commands/approval/shortcuts.js +259 -0
- package/dist/packages/vps-work/src/commands/attendance/applications.js +21 -0
- package/dist/packages/vps-work/src/commands/attendance/index.js +9 -0
- package/dist/packages/vps-work/src/commands/attendance/scheduling.js +183 -0
- package/dist/packages/vps-work/src/commands/attendance/shortcuts.js +131 -0
- package/dist/packages/vps-work/src/commands/auth/check.js +34 -0
- package/dist/packages/vps-work/src/commands/auth/index.js +21 -0
- package/dist/packages/vps-work/src/commands/auth/list.js +50 -0
- package/dist/packages/vps-work/src/commands/auth/scopes.js +17 -0
- package/dist/packages/vps-work/src/commands/auth/status.js +127 -0
- package/dist/packages/vps-work/src/commands/auth/tenants.js +35 -0
- package/dist/packages/vps-work/src/commands/auth/use.js +40 -0
- package/dist/packages/vps-work/src/commands/calendar/index.js +7 -0
- package/dist/packages/vps-work/src/commands/calendar/shortcuts.js +598 -0
- package/dist/packages/vps-work/src/commands/catalog.js +29 -0
- package/dist/packages/vps-work/src/commands/cli-install.js +70 -0
- package/dist/packages/vps-work/src/commands/collab-invoke.js +19 -0
- package/dist/packages/vps-work/src/commands/corporate/index.js +204 -0
- package/dist/packages/vps-work/src/commands/creative/index.js +369 -0
- package/dist/packages/vps-work/src/commands/documents/index.js +991 -0
- package/dist/packages/vps-work/src/commands/drive/index.js +435 -0
- package/dist/packages/vps-work/src/commands/exec.js +46 -0
- package/dist/packages/vps-work/src/commands/hr/business.js +22 -0
- package/dist/packages/vps-work/src/commands/hr/data-view-shortcuts.js +519 -0
- package/dist/packages/vps-work/src/commands/hr/index.js +15 -0
- package/dist/packages/vps-work/src/commands/hr/job-responsibilities.js +266 -0
- package/dist/packages/vps-work/src/commands/hr/payslip.js +45 -0
- package/dist/packages/vps-work/src/commands/hr/shortcuts.js +146 -0
- package/dist/packages/vps-work/src/commands/im/im-message-payload.js +150 -0
- package/dist/packages/vps-work/src/commands/im/index.js +7 -0
- package/dist/packages/vps-work/src/commands/im/shortcuts.js +856 -0
- package/dist/packages/vps-work/src/commands/knowledge/index.js +36 -0
- package/dist/packages/vps-work/src/commands/login.js +52 -0
- package/dist/packages/vps-work/src/commands/logout.js +12 -0
- package/dist/packages/vps-work/src/commands/mail/email.js +132 -0
- package/dist/packages/vps-work/src/commands/mail/index.js +9 -0
- package/dist/packages/vps-work/src/commands/mail/shortcuts.js +43 -0
- package/dist/packages/vps-work/src/commands/meeting/index.js +7 -0
- package/dist/packages/vps-work/src/commands/meeting/shortcuts.js +120 -0
- package/dist/packages/vps-work/src/commands/odoo-invoke.js +21 -0
- package/dist/packages/vps-work/src/commands/odoo-leaf-registry.js +13 -0
- package/dist/packages/vps-work/src/commands/office/index.js +59 -0
- package/dist/packages/vps-work/src/commands/okr/index.js +7 -0
- package/dist/packages/vps-work/src/commands/okr/shortcuts.js +75 -0
- package/dist/packages/vps-work/src/commands/org-structure.js +347 -0
- package/dist/packages/vps-work/src/commands/popup/index.js +147 -0
- package/dist/packages/vps-work/src/commands/product/abnormal-statistics.js +250 -0
- package/dist/packages/vps-work/src/commands/product/data-view-shortcuts.js +253 -0
- package/dist/packages/vps-work/src/commands/product/index.js +11 -0
- package/dist/packages/vps-work/src/commands/purchase/index.js +45 -0
- package/dist/packages/vps-work/src/commands/reauth.js +127 -0
- package/dist/packages/vps-work/src/commands/report/index.js +7 -0
- package/dist/packages/vps-work/src/commands/report/missing-daily-reports.js +9 -0
- package/dist/packages/vps-work/src/commands/report/shortcuts.js +251 -0
- package/dist/packages/vps-work/src/commands/sales/index.js +11 -0
- package/dist/packages/vps-work/src/commands/sales/period-params.js +75 -0
- package/dist/packages/vps-work/src/commands/sales/shortcuts-personnel.js +124 -0
- package/dist/packages/vps-work/src/commands/sales/shortcuts.js +800 -0
- package/dist/packages/vps-work/src/commands/shortcut-common.js +88 -0
- package/dist/packages/vps-work/src/commands/shortcut-params.js +22 -0
- package/dist/packages/vps-work/src/commands/skill/index.js +136 -0
- package/dist/packages/vps-work/src/commands/skill/personal.js +167 -0
- package/dist/packages/vps-work/src/commands/store/index.js +82 -0
- package/dist/packages/vps-work/src/commands/task/index.js +7 -0
- package/dist/packages/vps-work/src/commands/task/task-center.js +595 -0
- package/dist/packages/vps-work/src/commands/tools/index.js +127 -0
- package/dist/packages/vps-work/src/commands/vemory/index.js +7 -0
- package/dist/packages/vps-work/src/commands/vemory/shortcuts.js +53 -0
- package/dist/packages/vps-work/src/commands/vertu-aisales.js +30 -0
- package/dist/packages/vps-work/src/commands/vertu-erp.js +30 -0
- package/dist/packages/vps-work/src/core/aisales-mcp-client.js +35 -0
- package/dist/packages/vps-work/src/core/auth.js +240 -0
- package/dist/packages/vps-work/src/core/cli-bin.js +11 -0
- package/dist/packages/vps-work/src/core/cli-root-help.js +190 -0
- package/dist/packages/vps-work/src/core/client.js +158 -0
- package/dist/packages/vps-work/src/core/config.js +183 -0
- package/dist/packages/vps-work/src/core/corporate-client.js +52 -0
- package/dist/packages/vps-work/src/core/creative-attachment-download.js +48 -0
- package/dist/packages/vps-work/src/core/documents-client.js +57 -0
- package/dist/packages/vps-work/src/core/endpoints.js +46 -0
- package/dist/packages/vps-work/src/core/erp-mcp-client.js +11 -0
- package/dist/packages/vps-work/src/core/errors.js +107 -0
- package/dist/packages/vps-work/src/core/execute-catalog-client.js +42 -0
- package/dist/packages/vps-work/src/core/hermes-client.js +70 -0
- package/dist/packages/vps-work/src/core/im-attachment-download.js +109 -0
- package/dist/packages/vps-work/src/core/im-client.js +64 -0
- package/dist/packages/vps-work/src/core/keychain.js +58 -0
- package/dist/packages/vps-work/src/core/mail-client.js +82 -0
- package/dist/packages/vps-work/src/core/mcp-client.js +129 -0
- package/dist/packages/vps-work/src/core/office-client.js +25 -0
- package/dist/packages/vps-work/src/core/office-files.js +82 -0
- package/dist/packages/vps-work/src/core/paths.js +70 -0
- package/dist/packages/vps-work/src/core/public-result.js +43 -0
- package/dist/packages/vps-work/src/core/sales-invoice-attachments-client.js +75 -0
- package/dist/packages/vps-work/src/core/sales-query-client.js +452 -0
- package/dist/packages/vps-work/src/core/skill-catalog.js +158 -0
- package/dist/packages/vps-work/src/core/skill-key.js +19 -0
- package/dist/packages/vps-work/src/core/v-drive-store.js +392 -0
- package/dist/packages/vps-work/src/core/vertu-agent-env.js +32 -0
- package/dist/packages/vps-work/src/core/vps-auth-scopes.js +73 -0
- package/dist/packages/vps-work/src/core/vps-service-auth.js +382 -0
- package/dist/packages/vps-work/src/core/vps-service-config.js +83 -0
- package/dist/packages/vps-work/src/core/vps-service-http.js +187 -0
- package/dist/packages/vps-work/src/utils/at-file.js +34 -0
- package/dist/packages/vps-work/src/utils/cli-option-helpers.js +52 -0
- package/dist/packages/vps-work/src/utils/frontmatter.js +26 -0
- package/dist/packages/vps-work/src/utils/normalize-argv.js +51 -0
- package/dist/packages/vps-work/src/utils/parse-json-arg.js +85 -0
- package/dist/packages/vps-work/src/utils/prompt.js +46 -0
- package/dist/packages/vps-work/src/utils/read-text.js +44 -0
- package/dist/packages/vps-work/src/utils/schema-to-options.js +307 -0
- package/dist/packages/vps-work/src/utils/unwrap-envelope.js +31 -0
- package/dist/shared/im-org-tree/index.js +200 -0
- package/dist/vps-work.cjs +3 -3
- package/package.json +1 -1
- package/skills/vertu-aisales/SKILL.md +4 -2
|
@@ -0,0 +1,800 @@
|
|
|
1
|
+
import { attachSharedShortcutOptions, parseOptionalInt, resolveShortcutVpsServiceUrl, runSkillShortcutExecute, } from '../shortcut-common.js';
|
|
2
|
+
import { VertuError } from '../../core/errors.js';
|
|
3
|
+
import { buildSalesPeriodParams, dailySalesSkillKey, } from './period-params.js';
|
|
4
|
+
import { downloadSalesInvoiceAttachments, listSalesInvoiceAttachments, } from '../../core/sales-invoice-attachments-client.js';
|
|
5
|
+
function parseOptionalBoolean(raw, flag) {
|
|
6
|
+
if (raw === undefined || raw.trim() === '') {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const normalized = raw.trim().toLowerCase();
|
|
10
|
+
if (normalized === 'true' || normalized === '1') {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
if (normalized === 'false' || normalized === '0') {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
throw new VertuError(`${flag} 必须是 true 或 false。`);
|
|
17
|
+
}
|
|
18
|
+
function withOkrSource(params, opts) {
|
|
19
|
+
if (opts.okrSource) {
|
|
20
|
+
params.okr_source = opts.okrSource;
|
|
21
|
+
}
|
|
22
|
+
return params;
|
|
23
|
+
}
|
|
24
|
+
function periodOptions(cmd) {
|
|
25
|
+
return cmd
|
|
26
|
+
.option('--period <name>', 'today | this_week | last_week | this_month | last_month | last_n_days | YYYY-MM-DD | …')
|
|
27
|
+
.option('--start-date <date>', 'YYYY-MM-DD;单独传表示单日,配合 --end-date 表示范围')
|
|
28
|
+
.option('--end-date <date>', 'YYYY-MM-DD;单独传表示单日,配合 --start-date 表示范围')
|
|
29
|
+
.option('--days <n>', 'With period=last_n_days')
|
|
30
|
+
.option('--dept-l1 <name>', '一级部门')
|
|
31
|
+
.option('--dept-l2 <name>', '二级部门')
|
|
32
|
+
.option('--dept-l3 <name>', '三级部门')
|
|
33
|
+
.option('--dept-l4 <name>', '四级部门')
|
|
34
|
+
.option('--dept-l5 <name>', '五级部门')
|
|
35
|
+
.option('--department-mode <mode>', 'matched=匹配部门字段口径;raw=原始组织层级口径')
|
|
36
|
+
.option('--limit <n>', 'Max rows')
|
|
37
|
+
.option('--include-deposit', '含定金口径(默认不含)', false)
|
|
38
|
+
.option('--product-subcategory <name>', '商品细类精确过滤')
|
|
39
|
+
.option('--product-subcategories <names>', '多个商品细类,逗号分隔并精确匹配')
|
|
40
|
+
.option('--product-category <name>', '商品分类精确过滤,如 手机 / IOT配件')
|
|
41
|
+
.option('--product-major-categories <names>', '商品大类,逗号分隔,如 戒指,手链')
|
|
42
|
+
.option('--product-style <name>', '款式精确过滤,如 高定')
|
|
43
|
+
.option('--product-style-details <names>', '款式1,逗号分隔,如 黄金3,黄金4,金钻')
|
|
44
|
+
.option('--smart-wearable', '智能穿戴口径:戒指/手链 + vps-ep 系列白名单', false);
|
|
45
|
+
}
|
|
46
|
+
function customerRegionPeriodOptions(cmd) {
|
|
47
|
+
return cmd
|
|
48
|
+
.option('--period <name>', 'today | this_week | last_week | this_month | last_month | last_n_days | YYYY-MM-DD | …')
|
|
49
|
+
.option('--start-date <date>', 'YYYY-MM-DD;单独传表示单日,配合 --end-date 表示范围')
|
|
50
|
+
.option('--end-date <date>', 'YYYY-MM-DD;单独传表示单日,配合 --start-date 表示范围')
|
|
51
|
+
.option('--days <n>', 'With period=last_n_days')
|
|
52
|
+
.option('--country <name>', '客户国家/地区名称或国区号,如 马来西亚 / 60')
|
|
53
|
+
.option('--include-deposit', '含定金口径(默认不含)', false);
|
|
54
|
+
}
|
|
55
|
+
function dealerOrderPeriodOptions(cmd) {
|
|
56
|
+
return cmd
|
|
57
|
+
.option('--period <name>', 'today | this_week | last_week | this_month | last_month | last_n_days | YYYY-MM-DD | …')
|
|
58
|
+
.option('--start-date <date>', 'YYYY-MM-DD;单独传表示单日,配合 --end-date 表示范围')
|
|
59
|
+
.option('--end-date <date>', 'YYYY-MM-DD;单独传表示单日,配合 --start-date 表示范围')
|
|
60
|
+
.option('--days <n>', 'With period=last_n_days')
|
|
61
|
+
.option('--limit <n>', 'Max rows');
|
|
62
|
+
}
|
|
63
|
+
function accountOrderPeriodOptions(cmd) {
|
|
64
|
+
return cmd
|
|
65
|
+
.option('--period <name>', 'today | this_week | last_week | this_month | last_month | last_n_days | YYYY-MM-DD | …')
|
|
66
|
+
.option('--start-date <date>', '会计日期 YYYY-MM-DD;可与 --end-date 组成闭区间')
|
|
67
|
+
.option('--end-date <date>', '会计日期 YYYY-MM-DD;可与 --start-date 组成闭区间')
|
|
68
|
+
.option('--days <n>', 'With period=last_n_days')
|
|
69
|
+
.option('--dept-l1 <name>', '一级部门')
|
|
70
|
+
.option('--dept-l2 <name>', '二级部门')
|
|
71
|
+
.option('--dept-l3 <name>', '三级部门')
|
|
72
|
+
.option('--dept-l4 <name>', '四级部门')
|
|
73
|
+
.option('--dept-l5 <name>', '五级部门')
|
|
74
|
+
.option('--limit <n>', 'Max rows');
|
|
75
|
+
}
|
|
76
|
+
function addAgentFilter(params, opts) {
|
|
77
|
+
if (opts.dealerId !== undefined) {
|
|
78
|
+
if (!/^[1-9]\d*$/.test(opts.dealerId) || Number(opts.dealerId) > 2147483647)
|
|
79
|
+
throw new Error('--dealer-id 必须为正整数');
|
|
80
|
+
params.dealer_id = Number(opts.dealerId);
|
|
81
|
+
}
|
|
82
|
+
if (opts.agentName)
|
|
83
|
+
params.agent_name = opts.agentName;
|
|
84
|
+
else if (opts.dealer)
|
|
85
|
+
params.dealer = opts.dealer;
|
|
86
|
+
}
|
|
87
|
+
/** Features · 销售与经营 — `vps-work sales +…` → vps-daily-sales-report */
|
|
88
|
+
export function attachSalesShortcuts(parent) {
|
|
89
|
+
const skill = dailySalesSkillKey();
|
|
90
|
+
attachSharedShortcutOptions(parent
|
|
91
|
+
.command('+order-finance')
|
|
92
|
+
.alias('+finance-detail')
|
|
93
|
+
.description('按销售单号汇总订单、开票申请、正式发票和关联会计凭证')
|
|
94
|
+
.requiredOption('--order-no <no>', '销售单号,精确匹配')
|
|
95
|
+
.option('--invoice-no <no>', '发票/凭证号包含匹配')).action(async (opts, cmd) => {
|
|
96
|
+
const orderNo = opts.orderNo?.trim();
|
|
97
|
+
if (!orderNo)
|
|
98
|
+
throw new VertuError('--order-no is required.');
|
|
99
|
+
const params = { order_no: orderNo };
|
|
100
|
+
if (opts.invoiceNo?.trim())
|
|
101
|
+
params.invoice_no = opts.invoiceNo.trim();
|
|
102
|
+
await runSkillShortcutExecute(skill, 'order_finance', params, opts, cmd);
|
|
103
|
+
});
|
|
104
|
+
attachSharedShortcutOptions(parent
|
|
105
|
+
.command('+invoice-attachments')
|
|
106
|
+
.description('查询销售单关联的发票附件;可选下载到本地目录')
|
|
107
|
+
.requiredOption('--order-no <no>', '销售单号,精确匹配')
|
|
108
|
+
.option('--download-dir <path>', '下载全部附件到本地目录(不覆盖已有文件)')).action(async (opts, cmd) => {
|
|
109
|
+
const orderNo = opts.orderNo?.trim();
|
|
110
|
+
if (!orderNo)
|
|
111
|
+
throw new VertuError('--order-no is required.');
|
|
112
|
+
const serviceBaseUrl = resolveShortcutVpsServiceUrl(opts, cmd);
|
|
113
|
+
if (opts.validateOnly === true || opts.dryRun === true) {
|
|
114
|
+
console.log(JSON.stringify({
|
|
115
|
+
validateOnly: true,
|
|
116
|
+
endpoint: `${serviceBaseUrl}/v1/sales/invoice-attachments`,
|
|
117
|
+
order_no: orderNo,
|
|
118
|
+
download_dir: opts.downloadDir ?? null,
|
|
119
|
+
}, null, 2));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const result = await listSalesInvoiceAttachments(serviceBaseUrl, orderNo);
|
|
123
|
+
const downloads = opts.downloadDir
|
|
124
|
+
? await downloadSalesInvoiceAttachments(serviceBaseUrl, result.attachments, opts.downloadDir)
|
|
125
|
+
: undefined;
|
|
126
|
+
console.log(JSON.stringify(downloads ? { ...result, downloads } : result, null, 2));
|
|
127
|
+
});
|
|
128
|
+
attachSharedShortcutOptions(periodOptions(parent
|
|
129
|
+
.command('+headline-kpi')
|
|
130
|
+
.description('公司核心 KPI:销量/销额/订单(默认 period=today)')
|
|
131
|
+
.option('--include-today', 'Include today segment in response', true)
|
|
132
|
+
.option('--name <text>', '销售人员姓名 ILIKE')
|
|
133
|
+
.option('--salesperson-like <text>', '销售人员姓名 ILIKE')
|
|
134
|
+
.option('--work-id <code>', '工号')
|
|
135
|
+
.option('--barcode <code>', '工号'))).action(async (opts, cmd) => {
|
|
136
|
+
const p = buildSalesPeriodParams(opts, 'today');
|
|
137
|
+
if (opts.includeToday === false) {
|
|
138
|
+
p.include_today = false;
|
|
139
|
+
}
|
|
140
|
+
const salespersonLike = opts.salespersonLike ?? opts.name;
|
|
141
|
+
if (salespersonLike) {
|
|
142
|
+
p.salesperson_like = salespersonLike;
|
|
143
|
+
}
|
|
144
|
+
const workId = opts.workId ?? opts.barcode;
|
|
145
|
+
if (workId) {
|
|
146
|
+
p.work_id = workId;
|
|
147
|
+
}
|
|
148
|
+
await runSkillShortcutExecute(skill, 'headline_kpi', p, opts, cmd);
|
|
149
|
+
});
|
|
150
|
+
attachSharedShortcutOptions(periodOptions(parent
|
|
151
|
+
.command('+orders')
|
|
152
|
+
.alias('+order-list')
|
|
153
|
+
.description('订单与商品销量金额:默认按订单;可按订单-商品销售明细、期间商品或商品销售人员汇总(默认 period=today)')
|
|
154
|
+
.option('--vsn <text>', '商品 VSN 包含')
|
|
155
|
+
.option('--imei <text>', 'IMEI1 / IMEI2 包含')
|
|
156
|
+
.option('--activation-address <text>', '激活国家/省市/地址原文包含,需客户信息权限')
|
|
157
|
+
.option('--order-no <no>', '订单号包含')
|
|
158
|
+
.option('--logistics-no <no>', '物流单号 / 出库单号包含')
|
|
159
|
+
.option('--product <text>', '商品名称 / 编码 / 分类包含')
|
|
160
|
+
.option('--product-code <code>', '商品编码精确匹配')
|
|
161
|
+
.option('--group-by <mode>', '结果粒度:order(默认)/ order-product / product / product-salesperson')
|
|
162
|
+
.option('--customer <text>', '客户名称或可见手机号包含')
|
|
163
|
+
.option('--salesperson <name>', '销售人员姓名 / 工号包含')
|
|
164
|
+
.option('--channel <name>', '渠道包含')
|
|
165
|
+
.option('--order-state <state>', '订单状态包含,如 sale / partial_payment')
|
|
166
|
+
.option('--order-type <type>', '订单类型包含,如 订单 / 赠品 / 退货退款')
|
|
167
|
+
.option('--is-apportioned <boolean>', '是否分摊:true|false')
|
|
168
|
+
.option('--page <n>', '页码,从 1 开始', '1'))).action(async (opts, cmd) => {
|
|
169
|
+
const p = buildSalesPeriodParams(opts, 'today');
|
|
170
|
+
if (opts.vsn)
|
|
171
|
+
p.vsn = opts.vsn;
|
|
172
|
+
if (opts.imei)
|
|
173
|
+
p.imei = opts.imei;
|
|
174
|
+
if (opts.activationAddress)
|
|
175
|
+
p.activation_address = opts.activationAddress;
|
|
176
|
+
if (opts.orderNo) {
|
|
177
|
+
p.order_no = opts.orderNo;
|
|
178
|
+
}
|
|
179
|
+
if (opts.logisticsNo) {
|
|
180
|
+
p.logistics_no = opts.logisticsNo;
|
|
181
|
+
}
|
|
182
|
+
if (opts.product) {
|
|
183
|
+
p.product = opts.product;
|
|
184
|
+
}
|
|
185
|
+
if (opts.productCode)
|
|
186
|
+
p.product_code = opts.productCode;
|
|
187
|
+
if (opts.groupBy)
|
|
188
|
+
p.group_by = opts.groupBy.replaceAll('-', '_');
|
|
189
|
+
if (opts.customer) {
|
|
190
|
+
p.customer = opts.customer;
|
|
191
|
+
}
|
|
192
|
+
if (opts.salesperson) {
|
|
193
|
+
p.salesperson = opts.salesperson;
|
|
194
|
+
}
|
|
195
|
+
if (opts.channel) {
|
|
196
|
+
p.channel = opts.channel;
|
|
197
|
+
}
|
|
198
|
+
if (opts.orderState) {
|
|
199
|
+
p.order_state = opts.orderState;
|
|
200
|
+
}
|
|
201
|
+
if (opts.orderType) {
|
|
202
|
+
p.order_type = opts.orderType;
|
|
203
|
+
}
|
|
204
|
+
const isApportioned = parseOptionalBoolean(opts.isApportioned, '--is-apportioned');
|
|
205
|
+
if (isApportioned !== undefined) {
|
|
206
|
+
p.is_apportioned = isApportioned;
|
|
207
|
+
}
|
|
208
|
+
const page = parseOptionalInt(opts.page, '--page');
|
|
209
|
+
if (page !== undefined) {
|
|
210
|
+
p.page = page;
|
|
211
|
+
}
|
|
212
|
+
await runSkillShortcutExecute(skill, 'order_list', p, opts, cmd);
|
|
213
|
+
});
|
|
214
|
+
attachSharedShortcutOptions(accountOrderPeriodOptions(parent
|
|
215
|
+
.command('+account-orders')
|
|
216
|
+
.alias('+account-sales-detail')
|
|
217
|
+
.description('按会计科目反查凭证关联的销售订单行(默认 period=this_month,仅已过账)')
|
|
218
|
+
.option('--account-code <code>', '会计科目编码精确匹配')
|
|
219
|
+
.option('--account-name <name>', '会计科目名称包含匹配')
|
|
220
|
+
.option('--move-no <no>', '会计凭证号包含')
|
|
221
|
+
.option('--order-no <no>', '销售单号包含')
|
|
222
|
+
.option('--include-draft', '包含未过账凭证', false)
|
|
223
|
+
.option('--page <n>', '页码,从 1 开始', '1'))).action(async (opts, cmd) => {
|
|
224
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
225
|
+
const mappings = [
|
|
226
|
+
['accountCode', 'account_code'],
|
|
227
|
+
['accountName', 'account_name'],
|
|
228
|
+
['moveNo', 'move_no'],
|
|
229
|
+
['orderNo', 'order_no'],
|
|
230
|
+
];
|
|
231
|
+
for (const [option, param] of mappings) {
|
|
232
|
+
const value = opts[option];
|
|
233
|
+
if (typeof value === 'string' && value) {
|
|
234
|
+
p[param] = value;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (opts.includeDraft) {
|
|
238
|
+
p.include_draft = true;
|
|
239
|
+
}
|
|
240
|
+
const page = parseOptionalInt(opts.page, '--page');
|
|
241
|
+
if (page !== undefined) {
|
|
242
|
+
p.page = page;
|
|
243
|
+
}
|
|
244
|
+
await runSkillShortcutExecute(skill, 'account_sales_detail', p, opts, cmd);
|
|
245
|
+
});
|
|
246
|
+
attachSharedShortcutOptions(parent
|
|
247
|
+
.command('+invoice-status')
|
|
248
|
+
.alias('+invoice-applications')
|
|
249
|
+
.description('按销售单、FPSQ 申请单或购买方查询开票申请状态、记录 ID 和详情链接')
|
|
250
|
+
.option('--application-names <names>', 'FPSQ 申请单号精确匹配;逗号分隔,最多100个')
|
|
251
|
+
.option('--buyer <name>', '购买方名称包含匹配,与单号条件取交集')
|
|
252
|
+
.option('--order-names <names>', '销售单号精确匹配;多个单号用逗号分隔,最多100个')
|
|
253
|
+
.option('--limit <n>', '返回行数,1-1000,默认500')
|
|
254
|
+
.option('--offset <n>', '结果偏移,0-4999,默认0')).action(async (opts, cmd) => {
|
|
255
|
+
const orderNames = String(opts.orderNames ?? '').split(',').map((item) => item.trim()).filter(Boolean);
|
|
256
|
+
const applicationNames = String(opts.applicationNames ?? '').split(',').map((item) => item.trim()).filter(Boolean);
|
|
257
|
+
const buyer = opts.buyer?.trim();
|
|
258
|
+
if (!orderNames.length && !applicationNames.length && !buyer) {
|
|
259
|
+
throw new VertuError('请提供 --order-names、--application-names 或 --buyer。');
|
|
260
|
+
}
|
|
261
|
+
const p = {};
|
|
262
|
+
if (orderNames.length)
|
|
263
|
+
p.order_names = orderNames;
|
|
264
|
+
if (applicationNames.length)
|
|
265
|
+
p.application_names = applicationNames;
|
|
266
|
+
if (buyer)
|
|
267
|
+
p.buyer = buyer;
|
|
268
|
+
for (const [option, param, flag] of [
|
|
269
|
+
['limit', 'limit', '--limit'],
|
|
270
|
+
['offset', 'offset', '--offset'],
|
|
271
|
+
]) {
|
|
272
|
+
const value = parseOptionalInt(opts[option], flag);
|
|
273
|
+
if (value !== undefined)
|
|
274
|
+
p[param] = value;
|
|
275
|
+
}
|
|
276
|
+
await runSkillShortcutExecute(skill, 'sales_invoice_status', p, opts, cmd);
|
|
277
|
+
});
|
|
278
|
+
attachSharedShortcutOptions(parent
|
|
279
|
+
.command('+reconciled-receipts')
|
|
280
|
+
.alias('+sales-receipts')
|
|
281
|
+
.description('最近30天已对账销售单据的收款明细(固定排除有效退货)')
|
|
282
|
+
.option('--order-types <types>', 'sale,service,repair,jky;逗号分隔,默认全部')
|
|
283
|
+
.option('--order-names <names>', '销售单号精确匹配;逗号分隔')
|
|
284
|
+
.option('--salesperson <name>', '销售员名称包含')
|
|
285
|
+
.option('--department <name>', '部门名称包含')
|
|
286
|
+
.option('--company <name>', '单据所属公司名称包含')
|
|
287
|
+
.option('--invoiced <boolean>', 'true=存在开票申请,false=不存在开票申请(不表示真实申请状态)')
|
|
288
|
+
.option('--exclude-payment-methods <keywords>', '排除收款方式关键词;逗号分隔,传空字符串表示不排除')
|
|
289
|
+
.option('--limit <n>', '返回行数,1-1000,默认500')
|
|
290
|
+
.option('--row-limit <n>', 'limit 的兼容别名')
|
|
291
|
+
.option('--offset <n>', 'SQL行偏移,0-4999,默认0')).action(async (opts, cmd) => {
|
|
292
|
+
const p = {};
|
|
293
|
+
const listMappings = [
|
|
294
|
+
['orderTypes', 'order_types'],
|
|
295
|
+
['orderNames', 'order_names'],
|
|
296
|
+
['excludePaymentMethods', 'exclude_payment_methods'],
|
|
297
|
+
];
|
|
298
|
+
for (const [option, param] of listMappings) {
|
|
299
|
+
const value = opts[option];
|
|
300
|
+
if (typeof value === 'string')
|
|
301
|
+
p[param] = value.split(',').map((item) => item.trim()).filter(Boolean);
|
|
302
|
+
}
|
|
303
|
+
for (const [option, param] of [
|
|
304
|
+
['salesperson', 'salesperson'],
|
|
305
|
+
['department', 'department'],
|
|
306
|
+
['company', 'company'],
|
|
307
|
+
]) {
|
|
308
|
+
const value = opts[option];
|
|
309
|
+
if (value)
|
|
310
|
+
p[param] = value;
|
|
311
|
+
}
|
|
312
|
+
if (opts.invoiced !== undefined) {
|
|
313
|
+
if (opts.invoiced !== 'true' && opts.invoiced !== 'false') {
|
|
314
|
+
throw new VertuError('Invalid --invoiced: expected true or false.');
|
|
315
|
+
}
|
|
316
|
+
p.invoiced = opts.invoiced === 'true';
|
|
317
|
+
}
|
|
318
|
+
for (const [option, param, flag] of [
|
|
319
|
+
['limit', 'limit', '--limit'],
|
|
320
|
+
['rowLimit', 'row_limit', '--row-limit'],
|
|
321
|
+
['offset', 'offset', '--offset'],
|
|
322
|
+
]) {
|
|
323
|
+
const value = parseOptionalInt(opts[option], flag);
|
|
324
|
+
if (value !== undefined)
|
|
325
|
+
p[param] = value;
|
|
326
|
+
}
|
|
327
|
+
await runSkillShortcutExecute(skill, 'reconciled_sales_receipts', p, opts, cmd);
|
|
328
|
+
});
|
|
329
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent
|
|
330
|
+
.command('+dealer-orders')
|
|
331
|
+
.alias('+dealer-order-list')
|
|
332
|
+
.description('经销商订单列表:仅查询 JXSD 前缀订单(默认 period=this_month)')
|
|
333
|
+
.option('--order-no <no>', 'JXSD 订单号包含')
|
|
334
|
+
.option('--dealer <name>', '经销商名称包含')
|
|
335
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
336
|
+
.option('--salesperson <name>', '销售员名称包含')
|
|
337
|
+
.option('--customer <text>', '客户名称或手机号包含')
|
|
338
|
+
.option('--product <text>', '商品名称 / 编码 / 分类包含')
|
|
339
|
+
.option('--vsn <text>', 'VSN / IMEI 包含')
|
|
340
|
+
.option('--doc-type <type>', 'so=销售 / rt=退货 / ex=换货')
|
|
341
|
+
.option('--state <state>', 'draft / done')
|
|
342
|
+
.option('--nature <nature>', 'domestic / foreign')
|
|
343
|
+
.option('--page <n>', '页码,从 1 开始', '1'))).action(async (opts, cmd) => {
|
|
344
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
345
|
+
const mappings = [
|
|
346
|
+
['orderNo', 'order_no'],
|
|
347
|
+
['salesperson', 'salesperson'],
|
|
348
|
+
['customer', 'customer'],
|
|
349
|
+
['product', 'product'],
|
|
350
|
+
['vsn', 'vsn'],
|
|
351
|
+
['docType', 'doc_type'],
|
|
352
|
+
['state', 'state'],
|
|
353
|
+
['nature', 'nature'],
|
|
354
|
+
];
|
|
355
|
+
for (const [option, param] of mappings) {
|
|
356
|
+
const value = opts[option];
|
|
357
|
+
if (typeof value === 'string' && value) {
|
|
358
|
+
p[param] = value;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
addAgentFilter(p, opts);
|
|
362
|
+
const page = parseOptionalInt(opts.page, '--page');
|
|
363
|
+
if (page !== undefined) {
|
|
364
|
+
p.page = page;
|
|
365
|
+
}
|
|
366
|
+
await runSkillShortcutExecute(skill, 'dealer_order_list', p, opts, cmd);
|
|
367
|
+
});
|
|
368
|
+
attachSharedShortcutOptions(parent
|
|
369
|
+
.command('+dealer-inventory')
|
|
370
|
+
.description('经销商当前库存:数量、标准价金额、SKU 数及未定价库存')
|
|
371
|
+
.option('--dealer <name>', '经销商名称包含')
|
|
372
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
373
|
+
.option('--nature <nature>', 'domestic / foreign')
|
|
374
|
+
.option('--product <text>', '商品名称或编码包含')).action(async (opts, cmd) => {
|
|
375
|
+
const p = {};
|
|
376
|
+
addAgentFilter(p, opts);
|
|
377
|
+
if (opts.nature)
|
|
378
|
+
p.nature = opts.nature;
|
|
379
|
+
if (opts.product)
|
|
380
|
+
p.product = opts.product;
|
|
381
|
+
await runSkillShortcutExecute(skill, 'dealer_inventory_summary', p, opts, cmd);
|
|
382
|
+
});
|
|
383
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent
|
|
384
|
+
.command('+dealer-final-sales')
|
|
385
|
+
.description('经销商终销:按五件套客户类型统计数量、含税金额和订单数(默认本月)')
|
|
386
|
+
.option('--dealer <name>', '经销商名称包含')
|
|
387
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
388
|
+
.option('--nature <nature>', 'domestic / foreign'))).action(async (opts, cmd) => {
|
|
389
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
390
|
+
addAgentFilter(p, opts);
|
|
391
|
+
if (opts.nature)
|
|
392
|
+
p.nature = opts.nature;
|
|
393
|
+
await runSkillShortcutExecute(skill, 'dealer_final_sales_summary', p, opts, cmd);
|
|
394
|
+
});
|
|
395
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent
|
|
396
|
+
.command('+dealer-customer-portrait')
|
|
397
|
+
.alias('+dealer-portrait')
|
|
398
|
+
.description('经销商终销客户画像:背景、年龄、购买原因分布(默认本月)')
|
|
399
|
+
.option('--dealer <name>', '经销商名称包含')
|
|
400
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
401
|
+
.option('--nature <nature>', 'domestic / foreign'))).action(async (opts, cmd) => {
|
|
402
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
403
|
+
addAgentFilter(p, opts);
|
|
404
|
+
if (opts.nature)
|
|
405
|
+
p.nature = opts.nature;
|
|
406
|
+
await runSkillShortcutExecute(skill, 'dealer_customer_portrait', p, opts, cmd);
|
|
407
|
+
});
|
|
408
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent
|
|
409
|
+
.command('+agent-sell-in-out')
|
|
410
|
+
.alias('+sell-in-out')
|
|
411
|
+
.description('国内代理 Sell-in/Sell-out 对比(默认本月)')
|
|
412
|
+
.option('--dealer-id <id>', '代理商主体 ID,优先于名称')
|
|
413
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
414
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
415
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
416
|
+
addAgentFilter(p, opts);
|
|
417
|
+
await runSkillShortcutExecute(skill, 'agent_sell_in_out_summary', p, opts, cmd);
|
|
418
|
+
});
|
|
419
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent
|
|
420
|
+
.command('+agent-sell-in-out-details')
|
|
421
|
+
.alias('+sell-in-out-details')
|
|
422
|
+
.description('国内代理 Sell-in 与 Sell-out 逐行明细(默认本月)')
|
|
423
|
+
.option('--dealer-id <id>', '代理商主体 ID,优先于名称')
|
|
424
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
425
|
+
.option('--dealer <name>', '兼容参数:代理商名称')
|
|
426
|
+
.option('--detail-type <type>', 'sell_in | sell_out', 'sell_in')
|
|
427
|
+
.option('--page <n>', '页码,从 1 开始', '1')
|
|
428
|
+
.option('--page-size <n>', '每页条数,最大 200', '50'))).action(async (opts, cmd) => {
|
|
429
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
430
|
+
addAgentFilter(p, opts);
|
|
431
|
+
const page = parseOptionalInt(opts.page, '--page');
|
|
432
|
+
const pageSize = parseOptionalInt(opts.pageSize, '--page-size');
|
|
433
|
+
if (page !== undefined)
|
|
434
|
+
p.page = page;
|
|
435
|
+
if (pageSize !== undefined)
|
|
436
|
+
p.page_size = pageSize;
|
|
437
|
+
if (opts.detailType)
|
|
438
|
+
p.detail_type = opts.detailType;
|
|
439
|
+
await runSkillShortcutExecute(skill, 'agent_sell_in_out_details', p, opts, cmd);
|
|
440
|
+
});
|
|
441
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent
|
|
442
|
+
.command('+agent-five-source')
|
|
443
|
+
.alias('+agent-wujiantao')
|
|
444
|
+
.description('国内代理五件套来源:目标、真实 Sell-out、占比与完成率(默认本月)')
|
|
445
|
+
.option('--dealer-id <id>', '代理商主体 ID,优先于名称')
|
|
446
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
447
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
448
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
449
|
+
addAgentFilter(p, opts);
|
|
450
|
+
await runSkillShortcutExecute(skill, 'agent_five_source_metrics', p, opts, cmd);
|
|
451
|
+
});
|
|
452
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent
|
|
453
|
+
.command('+agent-five-source-half-year')
|
|
454
|
+
.alias('+agent-wujiantao-half-year')
|
|
455
|
+
.description('国内代理五件套半年度 Sell-out 真实业绩;可传自定义日期')
|
|
456
|
+
.option('--year <year>', '未传日期时使用的年份')
|
|
457
|
+
.option('--month <month>', '未传日期时用于判断上/下半年的月份')
|
|
458
|
+
.option('--dealer-id <id>', '代理商主体 ID,优先于名称')
|
|
459
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
460
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
461
|
+
const p = buildSalesPeriodParams(opts);
|
|
462
|
+
addAgentFilter(p, opts);
|
|
463
|
+
const year = parseOptionalInt(opts.year, '--year');
|
|
464
|
+
const month = parseOptionalInt(opts.month, '--month');
|
|
465
|
+
if (year !== undefined)
|
|
466
|
+
p.year = year;
|
|
467
|
+
if (month !== undefined)
|
|
468
|
+
p.month = month;
|
|
469
|
+
await runSkillShortcutExecute(skill, 'agent_five_source_half_year', p, opts, cmd);
|
|
470
|
+
});
|
|
471
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent
|
|
472
|
+
.command('+agent-walkin')
|
|
473
|
+
.description('国内代理自然进店 Walk-in 经销商销售订单(默认本月)')
|
|
474
|
+
.option('--dealer-id <id>', '代理商主体 ID,优先于名称')
|
|
475
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
476
|
+
.option('--dealer <name>', '兼容参数:代理商名称')
|
|
477
|
+
.option('--page <n>', '页码,从 1 开始', '1')
|
|
478
|
+
.option('--page-size <n>', '每页条数,最大 200', '50'))).action(async (opts, cmd) => {
|
|
479
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
480
|
+
addAgentFilter(p, opts);
|
|
481
|
+
const page = parseOptionalInt(opts.page, '--page');
|
|
482
|
+
const pageSize = parseOptionalInt(opts.pageSize, '--page-size');
|
|
483
|
+
if (page !== undefined)
|
|
484
|
+
p.page = page;
|
|
485
|
+
if (pageSize !== undefined)
|
|
486
|
+
p.page_size = pageSize;
|
|
487
|
+
await runSkillShortcutExecute(skill, 'agent_walkin_orders', p, opts, cmd);
|
|
488
|
+
});
|
|
489
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent.command('+agent-list')
|
|
490
|
+
.description('国内代理商名单(与代理商看板组织树同口径)'))).action(async (opts, cmd) => {
|
|
491
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
492
|
+
await runSkillShortcutExecute(skill, 'agent_list', p, opts, cmd);
|
|
493
|
+
});
|
|
494
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent.command('+agent-five-source-completion')
|
|
495
|
+
.alias('+agent-wujiantao-completion')
|
|
496
|
+
.description('国内代理五件套总体目标、Sell-in 实际与完成率')
|
|
497
|
+
.option('--dealer-id <id>', '代理商主体 ID,优先于名称')
|
|
498
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
499
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
500
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
501
|
+
addAgentFilter(p, opts);
|
|
502
|
+
await runSkillShortcutExecute(skill, 'agent_wujiantao_completion_rate', p, opts, cmd);
|
|
503
|
+
});
|
|
504
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent.command('+agent-top-metrics')
|
|
505
|
+
.description('代理商看板顶部指标:当期/当日/全年达成与时间进度')
|
|
506
|
+
.option('--current-date <date>', '当日业绩与时间进度日期,默认查询结束日')
|
|
507
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
508
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
509
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
510
|
+
addAgentFilter(p, opts);
|
|
511
|
+
if (opts.currentDate)
|
|
512
|
+
p.current_date = opts.currentDate;
|
|
513
|
+
await runSkillShortcutExecute(skill, 'agent_top_metrics', p, opts, cmd);
|
|
514
|
+
});
|
|
515
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent.command('+agent-performance')
|
|
516
|
+
.description('代理商业绩目标、实际与完成率')
|
|
517
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
518
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
519
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
520
|
+
addAgentFilter(p, opts);
|
|
521
|
+
await runSkillShortcutExecute(skill, 'agent_performance', p, opts, cmd);
|
|
522
|
+
});
|
|
523
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent.command('+agent-product-ranking')
|
|
524
|
+
.description('代理商产品系列销量排名')
|
|
525
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
526
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
527
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
528
|
+
addAgentFilter(p, opts);
|
|
529
|
+
await runSkillShortcutExecute(skill, 'agent_product_ranking', p, opts, cmd);
|
|
530
|
+
});
|
|
531
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent.command('+agent-delivery')
|
|
532
|
+
.description('代理商提货/发货情况,按分类、系列和材质汇总')
|
|
533
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
534
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
535
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
536
|
+
addAgentFilter(p, opts);
|
|
537
|
+
await runSkillShortcutExecute(skill, 'agent_delivery', p, opts, cmd);
|
|
538
|
+
});
|
|
539
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent.command('+agent-sku-ranking')
|
|
540
|
+
.description('代理商 SKU 销量排名')
|
|
541
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
542
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
543
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
544
|
+
addAgentFilter(p, opts);
|
|
545
|
+
await runSkillShortcutExecute(skill, 'agent_sku_ranking', p, opts, cmd);
|
|
546
|
+
});
|
|
547
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent.command('+agent-product-material-ranking')
|
|
548
|
+
.description('代理商指定产品系列的材质销售排名')
|
|
549
|
+
.requiredOption('--product-name <name>', '商品细类/产品系列')
|
|
550
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
551
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
552
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
553
|
+
addAgentFilter(p, opts);
|
|
554
|
+
p.product_name = opts.productName;
|
|
555
|
+
await runSkillShortcutExecute(skill, 'agent_product_material_ranking', p, opts, cmd);
|
|
556
|
+
});
|
|
557
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent.command('+agent-performance-detail')
|
|
558
|
+
.description('代理商业绩明细:目标、定金、当日、环比和同比')
|
|
559
|
+
.option('--current-date <date>', '当日业绩日期,默认查询结束日')
|
|
560
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
561
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
562
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
563
|
+
addAgentFilter(p, opts);
|
|
564
|
+
if (opts.currentDate)
|
|
565
|
+
p.current_date = opts.currentDate;
|
|
566
|
+
await runSkillShortcutExecute(skill, 'agent_performance_detail', p, opts, cmd);
|
|
567
|
+
});
|
|
568
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent.command('+agent-phone-sales-proportion')
|
|
569
|
+
.description('代理商手机系列或材质销量占比')
|
|
570
|
+
.option('--product-series <name>', '具体系列;all_materials 表示全部手机材质')
|
|
571
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
572
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
573
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
574
|
+
addAgentFilter(p, opts);
|
|
575
|
+
if (opts.productSeries)
|
|
576
|
+
p.product_series = opts.productSeries;
|
|
577
|
+
await runSkillShortcutExecute(skill, 'agent_phone_sales_proportion', p, opts, cmd);
|
|
578
|
+
});
|
|
579
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent.command('+agent-product-sales')
|
|
580
|
+
.description('代理商产品销售层级:代理商、系列、材质和 SKU')
|
|
581
|
+
.option('--category <name>', '商品大类,如手机/腕表/耳机/戒指')
|
|
582
|
+
.option('--series <name>', '商品细类/产品系列')
|
|
583
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
584
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
585
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
586
|
+
addAgentFilter(p, opts);
|
|
587
|
+
if (opts.category)
|
|
588
|
+
p.category = opts.category;
|
|
589
|
+
if (opts.series)
|
|
590
|
+
p.series = opts.series;
|
|
591
|
+
await runSkillShortcutExecute(skill, 'agent_product_sales', p, opts, cmd);
|
|
592
|
+
});
|
|
593
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent.command('+agent-iot-sales-proportion')
|
|
594
|
+
.description('代理商 IOT 系列或颜色销量占比')
|
|
595
|
+
.option('--iot-series <name>', '具体系列;all_colors 表示全部 IOT 颜色')
|
|
596
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
597
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
598
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
599
|
+
addAgentFilter(p, opts);
|
|
600
|
+
if (opts.iotSeries)
|
|
601
|
+
p.iot_series = opts.iotSeries;
|
|
602
|
+
await runSkillShortcutExecute(skill, 'agent_iot_sales_proportion', p, opts, cmd);
|
|
603
|
+
});
|
|
604
|
+
attachSharedShortcutOptions(dealerOrderPeriodOptions(parent.command('+agent-price-distribution')
|
|
605
|
+
.description('代理商手机/IOT 产品价格区间分布')
|
|
606
|
+
.option('--category <name>', '手机 | IOT配件', '手机')
|
|
607
|
+
.option('--agent-name <name>', '代理商完整名称(标准化括号后精确匹配)')
|
|
608
|
+
.option('--dealer <name>', '兼容参数:代理商名称'))).action(async (opts, cmd) => {
|
|
609
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
610
|
+
addAgentFilter(p, opts);
|
|
611
|
+
if (opts.category)
|
|
612
|
+
p.category = opts.category;
|
|
613
|
+
await runSkillShortcutExecute(skill, 'agent_price_distribution', p, opts, cmd);
|
|
614
|
+
});
|
|
615
|
+
attachSharedShortcutOptions(periodOptions(parent
|
|
616
|
+
.command('+order-logistics')
|
|
617
|
+
.alias('+logistics')
|
|
618
|
+
.description('订单物流精查:按订单号 / 物流单号 / 出库单号查询发货明细(默认 period=this_year)')
|
|
619
|
+
.option('--order-no <no>', '订单号 / 出库单号')
|
|
620
|
+
.option('--logistics-no <no>', '物流单号 / 出库单号')
|
|
621
|
+
.option('--department <name>', '兼容旧用法:销售员所在部门名称或路径包含;一般优先用 sales +orders')
|
|
622
|
+
.option('--salesperson <name>', '兼容旧用法:销售人员姓名包含;一般优先用 sales +orders')
|
|
623
|
+
.option('--name <name>', '兼容旧用法:销售人员姓名包含;一般优先用 sales +orders')
|
|
624
|
+
.option('--user-id <id>', '兼容旧用法:销售人员用户 ID;一般优先用 sales +orders')
|
|
625
|
+
.option('--page <n>', '页码,从 1 开始', '1'))).action(async (opts, cmd) => {
|
|
626
|
+
const p = buildSalesPeriodParams(opts, 'this_year');
|
|
627
|
+
if (opts.orderNo) {
|
|
628
|
+
p.order_no = opts.orderNo;
|
|
629
|
+
}
|
|
630
|
+
if (opts.logisticsNo) {
|
|
631
|
+
p.logistics_no = opts.logisticsNo;
|
|
632
|
+
}
|
|
633
|
+
if (opts.department) {
|
|
634
|
+
p.department = opts.department;
|
|
635
|
+
}
|
|
636
|
+
const salesperson = opts.salesperson ?? opts.name;
|
|
637
|
+
if (salesperson) {
|
|
638
|
+
p.salesperson = salesperson;
|
|
639
|
+
}
|
|
640
|
+
const userId = parseOptionalInt(opts.userId, '--user-id');
|
|
641
|
+
if (userId !== undefined) {
|
|
642
|
+
p.user_id = userId;
|
|
643
|
+
}
|
|
644
|
+
const page = parseOptionalInt(opts.page, '--page');
|
|
645
|
+
if (page !== undefined) {
|
|
646
|
+
p.page = page;
|
|
647
|
+
}
|
|
648
|
+
await runSkillShortcutExecute(skill, 'order_logistics', p, opts, cmd);
|
|
649
|
+
});
|
|
650
|
+
attachSharedShortcutOptions(periodOptions(parent
|
|
651
|
+
.command('+dept-breakdown')
|
|
652
|
+
.description('部门/销售员/产品线排名(默认 period=this_month)')
|
|
653
|
+
.option('--group-by <dim>', 'dept_l1|dept_l2|dept_l3|dept_l4|dept_l5|matched_dept|match_dept|salesperson|product_subcategory|…')
|
|
654
|
+
.option('--auto-next-level', 'Infer group_by from dept depth', true))).action(async (opts, cmd) => {
|
|
655
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
656
|
+
if (opts.groupBy) {
|
|
657
|
+
p.group_by = opts.groupBy;
|
|
658
|
+
}
|
|
659
|
+
if (opts.autoNextLevel === false) {
|
|
660
|
+
p.auto_next_level = false;
|
|
661
|
+
}
|
|
662
|
+
await runSkillShortcutExecute(skill, 'dept_breakdown', p, opts, cmd);
|
|
663
|
+
});
|
|
664
|
+
attachSharedShortcutOptions(customerRegionPeriodOptions(parent
|
|
665
|
+
.command('+customer-region')
|
|
666
|
+
.alias('+customer-region-summary')
|
|
667
|
+
.description('全域客户国家/地区业绩分布:订单数和营业额(默认本月)'))).action(async (opts, cmd) => {
|
|
668
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
669
|
+
if (opts.country?.trim()) {
|
|
670
|
+
p.country = opts.country.trim();
|
|
671
|
+
}
|
|
672
|
+
await runSkillShortcutExecute(skill, 'customer_region_summary', p, opts, cmd);
|
|
673
|
+
});
|
|
674
|
+
attachSharedShortcutOptions(periodOptions(parent
|
|
675
|
+
.command('+customer-mix')
|
|
676
|
+
.alias('+customer-sales-mix')
|
|
677
|
+
.description('新老客销售额占比(分类来源 customer_source.customer_category,默认 period=this_month)'))).action(async (opts, cmd) => {
|
|
678
|
+
await runSkillShortcutExecute(skill, 'customer_sales_mix', buildSalesPeriodParams(opts, 'this_month'), opts, cmd);
|
|
679
|
+
});
|
|
680
|
+
attachSharedShortcutOptions(periodOptions(parent
|
|
681
|
+
.command('+dept-comparison')
|
|
682
|
+
.description('环比 / 同比(默认 period=this_month)')
|
|
683
|
+
.option('--group-by <dim>', 'dept_l1|dept_l2|org_channel|product_style1|product_subcategory|…'))).action(async (opts, cmd) => {
|
|
684
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
685
|
+
if (opts.groupBy) {
|
|
686
|
+
p.group_by = opts.groupBy;
|
|
687
|
+
}
|
|
688
|
+
await runSkillShortcutExecute(skill, 'dept_comparison', p, opts, cmd);
|
|
689
|
+
});
|
|
690
|
+
attachSharedShortcutOptions(periodOptions(parent
|
|
691
|
+
.command('+target-achievement')
|
|
692
|
+
.description('部门业绩目标达成(默认 period=this_month)')
|
|
693
|
+
.option('--page <n>', '页码,从 1 开始', '1')).option('--okr-source <source>', 'legacy | postgres(默认 legacy,与管理端部门业绩目标一致)')).action(async (opts, cmd) => {
|
|
694
|
+
const p = withOkrSource(buildSalesPeriodParams(opts, 'this_month'), opts);
|
|
695
|
+
const page = parseOptionalInt(opts.page, '--page');
|
|
696
|
+
if (page !== undefined) {
|
|
697
|
+
p.page = page;
|
|
698
|
+
}
|
|
699
|
+
await runSkillShortcutExecute(skill, 'dept_target_achievement', p, opts, cmd);
|
|
700
|
+
});
|
|
701
|
+
attachSharedShortcutOptions(periodOptions(parent
|
|
702
|
+
.command('+dept-monthly-okr')
|
|
703
|
+
.description('部门月度业绩目标明细(dept_monthly_okr)')
|
|
704
|
+
.option('--match-dept <name>', '匹配部门模糊')
|
|
705
|
+
.option('--page <n>', '页码,从 1 开始', '1')).option('--okr-source <source>', 'legacy | postgres(默认 legacy,与管理端部门业绩目标一致)')).action(async (opts, cmd) => {
|
|
706
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
707
|
+
withOkrSource(p, opts);
|
|
708
|
+
if (opts.matchDept) {
|
|
709
|
+
p.match_dept = opts.matchDept;
|
|
710
|
+
}
|
|
711
|
+
const page = parseOptionalInt(opts.page, '--page');
|
|
712
|
+
if (page !== undefined) {
|
|
713
|
+
p.page = page;
|
|
714
|
+
}
|
|
715
|
+
const limit = parseOptionalInt(opts.limit, '--limit');
|
|
716
|
+
if (limit !== undefined) {
|
|
717
|
+
p.limit = limit;
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
p.limit = 100;
|
|
721
|
+
}
|
|
722
|
+
await runSkillShortcutExecute(skill, 'dept_monthly_okr', p, opts, cmd);
|
|
723
|
+
});
|
|
724
|
+
attachSharedShortcutOptions(periodOptions(parent
|
|
725
|
+
.command('+deposit-breakdown')
|
|
726
|
+
.description('含/不含定金销售额对照(默认 period=this_month)'))).action(async (opts, cmd) => {
|
|
727
|
+
await runSkillShortcutExecute(skill, 'with_without_deposit_breakdown', buildSalesPeriodParams(opts, 'this_month'), opts, cmd);
|
|
728
|
+
});
|
|
729
|
+
attachSharedShortcutOptions(periodOptions(parent.command('+refund-summary').description('退款 / 退换货汇总(默认 period=this_month)'))).action(async (opts, cmd) => {
|
|
730
|
+
await runSkillShortcutExecute(skill, 'refund_summary', buildSalesPeriodParams(opts, 'this_month'), opts, cmd);
|
|
731
|
+
});
|
|
732
|
+
attachSharedShortcutOptions(periodOptions(parent
|
|
733
|
+
.command('+service-sales')
|
|
734
|
+
.description('权益部/服务部销售(默认 period=last_n_days, days=30)')
|
|
735
|
+
.option('--person <name>', '经办人 ILIKE'))).action(async (opts, cmd) => {
|
|
736
|
+
const p = buildSalesPeriodParams(opts, 'last_n_days');
|
|
737
|
+
if (!opts.days) {
|
|
738
|
+
p.days = 30;
|
|
739
|
+
}
|
|
740
|
+
if (opts.person) {
|
|
741
|
+
p.person = opts.person;
|
|
742
|
+
}
|
|
743
|
+
await runSkillShortcutExecute(skill, 'service_dept_sales_summary', p, opts, cmd);
|
|
744
|
+
});
|
|
745
|
+
attachSharedShortcutOptions(periodOptions(parent
|
|
746
|
+
.command('+daily-trend')
|
|
747
|
+
.alias('+trend')
|
|
748
|
+
.description('按日/周/月走势、环比和明细维度(默认 day / this_month)')
|
|
749
|
+
.option('--grain <name>', 'day | week | month', 'day')
|
|
750
|
+
.option('--group-by <dim>', '主维度,如 org_channel / dept_l1 / product_style1')
|
|
751
|
+
.option('--breakdown-by <dim>', '明细维度,如 product_style1 / product_subcategory')
|
|
752
|
+
.option('--no-include-cumulative', 'Omit cumulative series')
|
|
753
|
+
.option('--no-include-comparison', 'Omit previous-period and change-rate fields')
|
|
754
|
+
.option('--include-yoy', 'Include year-over-year alignment', false))).action(async (opts, cmd) => {
|
|
755
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
756
|
+
if (opts.includeCumulative === false) {
|
|
757
|
+
p.include_cumulative = false;
|
|
758
|
+
}
|
|
759
|
+
if (opts.includeYoy) {
|
|
760
|
+
p.include_yoy = true;
|
|
761
|
+
}
|
|
762
|
+
if (opts.includeComparison === false) {
|
|
763
|
+
p.include_comparison = false;
|
|
764
|
+
}
|
|
765
|
+
if (opts.grain) {
|
|
766
|
+
p.grain = opts.grain;
|
|
767
|
+
}
|
|
768
|
+
if (opts.groupBy) {
|
|
769
|
+
p.group_by = opts.groupBy;
|
|
770
|
+
}
|
|
771
|
+
if (opts.breakdownBy) {
|
|
772
|
+
p.breakdown_by = opts.breakdownBy;
|
|
773
|
+
}
|
|
774
|
+
await runSkillShortcutExecute(skill, 'monthly_daily_trend', p, opts, cmd);
|
|
775
|
+
});
|
|
776
|
+
attachSharedShortcutOptions(periodOptions(parent
|
|
777
|
+
.command('+ranking')
|
|
778
|
+
.description('销售员 TOP/BOTTOM 榜(默认 period=this_month)')
|
|
779
|
+
.option('--metric <name>', 'amount | qty | orders', 'amount')
|
|
780
|
+
.option('--top-n <n>', 'TOP N', '10')
|
|
781
|
+
.option('--bottom-n <n>', 'BOTTOM N', '10')
|
|
782
|
+
.option('--no-exclude-zero', 'Include zero rows in bottom list'))).action(async (opts, cmd) => {
|
|
783
|
+
const p = buildSalesPeriodParams(opts, 'this_month');
|
|
784
|
+
if (opts.metric) {
|
|
785
|
+
p.metric = opts.metric;
|
|
786
|
+
}
|
|
787
|
+
const topN = parseOptionalInt(opts.topN, '--top-n');
|
|
788
|
+
if (topN !== undefined) {
|
|
789
|
+
p.top_n = topN;
|
|
790
|
+
}
|
|
791
|
+
const bottomN = parseOptionalInt(opts.bottomN, '--bottom-n');
|
|
792
|
+
if (bottomN !== undefined) {
|
|
793
|
+
p.bottom_n = bottomN;
|
|
794
|
+
}
|
|
795
|
+
if (opts.excludeZero === false) {
|
|
796
|
+
p.exclude_zero = false;
|
|
797
|
+
}
|
|
798
|
+
await runSkillShortcutExecute(skill, 'employee_ranking_top_bottom', p, opts, cmd);
|
|
799
|
+
});
|
|
800
|
+
}
|