diff --git a/backend/src/services/pickingBinSalesAnalysis.js b/backend/src/services/pickingBinSalesAnalysis.js index 6d294ab..cf44c10 100644 --- a/backend/src/services/pickingBinSalesAnalysis.js +++ b/backend/src/services/pickingBinSalesAnalysis.js @@ -169,6 +169,15 @@ async function fetchPickingBinSkuRows(cloudDb, wmsCoIds, options = {}) { AND oi.sku_type = 'combine' GROUP BY o.wms_co_id, ci.src_sku_id ), + shipAgg AS ( + -- 同一个 仓库+商品 可能同时命中多个来源分支(线上普通/线上组合装/全民客户普通/全民客户组合装), + -- 必须先按 仓库+商品 汇总成一行再 join,否则一个仓位行会被拆成多行、每行只带某分支的销量。 + SELECT wmsCoId, + skuId, + SUM(CAST(salesQty AS DECIMAL(18, 2))) AS salesQty + FROM ship + GROUP BY wmsCoId, skuId + ), binAgg AS ( SELECT wmsCoId, binName, @@ -192,7 +201,7 @@ async function fetchPickingBinSkuRows(cloudDb, wmsCoIds, options = {}) { ON sp.wmsCoId = b.wmsCoId AND sp.skuId = b.skuId AND sp.binType = b.binType JOIN binAgg a ON a.wmsCoId = b.wmsCoId AND a.binName = b.binName AND a.binType = b.binType - LEFT JOIN ship s + LEFT JOIN shipAgg s ON s.wmsCoId = b.wmsCoId AND s.skuId = b.skuId GROUP BY b.wmsCoId, b.binName, b.binType, b.skuId, a.skuCount, s.salesQty, sp.binCount HAVING ABS(SUM(CAST(b.binSkuQty AS DECIMAL(18, 2)))) > 0.0001 diff --git a/backend/test/pickingBinSalesAnalysis.test.js b/backend/test/pickingBinSalesAnalysis.test.js index b6e5fa5..f294762 100644 --- a/backend/test/pickingBinSalesAnalysis.test.js +++ b/backend/test/pickingBinSalesAnalysis.test.js @@ -135,6 +135,10 @@ test('fetchPickingBinSkuRows 只统计真实拣选仓位,排除 Pack 箱记录 assert.ok(!captured.sqlText.includes('b.binType IN')); assert.equal(captured.params.t0, undefined); assert.equal(captured.params.binTypes, undefined); + // 多来源 union 后必须再按 仓库+商品 汇总,否则同一仓位行会被拆成多行 + assert.ok(captured.sqlText.includes('shipAgg AS (')); + assert.ok(captured.sqlText.includes('LEFT JOIN shipAgg s')); + assert.ok(!captured.sqlText.includes('LEFT JOIN ship s')); }); test('销量覆盖 4 个来源:线上普通行 + 组合装展开 + 全民客户普通行 + 全民客户组合装展开', async () => {