diff --git a/AGENTS.md b/AGENTS.md index e6092a6..f0d1dce 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,7 +6,7 @@ - 保留他人的未提交修改和已有提交,仅暂存本任务内容;不得清理或覆盖未知工作。 - 推送前同步远端,逐项检查最终差异并验证;被拒绝后先查明并合并更新,禁止任何形式的强推。 - 不改写已发布历史,不批量选择冲突的某一方;不明确的行为取舍需要负责人确认。 -- 所有变更通过 PR 进入 `main`,不得直接推送主分支;独立审核与最新内容的验证通过后由维护者合并。 +- 所有变更通过 PR 进入 `main`,不得直接推送主分支;默认由非提交者审核。仓库所有者或管理员明确授权某个 PR 合并时,该书面授权可以代替独立审核,但仍必须同步最新主分支、验证最终提交并保留授权记录。 - 不提交凭据、私钥、环境秘密或真实业务数据;不把合并授权当成生产部署授权。 - 新建或修改数据表、映射表、查询和同步任务时,必须按实际过滤、关联、排序和唯一性设计索引,用执行计划和有代表性数据验证负载;禁止无边界全表扫描、无节制并发和重复全量同步。 - 开发机器使用中央系统数据库和公网 TLS Redis,无需安装本地数据库;生产部署必须先将代码通过 PR 合入 `main`,并在 `/opt/cangku/deploy.lock` 锁内从服务器 Git 工作区快进拉取和验证。 diff --git a/backend/src/services/mkhBomService.js b/backend/src/services/mkhBomService.js index 3f992de..0c7528b 100644 --- a/backend/src/services/mkhBomService.js +++ b/backend/src/services/mkhBomService.js @@ -39,9 +39,8 @@ // // ⑤ 配件仓位 —— 业务库 rpt.jst_packInventoryInfo; // sku_id 可能带“-包装序号”后缀,需按基础配件编码匹配。优先显示 bin_group / bin, -// 仓位为空时显示净数量大于 0 的 pack_id(箱号)与实际仓库名称。 -// 尚未分箱时使用 rpt.jst_inventoryInfo 的正库存仓库;连库存记录也没有时, -// 回退到 jst_sku.other_3 的商品档案仓库,并明确标注没有箱号,避免误报具体仓位。 +// 仓位为空时显示净数量大于 0 的 pack_id(箱号)与 wh_id(仓库)。 +// 该列只使用箱及仓位库存表,不用库存仓库或商品档案仓库冒充仓位。 // // ⑥ 配件库存 —— 业务库 rpt.jst_inventoryInfo 按 sku_id 汇总。 @@ -555,37 +554,6 @@ function buildPackLocationMap(rows, componentCodes) { return result; } -function buildInventoryWarehouseMap(rows, componentCodes) { - const positiveWarehouses = new Map(componentCodes.map((code) => [code, new Set()])); - const defaultWarehouses = new Map(); - - rows.forEach((row) => { - const componentCode = componentCodes.find((code) => normalizeCode(code) === normalizeCode(row.skuId)); - if (!componentCode) return; - - const defaultWarehouse = toText(row.defaultWarehouse); - if (defaultWarehouse) defaultWarehouses.set(componentCode, defaultWarehouse); - - const warehouseName = toText(row.warehouseName); - if (warehouseName && (toNumber(row.availableQty) ?? 0) > 0) { - positiveWarehouses.get(componentCode).add(warehouseName); - } - }); - - const result = new Map(); - componentCodes.forEach((code) => { - const warehouses = [...positiveWarehouses.get(code)]; - if (warehouses.length > 0) { - result.set(code, `${warehouses.join('、')}(未分箱)`); - return; - } - - const defaultWarehouse = defaultWarehouses.get(code); - if (defaultWarehouse) result.set(code, `${defaultWarehouse}(暂无箱号)`); - }); - return result; -} - /** * 列表:指定仓库的 MKH 模块化成品 + BOM 配件种类/数量。 * BOM 数据只做明细匹配,不向商品列表追加无仓库档案的编码。 @@ -725,18 +693,10 @@ async function getMkhBomDetail(mkhSkuId) { try { const stockRows = await cloudDatabase.query( - `SELECT s.sku_id AS skuId, - s.other_3 AS defaultWarehouse, - w.name AS warehouseName, - SUM(COALESCE(i.qty, 0)) AS qty, - SUM(COALESCE(i.qty, 0) - - COALESCE(i.order_lock, 0) - - COALESCE(i.pick_lock, 0)) AS availableQty - FROM dbo.jst_sku s WITH (NOLOCK) - LEFT JOIN rpt.jst_inventoryInfo i WITH (NOLOCK) ON i.sku_id = s.sku_id - LEFT JOIN rpt.jst_warehouseInfo w WITH (NOLOCK) ON w.wms_co_id = i.wms_co_id - WHERE s.sku_id IN (${placeholders}) - GROUP BY s.sku_id, s.other_3, i.wms_co_id, w.name`, + `SELECT i.sku_id AS skuId, SUM(i.qty) AS qty + FROM rpt.jst_inventoryInfo i WITH (NOLOCK) + WHERE i.sku_id IN (${placeholders}) + GROUP BY i.sku_id`, codeParams, { requestTimeout: 180000 } ); @@ -745,9 +705,6 @@ async function getMkhBomDetail(mkhSkuId) { if (!code) return; stockMap.set(code, (stockMap.get(code) || 0) + (toNumber(row.qty) ?? 0)); }); - buildInventoryWarehouseMap(stockRows, uncachedCodes).forEach((location, code) => { - locationMap.set(code, location); - }); stockQuerySucceeded = true; } catch (error) { console.warn('[MkhBom] 配件库存查询降级:', error.message); @@ -763,15 +720,13 @@ async function getMkhBomDetail(mkhSkuId) { const locRows = await cloudDatabase.query( `SELECT p.sku_id AS skuId, p.pack_id AS packId, - p.wms_co_id AS warehouseId, - w.name AS warehouseName, + p.wh_id AS warehouseId, p.bin AS bin, p.bin_group AS binGroup, SUM(p.qty) AS qty FROM rpt.jst_packInventoryInfo p WITH (NOLOCK) - LEFT JOIN rpt.jst_warehouseInfo w WITH (NOLOCK) ON w.wms_co_id = p.wms_co_id WHERE ${locationConditions.join(' OR ')} - GROUP BY p.sku_id, p.pack_id, p.wms_co_id, w.name, p.bin, p.bin_group + GROUP BY p.sku_id, p.pack_id, p.wh_id, p.bin, p.bin_group HAVING SUM(p.qty) > 0`, locationParams, { requestTimeout: 120000 } @@ -831,6 +786,5 @@ module.exports = { matchesPackSku, formatPackLocation, buildPackLocationMap, - buildInventoryWarehouseMap, }, }; diff --git a/backend/test/mkhBomService.test.js b/backend/test/mkhBomService.test.js index 2b8d6d8..346e9bb 100644 --- a/backend/test/mkhBomService.test.js +++ b/backend/test/mkhBomService.test.js @@ -155,27 +155,17 @@ test('pack inventory locations match a BOM component and its package suffixes', test('pack inventory location prefers bins and falls back to positive-stock box ids', () => { const locations = _private.buildPackLocationMap([ { - skuId: 'PART-1-1', warehouseId: 1, warehouseName: '半成品仓', binGroup: '成品区', bin: 'A-01', packId: 'BOX-1', qty: 2, + skuId: 'PART-1-1', warehouseId: 1, binGroup: '成品区', bin: 'A-01', packId: 'BOX-1', qty: 2, }, { skuId: 'PART-1-2', warehouseId: 1, binGroup: null, bin: '', packId: 'BOX-2', qty: 1, }, ], ['PART-1']); - assert.equal(locations.get('PART-1'), '半成品仓 · 成品区 / A-01、仓库 1 · 箱号 BOX-2'); + assert.equal(locations.get('PART-1'), '仓库 1 · 成品区 / A-01、仓库 1 · 箱号 BOX-2'); }); -test('inventory warehouse fallback distinguishes unboxed stock from a warehouse with no box record', () => { - const locations = _private.buildInventoryWarehouseMap([ - { - skuId: 'PART-1', defaultWarehouse: '默认半成品仓', warehouseName: '实际库存仓', availableQty: 12, - }, - { - skuId: 'PART-2', defaultWarehouse: '默认半成品仓', warehouseName: '实际库存仓', availableQty: 0, - }, - ], ['PART-1', 'PART-2', 'PART-3']); - - assert.equal(locations.get('PART-1'), '实际库存仓(未分箱)'); - assert.equal(locations.get('PART-2'), '默认半成品仓(暂无箱号)'); - assert.equal(locations.has('PART-3'), false); +test('components without pack inventory rows do not receive a fallback warehouse location', () => { + const locations = _private.buildPackLocationMap([], ['PART-1']); + assert.equal(locations.has('PART-1'), false); }); diff --git a/frontend/src/pages/MkhBomData/index.jsx b/frontend/src/pages/MkhBomData/index.jsx index ee278b3..57cc591 100644 --- a/frontend/src/pages/MkhBomData/index.jsx +++ b/frontend/src/pages/MkhBomData/index.jsx @@ -84,7 +84,7 @@ const PrintPreview = ({ data, onClose }) => {