71 Commits

Author SHA1 Message Date
134
fa0e604bf8 fix: 排除拣选位报表中的箱记录 2026-10-12 14:31:45 +08:00
e15898693f fix: 动销分析报表恢复「类型」列(恒为拣选),存货位仍不纳入
上一版为去掉冗余把「类型」整列删了,用户要求的报表列结构中该列仍在,
现恢复:导出与列表均保留「类型」列,值恒为「拣选」(存货位不参与统计,报表中不会出现)

验证(基准日 2026-10-08、30 天):
- 导出表头为 仓库 / 仓位 / 类型 / 商品编码 / 库存数量 / 仓位SKU数 / 商品销量 /
  分摊销量 / 商品日销 / 动销等级 / 统计天数 / 统计基准日
- 11897 行,类型列取值集合仅 ['拣选'],空值 0 行;分摊销量合计 98087 = 去重商品销量合计 98087
- 列表接口 binTypes=['拣选'],明细类型集合仅 ['拣选']
- 单测 9/9 通过;语法检查 182 文件通过;eslint 无报错、vite build 成功
2026-10-08 16:31:20 +08:00
99fa402092 feat: 拣选库位动销分析固定只统计拣选位,移除仓位类型切换
- 取数固定只取拣选位:bin CTE 直接过滤 bin_group 为空(更早过滤,存货位不再进入后续 CTE)
  移除可变的 binType IN 过滤与 resolveBinTypes,binType 字段恒为拣选
- 接口 payload 的 binTypes / binTypeOptions 固定为 ['拣选'];缓存键不再含类型
- 列表与导出移除「类型」列(全为拣选,冗余);页面移除类型多选器、底部文案改为「统计范围:仅拣选位」
- 口径说明补充统计范围;Tooltip 同步

验证(基准日 2026-10-08、30 天):
- 列表 11871 行 / 6821 个去重商品 / 库存 499141 / 去重商品销量 96847,明细类型集合仅为 ['拣选']
- 导出 11871 行,表头无「类型」列;分摊销量合计 96847 = 去重商品销量合计 96847(差异 -0.0017 浮点舍入)
- 旧参数 binType=存货 被忽略(仍返回拣选且命中同一缓存);7 / 60 天窗口与单仓过滤均正常
- 单测 9/9 通过;后端语法检查 182 文件通过;前端 eslint 无报错、vite build 成功
2026-10-08 13:09:49 +08:00
88a39975d3 feat: 动销等级改为按商品日销统计,列表与导出统一到仓位×商品粒度
- 取数统一为 fetchPickingBinSkuRows:一行 = 一个「仓位 × 商品编码」,列表与导出共用
  商品日销 = 商品窗口整档销量 ÷ 天数(不做仓位分摊);同一商品占几个仓位都是同一等级
  另输出分摊销量 = 商品销量 ÷ 占用同类型仓位数,供按行求和
- 列表页同步拆到商品粒度:新增商品编码列,去掉 SKU数列,搜索支持商品编码
- 汇总卡片主数值改为去重商品数,另显示明细行数;销量按去重商品口径统计
- 导出新增「分摊销量」「商品日销」列,表头口径标注更明确
- 性能:仓位类型选满时不拼 IN 过滤(避免参数嗅探走坏计划),全类型查询由超时降到约 30 秒

验证(基准日 2026-10-08、30 天、拣选位):
- 明细 11871 行 / 去重商品 6821 个 / 库存合计 499141(与旧口径一致)
- 跨多仓位的 2028 个商品,销量与等级不一致 0 个
- 导出行求和:分摊销量合计 96847 = 去重商品销量合计 96847(差异 -0.0017,浮点舍入)
- 单测 9/9 通过;语法检查 182 文件通过;前端 eslint 无报错、vite build 成功
2026-10-08 10:58:17 +08:00
31f184edd0 feat: 拣选库位动销分析导出增加商品编码,按仓位×商品编码拆行
- 新增 fetchPickingBinSkuRows:仓位 × 商品编码 粒度明细
  库存数量 = 该商品在此仓位的实际库存;销量 = 商品销量 ÷ 其占用同类仓位数(贡献)
- 导出列:仓库 / 仓位 / 类型 / 商品编码 / 库存数量 / SKU数 / 销量 / 日销 / 动销等级 / 统计天数 / 统计基准日
- SKU数、动销等级仍为仓位粒度(同一仓位多行重复),列表页口径不变(仍用 bin 级聚合)
- 同一次取数比对:7055 个仓位,列表与导出库存/销量 0 处差异,库存合计一致 497721
- 单测 11 条通过
2026-10-08 09:35:26 +08:00
817cc74a40 feat: 拣选库位动销分析默认按近 30 天销量划分等级
- 默认统计窗口 15 天 -> 30 天(后端 PICKING_BIN_DEFAULT_DAYS 与前端常量同步)
- 可切换窗口补充近 60 天(7 / 15 / 30 / 60)
- 等级阈值不变:爆款≥20 / 热卖 10-20 / 平销 3-10 / 滞销-1 1-3 / 滞销-2 (0,1) / 销售=0
- 单测断言同步更新;实测 30 天窗口:7091 个拣选库位,日销=销量÷30
2026-10-07 17:41:17 +08:00
70550b5499 feat: 新增二级页面「拣选库位动销分析」(大货库存监控)
- 后端:pickingBinSalesAnalysis 配置/服务/路由,仓位库存取 rpt.jst_packInventoryInfo
  (bin_group 空值=拣选位、存货=存货位),销量按「SKU 占用同类仓位数平均分摊」,
  恒等式「全部库位销量之和 = 有库位 SKU 销量之和」差异为 0
- 动销等级(近 N 天日销,默认 15):爆款>=20、热卖款 10-20、平销款 3-10、
  滞销款-1 1-3、滞销款-2 (0,1)、销售=0
- 前端:大货库存监控下新增二级菜单 + 页面(等级卡片、仓库/类型/天数/仓位筛选、
  分页表格、xlsx 导出),路由/菜单/页面权限四处注册
- 测试:后端 8 项单测通过;前端 lint/test/build 通过
2026-10-07 16:59:39 +08:00
30554720b2 feat: 客退看板新增「最晚退厂时间」列(工作平台数据,先占位待接入)
- 新增 supplierExitDateProvider.js:按供应商名匹配「最晚退厂时间」,
  数据源为工作平台库 WorkbenchDataShared(同实例,另有 WorkbenchIdentity /
  DataPlatformControl)。当前副本账号 cangku 无该库访问权限、系统设置里没有它的
  连接配置、.env 的 JIAN_SUN_DB_* 与 WORK_ORDER_* 也为空,因此先返回不可用。
  接入方式:IT 开通后配 SUPPLIER_EXIT_DATE_ENABLED=true + SUPPLIER_EXIT_DATE_SQL
  (查询需返回 supplierName / exitDate 两列),或直接写死 CODE_* 常量,无需改其他文件。
- returnStockWarningService.js:composeRows 增加 latestExitDate /
  latestExitDateLabel / latestExitDateAvailable;列表响应新增 exitDateMeta;
  导出列在「供应商是否合作」后新增「最晚退厂时间」(未接入时导出显示「待接入」);
  口径说明补一条(含完整取证:工作平台库不可达、副本无任何退厂字段、
  fys_supplier.NoCoopTime 是不可用的停更快照)。
- StockWarning.jsx:表格新增「最晚退厂时间」列,有值显示日期、未接入显示「待接入」
  (Tooltip 说明数据来源与开通方式);monitor.css 补占位符样式。
2026-09-28 16:06:40 +08:00
6c3b14fc10 客退看板「是否出库」列改为「供应商是否合作」
- returnStockWarningService.js: 新增 supplierCoopSign 取 js_supplier.coopersgn
  (LEFT JOIN dbo.js_supplier sp ON sp.scmname = sk.supplier_name),
  解析为 supplierCoop / supplierCoopLabel(1=合作 / 2=不合作 / 其它=未知),
  导出列「是否出库」同步替换为「供应商是否合作」
- StockWarning.jsx: 表格列替换,新增合作状态 Tag 与判断依据 Tooltip
- returnStockWarehouseRegistry.js: 更新崔桥工厂仓客退位复测结论
  (2026-09-27 已同步进副本:349 编码 / 507 件)
2026-09-28 15:34:55 +08:00
2745cf18ff feat: 库存动销分析「销售=0」导出按仓位拆行,库存数量按实际仓位统计
上一版把同一 SKU 的多个仓位用「、」拼在一格里,库存数量仍是 SKU 总量,
无法直接按仓位盘点。现改为每个仓位独立一行:

- bin CTE 改为按 (wms_co_id, sku_id, LTRIM(RTRIM(bin))) 分组,
  SUM(qty) 得到仓位级库存(各仓位行之和 = SKU 总库存,实测 240=60+69+35+76)
- 导出行 = graded(SKU 级「销售=0」判定)LEFT JOIN 仓位级明细:
  有仓位数据的 SKU 按仓位展开多行、库存数量取该仓位实际 qty(qty=0 的仓位行剔除);
  仓位表未覆盖的 SKU 保留一行(仓位为空、库存取总库存),不丢数据
- 排序:仓库 → SKU 总库存倒序 → SKU → 仓位名称,同一 SKU 的各行保持相邻
- 前端按钮提示同步更新拆行与兜底规则说明

验证:后端 syntax 通过、单测 9/9(全量 389/390,唯一失败为既有 Windows 环境项);
前端 eslint/build 通过;
本地实测:单仓(睢宁 196 行,原 128 SKU 行)样例 SKU 正确拆为 4 行且合计 240;
全仓导出 6798 行(原 3650),有仓位行 6529、仓位为空兜底行 269,覆盖 26 仓
2026-09-28 09:55:45 +08:00
fc280bfe27 feat: 库存动销分析「销售=0」导出增加系统仓位列
仓位用于定位滞销库存的实际存放位置,便于盘点与挪位处理。
数据源为 rpt.jst_packInventoryInfo.bin(jst_sku.bin 全部为空、库存表无仓位字段,
这是业务库唯一可得的系统仓位):
- 按 (wms_co_id, sku_id) 聚合,LTRIM 去空格后 DISTINCT,STRING_AGG 按名称排序用「、」连接
- 排除无仓位(bin 为空)与零库存(qty=0)的行,避免无效仓位干扰
- LEFT JOIN 且按「仓库+SKU」双条件匹配,避免跨仓错配
- 导出列顺序:仓库 | SKU编码 | SKU名称 | 系统仓位 | 库存数量 | 销量 | 统计天数 | 统计基准日
- 前端按钮提示同步说明多仓位拼接规则与留空含义

验证:后端 syntax 通过、单测 9/9(全量 389/390,唯一失败为既有 Windows 权限环境项);
前端 eslint/build/测试均通过;
本地实测导出 3650 行,系统仓位列就位,3381 行有仓位(92.6%,未覆盖的 7.4% 为
仓位表仅含 13 个仓库所致),销量列全为 0,最长仓位串 600 字符(Excel 上限内);
浏览器实测点击下载,文件名与列结构正确
2026-09-28 09:34:55 +08:00
2c8ed86c26 feat: 库存动销分析「销售=0」档支持导出 SKU 明细
- 后端新增 GET /api/stock-sales-analysis/zero-sales-export:
  复用与页面完全一致的分档口径(同一段 gradeCase 表达式 + 「有库存或有销量」过滤),
  输出仓库/SKU编码/SKU名称/库存数量/销量/统计天数/统计基准日的 SKU 级明细;
  支持.days 统计窗口、wmsCoId 逗号分隔过滤(仅允许仓库清单内的 ID,防任意拼参)、
  format=xlsx|csv,复用通用导出服务 exportWorkbook
- services 新增 fetchZeroSaleSkuRows:SKU 级「销售=0」查询,LEFT JOIN rpt.jst_sku 取名称,
  按仓库+库存倒序排列
- 前端工具栏新增「导出销售=0」按钮:跟随当前统计天数与仓库筛选,
  文件名含业务日期与仓库范围,失败时解出错误体给出可读提示
- 测试补充 3 条:防呆抛错、空参短路、SQL 口径与参数传递断言

验证:后端 syntax 通过、单测 8/8(全量 389 条仅既有 Windows 环境项失败);
前端 eslint/build/test 21/21 通过;
本地实测:全仓导出 3641 行、26 仓、销量列全为 0;
单仓(崔桥半成品包装 694 行)与页面「销售=0」SKU 数一致;
浏览器实测点击下载真实文件名「库存动销分析-销售0明细-20260926.xlsx」
2026-09-27 17:47:45 +08:00
f20bd3a487 feat: 侧边栏新增二级「库存动销分析」页面
按《仓库库存动销分析.xlsx》:
- 一级菜单「大货库存监控」下新增二级「库存动销分析」(路由 /planning/operation/stock-sales-analysis)
- 表头与表格结构按需求表:仓库 | 动销等级 | SKU数量 | SKU占比 | 库存数量 | 库存占比 | 销量,
  每仓 6 个动销等级(爆款/热卖款/平销款/滞销款-1/滞销款-2/销售=0)+ 1 行汇总,仓库列跨行合并
- 分档口径:日销 = 统计窗口内已发货件数 ÷ 窗口天数(7/14/30 天可选,默认 30),
  库存取 rpt.jst_inventoryInfo 实时库存,销量取 jst_Orders×jst_OrderItem 按 send_date
  (排除 Delete/Cancelled,与每日进销存发货口径一致),只统计有库存或有销量的 SKU
- 后端 /api/stock-sales-analysis:单条 SQL 一次覆盖全部仓库×等级,5 分钟缓存 + refresh=1 强制重算;
  返回逐仓 + 全仓合计、占比、无数据仓数量
- 前端页面:统计天数/仓库筛选、口径说明弹窗、合计行高亮、无数据仓计数提示
- 页面权限:前后端 sidebarPages 注册 pageKey=stock_sales_analysis,默认 planning 角色可见

验证:后端 syntax + 单测 5/5;前端 eslint、build、npm test 21/21;
本地接口 30 天窗口实测 27 仓(崔桥汇总 2080 SKU / 90822 件 / 54076 件销量),
占比逐仓闭合为 100%;浏览器实测侧边栏入口、表头、197 行渲染与截图均正常
2026-09-27 16:29:58 +08:00
0e77a70c33 feat: 每日进销存支持导入聚水潭官方进销存校正数据
业务库只能还原 4/12 个流水列且口径与聚水潭不一致(采购按单据日期、销售按订单发货日期,
jst_salesOutboundInfo.qty 全为 0),调拨/盘点/加工/其它出入库无单据表,故以官方导出为准:

- 新增本地表 daily_inventory_official(biz_date + warehouse 存期初/12 流水列/期末)
- 新增解析与导入服务:xlsx 解析、仓库 ID 映射、「期初+流水=期末」恒等式校验
- GET /api/daily-inventory 支持 ?date= 指定业务日,官方值优先、缺失回退推算并返回来源标记
- 新增 POST /api/daily-inventory/official-import(xlsx 上传/JSON)与 GET /official 查询
- 导入后回写本地库存快照(官方期初→上一日期末、官方期末→当日期末),使推算也对齐
- 前端:业务日切换、官方/推算来源标记、导入弹窗、口径说明同步更新
- 仓库清单补入「简尚-横林三仓主仓」(聚水潭导出中存在)
- 命令行导入:npm run inventory:import-official -- <日期> <xlsx>
2026-09-27 14:16:06 +08:00
c47a25b002 fix: 期初兜底弃用 pm_stockMonitor,全部改用实时库存扣流水回推
口径校准(对照聚水潭进销存导出 09-23 期初基准):
  崔桥工厂仓 基准 92388 / pm 路径 114286 / 回推 ≈89650
  横林二仓   基准 63780 / pm 路径  1197  / 回推 ≈64029
  山东青岗集 基准 88377 / pm 路径  67339 / 回推 ≈88210
  河南洛宁   基准 83305 / pm 路径  76992 / 回推 ≈82473

pm_stockMonitor.jsStock 只覆盖纳入监控范围的 SKU 子集(横林二仓实测
1197 vs 全仓 63780),系统性偏低,不能充当全仓结存;「实时库存扣
流水回推」与基准偏差 0.3%~2.7%(来自缺失的 8 类单据),显著更准。
2026-09-27 10:09:19 +08:00
c358aff781 feat: 每日进销存补充流水与期初数据,修复合计行 NaN
数据补充(全部来自数据库,无人工填数):
- 流水列接入 4/12:采购进仓(jst_purchase×Item,po_date,Confirmed/Finished)、
  采购退货(jst_purchaseout×Item,io_date)、销售出仓(jst_Orders×OrderItem,send_date,
  与周转率发货口径一致)、销售退货(jst_returnAndRefund×Item,as_date,实收 r_qty)。
  其余 8 列业务库确无单据表(已核实全库表清单),保持「—」待 DBA 建表
- 期初快照缺失时自动兜底:优先用 rpt.pm_stockMonitor(报表库 01:00 跑批 T-1 库存,
  按 dbWarehouse 聚合 jsStock,精确);监控表没有的虚拟仓(运营专用/京东仓)用
  实时库存扣自期初时刻以来的带符号流水回推(近似),推导结果补写快照逐日滚动

缺陷修复:
- 合计行仓库名显示 NaN:isTotal 渲染分支拦截了所有列,'合计' 被强转数字;
  现仓库名原样显示,无数据来源的流水列合计也显示「—」而非误导性 0
- 合计行:某列全部仓库均无数据时合计置空,避免 0 误读

新增 4 项单元测试(流水符号约定、参数展开、期初双路径推导)。
2026-09-27 09:54:59 +08:00
3bfc48b8b3 Merge branch 'codex/daily-inventory-snapshot-0924' into codex/daily-inventory-notify-0924
冲突文件:backend/src/index.js
处理理由:采用已解决好与他人 PR #40/#41 冲突的分支 A 版本,再补回本分支的通知接口
require、/api/daily-inventory-notification 路由注册与调度器启动,双方行为均保留。
2026-09-26 12:18:11 +08:00
734b974f45 Merge branch 'main' into codex/daily-inventory-snapshot-0924(同步 PR #40/#41)
冲突文件:backend/src/index.js
处理理由:main 侧(他人 PR #40、#41)新增崔桥工厂仓客退三类路由 require 与注册、统一 404 与 JSON 错误处理中间件;
本分支仅新增每日进销存快照任务的 require 与 startDailyInventorySnapshotScheduler() 调用。
采用 main 版本文件并补回本分支两处改动,双方行为均保留。
2026-09-26 12:16:30 +08:00
b0f56b27f5 客退看板首仓改为崔桥工厂仓(数据源:崔桥工厂客退临时上架仓)
按 2026-09-25 业务口径,第一张子看板由「横林二仓客退数据监控」改为
「崔桥工厂仓客退数据监控」,取数仓位同步改为聚水潭「崔桥工厂客退临时上架仓」。

- 仓库配置:简尚-崔桥工厂仓 wms_co_id=15038426,客退位 customize_qty_1
- key 由 henglin2 改为 cuiqiao,同步改路由 / 侧边栏 / 路由映射 / 权限条目
- 旧路径 returns-inventory/henlin2* 保留为重定向,避免历史链接 404
- 核销看板(returnMonitorService)仍指向横林二仓,前端入口已下线,本次未动

副本取证:该仓客退位当前只有 2 编码 / 2 件(退货单有量:ERP 3115 条 +
奇门 1812 条),属「客退上架库存未同步进副本」,页面需靠导入功能补数据。
2026-09-25 14:16:11 +08:00
90fa54bd8c Merge branch 'main' into codex/cangku-returns-monitor-001 (sync 41 commits, keep both sides) 2026-09-24 15:37:59 +08:00
134
345214daed fix: force live refresh on MKH sync 2026-09-24 14:49:08 +08:00
134
ba6ef97a40 fix: reject stale MKH backend responses 2026-09-24 14:02:24 +08:00
134
22e7264438 feat: show MKH refresh progress 2026-09-24 13:36:44 +08:00
134
ea312d9070 fix: force fresh MKH operational data 2026-09-24 13:30:09 +08:00
134
30f5df2206 fix: auto refresh MKH live data 2026-09-24 13:15:53 +08:00
56fba389e4 feat: 每日进销存增加定时截图通知
设置(仅管理员)
- 可配置是否启用、通知时间(默认 01:00 Asia/Shanghai)、多名通知用户
- 收件人在后端规范化:去空、去重、限长限量,配置持久化到 system_config
- 接口不返回通知中心密钥与对象存储密钥,只给是否已配置与脱敏预览

定时任务
- 单实例锁(MySQL GET_LOCK),按配置时间触发,命中后 30 分钟内失败最多补跑 3 次
- 服务端签发 15 分钟短时效 JWT 作为专用身份,令牌只写入浏览器 localStorage
- 等数据真正渲染完成(有数据行、无加载中、无错误提示)才截图,展开滚动容器后整页截图
- 对象键形如 daily-inventory/2026-09-25/daily-inventory-20260925-010000.png
- 截图上传成功后才逐用户发通知,单人失败不影响他人;正式推送按「业务日期+收件人」幂等
- 立即测试走同一链路但幂等键带随机数,不影响次日正式推送
- 每次执行记录时间、截图地址、通知对象、成功状态与失败原因

测试
- 覆盖收件人去重、通知时间校验、幂等键、业务日期与对象键、重试退避(17 项)
2026-09-24 11:22:51 +08:00
9dbe2793a5 feat: 每日进销存改为全部从数据库取数
- 期初数量改由本地快照表 daily_inventory_snapshot 提供(上一业务日期末),
  每日 01:30 定时任务落库,接口侧缺失时补写兜底,逐日自动滚动
- 新增 dailyInventorySnapshotDb / dailyInventorySnapshotJob 与手动补跑脚本
- dailyInventoryBaseline 瘦身为纯仓库映射,去掉所有硬编码业务数值
- 12 个流水列因业务库缺单据表返回空值,前端灰底显示「—」
- 修复 qty 全为 0 的仓库期末显示 null 的缺陷
- 新增 docs/库存流水单据表需求.md 给 DBA
2026-09-24 11:20:30 +08:00
e4b6bbcf31 feat: 新增每日进销存(大货库存监控) 2026-09-23 17:41:03 +08:00
cd936f5e92 feat: 新增每日进销存(大货库存监控) 2026-09-23 17:41:00 +08:00
9e2f26b1c9 feat: 新增每日进销存(大货库存监控) 2026-09-23 17:40:58 +08:00
2bd5dce3d7 feat: 新增每日进销存(大货库存监控) 2026-09-23 17:40:56 +08:00
23e576d26e feat: 新增每日进销存(大货库存监控) 2026-09-23 17:37:48 +08:00
d3f36d5359 feat: 新增每日进销存(大货库存监控) 2026-09-23 17:37:47 +08:00
819cd08de2 feat: 新增每日进销存(大货库存监控) 2026-09-23 17:37:45 +08:00
855586d5de feat: 新增每日进销存(大货库存监控) 2026-09-23 17:37:44 +08:00
592c52f891 refactor: 移除大货库存监控功能(26 仓)
应用户要求下线该功能组,侧边栏不再显示「大货库存监控」及 26 个仓库二级菜单。

移除内容:
- 前端页面 LargeStockMonitor、18 列定义、API 封装与样式
- 路由 /planning/large-stock-monitor/:warehouseSlug 与权限点 large_stock_monitor
- 侧边栏菜单组 stock-monitor-large 与 SIDEBAR_ROUTE_MAP 26 条映射
- 后端 /api/large-stock-monitor 路由挂载、largeStockMonitor.js、
  largeStockMonitorConfig.js、largeStockMonitorFlow.js 及端到端校验脚本

验证:
- 全仓无 largeStockMonitor 残留引用;后端 346 用例 345 通过
  (1 项失败为 resilientSecretStore.test.js,Windows 权限语义基线既有失败)
- 前端 lint + 测试 + 构建通过,产物中 LargeStockMonitor chunk 已消失
- 运行时验证:/api/large-stock-monitor/* 返回 404,页面与登录不受影响
2026-09-23 16:07:04 +08:00
134
66e0ddf709 fix: 用箱仓库存补齐滞后的配件库存 2026-09-23 15:42:46 +08:00
2bc949099b feat: 新增大货库存监控(26 仓)
按「库存监控」表格在侧边栏增加一级/二级菜单与共用页面
(路由 /planning/large-stock-monitor/:warehouseSlug)。

后端:
- 新增 largeStockMonitorConfig.js:26 仓配置,含 wms_co_id / 快照仓名 / routeKey / slug 映射
- 新增 largeStockMonitor.js:config / overview / sku-list 三个接口
- 新增 largeStockMonitorFlow.js:采购进仓、采购退货、销售出仓、销售退货四类单据流水取数
- 新增 verify-large-stock-monitor.js:26 仓端到端校验脚本
- index.js:挂载 /api/large-stock-monitor 路由

口径说明:
- 库存周转天数 = 月度发货总件数 / 月度日均库存件数 × 100%,下沉到 SQL CASE WHEN 计算
- 四级分级(滞销预警/慢流/呆滞/僵尸)按库龄 + 周转率双维度判定
- 期末数量取聚水潭实际库存 jst_inventoryInfo(按 wms_co_id 单仓),
  不用 pm_stockMonitor.jsStock —— 后者 warehouse 是「归属仓」口径,
  会把已调拨到外仓的货仍计在本仓名下(崔桥工厂仓因此虚高约 2.2 万)
- 截止时刻统一为快照日次日 01:00(跑批时点),库龄/分级均以此为基准,不再用 GETDATE()

前端:
- 新增 LargeStockMonitor 页面、18 列定义、API 封装与样式
- 路由与侧边栏:26 条二级菜单 + 权限点

冲突处理:
- 主仓库已下线 /api/dashboard(backend/src/api/dashboard.js 被移除),
  相应删除 dashboardRoutes 的 require 与挂载,避免后端启动时 require 失败崩溃
- 保留主仓库新增的「模块化库存监控」菜单组及其子项
2026-09-23 13:42:58 +08:00
acc088dd1a Merge branch 'main' into codex/cangku-returns-monitor-001 2026-09-23 11:39:39 +08:00
134
d32494ca12 fix: 消除前端循环分块导致的骨架屏 2026-09-23 11:35:47 +08:00
b3f4d1af64 feat: 客退数据监控五仓看板与本地运行稳定性加固
功能:
- 退货库存预警:横林二仓/苏北/睢宁/山东青岗集/河南洛宁 五张看板,
  按各仓客退上架仓对应的自定义库存位取数(各仓位不同:横林/苏北/睢宁 c1,山东 c2)
- 支持导入聚水潭「箱及仓位库存」导出文件覆盖创建时间、件数、供应商
  (副本只有仓库级数量,没有箱级维度,真实上架时间只能靠导入)
- 退货入库核销与每日库存监控:状态落本地系统库,含定时任务与差异清单
- 服务端导出(xlsx/csv),不受列表分页与展示上限约束

稳定性:
- 前端幂等请求在后端重启窗口内自动退避重连,不再直接抛 500
- 后端补齐统一 JSON 错误兜底与 /api JSON 404,异常不再返回 HTML
- 隧道 ssh 改为绝对路径解析并隐藏窗口,避免非交互式启动 ENOENT 与黑窗弹窗
- 业务库 DATETIME 按北京时间解析(useUTC: false),修复时间显示整体 +8 小时

合并:与上游「隧道断线自愈 + 配置负缓存」为同一问题的两份实现,
本分支采用上游方案(带 TTL 的负缓存 + 统一失效函数 + 单测)。
2026-09-23 11:32:16 +08:00
134
b8e60a19d2 fix: MKH刷新改用实时订单库存口径 2026-09-23 11:12:08 +08:00
134
fa91b9242c fix: 根治MKH同步后旧请求覆盖最新数据 2026-09-23 11:05:28 +08:00
5e029f831f fix: MKH-BOM 库存增加箱表兜底,修复「当日入库当天显示库存为 0」
现象:MKHDNZ0409260700491 当天已入库、实物有货,但页面上「现有库存」一直是 0;
点「同步最新数据」也不变。

根因:页面唯一的库存源 rpt.jst_inventoryInfo 是「有变化才写 updateTime」的同步表,
品牌仓尤其稀疏。该 SKU 在 inventoryInfo 里只有主仓 14138566 的一行 qty=0,
品牌仓连行都没有;而小时级的箱表 rpt.jst_packInventoryInfo 里,
品牌仓 bin='P-1' / qty=4 / createTime 当天 15:19 —— 当日入库的货只有箱表看得见。
页面取不到实时值 → 回退 09-18 监控快照 jsStock=0 → 显示 0。
「同步最新数据」按钮只清服务端缓存,不触发上游同步,所以点多少次都是 0。

改法(只补 0,不覆盖有效值):
  ① inventoryInfo 品牌仓 qty > 0        → 用它(stockSource='realtime',旧口径不变)
  ② 否则箱表品牌仓 bin 非空行合计 > 0    → 用它(stockSource='pack')
  ③ 否则 inventoryInfo 有品牌仓行(qty≤0) → 用 0
  ④ 否则回退监控快照 jsStock             → stockSource='monitor'

为什么不拿箱表当主源:两表都 > 0 且不等的 28 个成品里 25 个是 inventoryInfo 更大
(箱表只记已装箱的货,散货不计),拿它当主源会系统性低估库存、误报缺货。
另外必须过滤 bin 为空的行——那是装箱记录不是库存(品牌仓合计 21 件)。

影响面(实测):品牌仓 4,408 个成品里只有 3 个走箱表兜底
(MKHDNZ0409260601548=5 / MKHDNZ0409260700059=4 / MKHDNZ0409260700491=4,
同一箱 115870495000001201、货位 P-1、均为当天新建行),
预警列表 27 → 24 条(缺货 21 → 18),少的正是这 3 条「入库当天被误判缺货」。

前端:「现有库存」列 tooltip 增加 pack 来源说明(当天入库、实时库存表尚未同步)。

验证:check-syntax 151 文件通过;node --test 339/340(唯一失败是既有的
resilientSecretStore Windows 权限断言);mkhBomService.test.js 47/47;
HTTP 实测该 SKU stock=4 / stockSource=pack,点「同步最新数据」后仍正确。

遗留(需业务/上游跟进):inventoryInfo 当天 06:41 后就没再更新过
(09-17 / 09-18 都是全天逐小时更新),疑似上游同步任务异常;
箱表兜底只解决「当日入库看不见」,存量库存仍依赖该表恢复。
2026-09-19 18:11:38 +08:00
3e64702f12 fix: MKH-BOM 新增日销订单表兜底,修复上游快照漏算导致「库存 0 却不报缺货」
背景
- 业务反馈 MKHDNZ0409260601548 近 15 天实发 2 件、库存 0,页面既不报缺货也不进任何预警。
- 根因是双重盲区:
  ① 上游 rpt.pm_stockMonitor.fifteenAvgSale 漏算 —— 快照 09-18 下,窗口内有真实发货的
     178 个 MKH 成品编码里有 24 个被记成 0;其中 16 个订单全部来自样品专用店铺(上游同样
     不计样品单,属合理口径),剩 8 个是真实店铺(天猫/京东/拼多多)。该列是**保留两位
     小数的日均**(最小非零 0.07 = 1 件 ÷ 15 天),无法用四舍五入解释。
  ② 快照日销 = 0 且待发货订单 = 0 时,判定直接落到「无销量」分支、hasWarning=false,
     彻底排除在预警之外 —— 库存为 0 也救不回来。

改动
- 日销兜底(服务文件头新增 ⑦-a):**快照日销 ≤ 0 且近 15 天有真实发货**时,日销改用
  订单表实算值 = 已发货件数 ÷ 15;快照 > 0 一律以上游为准,不覆盖,避免两套口径打架。
  · 排除样品/测试专用店铺(shop_name 含「样品」「测试」),与上游口径对齐
  · 窗口 = 含今天在内的 15 个自然日,与分母同口径
  · 结果透出 dailySaleSource('snapshot'|'orders') / snapshotDailySale / recentShippedQty
- 取数:loadPendingOrderIndex 重构为 loadOrderIndex,一条查询同时覆盖「待发货
  (send_date IS NULL)」与「近 15 天已发货(send_date 在窗口内)」。实测合并后总耗时
  约 1.25s ≈ 单查待发货,而拆成两条独立查询要在现有开销之上再加一次全表扫描
  (首轮冷执行计划下 qmcust 单表可劣化到 20s+)。
- PENDING_ORDER_TABLES 更名 ORDER_TABLES:两张表的口径没变,只是不再仅服务待发货。
- 前端:日销来自兜底时加虚线下划 + 悬停说明,业务拿该数字与源系统 fifteenAvgSale
  对照时不会被误判为数据错误。

验证
- 语法检查:151 文件通过。
- 后端测试:338 项 337 通过。唯一失败为 resilientSecretStore 的权限断言(期望 0o600、
  Windows 实际 0o666),平台固有,与本改动无关。
- 新增回归测试:兜底生效 / 快照非零不覆盖 / 无发货不兜底 / 与订单驱动的优先级 /
  订单源降级时退化成旧口径;订单索引测试补充「已发货同编码跨表累加」与「样品、测试店排除」断言。
- 真实数据端到端(本地接口 /api/mkh-bom/list,4,223 个成品):
  · MKHDNZ0409260601548:日销 0 → 0.1333(source=orders),不再隐身
  · 日销 > 0 的商品 177 → 198;有效预警(缺货 + 预警)18 → 28
  · 21 个等级变化**全部是「无销量 → 缺货/正常/已排产」**,没有任何既有预警被降级
  · 其中 10 个新缺货均为「库存 0 + 近 15 天真实发货 9~27 件」的真盲区
  · 样品店排除按整行生效:实测某编码窗口内 5 件(含样品店 1 件)→ 索引记为 4 件
- 前端 eslint 通过、vite build 通过。

风险与回退
- 只影响「快照日销 = 0」的商品,快照 > 0 的商品行为完全不变。
- 订单源不可用时兜底自动失效,退化成旧口径(不会误报)。
- 回退:本提交可整体 revert,无表结构变更、无数据迁移。
2026-09-19 13:24:15 +08:00
b7aca2343b feat(配件库存监控/MKH-BOM): 新增「采购在途」数量列与在途明细弹窗,补货预警改按结余库存判定
配件库存监控:
- 列表「实时库存」后新增「采购在途数量」列,口径 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 单测新增结余库存缓冲/紧张用例与回归用例
2026-09-18 17:44:01 +08:00
be52bdd0b5 feat(配件库存监控/MKH-BOM): 两页面新增「同步最新数据」按钮
需求:配件库存监控、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 恢复后,打开两个页面确认按钮渲染与点击效果。
- 后端改动需重启服务后才生效。
2026-09-17 18:14:57 +08:00
0bb7c5905d fix(模块化库存监控): 已配置MKH 改从聚水潭 SKU 级 BOM 维护(jst_skuBomItem)判定
业务反馈上一版「已配置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,故采用后者。
2026-09-17 16:20:17 +08:00
e6ea1246f9 配件库存监控:已配置MKH改为"被MKH模块化成品引用"判定
- 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)。
2026-09-17 16:01:20 +08:00
75a8795723 配件库存监控:表头"SKU编码"改为"商品编码"
- 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 归零。经用户确认,本次保留现有产品资料库判定口径,
仅做表头文案变更。
2026-09-17 15:44:30 +08:00