@chaobinchen/mes-cli 1.0.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/README.md +225 -0
- package/bin/mes.js +263 -0
- package/package.json +40 -0
- package/src/commands/init.js +77 -0
- package/src/commands/material.js +483 -0
- package/src/commands/progress.js +172 -0
- package/src/commands/reports.js +334 -0
- package/src/commands/weighing.js +490 -0
- package/src/commands/work-order.js +508 -0
- package/src/config.js +90 -0
- package/src/db.js +92 -0
- package/src/display.js +78 -0
- package/src/encoding.js +57 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 称量相关命令(场景 9–17)
|
|
3
|
+
*
|
|
4
|
+
* 9. weighing-overview (wov) — 称量工单执行概览
|
|
5
|
+
* 10. weighing-tasks (wt) — 称量任务清单
|
|
6
|
+
* 11. pending-weighing (pw) — 未完成称量项
|
|
7
|
+
* 12. weighing-detail (wdt) — 称量执行明细
|
|
8
|
+
* 13. operator-records (orec) — 操作员称量记录
|
|
9
|
+
* 14. review-status (rvs) — 称量复核状态
|
|
10
|
+
* 15. accounting-status (acs) — 称量账务状态
|
|
11
|
+
* 16. weighing-exceptions (wex) — 称量异常或作废记录
|
|
12
|
+
* 17. weighing-duration (wdur) — 称量耗时分析
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const { queryWithParams, close, sql } = require('../db');
|
|
18
|
+
const { resolveConfig } = require('../config');
|
|
19
|
+
const { printTable, printDetail } = require('../display');
|
|
20
|
+
const chalk = require('chalk');
|
|
21
|
+
|
|
22
|
+
// ─── 场景 9:称量工单执行概览 ──────────────────────────────────────────────────
|
|
23
|
+
const WEIGHING_OVERVIEW_SQL = `
|
|
24
|
+
;WITH weight_work_order AS (
|
|
25
|
+
SELECT TOP (1) id, order_num
|
|
26
|
+
FROM psm_work_order
|
|
27
|
+
WHERE order_num = @order_num AND tr_type = 1 AND ISNULL(is_deleted, 0) = 0
|
|
28
|
+
ORDER BY id DESC
|
|
29
|
+
)
|
|
30
|
+
SELECT
|
|
31
|
+
ww.order_num,
|
|
32
|
+
we.execute_start_time,
|
|
33
|
+
we.execute_end_time,
|
|
34
|
+
we.execute_progress,
|
|
35
|
+
CASE
|
|
36
|
+
WHEN we.execute_state = 1 THEN N'未称量'
|
|
37
|
+
WHEN we.execute_state = 2 THEN N'称量中'
|
|
38
|
+
WHEN we.execute_state = 8 THEN N'称量完成'
|
|
39
|
+
ELSE N'未知状态'
|
|
40
|
+
END AS weighing_status,
|
|
41
|
+
CASE
|
|
42
|
+
WHEN we.stock_preparation_state = 1 THEN N'未发起领料'
|
|
43
|
+
WHEN we.stock_preparation_state = 2 THEN N'已发起领料'
|
|
44
|
+
ELSE N'未知状态'
|
|
45
|
+
END AS stock_preparation_status,
|
|
46
|
+
we.backhaul_failure_reason
|
|
47
|
+
FROM weight_work_order ww
|
|
48
|
+
OUTER APPLY (
|
|
49
|
+
SELECT TOP (1) *
|
|
50
|
+
FROM psm_weighing_work_order_execute
|
|
51
|
+
WHERE parent_id = ww.id AND ISNULL(is_deleted, 0) = 0
|
|
52
|
+
ORDER BY update_time DESC, id DESC
|
|
53
|
+
) we;
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
async function weighingOverviewCommand(orderNum, options, command) {
|
|
57
|
+
let dbConfig;
|
|
58
|
+
try { dbConfig = resolveConfig(command.parent.opts()); }
|
|
59
|
+
catch (err) { console.error(chalk.red('✖ 连接配置错误:'), err.message); process.exitCode = 1; return; }
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
console.log(chalk.dim(`⏳ 正在查询称量工单执行概览: ${orderNum} …`));
|
|
63
|
+
const rows = await queryWithParams(WEIGHING_OVERVIEW_SQL, [
|
|
64
|
+
{ name: 'order_num', type: sql.NVarChar(100), value: orderNum },
|
|
65
|
+
], dbConfig);
|
|
66
|
+
if (!rows || rows.length === 0) {
|
|
67
|
+
console.log(chalk.yellow(`⚠ 未找到工单号 ${orderNum} 的称量工单`));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
printDetail(rows[0], `称量工单执行概览 — ${orderNum}`);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.error(chalk.red('✖ 查询失败:'), err.message);
|
|
73
|
+
process.exitCode = 1;
|
|
74
|
+
} finally { await close(); }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── 场景 10:称量任务清单 ─────────────────────────────────────────────────────
|
|
78
|
+
const WEIGHING_TASKS_SQL = `
|
|
79
|
+
;WITH weight_work_order AS (
|
|
80
|
+
SELECT TOP (1) id
|
|
81
|
+
FROM psm_work_order
|
|
82
|
+
WHERE order_num = @order_num AND tr_type = 1 AND ISNULL(is_deleted, 0) = 0
|
|
83
|
+
ORDER BY id DESC
|
|
84
|
+
)
|
|
85
|
+
SELECT
|
|
86
|
+
w.number,
|
|
87
|
+
w.feeding_phase,
|
|
88
|
+
w.material_code,
|
|
89
|
+
w.material_name,
|
|
90
|
+
w.quantity,
|
|
91
|
+
w.recommended_batch,
|
|
92
|
+
CASE
|
|
93
|
+
WHEN w.state = 0 THEN N'待称量'
|
|
94
|
+
WHEN w.state = 1 THEN N'称量中'
|
|
95
|
+
WHEN w.state = 2 THEN N'已称量'
|
|
96
|
+
ELSE N'未知状态'
|
|
97
|
+
END AS task_status,
|
|
98
|
+
CASE
|
|
99
|
+
WHEN w.review_status = 1 THEN N'未称量复核'
|
|
100
|
+
WHEN w.review_status = 2 THEN N'称量复核中'
|
|
101
|
+
WHEN w.review_status = 4 THEN N'已称量复核'
|
|
102
|
+
ELSE N'未知状态'
|
|
103
|
+
END AS review_status,
|
|
104
|
+
CASE
|
|
105
|
+
WHEN w.accounting_state = 1 THEN N'无需过账'
|
|
106
|
+
WHEN w.accounting_state = 2 THEN N'未称量下架'
|
|
107
|
+
WHEN w.accounting_state = 4 THEN N'部分下架过账'
|
|
108
|
+
WHEN w.accounting_state = 8 THEN N'全部下架过账'
|
|
109
|
+
WHEN w.accounting_state = 16 THEN N'部分上架过账'
|
|
110
|
+
WHEN w.accounting_state = 32 THEN N'全部上架过账'
|
|
111
|
+
WHEN w.accounting_state = 64 THEN N'预投料复核全部移库过账'
|
|
112
|
+
WHEN w.accounting_state = 128 THEN N'预投料复核部分移库过账'
|
|
113
|
+
ELSE N'未知状态'
|
|
114
|
+
END AS accounting_status,
|
|
115
|
+
w.current_weighing_user,
|
|
116
|
+
w.note
|
|
117
|
+
FROM psm_weighing w
|
|
118
|
+
WHERE w.parent_id = (SELECT id FROM weight_work_order)
|
|
119
|
+
AND ISNULL(w.is_deleted, 0) = 0
|
|
120
|
+
ORDER BY w.number, w.material_code;
|
|
121
|
+
`;
|
|
122
|
+
|
|
123
|
+
async function weighingTasksCommand(orderNum, options, command) {
|
|
124
|
+
let dbConfig;
|
|
125
|
+
try { dbConfig = resolveConfig(command.parent.opts()); }
|
|
126
|
+
catch (err) { console.error(chalk.red('✖ 连接配置错误:'), err.message); process.exitCode = 1; return; }
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
console.log(chalk.dim(`⏳ 正在查询称量任务清单: ${orderNum} …`));
|
|
130
|
+
const rows = await queryWithParams(WEIGHING_TASKS_SQL, [
|
|
131
|
+
{ name: 'order_num', type: sql.NVarChar(100), value: orderNum },
|
|
132
|
+
], dbConfig);
|
|
133
|
+
printTable(rows, `称量任务清单 — ${orderNum}`);
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.error(chalk.red('✖ 查询失败:'), err.message);
|
|
136
|
+
process.exitCode = 1;
|
|
137
|
+
} finally { await close(); }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ─── 场景 11:未完成称量项 ─────────────────────────────────────────────────────
|
|
141
|
+
const PENDING_WEIGHING_SQL = `
|
|
142
|
+
;WITH weight_work_order AS (
|
|
143
|
+
SELECT TOP (1) id
|
|
144
|
+
FROM psm_work_order
|
|
145
|
+
WHERE order_num = @order_num AND tr_type = 1 AND ISNULL(is_deleted, 0) = 0
|
|
146
|
+
ORDER BY id DESC
|
|
147
|
+
)
|
|
148
|
+
SELECT
|
|
149
|
+
w.number,
|
|
150
|
+
w.material_code,
|
|
151
|
+
w.material_name,
|
|
152
|
+
w.quantity,
|
|
153
|
+
w.recommended_batch,
|
|
154
|
+
CASE
|
|
155
|
+
WHEN w.state = 0 THEN N'待称量'
|
|
156
|
+
WHEN w.state = 1 THEN N'称量中'
|
|
157
|
+
WHEN w.state = 2 THEN N'已称量'
|
|
158
|
+
ELSE N'未知状态'
|
|
159
|
+
END AS task_status,
|
|
160
|
+
w.current_weighing_user
|
|
161
|
+
FROM psm_weighing w
|
|
162
|
+
WHERE w.parent_id = (SELECT id FROM weight_work_order)
|
|
163
|
+
AND ISNULL(w.is_deleted, 0) = 0
|
|
164
|
+
AND w.state IN (0, 1)
|
|
165
|
+
ORDER BY w.number, w.material_code;
|
|
166
|
+
`;
|
|
167
|
+
|
|
168
|
+
async function pendingWeighingCommand(orderNum, options, command) {
|
|
169
|
+
let dbConfig;
|
|
170
|
+
try { dbConfig = resolveConfig(command.parent.opts()); }
|
|
171
|
+
catch (err) { console.error(chalk.red('✖ 连接配置错误:'), err.message); process.exitCode = 1; return; }
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
console.log(chalk.dim(`⏳ 正在查询未完成称量项: ${orderNum} …`));
|
|
175
|
+
const rows = await queryWithParams(PENDING_WEIGHING_SQL, [
|
|
176
|
+
{ name: 'order_num', type: sql.NVarChar(100), value: orderNum },
|
|
177
|
+
], dbConfig);
|
|
178
|
+
printTable(rows, `未完成称量项 — ${orderNum}`);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
console.error(chalk.red('✖ 查询失败:'), err.message);
|
|
181
|
+
process.exitCode = 1;
|
|
182
|
+
} finally { await close(); }
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ─── 场景 12:称量执行明细 ─────────────────────────────────────────────────────
|
|
186
|
+
const WEIGHING_DETAIL_SQL = `
|
|
187
|
+
;WITH weight_work_order AS (
|
|
188
|
+
SELECT TOP (1) id
|
|
189
|
+
FROM psm_work_order
|
|
190
|
+
WHERE order_num = @order_num AND tr_type = 1 AND ISNULL(is_deleted, 0) = 0
|
|
191
|
+
ORDER BY id DESC
|
|
192
|
+
)
|
|
193
|
+
SELECT
|
|
194
|
+
w.number,
|
|
195
|
+
w.material_code,
|
|
196
|
+
w.material_name,
|
|
197
|
+
w.quantity,
|
|
198
|
+
e.actual_quantity,
|
|
199
|
+
e.material_batch,
|
|
200
|
+
e.operator,
|
|
201
|
+
e.operation_start_time,
|
|
202
|
+
e.operation_end_time,
|
|
203
|
+
e.picking_store_name,
|
|
204
|
+
e.putaway_store_name,
|
|
205
|
+
CASE
|
|
206
|
+
WHEN e.id IS NULL THEN N'未执行'
|
|
207
|
+
WHEN e.is_dosing = 1 THEN N'已投加'
|
|
208
|
+
ELSE N'未投加'
|
|
209
|
+
END AS dosing_status,
|
|
210
|
+
CASE
|
|
211
|
+
WHEN e.id IS NULL THEN N'未执行'
|
|
212
|
+
WHEN e.is_canceled = 1 THEN N'已作废'
|
|
213
|
+
ELSE N'未作废'
|
|
214
|
+
END AS canceled_status
|
|
215
|
+
FROM psm_weighing w
|
|
216
|
+
LEFT JOIN psm_weighing_execute e ON e.parent_id = w.id
|
|
217
|
+
WHERE w.parent_id = (SELECT id FROM weight_work_order)
|
|
218
|
+
AND ISNULL(w.is_deleted, 0) = 0
|
|
219
|
+
AND (@material_code IS NULL OR w.material_code = @material_code)
|
|
220
|
+
ORDER BY w.number, e.operation_start_time, e.id;
|
|
221
|
+
`;
|
|
222
|
+
|
|
223
|
+
async function weighingDetailCommand(orderNum, options, command) {
|
|
224
|
+
let dbConfig;
|
|
225
|
+
try { dbConfig = resolveConfig(command.parent.opts()); }
|
|
226
|
+
catch (err) { console.error(chalk.red('✖ 连接配置错误:'), err.message); process.exitCode = 1; return; }
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
console.log(chalk.dim(`⏳ 正在查询称量执行明细: ${orderNum} …`));
|
|
230
|
+
const rows = await queryWithParams(WEIGHING_DETAIL_SQL, [
|
|
231
|
+
{ name: 'order_num', type: sql.NVarChar(100), value: orderNum },
|
|
232
|
+
{ name: 'material_code', type: sql.NVarChar(100), value: options.material || null },
|
|
233
|
+
], dbConfig);
|
|
234
|
+
printTable(rows, `称量执行明细 — ${orderNum}`);
|
|
235
|
+
} catch (err) {
|
|
236
|
+
console.error(chalk.red('✖ 查询失败:'), err.message);
|
|
237
|
+
process.exitCode = 1;
|
|
238
|
+
} finally { await close(); }
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ─── 场景 13:操作员称量记录 ───────────────────────────────────────────────────
|
|
242
|
+
const OPERATOR_RECORDS_SQL = `
|
|
243
|
+
SELECT
|
|
244
|
+
e.operator,
|
|
245
|
+
wo.order_num,
|
|
246
|
+
wo.batch_num,
|
|
247
|
+
w.material_code,
|
|
248
|
+
w.material_name,
|
|
249
|
+
e.material_batch,
|
|
250
|
+
e.actual_quantity,
|
|
251
|
+
e.operation_start_time,
|
|
252
|
+
e.operation_end_time,
|
|
253
|
+
e.picking_store_name,
|
|
254
|
+
e.putaway_store_name
|
|
255
|
+
FROM psm_weighing_execute e
|
|
256
|
+
INNER JOIN psm_weighing w ON e.parent_id = w.id
|
|
257
|
+
INNER JOIN psm_work_order wo
|
|
258
|
+
ON e.work_order_id = wo.id AND wo.tr_type = 1 AND ISNULL(wo.is_deleted, 0) = 0
|
|
259
|
+
WHERE ISNULL(w.is_deleted, 0) = 0
|
|
260
|
+
AND e.operator = @operator
|
|
261
|
+
AND e.operation_start_time >= @start_date
|
|
262
|
+
AND e.operation_start_time < DATEADD(DAY, 1, @end_date)
|
|
263
|
+
ORDER BY e.operation_start_time DESC, wo.order_num;
|
|
264
|
+
`;
|
|
265
|
+
|
|
266
|
+
async function operatorRecordsCommand(operator, options, command) {
|
|
267
|
+
let dbConfig;
|
|
268
|
+
try { dbConfig = resolveConfig(command.parent.opts()); }
|
|
269
|
+
catch (err) { console.error(chalk.red('✖ 连接配置错误:'), err.message); process.exitCode = 1; return; }
|
|
270
|
+
|
|
271
|
+
if (!options.start || !options.end) {
|
|
272
|
+
console.error(chalk.red('✖ 请通过 --start <YYYY-MM-DD> --end <YYYY-MM-DD> 指定时间范围'));
|
|
273
|
+
process.exitCode = 1;
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
console.log(chalk.dim(`⏳ 正在查询操作员 ${operator} 的称量记录 …`));
|
|
279
|
+
const rows = await queryWithParams(OPERATOR_RECORDS_SQL, [
|
|
280
|
+
{ name: 'operator', type: sql.NVarChar(100), value: operator },
|
|
281
|
+
{ name: 'start_date', type: sql.Date, value: new Date(options.start) },
|
|
282
|
+
{ name: 'end_date', type: sql.Date, value: new Date(options.end) },
|
|
283
|
+
], dbConfig);
|
|
284
|
+
printTable(rows, `操作员称量记录 — ${operator} (${options.start} ~ ${options.end})`);
|
|
285
|
+
} catch (err) {
|
|
286
|
+
console.error(chalk.red('✖ 查询失败:'), err.message);
|
|
287
|
+
process.exitCode = 1;
|
|
288
|
+
} finally { await close(); }
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// ─── 场景 14:称量复核状态 ─────────────────────────────────────────────────────
|
|
292
|
+
const REVIEW_STATUS_SQL = `
|
|
293
|
+
;WITH weight_work_order AS (
|
|
294
|
+
SELECT TOP (1) id
|
|
295
|
+
FROM psm_work_order
|
|
296
|
+
WHERE order_num = @order_num AND tr_type = 1 AND ISNULL(is_deleted, 0) = 0
|
|
297
|
+
ORDER BY id DESC
|
|
298
|
+
)
|
|
299
|
+
SELECT
|
|
300
|
+
w.number,
|
|
301
|
+
w.material_code,
|
|
302
|
+
w.material_name,
|
|
303
|
+
w.quantity,
|
|
304
|
+
CASE
|
|
305
|
+
WHEN w.review_status = 1 THEN N'未称量复核'
|
|
306
|
+
WHEN w.review_status = 2 THEN N'称量复核中'
|
|
307
|
+
WHEN w.review_status = 4 THEN N'已称量复核'
|
|
308
|
+
ELSE N'未知状态'
|
|
309
|
+
END AS review_status,
|
|
310
|
+
w.current_weighing_user,
|
|
311
|
+
w.weighing_progress
|
|
312
|
+
FROM psm_weighing w
|
|
313
|
+
WHERE w.parent_id = (SELECT id FROM weight_work_order)
|
|
314
|
+
AND ISNULL(w.is_deleted, 0) = 0
|
|
315
|
+
ORDER BY w.number, w.material_code;
|
|
316
|
+
`;
|
|
317
|
+
|
|
318
|
+
async function reviewStatusCommand(orderNum, options, command) {
|
|
319
|
+
let dbConfig;
|
|
320
|
+
try { dbConfig = resolveConfig(command.parent.opts()); }
|
|
321
|
+
catch (err) { console.error(chalk.red('✖ 连接配置错误:'), err.message); process.exitCode = 1; return; }
|
|
322
|
+
|
|
323
|
+
try {
|
|
324
|
+
console.log(chalk.dim(`⏳ 正在查询称量复核状态: ${orderNum} …`));
|
|
325
|
+
const rows = await queryWithParams(REVIEW_STATUS_SQL, [
|
|
326
|
+
{ name: 'order_num', type: sql.NVarChar(100), value: orderNum },
|
|
327
|
+
], dbConfig);
|
|
328
|
+
printTable(rows, `称量复核状态 — ${orderNum}`);
|
|
329
|
+
} catch (err) {
|
|
330
|
+
console.error(chalk.red('✖ 查询失败:'), err.message);
|
|
331
|
+
process.exitCode = 1;
|
|
332
|
+
} finally { await close(); }
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// ─── 场景 15:称量账务状态 ─────────────────────────────────────────────────────
|
|
336
|
+
const ACCOUNTING_STATUS_SQL = `
|
|
337
|
+
;WITH weight_work_order AS (
|
|
338
|
+
SELECT TOP (1) id
|
|
339
|
+
FROM psm_work_order
|
|
340
|
+
WHERE order_num = @order_num AND tr_type = 1 AND ISNULL(is_deleted, 0) = 0
|
|
341
|
+
ORDER BY id DESC
|
|
342
|
+
)
|
|
343
|
+
SELECT
|
|
344
|
+
w.number,
|
|
345
|
+
w.material_code,
|
|
346
|
+
w.material_name,
|
|
347
|
+
CASE
|
|
348
|
+
WHEN w.accounting_state = 1 THEN N'无需过账'
|
|
349
|
+
WHEN w.accounting_state = 2 THEN N'未称量下架'
|
|
350
|
+
WHEN w.accounting_state = 4 THEN N'部分下架过账'
|
|
351
|
+
WHEN w.accounting_state = 8 THEN N'全部下架过账'
|
|
352
|
+
WHEN w.accounting_state = 16 THEN N'部分上架过账'
|
|
353
|
+
WHEN w.accounting_state = 32 THEN N'全部上架过账'
|
|
354
|
+
WHEN w.accounting_state = 64 THEN N'预投料复核全部移库过账'
|
|
355
|
+
WHEN w.accounting_state = 128 THEN N'预投料复核部分移库过账'
|
|
356
|
+
ELSE N'未知状态'
|
|
357
|
+
END AS accounting_status,
|
|
358
|
+
CASE WHEN w.is_shifting_storage = 1 THEN N'是' ELSE N'否' END AS shifting_storage_flag,
|
|
359
|
+
CASE WHEN w.is_bucket_management = 1 THEN N'是' ELSE N'否' END AS bucket_management_flag,
|
|
360
|
+
CASE WHEN w.is_canceled = 1 THEN N'是' ELSE N'否' END AS canceled_flag
|
|
361
|
+
FROM psm_weighing w
|
|
362
|
+
WHERE w.parent_id = (SELECT id FROM weight_work_order)
|
|
363
|
+
AND ISNULL(w.is_deleted, 0) = 0
|
|
364
|
+
ORDER BY w.number, w.material_code;
|
|
365
|
+
`;
|
|
366
|
+
|
|
367
|
+
async function accountingStatusCommand(orderNum, options, command) {
|
|
368
|
+
let dbConfig;
|
|
369
|
+
try { dbConfig = resolveConfig(command.parent.opts()); }
|
|
370
|
+
catch (err) { console.error(chalk.red('✖ 连接配置错误:'), err.message); process.exitCode = 1; return; }
|
|
371
|
+
|
|
372
|
+
try {
|
|
373
|
+
console.log(chalk.dim(`⏳ 正在查询称量账务状态: ${orderNum} …`));
|
|
374
|
+
const rows = await queryWithParams(ACCOUNTING_STATUS_SQL, [
|
|
375
|
+
{ name: 'order_num', type: sql.NVarChar(100), value: orderNum },
|
|
376
|
+
], dbConfig);
|
|
377
|
+
printTable(rows, `称量账务状态 — ${orderNum}`);
|
|
378
|
+
} catch (err) {
|
|
379
|
+
console.error(chalk.red('✖ 查询失败:'), err.message);
|
|
380
|
+
process.exitCode = 1;
|
|
381
|
+
} finally { await close(); }
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// ─── 场景 16:称量异常或作废记录 ──────────────────────────────────────────────
|
|
385
|
+
const WEIGHING_EXCEPTIONS_SQL = `
|
|
386
|
+
;WITH weight_work_order AS (
|
|
387
|
+
SELECT TOP (1) id
|
|
388
|
+
FROM psm_work_order
|
|
389
|
+
WHERE order_num = @order_num AND tr_type = 1 AND ISNULL(is_deleted, 0) = 0
|
|
390
|
+
ORDER BY id DESC
|
|
391
|
+
)
|
|
392
|
+
SELECT
|
|
393
|
+
w.material_code,
|
|
394
|
+
w.material_name,
|
|
395
|
+
e.material_batch,
|
|
396
|
+
e.operator,
|
|
397
|
+
CASE WHEN e.is_canceled = 1 THEN N'已作废' ELSE N'未作废' END AS canceled_status,
|
|
398
|
+
e.canceled_time,
|
|
399
|
+
e.failure_reason,
|
|
400
|
+
e.mismatch_reason,
|
|
401
|
+
e.cancel_putaway_store_name
|
|
402
|
+
FROM psm_weighing_execute e
|
|
403
|
+
INNER JOIN psm_weighing w ON e.parent_id = w.id
|
|
404
|
+
WHERE e.work_order_id = (SELECT id FROM weight_work_order)
|
|
405
|
+
AND (
|
|
406
|
+
e.is_canceled = 1
|
|
407
|
+
OR ISNULL(e.failure_reason, N'') <> N''
|
|
408
|
+
OR ISNULL(e.mismatch_reason, N'') <> N''
|
|
409
|
+
)
|
|
410
|
+
ORDER BY e.operation_start_time DESC, e.id DESC;
|
|
411
|
+
`;
|
|
412
|
+
|
|
413
|
+
async function weighingExceptionsCommand(orderNum, options, command) {
|
|
414
|
+
let dbConfig;
|
|
415
|
+
try { dbConfig = resolveConfig(command.parent.opts()); }
|
|
416
|
+
catch (err) { console.error(chalk.red('✖ 连接配置错误:'), err.message); process.exitCode = 1; return; }
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
console.log(chalk.dim(`⏳ 正在查询称量异常/作废记录: ${orderNum} …`));
|
|
420
|
+
const rows = await queryWithParams(WEIGHING_EXCEPTIONS_SQL, [
|
|
421
|
+
{ name: 'order_num', type: sql.NVarChar(100), value: orderNum },
|
|
422
|
+
], dbConfig);
|
|
423
|
+
printTable(rows, `称量异常/作废记录 — ${orderNum}`);
|
|
424
|
+
} catch (err) {
|
|
425
|
+
console.error(chalk.red('✖ 查询失败:'), err.message);
|
|
426
|
+
process.exitCode = 1;
|
|
427
|
+
} finally { await close(); }
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// ─── 场景 17:称量耗时分析 ─────────────────────────────────────────────────────
|
|
431
|
+
const WEIGHING_DURATION_SQL = `
|
|
432
|
+
;WITH weight_work_order AS (
|
|
433
|
+
SELECT TOP (1) id, order_num
|
|
434
|
+
FROM psm_work_order
|
|
435
|
+
WHERE order_num = @order_num AND tr_type = 1 AND ISNULL(is_deleted, 0) = 0
|
|
436
|
+
ORDER BY id DESC
|
|
437
|
+
)
|
|
438
|
+
SELECT
|
|
439
|
+
wwo.order_num,
|
|
440
|
+
w.number,
|
|
441
|
+
w.feeding_phase,
|
|
442
|
+
w.material_code,
|
|
443
|
+
w.material_name,
|
|
444
|
+
w.quantity,
|
|
445
|
+
e.actual_quantity,
|
|
446
|
+
e.operator,
|
|
447
|
+
e.operation_start_time,
|
|
448
|
+
e.operation_end_time,
|
|
449
|
+
CAST(DATEDIFF(SECOND, e.operation_start_time, e.operation_end_time) / 60.0 AS DECIMAL(18, 2)) AS weighing_duration_minutes,
|
|
450
|
+
CASE
|
|
451
|
+
WHEN w.state = 0 THEN N'待称量'
|
|
452
|
+
WHEN w.state = 1 THEN N'称量中'
|
|
453
|
+
WHEN w.state = 2 THEN N'已称量'
|
|
454
|
+
ELSE N'未知状态'
|
|
455
|
+
END AS task_status
|
|
456
|
+
FROM weight_work_order wwo
|
|
457
|
+
INNER JOIN psm_weighing w ON w.parent_id = wwo.id AND ISNULL(w.is_deleted, 0) = 0
|
|
458
|
+
LEFT JOIN psm_weighing_execute e ON e.parent_id = w.id
|
|
459
|
+
WHERE e.id IS NOT NULL
|
|
460
|
+
ORDER BY weighing_duration_minutes DESC, w.number, e.operation_end_time DESC;
|
|
461
|
+
`;
|
|
462
|
+
|
|
463
|
+
async function weighingDurationCommand(orderNum, options, command) {
|
|
464
|
+
let dbConfig;
|
|
465
|
+
try { dbConfig = resolveConfig(command.parent.opts()); }
|
|
466
|
+
catch (err) { console.error(chalk.red('✖ 连接配置错误:'), err.message); process.exitCode = 1; return; }
|
|
467
|
+
|
|
468
|
+
try {
|
|
469
|
+
console.log(chalk.dim(`⏳ 正在分析称量耗时: ${orderNum} …`));
|
|
470
|
+
const rows = await queryWithParams(WEIGHING_DURATION_SQL, [
|
|
471
|
+
{ name: 'order_num', type: sql.NVarChar(100), value: orderNum },
|
|
472
|
+
], dbConfig);
|
|
473
|
+
printTable(rows, `称量耗时分析 — ${orderNum}`);
|
|
474
|
+
} catch (err) {
|
|
475
|
+
console.error(chalk.red('✖ 查询失败:'), err.message);
|
|
476
|
+
process.exitCode = 1;
|
|
477
|
+
} finally { await close(); }
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
module.exports = {
|
|
481
|
+
weighingOverviewCommand,
|
|
482
|
+
weighingTasksCommand,
|
|
483
|
+
pendingWeighingCommand,
|
|
484
|
+
weighingDetailCommand,
|
|
485
|
+
operatorRecordsCommand,
|
|
486
|
+
reviewStatusCommand,
|
|
487
|
+
accountingStatusCommand,
|
|
488
|
+
weighingExceptionsCommand,
|
|
489
|
+
weighingDurationCommand,
|
|
490
|
+
};
|