fix: read BOM locations from pack inventory
This commit is contained in:
parent
3fe0e55ad6
commit
65d009dcdc
@ -30,8 +30,9 @@
|
|||||||
// payload 里的 bomDetails / factoryRows / pricingRows 属成本与工厂行,结构不同
|
// payload 里的 bomDetails / factoryRows / pricingRows 属成本与工厂行,结构不同
|
||||||
// (无 SKU 条码),也不能直接当配件 BOM。
|
// (无 SKU 条码),也不能直接当配件 BOM。
|
||||||
//
|
//
|
||||||
// ④ 配件仓位 —— 业务库 SKU 档案中的库位字段;
|
// ④ 配件仓位 —— 业务库 rpt.jst_packInventoryInfo;
|
||||||
// 业务库 jst_sku.bin / other_bin 全库为空,故多数配件显示「未维护」。
|
// sku_id 可能带“-包装序号”后缀,需按基础配件编码匹配。优先显示 bin_group / bin,
|
||||||
|
// 仓位为空时显示净数量大于 0 的 pack_id(箱号)与 wh_id(仓库)。
|
||||||
//
|
//
|
||||||
// ⑤ 配件库存 —— 业务库 rpt.jst_inventoryInfo 按 sku_id 汇总。
|
// ⑤ 配件库存 —— 业务库 rpt.jst_inventoryInfo 按 sku_id 汇总。
|
||||||
|
|
||||||
@ -335,6 +336,46 @@ function countDistinctBomProducts(...boms) {
|
|||||||
|
|
||||||
const sumQty = (components) => components.reduce((sum, item) => sum + (toNumber(item.qty) || 0), 0);
|
const sumQty = (components) => components.reduce((sum, item) => sum + (toNumber(item.qty) || 0), 0);
|
||||||
|
|
||||||
|
function matchesPackSku(componentCode, packSkuCode) {
|
||||||
|
const component = toText(componentCode)?.toUpperCase();
|
||||||
|
const packSku = toText(packSkuCode)?.toUpperCase();
|
||||||
|
if (!component || !packSku) return false;
|
||||||
|
return packSku === component || packSku.startsWith(`${component}-`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPackLocation(row) {
|
||||||
|
const warehouseId = toText(row.warehouseId);
|
||||||
|
const binGroup = toText(row.binGroup);
|
||||||
|
const bin = toText(row.bin);
|
||||||
|
const packId = toText(row.packId);
|
||||||
|
const warehouse = warehouseId ? `仓库 ${warehouseId}` : '';
|
||||||
|
const position = [binGroup, bin].filter(Boolean).join(' / ')
|
||||||
|
|| (packId ? `箱号 ${packId}` : '');
|
||||||
|
return [warehouse, position].filter(Boolean).join(' · ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPackLocationMap(rows, componentCodes) {
|
||||||
|
const locations = new Map(componentCodes.map((code) => [code, new Set()]));
|
||||||
|
|
||||||
|
rows.forEach((row) => {
|
||||||
|
const componentCode = componentCodes.find((code) => matchesPackSku(code, row.skuId));
|
||||||
|
if (!componentCode) return;
|
||||||
|
const location = formatPackLocation(row);
|
||||||
|
if (location) locations.get(componentCode).add(location);
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = new Map();
|
||||||
|
locations.forEach((values, code) => {
|
||||||
|
const all = [...values];
|
||||||
|
if (!all.length) return;
|
||||||
|
const visible = all.slice(0, 10);
|
||||||
|
result.set(code, all.length > visible.length
|
||||||
|
? `${visible.join('、')} 等 ${all.length} 个位置`
|
||||||
|
: visible.join('、'));
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 找出「资料库里已维护 BOM、但业务库列表里没有」的模块化商品,
|
* 找出「资料库里已维护 BOM、但业务库列表里没有」的模块化商品,
|
||||||
* 作为追加行放在列表最前面,保证已维护的数据一定能在页面上看到。
|
* 作为追加行放在列表最前面,保证已维护的数据一定能在页面上看到。
|
||||||
@ -594,17 +635,31 @@ async function getMkhBomDetail(mkhSkuId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const locationParams = {};
|
||||||
|
const locationConditions = codes.map((code, index) => {
|
||||||
|
locationParams[`lc${index}`] = code;
|
||||||
|
locationParams[`lp${index}`] = `${code}-%`;
|
||||||
|
return `(p.sku_id = @lc${index} OR p.sku_id LIKE @lp${index})`;
|
||||||
|
});
|
||||||
const locRows = await cloudDatabase.query(
|
const locRows = await cloudDatabase.query(
|
||||||
`SELECT sku_id AS skuId,
|
`SELECT p.sku_id AS skuId,
|
||||||
COALESCE(NULLIF(LTRIM(RTRIM(bin)), N''), NULLIF(LTRIM(RTRIM(other_bin)), N'')) AS location
|
p.pack_id AS packId,
|
||||||
FROM dbo.jst_sku
|
p.wh_id AS warehouseId,
|
||||||
WHERE sku_id IN (${placeholders})`,
|
p.bin AS bin,
|
||||||
codeParams,
|
p.bin_group AS binGroup,
|
||||||
|
SUM(p.qty) AS qty
|
||||||
|
FROM rpt.jst_packInventoryInfo p
|
||||||
|
WHERE ${locationConditions.join(' OR ')}
|
||||||
|
GROUP BY p.sku_id, p.pack_id, p.wh_id, p.bin, p.bin_group
|
||||||
|
HAVING SUM(p.qty) > 0`,
|
||||||
|
locationParams,
|
||||||
{ requestTimeout: 120000 }
|
{ requestTimeout: 120000 }
|
||||||
);
|
);
|
||||||
locRows.forEach((row) => locationMap.set(toText(row.skuId), toText(row.location)));
|
buildPackLocationMap(locRows, codes).forEach((location, code) => {
|
||||||
|
locationMap.set(code, location);
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[MkhBom] 配件仓位查询降级:', error.message);
|
console.warn('[MkhBom] 配件箱号/仓位查询降级:', error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,5 +697,8 @@ module.exports = {
|
|||||||
buildBusinessBomExtraRows,
|
buildBusinessBomExtraRows,
|
||||||
mergeExtraRows,
|
mergeExtraRows,
|
||||||
filterExtraRows,
|
filterExtraRows,
|
||||||
|
matchesPackSku,
|
||||||
|
formatPackLocation,
|
||||||
|
buildPackLocationMap,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -93,3 +93,22 @@ test('BOM-only rows honor the same keyword filter as jst_sku rows', () => {
|
|||||||
|
|
||||||
assert.deepEqual(rows.map((row) => row.skuId), ['MKH-B-1']);
|
assert.deepEqual(rows.map((row) => row.skuId), ['MKH-B-1']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('pack inventory locations match a BOM component and its package suffixes', () => {
|
||||||
|
assert.equal(_private.matchesPackSku('PART-1', 'PART-1'), true);
|
||||||
|
assert.equal(_private.matchesPackSku('PART-1', 'PART-1-10'), true);
|
||||||
|
assert.equal(_private.matchesPackSku('PART-1', 'PART-10'), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('pack inventory location prefers bins and falls back to positive-stock box ids', () => {
|
||||||
|
const locations = _private.buildPackLocationMap([
|
||||||
|
{
|
||||||
|
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'), '仓库 1 · 成品区 / A-01、仓库 1 · 箱号 BOX-2');
|
||||||
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user