Files
AutoBOM/docs/BOM_Matching_Fix_Summary.md
Misaka_Company 78241f2b8d
Some checks failed
NTFY Notification / notify (push) Failing after 3s
fix: component worksheet rowCount false positive validation error
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>
2026-02-12 15:54:50 +08:00

7.2 KiB
Raw Permalink Blame History

BOM匹配验证逻辑修复总结

问题描述

Bug表现

当[部件]工作表匹配到1条记录但返回2个子件(接头+弹性元件)时,系统错误地报告"[部件]工作表匹配到2条记录"。

具体场景

  1. 某个型号在[部件]工作表匹配到 1条记录
  2. 该记录的"部件"物料库存不足
  3. M08_ComponentProcessor 返回 2个子件1个接头 + 1个弹性元件
  4. 验证逻辑误报:[部件]工作表匹配到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

关键变化

  1. 两阶段匹配流程:

    • Phase 1: 调用 MatchBOMRecord 获取标准匹配结果(包含正确的 rowCount
    • Phase 2: 如果匹配成功,调用 ProcessComponentRecord 处理部件逻辑
  2. rowCount 修复:

    • matchResult("rowCount") 使用标准匹配的 rowCount(始终是工作表行数)
    • 不再使用 componentMaterials.count(这是物料数量)
  3. 参数传递:

    • 将匹配的行号 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)

避免重复匹配

  • 旧模式: M09M08.ProcessComponentRecordM07.MatchBOMRecord
  • 新模式: M09M07.MatchBOMRecordM08.ProcessComponentRecord(rowNum)
  • 优势: 减少冗余匹配调用,代码更清晰

相关文档

  • docs/BOM匹配错误判断机制详解.md - 完整的验证规则说明
  • docs/M09_BOMExtractor_Flow.md - BOM提取流程图
  • docs/M08_ComponentProcessor_Design.md - 部件处理器设计文档

修改历史

  • 2025-02-12: 初始版本 - 修复部件工作表rowCount误报问题