fix: 配件库存用最新箱仓数据补齐 #28
@ -13,6 +13,8 @@
|
||||
// 目标仓库共 794 个 SKU
|
||||
// 库存 rpt.jst_inventoryInfo —— 实时库存 qty;按 wms_co_id 区分仓库
|
||||
// 目标仓库 wms_co_id = 15718196(rpt.jst_warehouseInfo 查得)
|
||||
// 该表为 0/缺行而箱及仓位库存已有正库存时,用同仓 rpt.jst_packInventoryInfo 合计补齐;
|
||||
// 只补 0,不覆盖 inventoryInfo 中的正库存,避免箱表未覆盖散货时低估库存。
|
||||
// 监控 rpt.pm_stockMonitor —— 日更快照,目标仓每天 794 行,覆盖全部 SKU:
|
||||
// fifteenAvgSale 近15天日均发货量(2026-09-17 由 7 天改 15 天,见下)
|
||||
// jsUseDays 库存可用天数
|
||||
@ -77,13 +79,39 @@ const SORTABLE_FIELDS = Object.freeze({
|
||||
category: 's.category',
|
||||
realtimeStock: 'realtimeStock',
|
||||
avgShip15d: 'm.fifteenAvgSale',
|
||||
stockDays: 'm.jsUseDays',
|
||||
stockDays: 'stockDays',
|
||||
warningLevel: 'warningOrder',
|
||||
mkhStatus: 'mkhStatus',
|
||||
location: 'location',
|
||||
warehouse: 's.other_3',
|
||||
});
|
||||
|
||||
// 实时库存优先使用库存表;库存表没有正库存时,用同仓「箱及仓位库存」补齐。
|
||||
// 聚水潭上游可能已经生成箱/仓位库存,但 jst_inventoryInfo 仍停留在旧值。
|
||||
// 只补 0/缺行,避免箱表只记录部分装箱库存时压低库存表中的有效库存。
|
||||
const buildRealtimeStockSql = () => (
|
||||
`CASE
|
||||
WHEN inv.qty > 0 THEN inv.qty
|
||||
WHEN ISNULL(ploc.packQty, 0) > 0 THEN ploc.packQty
|
||||
WHEN inv.qty IS NOT NULL THEN inv.qty
|
||||
ELSE ISNULL(m.jsStock, 0)
|
||||
END`
|
||||
);
|
||||
|
||||
// 箱表兜底生效时,监控快照的 jsUseDays 仍基于旧库存,需要同步重算。
|
||||
// 无日销时返回 NULL,不能因为旧快照为 0 天而把已有库存误报为低库存。
|
||||
const buildStockDaysSql = () => (
|
||||
`CASE
|
||||
WHEN ISNULL(inv.qty, 0) <= 0 AND ISNULL(ploc.packQty, 0) > 0 THEN
|
||||
CASE
|
||||
WHEN ISNULL(m.fifteenAvgSale, 0) > 0
|
||||
THEN CAST(ploc.packQty / NULLIF(m.fifteenAvgSale, 0) AS DECIMAL(18, 2))
|
||||
ELSE NULL
|
||||
END
|
||||
ELSE m.jsUseDays
|
||||
END`
|
||||
);
|
||||
|
||||
/**
|
||||
* 「已配置MKH」判定表达式(2026-09-17 晚修正口径):
|
||||
* 该 SKU 作为「配件商品编码」(map_outer_sku_id)出现在聚水潭 SKU 级 BOM 维护
|
||||
@ -107,8 +135,9 @@ const buildConfiguredExistsSql = () => (
|
||||
const buildWarningOrderSql = () => (
|
||||
`CASE
|
||||
WHEN cfg.configured = 0 THEN 2
|
||||
WHEN COALESCE(inv.qty, m.jsStock, 0) <= 0 THEN 0
|
||||
WHEN m.jsUseDays IS NOT NULL AND m.jsUseDays <= ${LOW_STOCK_DAYS_THRESHOLD} THEN 1
|
||||
WHEN ${buildRealtimeStockSql()} <= 0 THEN 0
|
||||
WHEN ${buildStockDaysSql()} IS NOT NULL
|
||||
AND ${buildStockDaysSql()} <= ${LOW_STOCK_DAYS_THRESHOLD} THEN 1
|
||||
ELSE 2
|
||||
END`
|
||||
);
|
||||
@ -171,15 +200,29 @@ OUTER APPLY (
|
||||
WHERE UPPER(LTRIM(RTRIM(TRY_CONVERT(NVARCHAR(100), c.sku)))) = UPPER(LTRIM(RTRIM(TRY_CONVERT(NVARCHAR(100), s.sku_id))))
|
||||
ORDER BY c.createTime DESC
|
||||
) cost
|
||||
-- 仓位:主货位(真货位),见 buildLocationSql() 说明
|
||||
OUTER APPLY (
|
||||
SELECT TOP 1 NULLIF(LTRIM(RTRIM(pk.bin)), N'') AS bin
|
||||
FROM dbo.jst_packInventoryInfo pk WITH (NOLOCK)
|
||||
WHERE pk.sku_id = s.sku_id
|
||||
AND NULLIF(LTRIM(RTRIM(pk.bin)), N'') IS NOT NULL
|
||||
AND ISNULL(pk.qty, 0) > 0
|
||||
ORDER BY pk.qty DESC
|
||||
) ploc
|
||||
-- 仓位及箱库存:先按 SKU/仓位一次性汇总,再关联目标仓,避免对 794 个 SKU
|
||||
-- 分别扫描无独立索引的上游视图。rn=1 是库存最大的主货位,packQty 是该 SKU 全仓合计。
|
||||
LEFT JOIN (
|
||||
SELECT ranked.sku_id, ranked.wms_co_id, ranked.bin, ranked.packQty
|
||||
FROM (
|
||||
SELECT
|
||||
pk.sku_id,
|
||||
pk.wms_co_id,
|
||||
NULLIF(LTRIM(RTRIM(pk.bin)), N'') AS bin,
|
||||
SUM(ISNULL(pk.qty, 0)) AS binQty,
|
||||
SUM(SUM(ISNULL(pk.qty, 0))) OVER (PARTITION BY pk.sku_id, pk.wms_co_id) AS packQty,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY pk.sku_id, pk.wms_co_id
|
||||
ORDER BY SUM(ISNULL(pk.qty, 0)) DESC, NULLIF(LTRIM(RTRIM(pk.bin)), N'') ASC
|
||||
) AS rn
|
||||
FROM dbo.jst_packInventoryInfo pk WITH (NOLOCK)
|
||||
WHERE pk.wms_co_id = @wmsCoId
|
||||
AND NULLIF(LTRIM(RTRIM(pk.bin)), N'') IS NOT NULL
|
||||
GROUP BY pk.sku_id, pk.wms_co_id, NULLIF(LTRIM(RTRIM(pk.bin)), N'')
|
||||
HAVING SUM(ISNULL(pk.qty, 0)) > 0
|
||||
) ranked
|
||||
WHERE ranked.rn = 1
|
||||
) ploc ON ploc.sku_id = s.sku_id AND ploc.wms_co_id = @wmsCoId
|
||||
-- 模块化配置状态:见文件头部「已配置MKH」口径说明
|
||||
OUTER APPLY (
|
||||
SELECT CAST(CASE WHEN ${buildConfiguredExistsSql()} THEN 1 ELSE 0 END AS BIT) AS configured
|
||||
@ -362,13 +405,13 @@ const buildProjection = () => `
|
||||
s.sku_id AS skuId,
|
||||
s.name AS productName,
|
||||
s.category AS category,
|
||||
COALESCE(inv.qty, m.jsStock, 0) AS realtimeStock,
|
||||
${buildRealtimeStockSql()} AS realtimeStock,
|
||||
ISNULL(ptr.transit, 0) AS purchaseTransit,
|
||||
ISNULL(pin.inQty, 0) AS inQtyToday,
|
||||
ISNULL(m.nearSecondSale, 0) AS outQtyToday,
|
||||
${buildLocationSql()} AS location,
|
||||
m.fifteenAvgSale AS avgShip15d,
|
||||
m.jsUseDays AS stockDays,
|
||||
${buildStockDaysSql()} AS stockDays,
|
||||
m.safetyDays AS safetyDays,
|
||||
m.suggestStock AS suggestStock,
|
||||
s.other_3 AS warehouse,
|
||||
@ -445,7 +488,7 @@ async function buildSummary(whereSql, params) {
|
||||
`SELECT
|
||||
COUNT_BIG(*) AS skuCount,
|
||||
SUM(CASE WHEN ${buildWarningOrderSql()} = 0 THEN 1 ELSE 0 END) AS outOfStockCount,
|
||||
CAST(ISNULL(SUM(COALESCE(inv.qty, m.jsStock, 0)), 0) AS BIGINT) AS totalStock,
|
||||
CAST(ISNULL(SUM(${buildRealtimeStockSql()}), 0) AS BIGINT) AS totalStock,
|
||||
CAST(ISNULL(SUM(ISNULL(ptr.transit, 0)), 0) AS BIGINT) AS totalPurchaseTransit,
|
||||
SUM(CASE WHEN ${buildWarningOrderSql()} = 1 THEN 1 ELSE 0 END) AS lowStockDaysCount,
|
||||
SUM(CASE WHEN cfg.configured = 1 THEN 1 ELSE 0 END) AS configuredCount,
|
||||
|
||||
@ -271,7 +271,10 @@ const AccessoryStockPanel = () => {
|
||||
: int(value)),
|
||||
},
|
||||
{
|
||||
title: '实时库存',
|
||||
title: <TipTitle
|
||||
title="实时库存"
|
||||
tip="优先取目标仓 jst_inventoryInfo;该表为 0 或缺少记录、但箱及仓位库存已有正库存时,用同仓 jst_packInventoryInfo 合计补齐"
|
||||
/>,
|
||||
dataIndex: 'realtimeStock',
|
||||
width: 96,
|
||||
align: 'right',
|
||||
@ -281,7 +284,7 @@ const AccessoryStockPanel = () => {
|
||||
{
|
||||
title: <TipTitle
|
||||
title="采购在途数量"
|
||||
tip="该商品(配件)全部采购单中『已下单 − 已入库』的未到货数量合计,取自 jst_purchaseItem.qty − inQty"
|
||||
tip="采购单中『已下单 − 已入库』的数量,取自 jst_purchaseItem.qty − inQty;它与实时库存独立,上游采购到货字段滞后时可能同时显示库存和在途"
|
||||
/>,
|
||||
dataIndex: 'purchaseTransit',
|
||||
width: 120,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user