forked from lunanting/cangku
按「库存监控」表格在侧边栏增加一级/二级菜单与共用页面 (路由 /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 失败崩溃 - 保留主仓库新增的「模块化库存监控」菜单组及其子项
165 lines
9.4 KiB
JavaScript
165 lines
9.4 KiB
JavaScript
// 大货库存监控配置(前端镜像)
|
||
// 与 backend/src/config/largeStockMonitorConfig.js 同步
|
||
// 修改需双端同步;运行期也可通过 /api/large-stock-monitor/config 获取
|
||
|
||
const RAW_WAREHOUSES = [
|
||
{ idx: 1, group: '半成品部件仓', warehouse: '简尚-崔桥工厂半成品包装项目仓主仓' },
|
||
{ idx: 2, group: '运营C', warehouse: '简尚-运营C崔桥工厂仓专用仓主仓' },
|
||
{ idx: 3, group: '崔桥工厂仓', warehouse: '简尚-崔桥工厂仓主仓' },
|
||
{ idx: 4, group: '崔桥工厂仓', warehouse: '简尚-崔桥工厂京东仓主仓' },
|
||
{ idx: 5, group: '崔桥工厂品牌仓', warehouse: '简尚-崔桥工厂品牌仓主仓' },
|
||
{ idx: 6, group: '河南洛宁仓', warehouse: '简尚-河南洛宁仓主仓' },
|
||
{ idx: 7, group: '运营D', warehouse: '简尚-运营D专用仓库主仓' },
|
||
{ idx: 8, group: '运营C', warehouse: '简尚-运营C横林二仓专用仓主仓' },
|
||
{ idx: 9, group: '横林二仓', warehouse: '简尚-横林二仓主仓' },
|
||
{ idx: 10, group: '横林三仓', warehouse: '简尚-横林三仓主仓' },
|
||
{ idx: 11, group: '山东品牌仓', warehouse: '简尚-山东品牌仓主仓' },
|
||
{ idx: 12, group: '山东青集岗仓', warehouse: '简尚-山东青岗集京东仓主仓' },
|
||
{ idx: 13, group: '山东青集岗仓', warehouse: '简尚-山东青岗集仓主仓' },
|
||
{ idx: 14, group: '运营D', warehouse: '简尚-运营D苏北专用仓主仓' },
|
||
{ idx: 15, group: '运营C', warehouse: '简尚-运营C新苏北专用仓主仓' },
|
||
{ idx: 16, group: '苏北仓', warehouse: '简尚-新苏北仓主仓' },
|
||
{ idx: 17, group: '分销仓', warehouse: '简尚-分销部苏北专用仓主仓' },
|
||
{ idx: 18, group: '运营D', warehouse: '简尚-运营D睢宁专用仓主仓' },
|
||
{ idx: 19, group: '运营C', warehouse: '简尚-运营C睢宁专用仓主仓' },
|
||
{ idx: 20, group: '睢宁仓', warehouse: '简尚-睢宁仓主仓' },
|
||
{ idx: 21, group: '沭阳金源涌动', warehouse: '简尚-沭阳金源涌动项目仓主仓' },
|
||
{ idx: 22, group: '分销仓', warehouse: '简尚-分销部睢宁专用仓主仓' },
|
||
{ idx: 23, group: '外贸仓', warehouse: '简尚-新外贸仓主仓' },
|
||
{ idx: 24, group: '外贸仓', warehouse: '简尚-济南外贸仓主仓' },
|
||
{ idx: 25, group: '分销仓', warehouse: '简尚-分销部专用仓主仓' },
|
||
{ idx: 26, group: '分销仓', warehouse: '简尚-分销部崔桥专用仓主仓' },
|
||
];
|
||
|
||
function slugify(value) {
|
||
return String(value || '')
|
||
.toLowerCase()
|
||
.replace(/[^a-z0-9\u4e00-\u9fa5-]+/g, '-')
|
||
.replace(/^-+|-+$/g, '');
|
||
}
|
||
|
||
export const LARGE_STOCK_MONITOR_WAREHOUSES = RAW_WAREHOUSES.map((entry) => ({
|
||
...entry,
|
||
routeKey: `stock-monitor-large-${entry.idx}`,
|
||
slug: slugify(entry.warehouse) || `warehouse-${entry.idx}`,
|
||
}));
|
||
|
||
// 18 列表头(按 Excel 第 1 行 D..U 列)
|
||
// available=false:业务库(220 张表)中不存在该单据类型,无数据源,显示「—」
|
||
// estimated=true:由已有字段推导(非原始字段),前端标「估」并在悬浮说明中写明推导式
|
||
export const OVERVIEW_COLUMNS = [
|
||
{
|
||
key: 'periodStart',
|
||
label: '期初数据',
|
||
available: true,
|
||
estimated: true,
|
||
tip: '业务库仅保留最新单日库存快照,无上月末历史。此值由「期末 − 采购进仓 + 采购退货 + 销售出仓 − 销售退货」倒推,未包含加工/调拨/盘点/其它出库。倒推为负时显示「—」。',
|
||
},
|
||
{ key: 'processIn', label: '加工进仓', available: false, tip: '业务库无加工单据表,无数据源' },
|
||
{
|
||
key: 'purchaseReturn',
|
||
label: '采购退货',
|
||
available: true,
|
||
tip: '本月采购退货出库单数量:rpt.jst_purchaseout + jst_purchaseoutItem(状态 Confirmed)',
|
||
},
|
||
{
|
||
key: 'purchaseIn',
|
||
label: '采购进仓',
|
||
available: true,
|
||
tip: '本月采购单已入库数量:rpt.jst_purchase + jst_purchaseItem 的 inQty(状态 Confirmed/Finished)',
|
||
},
|
||
{
|
||
key: 'salesOut',
|
||
label: '销售出仓',
|
||
available: true,
|
||
tip: '本月销售出库单件数:rpt.jst_salesOutboundInfo(状态 Confirmed)关联 jst_OrderItem.qty',
|
||
},
|
||
{ key: 'stocktake', label: '盘点', available: false, tip: '业务库无盘点单据表,无数据源' },
|
||
{ key: 'processOut', label: '加工出仓', available: false, tip: '业务库无加工单据表,无数据源' },
|
||
{ key: 'transferOut', label: '调拨出', available: false, tip: '业务库无调拨单据表,无数据源' },
|
||
{
|
||
key: 'salesReturn',
|
||
label: '销售退货',
|
||
available: true,
|
||
tip: '本月退货退款单实退数量:rpt.jst_returnAndRefundInfo + jst_returnAndRefundItem 的 r_qty(状态 Confirmed,按仓库名称匹配)',
|
||
},
|
||
{ key: 'transferIn', label: '调拨入', available: false, tip: '业务库无调拨单据表,无数据源' },
|
||
{
|
||
key: 'openingBalance',
|
||
label: '期初',
|
||
available: true,
|
||
estimated: true,
|
||
tip: '同期初数据(倒推估算值)',
|
||
},
|
||
{ key: 'otherOut', label: '其它出库', available: false, tip: '业务库无其它出库单据表,无数据源' },
|
||
{
|
||
key: 'closingQty',
|
||
label: '期末数量',
|
||
available: true,
|
||
tip: '聚水潭实际库存 rpt.jst_inventoryInfo(按仓库 wms_co_id 单仓实物口径,实时值)。业务库无历史时点库存快照,此值随每次刷新滚动到最新;与某日「期末结存」的差额即该日之后的出入库与仓间调拨',
|
||
},
|
||
{ key: 'turnoverDays', label: '库存周转天数', available: true, tip: '月度发货总件数 / 月度日均库存件数 × 100%(按业务库口径,数值实为周转率 %)' },
|
||
{ key: 'warnLevel', label: '滞销预警级库存量', available: true, tip: '库龄 < 3 个月 且 30% ≤ 周转率 ≤ 199% 的 SKU 库存合计(按库存快照分级,口径为归属库存,与期末实际库存不同)' },
|
||
{ key: 'slowLevel', label: '商品慢流级库存量', available: true, tip: '库龄 < 3 个月 且 周转率 ≥ 200%;或 库龄 > 3 个月 且 30% ≤ 周转率 ≤ 99%' },
|
||
{ key: 'staleLevel', label: '商品呆滞级库存量', available: true, tip: '库龄 3~12 个月 且 周转率 ≥ 100%;或 库龄 > 12 个月 且 100% ≤ 周转率 ≤ 199%' },
|
||
{ key: 'zombieLevel', label: '商品僵尸级库存量', available: true, tip: '库龄 > 12 个月 且 周转率 ≥ 200%' },
|
||
];
|
||
|
||
// 分级标签(与后端 levelExpr 返回值一致)
|
||
export const LEVEL_LABEL = {
|
||
warn: '滞销预警',
|
||
slow: '商品慢流',
|
||
stale: '商品呆滞',
|
||
zombie: '商品僵尸',
|
||
};
|
||
|
||
export const LEVEL_FILTER_OPTIONS = [
|
||
{ label: '全部级别', value: '' },
|
||
{ label: '滞销预警', value: 'warn' },
|
||
{ label: '商品慢流', value: 'slow' },
|
||
{ label: '商品呆滞', value: 'stale' },
|
||
{ label: '商品僵尸', value: 'zombie' },
|
||
];
|
||
|
||
export const LEVEL_COLOR = {
|
||
warn: 'gold',
|
||
slow: 'orange',
|
||
stale: 'volcano',
|
||
zombie: 'magenta',
|
||
};
|
||
|
||
export function findWarehouseBySlug(slug) {
|
||
return LARGE_STOCK_MONITOR_WAREHOUSES.find((entry) => entry.slug === slug) || null;
|
||
}
|
||
|
||
export function findWarehouseByName(name) {
|
||
return LARGE_STOCK_MONITOR_WAREHOUSES.find((entry) => entry.warehouse === name) || null;
|
||
}
|
||
|
||
export const PAGE_KEY = 'large_stock_monitor';
|
||
export const PAGE_PATH = '/planning/large-stock-monitor/:warehouseSlug';
|
||
export const PAGE_NAME = '大货库存监控';
|
||
|
||
// 顶部"算式说明"提示
|
||
export const FORMULA_DESCRIPTION = [
|
||
'库存周转天数 = 月度发货总件数 / 月度日均库存件数 × 100%(按业务库约定列名沿用「周转天数」,数值实际为周转率,单位 %)',
|
||
'月度发货总件数 ≈ threeAvgSale × 30 (业务库仅有最近 3 天日均销量)',
|
||
'月度日均库存件数 ≈ jsStock (业务库仅保留最近一天库存快照,作为日均近似)',
|
||
'数据截止时刻:库存快照日次日的凌晨 01:00(业务库每天凌晨 1 点跑批,快照标注为前一日 T-1)',
|
||
'库龄(月) = (数据截止时刻 - SKU 入库时间) / 30',
|
||
'分级口径:',
|
||
'· 滞销预警:库龄 < 3 个月 且 30% ≤ 周转天数 ≤ 199%',
|
||
'· 商品慢流:库龄 < 3 个月 且 周转天数 ≥ 200%;或 库龄 > 3 个月 且 30% ≤ 周转天数 ≤ 99%',
|
||
'· 商品呆滞:库龄 3~12 个月 且 周转天数 ≥ 100%;或 库龄 > 12 个月 且 100% ≤ 周转天数 ≤ 199%',
|
||
'· 商品僵尸:库龄 > 12 个月 且 周转天数 ≥ 200%',
|
||
'流水类字段(本期 = 快照所在自然月 1 日 00:00 ~ 数据截止时刻,左闭右开):',
|
||
'· 采购进仓:jst_purchase + jst_purchaseItem.inQty(Confirmed/Finished)',
|
||
'· 采购退货:jst_purchaseout + jst_purchaseoutItem.qty(Confirmed)',
|
||
'· 销售出仓:jst_salesOutboundInfo 关联 jst_OrderItem.qty(Confirmed)',
|
||
'· 销售退货:jst_returnAndRefundInfo + jst_returnAndRefundItem.r_qty(Confirmed,按仓库名匹配)',
|
||
'· 期初数据:业务库无上月末快照,由「期末 − 采购进仓 + 采购退货 + 销售出仓 − 销售退货」倒推(标「估」,倒推为负时显示「—」)',
|
||
'· 加工进仓 / 加工出仓 / 盘点 / 调拨出 / 调拨入 / 其它出库:业务库无对应单据表,无数据源',
|
||
'期末数量:聚水潭实际库存 jst_inventoryInfo(按 wms_co_id 单仓实物口径)',
|
||
'注意:pm_stockMonitor 的 warehouse 是「归属仓」,jsStock 含已调拨到外仓的货,不能代表本仓实物库存',
|
||
'四级库存量:只能由库存快照分级得出(实时库存表无入库时间与销量字段),口径为归属库存,与期末实际库存不同',
|
||
]; |