Some checks failed
NTFY Notification / notify (push) Failing after 3s
Fix critical bug in BOM matching validation: when [部件] worksheet matches 1 record
but returns 2 sub-components, system incorrectly reports "matched 2 records".
Root Cause:
- matchResult("rowCount") incorrectly used componentMaterials.count (material count)
- instead of bomMatchResult("rowCount") (worksheet row count)
- Caused 1-row match returning 2 sub-components to be misreported as "2 matches"
Solution:
1. M08_ComponentProcessor.ProcessComponentRecord: Add matchedRowNum parameter,
receive matched row number from caller, avoid redundant internal matching
2. M09_BOMExtractor: Implement two-phase matching flow
- Phase 1: Call MatchBOMRecord to get standard match result (with correct rowCount)
- Phase 2: If match succeeds, call ProcessComponentRecord to process component logic
- matchResult("rowCount") always uses standard match's rowCount (worksheet row count)
Fix Results:
- Scenario 1: [部件] matches 1 row, returns 1 component → rowCount=1 ✅
- Scenario 2: [部件] matches 1 row, returns 2 sub-components → rowCount=1 ✅ (no false positive)
- Scenario 3: [部件] matches 0 rows → rowCount=0, correct error ✅
- Scenario 4: [部件] matches 2+ rows → rowCount=2, correct error ✅
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
7.2 KiB
7.2 KiB
BOM匹配验证逻辑修复总结
问题描述
Bug表现
当[部件]工作表匹配到1条记录但返回2个子件(接头+弹性元件)时,系统错误地报告"[部件]工作表匹配到2条记录"。
具体场景
- 某个型号在[部件]工作表匹配到 1条记录
- 该记录的"部件"物料库存不足
- M08_ComponentProcessor 返回 2个子件:1个接头 + 1个弹性元件
- 验证逻辑误报:
[部件]工作表匹配到2条记录❌ 这是误报!
根本原因
位置: M09_BOMExtractor.bas 第428行(修复前)
错误代码:
' 当处理[部件]工作表时
matchResult("rowCount") = componentMaterials.count ' ❌ 错误:这是物料数量,不是匹配行数
问题分析:
rowCount应该表示工作表匹配的行数(应该是1)- 但代码错误地使用了返回的物料数量(变成2)
- 当库存不足返回2个子件时,
rowCount=2被错误理解为"匹配了2行"
解决方案
修改文件1: M08_ComponentProcessor.bas
修改内容:函数签名变更
修改前:
Public Function ProcessComponentRecord( _
ByVal wsComponent As Worksheet, _
ByVal params As Object, _
ByVal logger As clsErrorLogger _
) As Collection
修改后:
Public Function ProcessComponentRecord( _
ByVal wsComponent As Worksheet, _
ByVal params As Object, _
ByVal logger As clsErrorLogger, _
ByVal matchedRowNum As Long _ ' ✅ 新增参数:匹配的行号
) As Collection
修改原因
- 传递匹配行号,让 ProcessComponentRecord 知道要处理哪一行
- 避免内部重复调用
MatchBOMRecord(去除冗余匹配逻辑) - 使用传入的行号而不是内部重新匹配
内部逻辑变更
- 移除: 内部的
MatchBOMRecord调用 - 新增:
matchedRowNum参数验证 - 修改: 所有
rowNum变量引用改为matchedRowNum - 修改: 错误处理器使用传入的
matchedRowNum
修改文件2: M09_BOMExtractor.bas
修改位置:MatchAllMaterialTypesWithValidation 函数 (约第417-448行)
修改前逻辑:
' "部件"工作表特殊处理
If sheetName = BOMLIB_SHEET_COMPONENT Then
Debug.Print " -> 使用部件处理逻辑"
Dim componentMaterials As Collection
Set componentMaterials = M08_ComponentProcessor.ProcessComponentRecord( _
ws, params, logger)
Debug.Print " -> 返回物料数: " & componentMaterials.count
matchResult("success") = (componentMaterials.count > 0)
matchResult("rowCount") = componentMaterials.count ' ❌ 错误:物料数量
' 添加物料...
End If
修改后逻辑:
' "部件"工作表特殊处理
If sheetName = BOMLIB_SHEET_COMPONENT Then
Debug.Print " -> 使用部件处理逻辑"
' 步骤1: 先调用标准匹配获取匹配行数(这是工作表匹配的行数,不是物料数量)
Dim bomMatchResult As Object
Set bomMatchResult = M07_BOMMatcher.MatchBOMRecord(ws, params)
Debug.Print " -> 标准匹配: success=" & bomMatchResult("success") & ", rowCount=" & bomMatchResult("rowCount")
' 步骤2: 如果标准匹配成功,调用部件处理器
Dim componentMaterials As Collection
Set componentMaterials = New Collection
If bomMatchResult("success") Then
Set componentMaterials = M08_ComponentProcessor.ProcessComponentRecord( _
ws, params, logger, bomMatchResult("rowNums")(1))
Debug.Print " -> 返回物料数: " & componentMaterials.count
End If
' 步骤3: 创建匹配结果对象,使用标准匹配的rowCount(工作表行数,不是物料数)
matchResult("success") = bomMatchResult("success")
matchResult("rowCount") = bomMatchResult("rowCount") ' ✅ 修复:使用标准匹配的rowCount
Set matchResult("rowNums") = bomMatchResult("rowNums")
' 步骤4: 添加物料到匹配结果
Dim compMat As Variant
For Each compMat In componentMaterials
Debug.Print " [" & compMat("materialType") & "] 名称=[" & compMat("materialName") & "] 编码=[" & compMat("materialCode") & "]"
matchResult("materials").Add compMat
Next compMat
End If
关键变化
-
两阶段匹配流程:
- Phase 1: 调用
MatchBOMRecord获取标准匹配结果(包含正确的rowCount) - Phase 2: 如果匹配成功,调用
ProcessComponentRecord处理部件逻辑
- Phase 1: 调用
-
rowCount 修复:
matchResult("rowCount")使用标准匹配的rowCount(始终是工作表行数)- 不再使用
componentMaterials.count(这是物料数量)
-
参数传递:
- 将匹配的行号
bomMatchResult("rowNums")(1)传递给ProcessComponentRecord - 避免了部件处理器内部的重复匹配
- 将匹配的行号
预期效果对比
修复前
场景:[部件]匹配1行,库存不足,返回2个子件
结果:rowCount = 2 (物料数量)
验证:❌ "[部件]工作表匹配到2条记录" (误报)
修复后
场景:[部件]匹配1行,库存不足,返回2个子件
结果:rowCount = 1 (工作表行数) ✅
验证:✅ 正确识别为1行匹配,返回2个物料
验证清单
场景1:部件工作表返回1个部件
- 匹配1行 → rowCount=1 ✅
- 返回1个部件物料 → materials.count=1
- 验证通过 ✅
场景2:部件工作表返回2个子件(核心修复场景)
- 匹配1行 → rowCount=1 ✅ (不再误报为2)
- 返回2个子件(接头+弹性元件)→ materials.count=2
- 验证通过 ✅ (不再报错"匹配到2条记录")
场景3:部件工作表匹配0条
- 匹配0行 → rowCount=0
- 返回0个物料 → materials.count=0
- 正确报错:"[部件]工作表未匹配到记录" ✅
场景4:部件工作表匹配2条
- 匹配2行 → rowCount=2
- 正确报错:"[部件]工作表匹配到2条记录" ✅
场景5:其他工作表的多条匹配
- [接头]工作表匹配2条 → 正确报错 ✅
- [弹性元件]工作表匹配2条 → 正确报错 ✅
- 验证逻辑不受影响 ✅
技术要点
rowCount 的语义
- 定义:
rowCount表示工作表中匹配的行数 - 不是: 返回的物料数量
- 关键区别: 1行匹配可能返回0个、1个或多个物料
两阶段匹配模式
阶段1: 标准匹配 → MatchBOMRecord
├─ 返回: success, rowCount, rowNums
└─ rowCount = 工作表行数 (始终正确)
阶段2: 部件处理 → ProcessComponentRecord(matchedRowNum)
├─ 输入: matchedRowNum (从阶段1获取)
├─ 返回: Collection of materials
└─ materials.count = 物料数量 (可能≠rowCount)
避免重复匹配
- 旧模式:
M09→M08.ProcessComponentRecord→M07.MatchBOMRecord - 新模式:
M09→M07.MatchBOMRecord→M08.ProcessComponentRecord(rowNum) - 优势: 减少冗余匹配调用,代码更清晰
相关文档
docs/BOM匹配错误判断机制详解.md- 完整的验证规则说明docs/M09_BOMExtractor_Flow.md- BOM提取流程图docs/M08_ComponentProcessor_Design.md- 部件处理器设计文档
修改历史
- 2025-02-12: 初始版本 - 修复部件工作表rowCount误报问题