@qinsilk/scm-agent-tools 0.0.0-stage → 0.2.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 +48 -2
- package/cli/api.js +114 -0
- package/cli/commands/data.js +112 -0
- package/cli/commands/docs.js +121 -0
- package/cli/commands/log.js +152 -0
- package/cli/commands/query.js +285 -0
- package/cli/commands/report.js +213 -0
- package/cli/commands/staff.js +505 -0
- package/cli/config.js +46 -0
- package/cli/datasets.js +541 -0
- package/cli/scm-agent.js +110 -0
- package/cli/skills.js +89 -0
- package/cli/table.js +43 -0
- package/docs/api.md +2067 -0
- package/docs/cli.md +252 -0
- package/package.json +27 -4
- package/reports/excel.js +107 -0
- package/reports/factoryEvaluation.js +128 -0
- package/reports/goodsBom.js +648 -0
- package/reports/goodsFirstOrder.js +409 -0
- package/reports/goodsInventory.js +220 -0
- package/reports/goodsSize.js +440 -0
- package/reports/image.js +216 -0
- package/reports/leakCheck.js +694 -0
- package/reports/materialPurchaseExport.js +393 -0
- package/reports/materialTransit.js +593 -0
- package/reports/processFollowup.js +312 -0
- package/reports/produceBoard.js +353 -0
- package/reports/produceCost.js +366 -0
- package/reports/produceCostDetail.js +622 -0
- package/reports/produceDaily.js +485 -0
- package/reports/storeInGoods.js +276 -0
- package/reports/supplierBalance.js +671 -0
- package/reports/ticketStatistics.js +289 -0
- package/reports/util.js +83 -0
- package/skills/bom/345/225/206/345/223/201/344/277/241/346/201/257/SKILL.md +40 -0
- package/skills/qinsilk-scm/SKILL.md +126 -0
- package/skills//344/276/233/345/272/224/345/225/206/345/257/271/350/264/246/345/257/274/345/207/272/SKILL.md +38 -0
- package/skills//345/225/206/345/223/201/345/260/272/345/257/270/344/277/241/346/201/257/SKILL.md +40 -0
- package/skills//345/225/206/345/223/201/351/246/226/345/215/225/351/235/242/346/226/231/345/267/245/345/272/217/345/215/225/344/273/267/SKILL.md +39 -0
- package/skills//345/267/245/345/216/202/350/257/204/344/274/260/346/212/245/350/241/250/SKILL.md +39 -0
- package/skills//346/210/220/345/223/201/345/205/245/345/272/223/346/212/245/350/241/250/SKILL.md +40 -0
- package/skills//346/234/215/350/243/205/346/210/220/345/223/201/347/233/230/345/255/230/350/241/250/SKILL.md +39 -0
- package/skills//346/274/217/346/226/231/346/243/200/346/265/213/346/227/245/346/212/245/SKILL.md +40 -0
- package/skills//347/211/251/346/226/231/345/234/250/351/200/224/347/212/266/346/200/201/346/212/245/350/241/250/SKILL.md +40 -0
- package/skills//347/211/251/346/226/231/351/207/207/350/264/255/345/257/274/345/207/272/SKILL.md +39 -0
- package/skills//347/224/237/344/272/247/345/215/225/345/267/245/345/272/217/350/267/237/350/277/233/350/241/250/SKILL.md +39 -0
- package/skills//347/224/237/344/272/247/346/210/220/346/234/254/346/212/245/350/241/250/SKILL.md +41 -0
- package/skills//347/224/237/344/272/247/346/210/220/346/234/254/346/212/245/350/241/250/346/230/216/347/273/206/SKILL.md +40 -0
- package/skills//347/224/237/344/272/247/346/227/245/346/212/245/SKILL.md +42 -0
- package/skills//347/224/237/344/272/247/347/212/266/345/206/265/347/273/237/350/256/241/350/241/250/SKILL.md +39 -0
- package/skills//347/224/237/344/272/247/350/212/202/347/202/271/347/234/213/346/235/277/SKILL.md +39 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// 生产状况统计表:拉取全部已领包菲票,按「领包人直属部门 + 商品货号」汇总领货与交货,
|
|
2
|
+
// 并逐个生产单查询裁床工序(core=2)的最早汇报日期作为开始日期。
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { call } = require('../cli/api');
|
|
6
|
+
const { dateStr, createThrottle, fetchAll } = require('./util');
|
|
7
|
+
const { createWorkbook, saveWorkbook } = require('./excel');
|
|
8
|
+
|
|
9
|
+
// 表头:与原脚本一致;「已下单待分配裁片数量」「备注」两列数据源暂无,保持空列
|
|
10
|
+
const TITLES = ['部门', '人员数量', '开始日期', '款号', '领货数量', '已交货', '组上剩余裁片数', '预计日产量', '预计可生产天数', '可交完货时间', '已下单待分配裁片数量', '备注'];
|
|
11
|
+
// 各列宽度:与原脚本一致
|
|
12
|
+
const WIDTHS = [9, 9, 13, 16, 9.5, 9, 14.625, 12.75, 16.25, 16.25, 14, 12.375];
|
|
13
|
+
// 保留两位小数的列:预计日产量、预计可生产天数
|
|
14
|
+
const DECIMAL_COLUMNS = new Set([8, 9]);
|
|
15
|
+
// 数值列(不含两位小数格式):人员数量、领货数量、已交货、组上剩余裁片数
|
|
16
|
+
const INTEGER_COLUMNS = new Set([2, 5, 6, 7]);
|
|
17
|
+
|
|
18
|
+
const BORDER = {
|
|
19
|
+
top: { style: 'thin', color: { argb: 'FF000000' } },
|
|
20
|
+
left: { style: 'thin', color: { argb: 'FF000000' } },
|
|
21
|
+
bottom: { style: 'thin', color: { argb: 'FF000000' } },
|
|
22
|
+
right: { style: 'thin', color: { argb: 'FF000000' } },
|
|
23
|
+
};
|
|
24
|
+
const CENTER = { vertical: 'middle', horizontal: 'center', wrapText: true };
|
|
25
|
+
|
|
26
|
+
/** 保留两位小数(四舍五入) */
|
|
27
|
+
function round2(value) {
|
|
28
|
+
return Math.round(value * 100) / 100;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** yyyy-MM-dd 字符串转 UTC 毫秒,便于做日期差与加天数 */
|
|
32
|
+
function toUtc(dayStr) {
|
|
33
|
+
const [y, m, d] = dayStr.split('-').map(Number);
|
|
34
|
+
return Date.UTC(y, m - 1, d);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** 从 fromStr 到 toStr 经过的天数(toStr - fromStr,按日历日计算) */
|
|
38
|
+
function dayDiff(fromStr, toStr) {
|
|
39
|
+
return Math.round((toUtc(toStr) - toUtc(fromStr)) / 86400000);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** yyyy-MM-dd 加 n 天,返回 yyyy-MM-dd */
|
|
43
|
+
function addDays(dayStr, n) {
|
|
44
|
+
const [y, m, d] = dayStr.split('-').map(Number);
|
|
45
|
+
const result = new Date(Date.UTC(y, m - 1, d) + n * 86400000);
|
|
46
|
+
const pad = (v) => String(v).padStart(2, '0');
|
|
47
|
+
return `${result.getUTCFullYear()}-${pad(result.getUTCMonth() + 1)}-${pad(result.getUTCDate())}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 字符串比较,用于稳定排序 */
|
|
51
|
+
function compareText(a, b) {
|
|
52
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 查询一个生产单的裁床工序 id 集合(core=2 的节点)。
|
|
57
|
+
* 逐单查询受每 Key 每分钟 30 次限制,由调用方节流;查询失败返回空集合,
|
|
58
|
+
* 对应行的开始日期留空。
|
|
59
|
+
*/
|
|
60
|
+
async function fetchCutProcessIds(throttle, sn, cache) {
|
|
61
|
+
if (cache.has(sn)) {
|
|
62
|
+
return cache.get(sn);
|
|
63
|
+
}
|
|
64
|
+
let ids = new Set();
|
|
65
|
+
try {
|
|
66
|
+
const processes = await throttle(() => call('GET', '/api/agent/v1/produce/work-processes', { sn }));
|
|
67
|
+
ids = new Set(
|
|
68
|
+
(Array.isArray(processes) ? processes : [])
|
|
69
|
+
.filter((process) => process.core === 2 && process.id != null)
|
|
70
|
+
.map((process) => String(process.id))
|
|
71
|
+
);
|
|
72
|
+
} catch (e) {
|
|
73
|
+
// 查询失败按无裁床工序处理,不中断整份报表
|
|
74
|
+
}
|
|
75
|
+
cache.set(sn, ids);
|
|
76
|
+
return ids;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 取一张菲票在裁床工序上的最早汇报日期。
|
|
81
|
+
* 汇报日期优先取业务时间,为空时取创建时间(DTO 已格式化为 yyyy-MM-dd)。
|
|
82
|
+
*/
|
|
83
|
+
function firstCutDate(ticket, cutIds) {
|
|
84
|
+
const dates = [];
|
|
85
|
+
for (const progress of ticket.workProcessProgress || []) {
|
|
86
|
+
if (!cutIds.has(String(progress.workProcessId))) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
for (const report of progress.reports || []) {
|
|
90
|
+
const day = report.businessTime || report.createTime;
|
|
91
|
+
if (day) {
|
|
92
|
+
dates.push(day);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// yyyy-MM-dd 字符串按字典序比较即可得到最早日期
|
|
97
|
+
return dates.length ? dates.sort()[0] : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 生成生产状况统计表 Excel。统计日期为今天。
|
|
102
|
+
* 预计日产量 = 已交货 / (统计日 - 开始日期 - 1);可交完货时间 = 统计日 + 剩余天数向上取整 - 1 天。
|
|
103
|
+
*/
|
|
104
|
+
async function generate(argv = {}) {
|
|
105
|
+
const reportDate = dateStr();
|
|
106
|
+
const outDir = argv.out || process.cwd();
|
|
107
|
+
const filePath = path.resolve(outDir, `生产状况统计表-${reportDate}.xlsx`);
|
|
108
|
+
if (fs.existsSync(filePath)) {
|
|
109
|
+
throw new Error(`输出文件已存在:${filePath};如需替换请先删除旧文件或更换输出目录`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 菲票统计固定只查已领包(lendPackageState=1),与原脚本口径一致;
|
|
113
|
+
// 编菲日期区间由 --from/--to 传入,数据量大的公司收窄范围可避免全量分页撞每日限流
|
|
114
|
+
const ticketParams = { lendPackageState: 1 };
|
|
115
|
+
if (argv.from) {
|
|
116
|
+
ticketParams.from = argv.from;
|
|
117
|
+
}
|
|
118
|
+
if (argv.to) {
|
|
119
|
+
ticketParams.to = argv.to;
|
|
120
|
+
}
|
|
121
|
+
const { list: tickets } = await fetchAll(
|
|
122
|
+
(page, limit) => call('GET', '/api/agent/v1/ticket/statistics', { ...ticketParams, page, limit })
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// 部门启用账号数:状态 1 为启用,按直属部门 id 计数
|
|
126
|
+
const { list: users } = await fetchAll(
|
|
127
|
+
(page, limit) => call('GET', '/api/agent/v1/user-list', { page, limit })
|
|
128
|
+
);
|
|
129
|
+
const departmentCounts = new Map();
|
|
130
|
+
for (const user of users) {
|
|
131
|
+
if (user.status === 1 && user.departmentId != null) {
|
|
132
|
+
const key = String(user.departmentId);
|
|
133
|
+
departmentCounts.set(key, (departmentCounts.get(key) || 0) + 1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 逐生产单查裁床工序:服务器每 Key 每分钟 30 次,间隔 2100 毫秒
|
|
138
|
+
const throttle = createThrottle(2100);
|
|
139
|
+
const cutCache = new Map();
|
|
140
|
+
const grouped = new Map();
|
|
141
|
+
const missingOrders = new Set();
|
|
142
|
+
for (const ticket of tickets) {
|
|
143
|
+
const dept = ticket.departmentName || '未设置部门';
|
|
144
|
+
const deptId = String(ticket.departmentId == null ? '' : ticket.departmentId);
|
|
145
|
+
const userName = ticket.lendPackageUserName || '';
|
|
146
|
+
const goods = ticket.goodsSn || '未设置货号';
|
|
147
|
+
// 分组键含领包人 id:同部门同名不同人各自成行,与原脚本一致
|
|
148
|
+
const key = [dept, deptId, String(ticket.lendPackageUserId == null ? '' : ticket.lendPackageUserId), userName, goods].join('\u0001');
|
|
149
|
+
if (!grouped.has(key)) {
|
|
150
|
+
grouped.set(key, { dept, deptId, goods, userName, total: 0, done: 0, dates: [] });
|
|
151
|
+
}
|
|
152
|
+
const item = grouped.get(key);
|
|
153
|
+
const quantity = ticket.totalNumber || 0;
|
|
154
|
+
item.total += quantity;
|
|
155
|
+
// 扫菲节点已完成(ticketState=1)视为已交货
|
|
156
|
+
item.done += ticket.ticketState === 1 ? quantity : 0;
|
|
157
|
+
const cutIds = await fetchCutProcessIds(throttle, ticket.produceOrdersSn, cutCache);
|
|
158
|
+
const cutDate = firstCutDate(ticket, cutIds);
|
|
159
|
+
if (cutDate) {
|
|
160
|
+
item.dates.push(cutDate);
|
|
161
|
+
} else {
|
|
162
|
+
missingOrders.add(String(ticket.produceOrdersSn));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 行顺序:部门、款号、领包人名称依次排序
|
|
167
|
+
const items = [...grouped.values()].sort((a, b) =>
|
|
168
|
+
compareText(a.dept, b.dept) || compareText(a.goods, b.goods) || compareText(a.userName, b.userName));
|
|
169
|
+
|
|
170
|
+
// 组装数据行并推算产量与可交完货时间
|
|
171
|
+
const rows = items.map((item) => {
|
|
172
|
+
const begin = item.dates.length ? item.dates.sort()[0] : '';
|
|
173
|
+
const remain = item.total - item.done;
|
|
174
|
+
let daily = null;
|
|
175
|
+
let days = null;
|
|
176
|
+
if (begin) {
|
|
177
|
+
// 开始日期次日起算有效生产天数
|
|
178
|
+
const elapsed = dayDiff(begin, reportDate) - 1;
|
|
179
|
+
if (elapsed > 0 && item.done > 0) {
|
|
180
|
+
daily = round2(item.done / elapsed);
|
|
181
|
+
if (daily) {
|
|
182
|
+
days = round2(remain / daily);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
let finish = '';
|
|
187
|
+
if (remain === 0) {
|
|
188
|
+
finish = '已交完';
|
|
189
|
+
} else if (days !== null) {
|
|
190
|
+
finish = addDays(reportDate, Math.ceil(days) - 1);
|
|
191
|
+
}
|
|
192
|
+
return [
|
|
193
|
+
item.dept,
|
|
194
|
+
departmentCounts.get(item.deptId) || 0,
|
|
195
|
+
begin,
|
|
196
|
+
item.goods,
|
|
197
|
+
item.total,
|
|
198
|
+
item.done,
|
|
199
|
+
remain,
|
|
200
|
+
daily === null ? '' : daily,
|
|
201
|
+
days === null ? '' : days,
|
|
202
|
+
finish,
|
|
203
|
+
'',
|
|
204
|
+
'',
|
|
205
|
+
];
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Excel:sheet 名与标题都带统计日期
|
|
209
|
+
const [year, month, day] = reportDate.split('-');
|
|
210
|
+
const workbook = createWorkbook();
|
|
211
|
+
const sheet = workbook.addWorksheet(`${Number(month)}.${Number(day)}`);
|
|
212
|
+
|
|
213
|
+
// 第 1 行:表标题,整行合并,无表框
|
|
214
|
+
const titleRow = sheet.addRow([`${Number(year)}年${Number(month)}月${Number(day)}日生产状况统计表`]);
|
|
215
|
+
titleRow.height = 35;
|
|
216
|
+
const titleCell = titleRow.getCell(1);
|
|
217
|
+
titleCell.font = { bold: true, size: 14 };
|
|
218
|
+
titleCell.alignment = { vertical: 'middle', horizontal: 'center' };
|
|
219
|
+
|
|
220
|
+
// 第 2 行:表头
|
|
221
|
+
const headerRow = sheet.addRow(TITLES);
|
|
222
|
+
headerRow.height = 30;
|
|
223
|
+
headerRow.eachCell((cell) => {
|
|
224
|
+
cell.font = { bold: true };
|
|
225
|
+
cell.border = BORDER;
|
|
226
|
+
cell.alignment = CENTER;
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// 数据行:全部居中;预计日产量、预计可生产天数为两位小数数值
|
|
230
|
+
rows.forEach((values) => {
|
|
231
|
+
const added = sheet.addRow(values);
|
|
232
|
+
added.height = 25;
|
|
233
|
+
added.eachCell((cell, colNumber) => {
|
|
234
|
+
cell.border = BORDER;
|
|
235
|
+
cell.alignment = CENTER;
|
|
236
|
+
if (DECIMAL_COLUMNS.has(colNumber) && cell.value !== '') {
|
|
237
|
+
cell.numFmt = '0.00';
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// 同一部门的连续行合并部门与人员数量两列
|
|
243
|
+
let start = 3;
|
|
244
|
+
while (start < rows.length + 3) {
|
|
245
|
+
let end = start;
|
|
246
|
+
while (end + 1 < rows.length + 3 && rows[end - 2][0] === rows[start - 3][0]) {
|
|
247
|
+
end += 1;
|
|
248
|
+
}
|
|
249
|
+
if (end > start) {
|
|
250
|
+
sheet.mergeCells(start, 1, end, 1);
|
|
251
|
+
sheet.mergeCells(start, 2, end, 2);
|
|
252
|
+
}
|
|
253
|
+
start = end + 1;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// 末行:合计已交货、剩余裁片数、预计日产量(其余列留空,与原脚本一致)
|
|
257
|
+
const totalRowNumber = rows.length + 3;
|
|
258
|
+
const totalValues = TITLES.map((_, i) => {
|
|
259
|
+
const colNumber = i + 1;
|
|
260
|
+
if (colNumber === 6 || colNumber === 7 || colNumber === 8) {
|
|
261
|
+
return rows.reduce((sum, row) => sum + (row[colNumber - 1] || 0), 0);
|
|
262
|
+
}
|
|
263
|
+
return '';
|
|
264
|
+
});
|
|
265
|
+
const totalRow = sheet.addRow(totalValues);
|
|
266
|
+
totalRow.height = 25;
|
|
267
|
+
totalRow.eachCell((cell, colNumber) => {
|
|
268
|
+
cell.border = BORDER;
|
|
269
|
+
cell.alignment = CENTER;
|
|
270
|
+
if (colNumber === 8) {
|
|
271
|
+
cell.numFmt = '0.00';
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
WIDTHS.forEach((width, i) => {
|
|
276
|
+
sheet.getColumn(i + 1).width = width;
|
|
277
|
+
});
|
|
278
|
+
sheet.mergeCells(1, 1, 1, TITLES.length);
|
|
279
|
+
|
|
280
|
+
await saveWorkbook(workbook, filePath);
|
|
281
|
+
|
|
282
|
+
let summary = `生产状况统计表已生成:读取 ${tickets.length} 张已领包菲票,汇总 ${rows.length} 行`;
|
|
283
|
+
if (missingOrders.size) {
|
|
284
|
+
summary += `;${missingOrders.size} 张生产单未取得裁床汇报日期,相关行开始日期留空`;
|
|
285
|
+
}
|
|
286
|
+
return { filePaths: [filePath], summary };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
module.exports = { generate };
|
package/reports/util.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// 报表公共工具:文件名时间戳、日期区间、请求节流、分页取全量。
|
|
2
|
+
// 报表模块全部同步复用这里的函数,保证各报表行为一致。
|
|
3
|
+
|
|
4
|
+
/** 补齐两位数字,例如 3 → "03" */
|
|
5
|
+
function pad(n) {
|
|
6
|
+
return String(n).padStart(2, '0');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** 文件名用时间戳,格式 yyyymmddHHMMSS(与原 Python 报表一致) */
|
|
10
|
+
function timestamp(date = new Date()) {
|
|
11
|
+
return (
|
|
12
|
+
`${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}` +
|
|
13
|
+
`${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** 日期字符串 yyyy-MM-dd */
|
|
18
|
+
function dateStr(date = new Date()) {
|
|
19
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** 给日期加减天数,返回 yyyy-MM-dd */
|
|
23
|
+
function shiftDate(days, base = new Date()) {
|
|
24
|
+
const d = new Date(base.getTime() + days * 24 * 60 * 60 * 1000);
|
|
25
|
+
return dateStr(d);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** 等待指定毫秒 */
|
|
29
|
+
function sleep(ms) {
|
|
30
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 创建请求节流器:保证相邻两次请求间隔至少 intervalMs 毫秒。
|
|
35
|
+
* 服务器按每 Key 每分钟限流,逐单取详情的报表必须节流,否则会收到 484。
|
|
36
|
+
* 用法:const t = createThrottle(2000); const data = await t(() => call(...));
|
|
37
|
+
*/
|
|
38
|
+
function createThrottle(intervalMs = 2000) {
|
|
39
|
+
let lastAt = 0;
|
|
40
|
+
return async function throttled(task) {
|
|
41
|
+
const wait = lastAt + intervalMs - Date.now();
|
|
42
|
+
if (wait > 0) {
|
|
43
|
+
await sleep(wait);
|
|
44
|
+
}
|
|
45
|
+
lastAt = Date.now();
|
|
46
|
+
return task();
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 分页取全量数据。
|
|
52
|
+
* fetchPage(page, limit) 返回 { list, total };逐页累加直到取完或达到 maxPages。
|
|
53
|
+
* 服务器单页上限 100,因此 limit 固定不超过 100。
|
|
54
|
+
*/
|
|
55
|
+
async function fetchAll(fetchPage, { limit = 100, maxPages = 500, onProgress } = {}) {
|
|
56
|
+
const list = [];
|
|
57
|
+
let page = 1;
|
|
58
|
+
let total = Infinity;
|
|
59
|
+
while (list.length < total && page <= maxPages) {
|
|
60
|
+
const data = await fetchPage(page, limit);
|
|
61
|
+
total = typeof data.total === 'number' ? data.total : list.length + data.list.length;
|
|
62
|
+
list.push(...data.list);
|
|
63
|
+
if (onProgress) {
|
|
64
|
+
onProgress(list.length, total);
|
|
65
|
+
}
|
|
66
|
+
// 返回条数不足一页说明已经是最后一页
|
|
67
|
+
if (!data.list.length || data.list.length < limit) {
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
page += 1;
|
|
71
|
+
}
|
|
72
|
+
return { list, total };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** 数字格式化:空值返回空字符串,其余保留指定小数位 */
|
|
76
|
+
function num(value, digits = 2) {
|
|
77
|
+
if (value === null || value === undefined || value === '' || Number.isNaN(Number(value))) {
|
|
78
|
+
return '';
|
|
79
|
+
}
|
|
80
|
+
return Number(value).toFixed(digits).replace(/\.?0+$/, '') || '0';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = { pad, timestamp, dateStr, shiftDate, sleep, createThrottle, fetchAll, num };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bom商品信息
|
|
3
|
+
description: 生成 BOM 商品信息报表目录(每个商品一个 Excel:横向商品信息 44 列 + BOM 30 列动态色码表头 + 物料单品明细)。当用户说「导出商品 BOM」「这款商品的物料清单给我」「把最新商品的 BOM 都导出来」等时使用。需要已配置秦丝生产ERP AI 助手(scm-agent)。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# bom商品信息
|
|
7
|
+
|
|
8
|
+
为每个商品生成一个独立的 Excel 文件,统一放在一个报表目录里。每个文件只有一张「商品信息」工作表,从上到下分三段:横向商品信息(44 列,含图片、价格、库存、设计师、跟单员等)、默认只读口径的物料清单 BOM(30 个固定物料列,按商品成衣颜色与尺码动态生成「颜色 → 尺码 → 物料规格/用量/含损耗用量」三级表头)、物料单品明细(23 列,按物料 SKU 汇总,含页面默认隐藏列)。
|
|
9
|
+
|
|
10
|
+
## 前置检查
|
|
11
|
+
|
|
12
|
+
先执行 `scm-agent status`。输出「已配置完成」即可继续;报错时引导用户在生产ERP 网页右上角「AI 助手」页面生成 AI Key,再执行 `scm-agent login --key qsk-xxxx`。
|
|
13
|
+
|
|
14
|
+
## 生成命令
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
scm-agent report goods-bom [--sn 款号] [--limit 数量] [--out 输出目录]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- `--sn`:只导出指定款号。
|
|
21
|
+
- `--limit`:最多导出的商品数,默认 100。
|
|
22
|
+
- `--out`:输出目录,默认当前目录。
|
|
23
|
+
|
|
24
|
+
## 输出说明
|
|
25
|
+
|
|
26
|
+
- 输出目录:`bom商品信息<生成时间yyyymmddHHMMSS>/`,每个商品一个 Excel,文件名 `商品名称(商品货号).xlsx`。
|
|
27
|
+
- 不传 `--sn` 时,按商品更新时间从近到远取最新的 `--limit` 个商品。
|
|
28
|
+
- 「含损耗用量」按「用量 × (1 + 损耗率/100) ÷ (1 − 缩水率/100)」计算,保留四位小数。
|
|
29
|
+
- 没有 BOM 的商品仍然有完整的商品信息行和 BOM 表头,只是没有 BOM 明细行。
|
|
30
|
+
- 物料单品明细的「单件用量」按 BOM 用量合计除以去重成衣 SKU 数计算,数量为 0 时显示「-」。
|
|
31
|
+
|
|
32
|
+
## 汇报格式
|
|
33
|
+
|
|
34
|
+
先说结论:商品个数、其中有 BOM 与没有 BOM 的数量;再给输出目录;最后提示每个商品一个文件。不要把表格内容粘贴到对话里。
|
|
35
|
+
|
|
36
|
+
## 安全铁律
|
|
37
|
+
|
|
38
|
+
- 本技能只读,不会修改任何数据。
|
|
39
|
+
- 不要向用户展示 AI Key(只可显示前 12 位)。
|
|
40
|
+
- 命令失败时最多重试一次;仍失败则把错误信息原样转告用户。
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qinsilk-scm
|
|
3
|
+
description: 查询秦丝生产ERP数据:商品、物料、生产单(含裁床等工序进度)、物料采购单、设计款与IE核价、版单(打版工单与节点进度),并生成官方报表(生产日报、生产节点看板、生产成本报表、服装成品盘存表、供应商对账等 16 个),还能查数据变动日志(谁在什么时候改了什么、通过什么操作改的),以及员工管理与权限管理(创建员工、给角色增加或移除权限),也支持按用户自己的口径做个性化报表(查接口字段含义、取值枚举与单位)。当用户说「查生产单」「A123 完成裁床了吗」「最近的物料采购单」「找一下叫XX的物料」「设计款列表」「这款的IE核价」「A123 的版单到哪个节点了」「上周下的版单」「WL1002 是谁改的」「这个名字什么时候改的」「生成生产日报」「导出服装成品盘存表」「跟XX厂出对账单」「帮我创建名称为张三的员工」「帮我给仓库管理员增加XX的YY权限」「帮我给张三去掉XX权限」「我想按我们公司的口径做一张统计表」「这个字段是什么意思」「官方报表格式不合适,帮我重新排一份」等涉及生产ERP数据或员工权限的问题时使用。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 秦丝生产ERP 查询与管理
|
|
7
|
+
|
|
8
|
+
通过 `scm-agent` 命令行查询生产ERP数据,权限等于当前配置的用户本人(只能查到该用户在网页上有权限看的数据)。除查询外还能生成官方报表文件(Excel/HTML),以及执行两类写操作:创建员工、给角色增加或移除权限(写操作必须走两步确认,见下方铁律)。
|
|
9
|
+
|
|
10
|
+
## 前置检查
|
|
11
|
+
|
|
12
|
+
执行 `scm-agent status`。不可用(未配置 / Key 失效)时,引导用户打开生产ERP网页右上角「AI 助手」页面生成或启用 AI Key,然后执行 `scm-agent login --key qsk-xxxx`,不要自行猜测 Key。
|
|
13
|
+
|
|
14
|
+
## 场景到命令的映射
|
|
15
|
+
|
|
16
|
+
| 用户说的话 | 命令 |
|
|
17
|
+
|---|---|
|
|
18
|
+
| 查商品 / 找款 A123 / A123 是什么 | `scm-agent goods search --name A123` |
|
|
19
|
+
| A123 的颜色尺码、工序 | `scm-agent goods detail --id <从search获得>` |
|
|
20
|
+
| 找物料 / 华达呢是什么料 | `scm-agent material search --name 华达呢` |
|
|
21
|
+
| 物料的规格库存 | `scm-agent material detail --id <从search获得>` |
|
|
22
|
+
| 查生产单 / 款 A123 的生产单 | `scm-agent produce list --sn A123` |
|
|
23
|
+
| A123 完成裁床了吗 / 生产进度 | `scm-agent produce detail --sn <生产单号>` |
|
|
24
|
+
| 最近的物料采购单 / XX 供应商的采购单 | `scm-agent material-purchase list --from 2026-10-01 --to 2026-10-24` |
|
|
25
|
+
| 某张采购单的明细 | `scm-agent material-purchase detail --sn <采购单号>` |
|
|
26
|
+
| 设计款列表 / 款 A123 的设计进度 / 最近的设计款 | `scm-agent design list --sn A123`(日期筛选 `--dateType update\|create\|dev --from 2026-10-01 --to 2026-10-24`,至少给一项条件) |
|
|
27
|
+
| 这款的IE核价 / 每个颜色的核价明细 / 导出IE核价 | `scm-agent design ie-list --sn A123`(`--dateType dev\|create`,可不带条件全量翻页) |
|
|
28
|
+
| 版单列表 / 款 A123 的版单 / 上周下的版单 | `scm-agent design-order list --sn A123`(`--states` 逗号分隔状态:1未开始 2进行中 3已结束 4废版 9撤销 10审批中 12审批拒绝;`--node 纸样` 按当前节点;`--overdue pattern\|sweep` 纸样/车版逾期;`--dateType business下版(默认)\|create\|update\|finish\|delivery`;至少给一项条件) |
|
|
29
|
+
| A123 的版单到哪个节点了 / 版单的打版数量、BOM、工艺要求 | `scm-agent design-order detail --sn <版单号>`(也可 `--id <版单id>`;返回节点进度、打版数量明细、BOM、尺码指示、工艺要求与同款全部版单) |
|
|
30
|
+
| 生成/导出报表(日报、看板、盘存表、对账单、成本报表等) | `scm-agent report <报表名>`(全部报表见 `scm-agent report --help`;每个报表有同名中文技能,含参数与输出说明,优先按对应技能执行) |
|
|
31
|
+
| WL1002 是谁改的 / 这个名称什么时候改的 / 改动记录 | 两步:① `scm-agent log db-change --keywords WL1002`(可加 `--table scm_material --from 2026-09-01`,至少给一项条件,返回时间倒序的变动行:操作人、操作类型、变动字段值)② 需要知道具体操作入口时,取某行的 `requestId` 执行 `scm-agent log api --request-id <请求标识>`,得到操作内容与接口路径 |
|
|
32
|
+
| 创建员工 / 建子账号 | `scm-agent user add --name 张三 --login-name abcd --password 1234 --roles 设计师,版师`(先预览,用户同意后加 `--confirm`) |
|
|
33
|
+
| 张三有哪几个角色 / 查员工角色 / 张三的 uid | 两步:① `scm-agent user search --name 张三`(按姓名/手机号/工号模糊,拿到 uid,同名时按部门区分)② `scm-agent user detail --uid <uid>`(返回角色列表) |
|
|
34
|
+
| 查角色 / 角色有哪些权限 | `scm-agent role list`、`scm-agent role detail --name 设计师`(返回权限清单与使用该角色的用户) |
|
|
35
|
+
| 给仓库管理员增加XX的YY权限 / 去掉权限 | `scm-agent role add-permission --name 仓库管理员 --permissions "XX的YY"` 或 `role remove-permission`(先预览,用户同意后加 `--confirm`;`--permissions` 可给多个,逗号分隔) |
|
|
36
|
+
| 这个字段是什么意思 / 返回里的 XX 是什么 / 想自己做报表 | `scm-agent docs api --find <字段名>`(输出该字段的含义、取值枚举、单位与为空的原因) |
|
|
37
|
+
| 按我们公司的口径出统计表 / 把 BOM、库存、对账原始数据给我 | `scm-agent data --list` 选数据集再取数(必填条件见输出;日期跨度上限 92 天;一次一页) |
|
|
38
|
+
| 列出有哪些权限 / 权限叫什么名字 | `scm-agent permission list --keyword 查看` |
|
|
39
|
+
|
|
40
|
+
- 列表默认每页 20 条,加 `--limit 100 --page N` 翻页;需要精确解析时加 `--json`。
|
|
41
|
+
- 生产单详情中的工序进度:`workProcesses` 数组里 `core=2` 的是裁床工序;单据级 `cutBedState` 含义:1不含裁床 2待裁床 3裁床中 4已完成。状态值见命令输出的列头注释。
|
|
42
|
+
- IE 核价是设计款×颜色一行的明细:`materials` 物料行、`processFees` 工序费用行、`costFees` 特殊工艺费用行、`accountItems` 其他费用行;无供应商资料权限时供应商字段为空,工序开启隐藏价格时单价为空,如实说明即可,不要猜测。
|
|
43
|
+
- 版单是旗舰版功能:企业未开通旗舰版时命令返回「版单管理为旗舰版专属功能」,向用户如实转述并建议在网页确认开通,不要当成工具故障。版单列表默认只查未开始/进行中/审批中(与网页一致),要查已结束、废版、撤销的版单需显式传 `--states`;按版单号查详情不受该默认限制。详情里的供应商字段与工序单价同样受用户权限遮蔽,为空时如实说明。
|
|
44
|
+
- report 命令会生成文件(可能耗时几分钟,逐单取数内置节流),完成后向用户汇报文件路径与关键统计,不要把文件内容粘贴到对话。
|
|
45
|
+
- 数据变动日志的关键词按完整词匹配变动数据(用完整货号 WL1002 查询能命中,只输入 WL 查不到);默认查近 30 天,更早的加 `--from`;操作时间没有请求标识的旧记录显示 `-` 且无法查接口日志。返回「还有更多」时可用提示的 `--line-num/--scroll-id` 续查。子账号查接口日志只能看到自己发起的请求(他人请求标识查到空是正常权限行为,与网页一致)。
|
|
46
|
+
- 权限不足提示(如「权限不足,请联系管理员设置「商品管理的查看」权限」)引号内是**一整段权限中文名**,把它原样作为 `--permissions` 参数即可,不要拆开或改写。
|
|
47
|
+
- **权限备注会重名**(真实环境 931 项菜单里有 48 个重名备注,最典型的是「商品管理的查看」对应按设计师、跟单员、品牌、设计助理判断四个变体)。命令按「菜单 id → 权限 key → 备注精确 → 唯一包含」的顺序解析,命中多项时不猜,会报错列出带**标题、权限 key、菜单 id** 的候选。此时把候选转述给用户,让他选一个后用权限 key 或菜单 id 重新提交;不要自己挑一个继续。
|
|
48
|
+
- 角色名支持精确或唯一前缀匹配;有歧义时命令会报错列出候选,让用户选择后用 `--id` 重试。
|
|
49
|
+
|
|
50
|
+
## 用户要自己做报表时(官方报表之外)
|
|
51
|
+
|
|
52
|
+
用户说「我想按我们公司的口径统计一张表」「官方报表不是我要的格式,你帮我重新排一份」
|
|
53
|
+
「这个字段是什么意思」时,按下面四步做,不要凭字段名猜含义:
|
|
54
|
+
|
|
55
|
+
1. 先想清楚要什么数据:单据类(生产单、采购单)用查询命令加 `--json` 看结构,
|
|
56
|
+
例如 `scm-agent produce detail --sn <单号> --json`;BOM、库存、对账、统计这类
|
|
57
|
+
查询命令没有的数据,用 `scm-agent data --list` 选数据集。
|
|
58
|
+
2. `data` 命令的必填条件写在 `--list` 输出里,缺条件会直接报错并给示例命令;
|
|
59
|
+
日期跨度上限 92 天,要更久就按季度拆段,分几次查再合并。
|
|
60
|
+
3. 用 `scm-agent docs api --find <字段名>` 查字段含义、取值枚举、单位与为空的原因。
|
|
61
|
+
不要 `--print` 整份文档——它有两千多行,会占满上下文,只查需要的那几个字段。
|
|
62
|
+
「数据集:」开头的章节是 `data` 命令的,每节开头有一张「常用字段」摘要表,做统计先看它。
|
|
63
|
+
4. 字段说明里写「无某某权限时为空」的,空值表示当前账号看不到,不是没有数据;
|
|
64
|
+
写「仅详情接口返回」的,列表里恒为 null。这两种都不能当成 0 统计。
|
|
65
|
+
|
|
66
|
+
**取数的两条限制要如实告诉用户**:
|
|
67
|
+
|
|
68
|
+
- 关键词参数一次只能给一个词,「统计这 5 个款」是 5 次查询,先说明耗时。
|
|
69
|
+
- `data` 一次只取一页(默认 20 条,`--limit 100` 到顶),末尾会提示还有多少条;
|
|
70
|
+
要全量时逐页取,取完再算,不要边取边猜。
|
|
71
|
+
|
|
72
|
+
**哪些需求该回到官方报表**:需要跨数据集关联(采购单与入库单按单号对上)、
|
|
73
|
+
需要服务端算好的口径(按时交货率以外的复杂聚合)、或数据量明显超过几十页时,
|
|
74
|
+
官方报表更可靠;如实说明,不要用近似字段拼。
|
|
75
|
+
|
|
76
|
+
## 写操作铁律(所有写命令通用)
|
|
77
|
+
|
|
78
|
+
本节适用于全部写命令:当前是 `user add`、`role add-permission`、`role remove-permission`,以后新增的写命令同样遵守。
|
|
79
|
+
|
|
80
|
+
### 1. 确认必须用当前 Agent 的原生交互界面
|
|
81
|
+
|
|
82
|
+
**先输出完整预览,再调用 `AskUserQuestion` 让用户在对话框里做选择。** 不要把「请回复确认」当成交互方式——那要多打一遍字,用户容易输错或懒得回。
|
|
83
|
+
|
|
84
|
+
`AskUserQuestion` 是 ZCode、Claude Code、WorkBuddy 都提供的工具,Codex 里同能力的工具叫 `request_user_input`。你在哪个宿主里运行就找对应名字:
|
|
85
|
+
|
|
86
|
+
| 宿主 | 工具名 | 说明 |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| ZCode、Claude Code、WorkBuddy | `AskUserQuestion` | 每问 2-4 个选项;客户端自动附带「Other」入口让用户自己输入文字 |
|
|
89
|
+
| Codex CLI | `request_user_input` | 每问 2-3 个选项,界面自带「None of the above」与备注输入;**只在 Plan 模式可用**,Default 模式下该工具不存在 |
|
|
90
|
+
| 其他或不确定 | 无 | 退化为文字提问,明确要求用户回复「确认」或「取消」 |
|
|
91
|
+
|
|
92
|
+
调用前先确认自己能用的工具里到底有没有它。确实没有(或调用报工具不存在),再用文字提问,不要反复重试。
|
|
93
|
+
|
|
94
|
+
**对话框内容**:
|
|
95
|
+
|
|
96
|
+
- 问题写一句摘要,例如「给角色「仓库管理员」增加 1 项权限「商品管理的查看」,该角色 3 个用户会受影响」。
|
|
97
|
+
- **只给两个选项:`确认`(按预览执行)、`取消`(不执行)**。**不要加「调整」选项**——想改参数的用户会直接用对话框自带的「Other」(Codex 是「None of the above」加备注)输入文字,这是宿主提供的自由输入能力,不是我们定义的第三个选项。
|
|
98
|
+
- 用户选了 `确认` → 加 `--confirm` 执行。
|
|
99
|
+
- 用户选了 `取消` → 不发写请求,结束本次操作。
|
|
100
|
+
- 用户在「Other」/备注里写了文字(可能同时选了某个选项)→ **把这段文字当作用户的新要求**,按它调整参数、重新预览,再弹一次同样的对话框。不要把它当成确认执行。
|
|
101
|
+
- 如果用户在「Other」里只说「改一下」「调整」之类没有具体内容的话 → 追问要改哪一项(角色、权限、员工字段),拿到答复后重新预览。
|
|
102
|
+
|
|
103
|
+
- 提问前先把完整预览输出到正文(改动明细、现有与改动后的数量、受影响用户),让用户在回答前就能看到全部细节。
|
|
104
|
+
- 预览与确认必须是两次独立的用户交互,同一轮里不得自动连续完成。
|
|
105
|
+
|
|
106
|
+
**为什么弹框只能由 Agent 完成**:命令行在 Agent 的子进程里运行,没有交互终端,CLI 自己无法弹框。所以 CLI 只负责「不给 `--confirm` 就只输出预览」,确认这一步必须由 Agent 调用自己的交互工具完成。
|
|
107
|
+
|
|
108
|
+
**Codex 的特别注意**:`request_user_input` 只在 Plan 模式存在。Default 模式下调用会失败或不可见,此时不要反复尝试,直接改用文字提问(把预览写进正文,让用户回「确认」或「取消」)。
|
|
109
|
+
|
|
110
|
+
### 2. 其余规则
|
|
111
|
+
|
|
112
|
+
1. **多角色必须询问**:用户说「给张三增加权限」时,先 `user search --name 张三` 取其 uid(同名多人时把候选列出让用户确认是哪一个),再 `user detail --uid` 查角色;多于一个角色时列出角色让用户选择,不自行决定改哪个。
|
|
113
|
+
2. **影响面必须告知**:改角色权限影响该角色下全部用户,预览里的「影响范围」必须向用户转述,并出现在确认弹框的摘要里。
|
|
114
|
+
3. **系统角色不修改**:`role detail` 显示系统内置角色时,向用户解释不能修改,建议复制一个自定义角色。
|
|
115
|
+
4. **联动权限如实转述**:服务端报「联动权限未选择:勾选A时,需要勾选B」时,向用户解释并把联动权限一并加入 `--permissions` 后重新走预览确认,不擅自去掉。
|
|
116
|
+
5. 密码只在用户明确给出时使用,预览与汇报中一律不显示;创建后提醒用户首次登录可修改密码。
|
|
117
|
+
|
|
118
|
+
## 汇报格式
|
|
119
|
+
|
|
120
|
+
先结论后明细:第一句直接回答用户的问题(如「A123 的裁床工序已完成,整体进度 80%」),再用简短列表或表格给明细(单号、数量、日期)。数量、金额原样转述,不要四舍五入或换算单位;查不到数据时如实说明查询条件,不编造。
|
|
121
|
+
|
|
122
|
+
## 安全铁律
|
|
123
|
+
|
|
124
|
+
- 只执行本技能列出的命令。查询类命令直接执行;写操作(`user add`、`role add-permission`、`role remove-permission`)必须遵守「写操作铁律」的两步确认,除此之外不执行任何其他写操作。
|
|
125
|
+
- AI Key 全文不进入对话(只允许显示前 12 位);本机配置文件不读取展示。
|
|
126
|
+
- 错误信息如实呈现给用户,不重试超过一次;提示「权限不足」时告知用户这是账号权限所致,与工具无关。
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: 供应商对账导出
|
|
3
|
+
description: 生成供应商对账 Excel(11 表对账单,每个供应商一个文件,覆盖加工、采购、退货、收付款等往来)。当用户说「和这家供应商对账」「导出供应商对账单」「全部供应商的对账数据给我」等时使用。需要已配置秦丝生产ERP AI 助手(scm-agent)。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 供应商对账导出
|
|
7
|
+
|
|
8
|
+
生成一个对账期间内与供应商的全部往来对账 Excel,每个供应商一个文件。文件里最多 11 张工作表:汇总、外发工序加工、委外加工入库、次品返厂、返修、物料采购、物料入库、物料退货、收付款、优惠金额、应收应付记账,给财务或采购与供应商逐项核对往来。
|
|
9
|
+
|
|
10
|
+
## 前置检查
|
|
11
|
+
|
|
12
|
+
先执行 `scm-agent status`。输出「已配置完成」即可继续;报错时引导用户在生产ERP 网页右上角「AI 助手」页面生成 AI Key,再执行 `scm-agent login --key qsk-xxxx`。
|
|
13
|
+
|
|
14
|
+
## 生成命令
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
scm-agent report supplier-balance [--supplier 供应商名称] [--from yyyy-MM-dd] [--to yyyy-MM-dd] [--out 输出目录]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- `--supplier`:供应商名称;传了只为这一家生成对账文件,不传则为全部供应商各生成一个文件。
|
|
21
|
+
- `--from`:对账日期起,格式 yyyy-MM-dd。
|
|
22
|
+
- `--to`:对账日期止,格式 yyyy-MM-dd。
|
|
23
|
+
- `--out`:输出目录,默认当前目录。
|
|
24
|
+
|
|
25
|
+
## 输出说明
|
|
26
|
+
|
|
27
|
+
- 每个供应商一个 Excel 文件,文件名包含供应商名称与对账期间。
|
|
28
|
+
- 「汇总」工作表始终保留;某类往来没有数据时不创建对应的明细工作表。
|
|
29
|
+
|
|
30
|
+
## 汇报格式
|
|
31
|
+
|
|
32
|
+
先说结论:对账期间、供应商个数、生成的文件数;再给文件路径(多个文件时给目录);最后提示可以用 Excel 打开。不要把表格内容粘贴到对话里。
|
|
33
|
+
|
|
34
|
+
## 安全铁律
|
|
35
|
+
|
|
36
|
+
- 本技能只读,不会修改任何数据。
|
|
37
|
+
- 不要向用户展示 AI Key(只可显示前 12 位)。
|
|
38
|
+
- 命令失败时最多重试一次;仍失败则把错误信息原样转告用户。
|
package/skills//345/225/206/345/223/201/345/260/272/345/257/270/344/277/241/346/201/257/SKILL.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: 商品尺寸信息
|
|
3
|
+
description: 生成商品尺寸信息报表目录(每个商品一个 Excel,默认部位纵向、尺码横向,可用参数转置为「尺寸纵向」)。当用户说「导出尺寸表」「这款商品的尺寸指示给我」「尺寸表转一下,尺码竖着排」等时使用。需要已配置秦丝生产ERP AI 助手(scm-agent)。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 商品尺寸信息
|
|
7
|
+
|
|
8
|
+
为每个商品生成一个尺寸指示 Excel 文件,统一放在一个报表目录里。默认方向与商品页面的尺寸指示表一致:部位为行、尺码为列,包含部位、测量方法、图片、误差范围、打样尺寸、洗前档差、洗前尺寸、洗后档差、洗后尺寸,部位图片嵌在图片列。
|
|
9
|
+
|
|
10
|
+
## 前置检查
|
|
11
|
+
|
|
12
|
+
先执行 `scm-agent status`。输出「已配置完成」即可继续;报错时引导用户在生产ERP 网页右上角「AI 助手」页面生成 AI Key,再执行 `scm-agent login --key qsk-xxxx`。
|
|
13
|
+
|
|
14
|
+
## 生成命令
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
scm-agent report goods-size [--sn 款号] [--limit 数量] [--transpose] [--out 输出目录]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- `--sn`:只导出指定款号。
|
|
21
|
+
- `--limit`:最多导出的商品数,默认 100。
|
|
22
|
+
- `--transpose`:把表格行列互换,转置为「尺寸纵向」——尺码为行、部位为列;先集中列出全部洗前尺寸,再列出全部洗后尺寸,最后是测量方法、误差范围、打样尺寸;转置表不嵌图片。
|
|
23
|
+
- `--out`:输出目录,默认当前目录。
|
|
24
|
+
|
|
25
|
+
## 输出说明
|
|
26
|
+
|
|
27
|
+
- 输出目录:`商品尺寸信息<生成时间yyyymmddHHMMSS>/`,每个商品一个 Excel,文件名 `商品名称(商品货号).xlsx`。
|
|
28
|
+
- 默认方向同时导出洗前和洗后尺寸。
|
|
29
|
+
- 「尺寸纵向」表里洗前尺寸行的首列为浅绿色、洗后尺寸行的首列为浅紫色,两种尺寸一眼可分。
|
|
30
|
+
- 个别商品尺寸数据结构异常或图片下载失败时,会在该文件里追加一张「导出说明」工作表,记录失败原因。
|
|
31
|
+
|
|
32
|
+
## 汇报格式
|
|
33
|
+
|
|
34
|
+
先说结论:商品个数、成功与失败的数量;再给输出目录;最后提示每个商品一个文件。不要把表格内容粘贴到对话里。
|
|
35
|
+
|
|
36
|
+
## 安全铁律
|
|
37
|
+
|
|
38
|
+
- 本技能只读,不会修改任何数据。
|
|
39
|
+
- 不要向用户展示 AI Key(只可显示前 12 位)。
|
|
40
|
+
- 命令失败时最多重试一次;仍失败则把错误信息原样转告用户。
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: 商品首单面料工序单价
|
|
3
|
+
description: 生成商品首单面料工序单价 Excel(商品信息 + 首单 BOM 面料 + 工序单价,按面料与工序数量的最小公倍数合并单元格)。当用户说「首单面料导出来」「商品工序单价表」「把新款的首单用料和工价整理一份」等时使用。需要已配置秦丝生产ERP AI 助手(scm-agent)。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 商品首单面料工序单价
|
|
7
|
+
|
|
8
|
+
生成一个 Excel,每个商品占一个横向区块:左侧是商品主图与商品信息,右侧是该商品历史最早生产单(首单)的 BOM 面料清单和商品工序单价。同一商品的面料行数与工序行数通常不同,行数按两者的最小公倍数取齐:商品级字段合并整个区块,每条面料和每条工序按重复次数合并连续行,保证两段表格上下对齐。
|
|
9
|
+
|
|
10
|
+
## 前置检查
|
|
11
|
+
|
|
12
|
+
先执行 `scm-agent status`。输出「已配置完成」即可继续;报错时引导用户在生产ERP 网页右上角「AI 助手」页面生成 AI Key,再执行 `scm-agent login --key qsk-xxxx`。
|
|
13
|
+
|
|
14
|
+
## 生成命令
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
scm-agent report goods-first-order [--sn 款号] [--limit 数量] [--out 输出目录]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- `--sn`:只导出指定款号。
|
|
21
|
+
- `--limit`:最多导出的商品数,默认 100。
|
|
22
|
+
- `--out`:输出目录,默认当前目录。
|
|
23
|
+
|
|
24
|
+
## 输出说明
|
|
25
|
+
|
|
26
|
+
- 输出文件:`商品首单面料工序单价<生成时间yyyymmddHHMMSS>.xlsx`,写在当前目录或 `--out` 目录。
|
|
27
|
+
- 首单面料、首单日期、新款/翻单标记、创建人直接使用系统返回的字段;没有首单日期、首单面料或工序单价的商品仍然导出,对应位置留空。
|
|
28
|
+
- 商品主图嵌在第一列,随单元格大小缩放;前两行与前两列冻结,滚动时表头和图片列保持可见。
|
|
29
|
+
- 不传 `--sn` 时,按商品更新时间从近到远取最新的 `--limit` 个商品。
|
|
30
|
+
|
|
31
|
+
## 汇报格式
|
|
32
|
+
|
|
33
|
+
先说结论:商品个数、有首单面料与有工序单价的商品数;再给文件路径;最后提示可以用 Excel 打开。不要把表格内容粘贴到对话里。
|
|
34
|
+
|
|
35
|
+
## 安全铁律
|
|
36
|
+
|
|
37
|
+
- 本技能只读,不会修改任何数据。
|
|
38
|
+
- 不要向用户展示 AI Key(只可显示前 12 位)。
|
|
39
|
+
- 命令失败时最多重试一次;仍失败则把错误信息原样转告用户。
|