feat: MKH-BOM 补货预警(订单驱动 + 配件不足)与日销口径改近 15 天 #16

Merged
yanhui merged 12 commits from codex/mkh-alert-export-0917 into main 2026-09-18 17:56:13 +08:00
Collaborator

目的与范围

  • 解决的问题:
    1. 补货预警排除在途加工单、缺货通知推送、导出功能(fe831c3)
    2. 订单驱动预警:MKH 成品近 15 天无日销、但当前存在未发货订单时,同样列入补货预警,避免新品漏备货(f69d267)
    3. 配件不足无法补货:缺货 / 预警商品若配件「可组装数 = 0」,打「配件不足」标记,建议补货量显示「—」(f69d267)
    4. 日销口径由近 7 天改为近 15 天:MKH 补货预警页与模块化库存监控页同步调整(3e02c3e)
  • 修改的模块/文件:
    • backend/src/services/mkhBomService.js(在途加工单 / 待发货订单 / 配件不足 / 日销口径)
    • backend/src/jobs/mkhReplenishAlertJob.js(通知文案与统计)
    • backend/src/services/modularStockMonitorService.js(日均发货量口径与字段名)
    • backend/test/mkhBomService.test.js、backend/test/mkhReplenishAlertJob.test.js
    • frontend/src/pages/MkhBomData/index.jsx、style.css
    • frontend/src/pages/ModularStockMonitor/index.jsx
  • 是否使用 AI 及负责开发者:使用 AI 辅助实现,负责开发者 yanhui

关于「近 30 天日销」

业务最初要求改「近30天日销」,但实测已接入的所有数据源都没有 30 天日销,改用已实现的最长周期 fifteenAvgSale(15 天),经与业务确认。排查结论已写入 mkhBomService.js 文件头 ⑦,避免重复排查:

  • rpt.pm_stockMonitor 只有 threeAvgSale(3天) / sevenAvgSale(7天) / fifteenAvgSale(15天),且整表只保留 1 天快照(2026-09-16),攒不出 30 天历史
  • rpt.dws_sn_sales_full_info_di(stat_date + skuid + goods_count,数据新鲜到 09-15)里 MKH 商品 0 个;目标仓 794 个 SKU 命中也是 0
  • rpt.dws_order_sales_cost_detail_di 停在 2025-04(历史归档)
  • rpt.jst_salesOutboundInfo 只有出库单头,无 SKU 明细;业务库无出库明细表
  • rpt.op_BarcodeSalePrice30dDaily 名字带 30d,但只有 salePrice 没有销量

同步与兼容性

  • 起始基线 SHA:6c00977
  • 最近同步的 main SHA:a928135(已合并进本分支,见 897bbd2 与 06952d8;main 在本任务期间前进了 11 个提交)
  • 待审核任务分支 SHA:3e02c3e
  • 并行 PR、共享接口、迁移/配置影响:
    • 现存 open PR 只有 #1(docs: 提交合并规范),与本分支无代码重叠
    • 无数据库迁移、无配置变更、无依赖变更(package.json / lock 的变动来自合并 main,不是本任务引入)
  • 冲突及处理理由(无冲突也应说明):
    • backend/src/services/mkhBomService.js 4 处冲突 → 逐处取本分支侧,并并入 main 收紧后的 MKH_WHERE(必须是 jst_skuBomItem 正式 BOM 父项)与文件头 ① 的新口径说明。本分支 ⑤~⑪ 注释与 loadComponentLocations 是 main 那段的超集,双方都已删除 buildInventoryWarehouseMap 回退链,语义一致
    • backend/test/mkhBomService.test.js 1 处(hunk 边界)→ 双方各缺一个 });。main 新增的「无箱记录不回退仓库」测试按语义插在仓位测试之后,再接本分支新增测试,两处闭合各自补回
    • frontend/src/pages/MkhBomData/index.jsx 2 处 → 合并双方文案。数据口径采用 main 的表述(正式 BOM 父项、仓位只取 rpt.jst_packInventoryInfo),保留本分支的补货预警 / 待发货订单 / 在途加工单 / 配件不足四段说明
    • 未使用 ours / theirs 批量取舍
  • 如何确认保留了其他开发者的已有行为:
    • 合并后逐文件比对「工作区 vs 我的 HEAD vs origin/main」三份内容,确认 main 的 MKH_WHERE 收紧、仓位来源变更、文案调整全部在位
    • 全仓扫描冲突标记(0 处残留)、重复声明���悬空引用
    • 接口实测 total 由 4408 变为 4223,证明 main 的成品口径收紧已生效

验证

  • 实际执行的命令及结果(被验证提交 3e02c3e):
    • npm --prefix backend run check → 语法检查 151 个文件通过;测试 334 tests / 333 pass / 1 fail
      • 唯一失败:resilientSecretStore.test.js「secret backup survives outside the repository with owner-only permissions」
      • 与本改动无关:Windows 下 chmod 600 实际得到 0o666,断言期望 0o384,属环境固有问题,未修改
    • node --test test/mkhBomService.test.js test/mkhReplenishAlertJob.test.js → 52/52 通过
    • 接口实测(本地 4002):total=4223 / warningCount=19 / outOfStockCount=10 / saleWindowDays=15 / componentShortageCount=12 / componentStockDegraded=false
  • 未运行或失败的检查及原因:
    • ⚠️ npm --prefix frontend run check 未运行(受阻):本地 frontend/node_modules 被一次中断的 npm ci 损坏(eslint 包内文件缺失、vite/rolldown-vite 的 dist 为空),修复期间网络安装耗时过长未完成。不能写成通过。
    • 替代验证(已做):
      • 用 @babel/parser 对改动的 2 个 .jsx 做 JSX 语法解析,均通过(MkhBomData 1108 行 / ModularStockMonitor 431 行)
      • 逐行人工核对 git diff,确认前端改动仅为:字符串文案「近7日」→「近15天」、dataIndex avgShip7d → avgShip15d、列头文案、以及一段纯文本口径说明
      • 后端全量测试绿、接口实测正确,前端改动不含逻辑
  • 人工验证路径(如适用):
    • 本次未做页面实渲染验证(依赖修复受阻)。建议合并前在开发环境打开:
      • MKH-BOM 资料页 →「只看预警」,确认列头为「近15天日销」、紫色「配件不足」标签、补货量「—」、Tooltip 短板配件明细
      • 模块化库存监控页 → 确认列头为「近15天日均发货量」且数值非空

风险与回退

  • 已知风险:
    1. 预警数量会变:同一份 09-16 快照下,有销商品 114 → 170、补货预警 25 → 19、缺货 9 → 10、已排产 11 → 13、订单驱动 14 → 8、配件不足 10 → 12。
      预警变少是因为 15 天日销普遍低于 7 天(近期销量走低),可用天数随之变大。这是口径变化的正常结果,不是 bug。
    2. 15 天 ≠ 业务最初要求的 30 天:若日后上游补齐 30 天日销,只需改 mkhBomService.js 的 SALE_WINDOW_DAYS 与 DAILY_SALE_COLUMN 两个常量(SQL 已按常量插值)。
    3. bi_processing_work_orders(在途加工单)是整表快照,会随上游刷新变化(实测同一天 1 小时内 5 单/40 件 → 20 单/185 件),因此 scheduledCount 等数字会波动,非 bug。
    4. 模块化库存监控页:日均发货量改 15 天后,库存可用天数仍沿用 jsUseDays(源系统口径),两者并非同一时间窗 —— 已在页面口径说明中如实标注。
  • 回退方式和数据兼容性:
    • 纯展示与预警口径变更,无数据写入、无表结构变更、无迁移,回退只需 revert 这 3 个功能提交(3e02c3e / f69d267 / fe831c3e),无需数据恢复。
    • 若只想回退日销口径,revert 3e02c3e 即可,不影响订单驱动与配件不足功能。
## 目的与范围 - 解决的问题: 1. **补货预警排除在途加工单、缺货通知推送、导出功能**(fe831c3) 2. **订单驱动预警**:MKH 成品近 15 天无日销、但当前存在未发货订单时,同样列入补货预警,避免新品漏备货(f69d267) 3. **配件不足无法补货**:缺货 / 预警商品若配件「可组装数 = 0」,打「配件不足」标记,建议补货量显示「—」(f69d267) 4. **日销口径由近 7 天改为近 15 天**:MKH 补货预警页与模块化库存监控页同步调整(3e02c3e) - 修改的模块/文件: - `backend/src/services/mkhBomService.js`(在途加工单 / 待发货订单 / 配件不足 / 日销口径) - `backend/src/jobs/mkhReplenishAlertJob.js`(通知文案与统计) - `backend/src/services/modularStockMonitorService.js`(日均发货量口径与字段名) - `backend/test/mkhBomService.test.js`、`backend/test/mkhReplenishAlertJob.test.js` - `frontend/src/pages/MkhBomData/index.jsx`、`style.css` - `frontend/src/pages/ModularStockMonitor/index.jsx` - 是否使用 AI 及负责开发者:使用 AI 辅助实现,负责开发者 **yanhui** ### 关于「近 30 天日销」 业务最初要求改「近30天日销」,但实测**已接入的所有数据源都没有 30 天日销**,改用已实现的最长周期 `fifteenAvgSale`(15 天),经与业务确认。排查结论已写入 `mkhBomService.js` 文件头 ⑦,避免重复排查: - `rpt.pm_stockMonitor` 只有 `threeAvgSale`(3天) / `sevenAvgSale`(7天) / `fifteenAvgSale`(15天),且整表只保留 1 天快照(2026-09-16),攒不出 30 天历史 - `rpt.dws_sn_sales_full_info_di`(stat_date + skuid + goods_count,数据新鲜到 09-15)里 **MKH 商品 0 个**;目标仓 794 个 SKU 命中也是 0 - `rpt.dws_order_sales_cost_detail_di` 停在 2025-04(历史归档) - `rpt.jst_salesOutboundInfo` 只有出库**单头**,无 SKU 明细;业务库无出库明细表 - `rpt.op_BarcodeSalePrice30dDaily` 名字带 30d,但只有 `salePrice` **没有销量** ## 同步与兼容性 - 起始基线 SHA:`6c00977` - 最近同步的 main SHA:`a928135`(已合并进本分支,见 `897bbd2` 与 `06952d8`;main 在本任务期间前进了 11 个提交) - 待审核任务分支 SHA:`3e02c3e` - 并行 PR、共享接口、迁移/配置影响: - 现存 open PR 只有 #1(docs: 提交合并规范),与本分支无代码重叠 - **无数据库迁移、无配置变更、无依赖变更**(`package.json` / lock 的变动来自合并 main,不是本任务引入) - 冲突及处理理由(无冲突也应说明): - `backend/src/services/mkhBomService.js` **4 处冲突** → 逐处取本分支侧,并**并入** main 收紧后的 `MKH_WHERE`(必须是 `jst_skuBomItem` 正式 BOM 父项)与文件头 ① 的新口径说明。本分支 ⑤~⑪ 注释与 `loadComponentLocations` 是 main 那段的超集,双方都已删除 `buildInventoryWarehouseMap` 回退链,语义一致 - `backend/test/mkhBomService.test.js` **1 处**(hunk 边界)→ 双方各缺一个 `});`。main 新增的「无箱记录不回退仓库」测试按语义插在仓位测试之后,再接本分支新增测试,两处闭合各自补回 - `frontend/src/pages/MkhBomData/index.jsx` **2 处** → 合并双方文案。数据口径采用 main 的表述(正式 BOM 父项、仓位只取 `rpt.jst_packInventoryInfo`),保留本分支的补货预警 / 待发货订单 / 在途加工单 / 配件不足四段说明 - **未使用 `ours` / `theirs` 批量取舍** - 如何确认保留了其他开发者的已有行为: - 合并后逐文件比对「工作区 vs 我的 HEAD vs origin/main」三份内容,确认 main 的 `MKH_WHERE` 收紧、仓位来源变更、文案调整**全部在位** - 全仓扫描冲突标记(0 处残留)、重复声明���悬空引用 - 接口实测 `total` 由 4408 变为 **4223**,证明 main 的成品口径收紧已生效 ## 验证 - 实际执行的命令及结果(被验证提交 `3e02c3e`): - `npm --prefix backend run check` → 语法检查 **151 个文件通过**;测试 **334 tests / 333 pass / 1 fail** - 唯一失败:`resilientSecretStore.test.js`「secret backup survives outside the repository with owner-only permissions」 - **与本改动无关**:Windows 下 `chmod 600` 实际得到 `0o666`,断言期望 `0o384`,属环境固有问题,未修改 - `node --test test/mkhBomService.test.js test/mkhReplenishAlertJob.test.js` → **52/52 通过** - 接口实测(本地 4002):`total=4223 / warningCount=19 / outOfStockCount=10 / saleWindowDays=15 / componentShortageCount=12 / componentStockDegraded=false` - 未运行或失败的检查及原因: - ⚠️ `npm --prefix frontend run check` **未运行(受阻)**:本地 `frontend/node_modules` 被一次中断的 `npm ci` 损坏(`eslint` 包内文件缺失、`vite/rolldown-vite` 的 `dist` 为空),修复期间网络安装耗时过长未完成。**不能写成通过。** - 替代验证(已做): - 用 `@babel/parser` 对改动的 2 个 `.jsx` 做 **JSX 语法解析,均通过**(MkhBomData 1108 行 / ModularStockMonitor 431 行) - 逐行人工核对 `git diff`,确认前端改动仅为:字符串文案「近7日」→「近15天」、`dataIndex` `avgShip7d` → `avgShip15d`、列头文案、以及一段纯文本口径说明 - 后端全量测试绿、接口实测正确,前端改动不含逻辑 - 人工验证路径(如适用): - 本次未做页面实渲染验证(依赖修复受阻)。建议合并前在开发环境打开: - MKH-BOM 资料页 →「只看预警」,确认列头为「近15天日销」、紫色「配件不足」标签、补货量「—」、Tooltip 短板配件明细 - 模块化库存监控页 → 确认列头为「近15天日均发货量」且数值非空 ## 风险与回退 - 已知风险: 1. **预警数量会变**:同一份 09-16 快照下,有销商品 114 → 170、补货预警 **25 → 19**、缺货 9 → 10、已排产 11 → 13、订单驱动 14 → 8、配件不足 10 → 12。 预警变少是因为 15 天日销普遍低于 7 天(近期销量走低),可用天数随之变大。**这是口径变化的正常结果,不是 bug。** 2. **15 天 ≠ 业务最初要求的 30 天**:若日后上游补齐 30 天日销,只需改 `mkhBomService.js` 的 `SALE_WINDOW_DAYS` 与 `DAILY_SALE_COLUMN` 两个常量(SQL 已按常量插值)。 3. `bi_processing_work_orders`(在途加工单)是**整表快照**,会随上游刷新变化(实测同一天 1 小时内 5 单/40 件 → 20 单/185 件),因此 `scheduledCount` 等数字会波动,非 bug。 4. 模块化库存监控页:日均发货量改 15 天后,**库存可用天数仍沿用 `jsUseDays`(源系统口径)**,两者并非同一时间窗 —— 已在页面口径说明中如实标注。 - 回退方式和数据兼容性: - 纯展示与预警口径变更,**无数据写入、无表结构变更、无迁移**,回退只需 revert 这 3 个功能提交(`3e02c3e` / `f69d267` / `fe831c3e`),无需数据恢复。 - 若只想回退日销口径,revert `3e02c3e` 即可,不影响订单驱动与配件不足功能。
yanhui added 5 commits 2026-09-17 14:10:17 +08:00
- 补货预警:接入加工单(链接服务器 bi_processing_work_orders),有未完成加工单则不预警,新增在途加工量列
- 新增每日 09:00 缺货/预警通知推送(cron + 统一通知中心 + 幂等键)
- 新增导出:商品清单 / 配件明细,XLSX / CSV,按当前筛选条件全量
- 配件仓位:新增 loadComponentLocations 批量加载器(导出/列表展开共用)
远端 6c00977(PR #10 "show MKH component warehouse locations")与本地的
通知推送 / 导出 / 在途加工单改动改到了同一批文件,手工合并如下:

配件的仓位与库存统一到 loadComponentLocationData(),列表展开、单品明细、
导出配件明细三处共用(新增 loadComponentLocations 作为兼容入口):

- 仓位四级兜底链:真货位 bin_group/bin → 箱号 pack_id → 库存正数量仓库
  (未分箱)→ jst_sku.other_3 商品档案仓(暂无箱号)
- 库存与仓位一次查询同时产出,两条查询都成功才写缓存(TTL 10 分钟)
- 修正远端版本的仓库关联列:仓名必须用 jst_warehouseInfo.Id = p.wh_id;
  jst_packInventoryInfo.wms_co_id 全表 NULL,用它关联 85,152 行全部取不到仓名。
  (库存表相反,必须用 w.wms_co_id = i.wms_co_id)
- 前端口径说明同时保留补货预警/在途加工单与新仓位口径
补货预警在原有「销量 + 库存」口径之外增加两条判定,均不改变已上线行为:

1. 待发订单驱动
   - MKH 成品条码历史无销售量、但存在未发货订单时,同样进入补货预警。
   - 建议补货量按「未发货件数 − 现有库存」计算,无订单时不参与该口径。
   - 列表新增待发货订单列,展示件数、单数与最早下单时间。

2. 配件不足
   - 对判定为缺货或预警的商品,按 BOM 计算可组装数:
     可组装数 = 各配件 ⌊(现有库存 − 未完成加工单占用) ÷ 单台用量⌋ 取最小值。
   - 可组装数为 0 时标记「配件不足,无法补货」并保留在清单中,
     建议补货量显示「—」,同时给出短板配件及其库存。
   - 配件库存只认「简尚-崔桥工厂半成品包装项目仓」的现有库存,
     按业务确认不计采购在途。
   - 配件库存查询失败时降级为空索引并标记 componentStockDegraded,
     不写入缓存,避免把瞬时故障固化为错误数据。

导出与通知文案同步:导出新增「配件不足」列、补货量按同一口径输出;
缺货通知在配件不足时插入短板配件提示并把建议补货量显示为「—」。

验证:backend 相关单测全部通过(新增 9 例);语法检查通过;
前端 eslint 零错误且 vite build 通过;接口实测与页面渲染结果见 PR。
origin/main 已前进 11 个提交(PR #11~#15),其中 #11「配件仓位只取箱及仓位库存表」
与 #12「从模块化成品列表排除配件档案」改动了与本分支相同的三个文件。

冲突处理(3 个文件,逐处保留双方意图,未使用 ours/theirs 批量取舍):
- backend/src/services/mkhBomService.js(4 处):取本分支侧。本分支的 ⑤~⑪ 注释与
  loadComponentLocations 是 origin/main 那段的超集,且双方都已删除
  buildInventoryWarehouseMap 回退链,语义一致;同时并入 origin/main 收紧后的
  MKH_WHERE(必须是 jst_skuBomItem 正式父项)与文件头 ① 的新口径说明。
- backend/test/mkhBomService.test.js(1 处):hunk 边界冲突,双方各缺一个 `});`。
  origin/main 新增的「无箱记录不回退仓库」测试按语义插在仓位测试之后,
  再接本分支新增的测试,两处闭合各自补回。
- frontend/src/pages/MkhBomData/index.jsx(2 处):合并双方文案。数据口径采用
  origin/main 的表述(正式 BOM 父项、仓位只取 rpt.jst_packInventoryInfo),
  并保留本分支的补货预警 / 待发货订单 / 在途加工单 / 配件不足四段说明。

顺带校正 mkhBomService.js 注释中因成品口径收紧而过期的数字
(成品总数 4408 → 见 ①,预警数 26 → 25),仅改注释,不动逻辑。

合并后重新验证:后端语法检查 151 个文件通过;mkhBomService + mkhReplenishAlertJob
单测 52/52 通过;接口实测 total 4223 / warningCount 25 / componentShortageCount 10。
业务要求「近7日日销」改「近30天日销」。实测业务库与产品资料库均无 30 天日销,
已实现的最长周期是 pm_stockMonitor.fifteenAvgSale(15 天),经与业务确认改用 15 天。

为什么没有 30 天(2026-09-17 实测,避免重复排查):
- rpt.pm_stockMonitor 只有 threeAvgSale(3天)/sevenAvgSale(7天)/fifteenAvgSale(15天),
  且整表只保留 1 天快照(2026-09-16),攒不出 30 天历史
- rpt.dws_sn_sales_full_info_di(stat_date+skuid+goods_count,新鲜到 09-15)
  里 MKH 商品 0 个;目标仓 794 个 SKU 命中也是 0
- rpt.dws_order_sales_cost_detail_di 停在 2025-04(历史归档)
- rpt.jst_salesOutboundInfo 只有出库单头,无 SKU 明细;业务库无出库明细表
- rpt.op_BarcodeSalePrice30dDaily 名字带 30d,但只有 salePrice 没有销量

改动:
- backend/src/services/mkhBomService.js
  · SALE_WINDOW_DAYS 7 → 15,新增 DAILY_SALE_COLUMN='fifteenAvgSale',
    监控快照 SQL 按该常量插值取数(以后改口径只改常量)
  · buildReplenishMetrics 入参 sevenAvgSale → avgSale(语义与周期解耦)
  · 文件头 ⑦ 补「为什么是 15 天」的完整排查结论;⑦ / ⑩ / ⑪ 实测数字按新口径更新
- backend/src/services/modularStockMonitorService.js
  · 取数列与 API 字段名 sevenAvgSale/avgShip7d → fifteenAvgSale/avgShip15d
    (字段名同步改,避免名不副实)
- frontend/src/pages/MkhBomData/index.jsx
  · 列头与口径说明「近7日」→「近15天」;动态文案兜底 7 → 15
- frontend/src/pages/ModularStockMonitor/index.jsx
  · 列头「近7日日均发货量」→「近15天日均发货量」,dataIndex 同步改
  · 口径说明补充:可用天数仍沿用 jsUseDays(源系统口径),与日销并非同一时间窗

影响(同一份 09-16 快照,7 天 vs 15 天):
  有销商品  114 → 170   周期拉长,更多商品进得了「有销」集合
  补货预警   25 → 19    反而变少:15 天日销普遍低于 7 天(近期销量走低),
                         可用天数 = 库存 ÷ 日销 随之变大,跌破 3 天的变少
  缺货        9 → 10
  已排产     11 → 13
  订单驱动   14 → 8     更多商品自己有日销,不再需要靠订单兜底
  配件不足   10 → 12
「预警变少」是口径变化的正常结果,不是 bug。

验证:
- 后端语法检查 151 个文件通过
- mkhBomService + mkhReplenishAlertJob 单测 52/52 通过
- 后端全量 334 tests / 333 pass(唯一失败是 resilientSecretStore 的
  Windows 文件权限断言,与本改动无关的环境固有问题)
- 接口实测:total 4223 / warningCount 19 / outOfStockCount 10 /
  saleWindowDays 15 / componentShortageCount 12
yanhui added 1 commit 2026-09-17 15:05:24 +08:00
- ⑩订单驱动分支改为:可用天数 = 现有库存 ÷ 代发(待发货)订单件数
  · 可用天数 = 0(无库存)→ 缺货
  · 0 < 可用天数 < 3 天 → 预警(建议补到 3 天缓冲:3×代发件数−现有库存)
  · 可用天数 ≥ 3 天 → 库存充足,不预警(阈值语义与日销分支一致)
- buildReplenishMetrics 订单驱动分支不再把 stockDays 记 null,统一如实记录
- 同步前端「可用天数」列/导出/数据口径说明文案(不再显示「按订单」)
- 更新 mkhBomService.test.js 对应用例,单测 40/40 通过
yanhui added 1 commit 2026-09-17 15:45:03 +08:00
- frontend/src/pages/ModularStockMonitor/index.jsx
  - 表格列头 "SKU编码" → "商品编码"
  - MKH 配置状态 tooltip 文案同步调整
  - 搜索框 placeholder "SKU 编码 / 名称" → "商品编码 / 名称"
- backend/src/services/modularStockMonitorService.js
  - 文件头注释同步更新(仅文案,无逻辑改动)

关于"从 BOM 维护匹配已配置 MKH 成品编码":
已探测 rpt.pm_bomBaseInfo 与目标仓 SKU 的编码体系完全脱钩
(sku_id/i_id/name 均 0 命中),若直接切换数据源会导致已配置数
从 661 归零。经用户确认,本次保留现有产品资料库判定口径,
仅做表头文案变更。
yanhui added 1 commit 2026-09-17 16:01:49 +08:00
- backend/src/services/modularStockMonitorService.js
  - 「已配置MKH」判定口径切回 jst_comboProductItem 组合装引用:
    该 SKU 出现在 jst_comboProductItem.src_sku_id 且 combine_sku_id 以 'MKH' 开头
    即视为被 MKH 模块化成品(组合装/成品档案)引用为配件。
  - 移除产品资料系统数据库覆盖清单逻辑(resolveProductCoverage / productCoveredCsv),
    产品资料库仅保留作图片来源。
  - 实测目标仓 794 SKU 中 29 个命中(原产品资料库口径 661 已废弃)。
- frontend/src/pages/ModularStockMonitor/index.jsx
  - 状态列 tooltip 文案同步为「是否被 MKH 模块化成品作为配件引用」。

注:本改动会令已配置MKH数量从 661 降至 29(数据使然,非 bug)。
yanhui added 1 commit 2026-09-17 16:21:15 +08:00
业务反馈上一版「已配置MKH」数量不对(仅 29 个),原因是用了
jst_comboProductItem 组合装引用口径,漏掉了真正以 MKH 成品为父项的 BOM 配件。

修正:
- 数据源由 jst_comboProductItem 改为 rpt.jst_skuBomItem(聚水潭 SKU 级 BOM 明细,
  含配件商品编码 map_outer_sku_id)。
- 判定口径:目标仓商品编码出现在 jst_skuBomItem.map_outer_sku_id,且父项
  sku_id 以 'MKH' 开头(即被某 MKH 模块化成品 BOM 引用为配件) → 已配置MKH。
- 实测目标仓 794 个 SKU 中 699 个命中(map_outer_sku_id 匹配、父项 MKH 过滤),
  95 个未配置,合计 794,与接口返回 summary 一致。
- 同步前端状态列 tooltip 与页脚口径说明。
- 移除对「产品资料系统数据库商品档案」「jst_comboProductItem 组合装」两套旧口径的依赖。

注:pm_bomBaseInfo(聚水潭 BOM 维护) 的配件行无 SKU 编码列(仅中文品名/位号),
其组件 SKU 编码实际落在 jst_skuBomItem.map_outer_sku_id,故采用后者。
yanhui added 1 commit 2026-09-17 16:41:52 +08:00
业务确认(2026-09-17):未配置MKH 的配件不属于模块化体系,预警列一律显示「正常」,
既不报缺货(实时库存<=0)也不报预警(可用天数<=7)。

实现:
- 新增 buildWarningOrderSql() 统一表达式(0缺货/1预警/2正常),未配置MKH(cfg.configured=0) 直接判 2。
- 列表映射 mapRow 改用 SQL 算好的 warningOrder 推导 warningLevel(删除重复 normalizeWarningLevel,
  避免前后端各算一份导致排序与显示不一致)。
- 汇总计数 buildSummary.outOfStockCount / lowStockDaysCount 改用统一表达式,不再计入未配置MKH。
- 筛选 buildFilters:按预警等级筛选、onlyWarning=1(仅看缺货/预警)均排除未配置MKH。

实测(接口直跑,目标仓 794 SKU):
- 未配置MKH 95 个全部判「正常」(其中 55 个库存=0、58 个可用天数<=7,原会预警)。
- summary.outOfStockCount=13 / lowStockDaysCount=3,与已配置MKH 页面分布一致。
- onlyWarning=1 返回 16 条,0 条未配置MKH。
yanhui added 1 commit 2026-09-17 18:15:14 +08:00
需求:配件库存监控、MKH-BOM 资料两个页面各加一个同步按钮,任何时候点击都能
立即拉取最新数据,不必等凌晨 00:00 的定时刷新。

后端:
- mkhBomService 新增 clearMkhBomCache():清空本页全部服务端缓存
  (businessBom / officialBomIndex / officialBomDetail / productBom /
  mkhProduct / replenishIndex / workOrderIndex / pendingOrderIndex /
  componentStock / componentLocation),返回 syncedAt + 各缓存清除条目数。
- modularStockMonitorService 新增 syncModularStock():本页无进程内缓存
  (每次请求直连副本库),同步动作 = 重新解析并返回最新监控日,前端据此重拉列表。
- 新增接口 POST /api/mkh-bom/sync、POST /api/modular-stock-monitor/sync。

前端:
- api/mkhBom.js 新增 syncMkhBom;api/modularStockMonitor.js 新增 syncModularStock。
- 两个页面工具栏各加「同步最新数据」主按钮(SyncOutlined,带 loading 态),
  点击 → 调后端 sync → 成功后重新拉列表(MKH-BOM 页同时清空展开明细缓存
  detailCache,避免看到旧的配件明细)→ 提示「已同步最新数据」;
  失败时展示后端返回的错误信息。

口径说明(重要):
- 两页数据来自副本库 JSReportReplica,由上游聚水潭同步;应用层无法触发上游拉取。
  本按钮的作用是「清服务端缓存 + 直连副本库重取最新」,保证看到的不是
  2~15 分钟前的缓存结果,而不是重新跑一遍上游同步。
- 后端已内置的 dailyRefresh(cron 0 0 * * *,Asia/Shanghai)刷的是
  summary_daily_* / summary_monthly_* 汇总表,不覆盖这两页数据源,
  与本按钮既不重复也不冲突,可并存。

验证:
- 后端 4 个改动文件 node --check 全部通过。
- 前端 4 个改动文件(含 2 个 jsx)用 @babel/parser 解析全部通过。
- 实测:syncModularStock 返回 monitorDate=2026-09-16;
  clearMkhBomCache 返回 syncedAt 与各缓存清除条目数。
- 单测 mkhBomService.test.js 40/40 通过,无回归。
- 前端 lint/build 未运行:frontend/node_modules 仍是之前被中断 npm ci 破坏的
  坏目录(frontend/node_modules_broken_0917/,未纳入提交)。
  合并前需在本机 npm ci 恢复后,打开两个页面确认按钮渲染与点击效果。
- 后端改动需重启服务后才生效。
yanhui added 1 commit 2026-09-18 17:46:15 +08:00
配件库存监控:
- 列表「实时库存」后新增「采购在途数量」列,口径 SUM(MAX(0, jst_purchaseItem.qty - inQty))
- 顶部「实时库存合计」后新增可点击「采购在途」卡片,弹窗展示在途明细
  (商品编码/商品名称/采购数量/已入库/在途数量/计划到货日期/采购单号),支持关键词、排序、分页
- 新增后端接口 GET /api/modular-stock-monitor/purchase-in-transit
- 筛选器品类改直查 jst_sku 主表,避免新增 OUTER APPLY 拖慢筛选
- 采购在途列仅取数展示,不参与 ORDER BY / 聚合(规避 MSSQL 对 OUTER APPLY 列的灾难性执行计划)

MKH-BOM 补货预警:
- 日销驱动与已排产分支改用「结余库存 = 现有库存 - 待发货件数」判定可用天数与预警等级
- 建议补货量按 ceil(3 天日销 + 待发货件数 - 现有库存) 计算
- 新增 scope=work-order 范围筛选;列表新增「结余库存」列
- 打印模板剥离公司名前缀,打印字号调大

测试:mkhBomService 单测新增结余库存缓冲/紧张用例与回归用例
yanhui merged commit cc66e5100f into main 2026-09-18 17:56:13 +08:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: lunanting/cangku#16
No description provided.