refactor: integrate [领料配置] into [平台配置清单] and add category selection conditions
Major changes: - Remove dependency on separate [领料配置] sheet - Load all data from [平台配置清单] in a single pass - Add "类别选用条件" (CategorySelectCondition) field to support dynamic category filtering - Add "66代码" (Code66) field for 66-system integration - Refactor completeness check logic to only verify required categories Data model changes: - clsMaterialItem: Add CategorySelectCondition and Code66 properties - clsCategory: Add CategorySelectCondition property and IsRequiredForModel() method - clsBOMManager: Refactor LoadData() to use single data source - clsBOMManager: Add GetRequiredCategories() method - clsBOMManager: Update GetValidMaterialsByModel() to use category selection conditions New workflow: - LoadData() now takes only wsPlatform parameter (removed wsConfig) - Categories are filtered by "类别选用条件" before completeness check - Categories with empty selection condition are always required - Categories with non-empty condition are checked against model specifications Documentation: - Update CLAUDE.md with new sheet structure - Add comprehensive refactoring plan document - Add v2.0 LoadData flow analysis with Mermaid diagrams - Add platform configuration sheet reference Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
40
CLAUDE.md
40
CLAUDE.md
@@ -65,17 +65,33 @@ Model String → Parser → Extractor → Conditions Dictionary → Matcher →
|
|||||||
|
|
||||||
## Data Sheets Structure
|
## Data Sheets Structure
|
||||||
|
|
||||||
The Excel workbook contains two key sheets:
|
The Excel workbook contains one main configuration sheet:
|
||||||
|
|
||||||
**领料配置** (Picking Configuration)
|
|
||||||
- Defines category hierarchy
|
|
||||||
- Specifies which materials require picking
|
|
||||||
- Columns: 物料代号, 类别名称, 上层类别名称
|
|
||||||
|
|
||||||
**平台配置清单** (Platform Configuration)
|
**平台配置清单** (Platform Configuration)
|
||||||
- Complete material database
|
- Complete material database with category hierarchy
|
||||||
- Rows 1-3: Headers
|
- All configuration data in one sheet (simplified from previous two-sheet design)
|
||||||
- Row 4+: Data
|
- Row 1-3: Headers and product info
|
||||||
|
- Row 4+: Material data
|
||||||
|
|
||||||
|
**Column Structure** (Row 3):
|
||||||
|
| Column | Field Name | Description | Example |
|
||||||
|
|--------|-----------|-------------|---------|
|
||||||
|
| A | 行号 | Line number | 10, 20, 30 |
|
||||||
|
| B | 模块 | Module name | 316L部件 |
|
||||||
|
| C | 代号 | Material code | 01011009557 |
|
||||||
|
| D | 名称 | Material name | 径向低压接头部件 |
|
||||||
|
| E | 数量 | Quantity | 1.0 |
|
||||||
|
| F | 选择条件 | Material selection condition | gclj=M20 AND jycz=1 |
|
||||||
|
| G | 备注 | Remarks | |
|
||||||
|
| H | 类别 | Category name | 部件 |
|
||||||
|
| I | 上层类别 | Parent category name | (empty for root categories) |
|
||||||
|
| J | 类别选用条件 | Category selection condition | lcfw=M02 |
|
||||||
|
| K | 66代码 | 66-system code | 66021009557 |
|
||||||
|
|
||||||
|
**Category Selection Condition (类别选用条件)**:
|
||||||
|
- Empty: Category is always required
|
||||||
|
- Not empty: Category is required only when the condition matches the model specifications
|
||||||
|
- Uses same syntax as material selection conditions (AND, OR, !=, parentheses)
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
@@ -120,7 +136,11 @@ Run tests from the VBA editor or Excel macros:
|
|||||||
## Key Business Logic
|
## Key Business Logic
|
||||||
|
|
||||||
- Materials have "selection conditions" expressed as logical formulas
|
- Materials have "selection conditions" expressed as logical formulas
|
||||||
- Not all materials in platform config require picking (filtered by "领料配置" sheet)
|
- Categories have "category selection conditions" to determine if they are required for a given model
|
||||||
|
- **Completeness Check Logic**:
|
||||||
|
- First, determine which categories are required by matching "类别选用条件" with model specifications
|
||||||
|
- Then, check if each required category has exactly one matching material
|
||||||
|
- Categories not required for the model are excluded from completeness check
|
||||||
- Supports "部件" (component) vs "零件" (part) picking strategies
|
- Supports "部件" (component) vs "零件" (part) picking strategies
|
||||||
- Condition extraction is data-driven (new variables can be added)
|
- Condition extraction is data-driven (new variables can be added)
|
||||||
- Category tree can have arbitrary depth
|
- Category tree can have arbitrary depth
|
||||||
|
|||||||
@@ -31,33 +31,44 @@ End Sub
|
|||||||
|
|
||||||
' ========================================
|
' ========================================
|
||||||
' LoadData 方法
|
' LoadData 方法
|
||||||
' 功能: 从Excel工作表加载BOM数据并构建数据结构
|
' 功能: 从[平台配置清单]工作表加载BOM数据并构建数据结构
|
||||||
' 参数:
|
' 参数:
|
||||||
' wsConfig - [领料配置]工作表对象,包含类别层级和需要领料的物料
|
' wsPlatform - [平台配置清单]工作表对象,包含完整的物料信息和类别配置
|
||||||
' wsPlatform - [平台配置清单]工作表对象,包含完整的物料信息(代号/名称/数量/条件)
|
' 表结构: 行号|模块|代号|名称|数量|选择条件|备注|类别|上层类别|类别选用条件|66代码
|
||||||
|
' 数据从第4行开始(前3行是标题)
|
||||||
' 处理步骤:
|
' 处理步骤:
|
||||||
' 1. 从[平台配置清单]加载所有物料的基础信息到dictAllMaterials
|
' 1. 从[平台配置清单]一次性加载所有物料信息和类别信息
|
||||||
' 2. 从[领料配置]加载类别信息,筛选需要领料的物料
|
' 2. 建立类别的父子关系,构建层级树
|
||||||
' 3. 建立类别的父子关系,构建层级树
|
'
|
||||||
' 注意:
|
' 重要说明:
|
||||||
' - 只有出现在[领料配置]中的物料才会被添加到类别中
|
' - 所有数据都从[平台配置清单]读取,不再需要[领料配置]表
|
||||||
' - 未在[领料配置]中的物料表示不需要领料
|
' - 只有"类别"字段不为空的物料才会被添加到类别中
|
||||||
|
' - "类别选用条件"用于判断该类别是否需要(为空表示总是需要)
|
||||||
|
' - "66代码"是66系统使用的代码
|
||||||
' ========================================
|
' ========================================
|
||||||
Public Sub LoadData(wsConfig As Worksheet, wsPlatform As Worksheet)
|
Public Sub LoadData(wsPlatform As Worksheet)
|
||||||
Dim i As Long, lastRow As Long
|
Dim i As Long, lastRow As Long
|
||||||
Dim mat As clsMaterialItem
|
Dim mat As clsMaterialItem
|
||||||
Dim cat As clsCategory
|
Dim cat As clsCategory
|
||||||
|
|
||||||
' ========================================
|
' ========================================
|
||||||
' 第一步: 从平台配置清单加载所有物料的基础信息
|
' 第一步: 从平台配置清单加载所有物料和类别信息
|
||||||
' 说明:
|
' 说明:
|
||||||
' - 读取C列(代号)、D列(名称)、E列(数量)、F列(选择条件)
|
' 读取列说明:
|
||||||
|
' - C列(代号)、D列(名称)、E列(数量)、F列(选择条件)
|
||||||
|
' - H列(类别)、I列(上层类别)、J列(类别选用条件)、K列(66代码)
|
||||||
' - 从第4行开始读取(前3行是标题)
|
' - 从第4行开始读取(前3行是标题)
|
||||||
' - 所有物料存入dictAllMaterials字典,以代号为键
|
'
|
||||||
' 目的: 建立完整的物料信息库,供后续按代号查询
|
' 处理逻辑:
|
||||||
|
' 1. 所有物料都存入dictAllMaterials字典(包括无类别的物料)
|
||||||
|
' 2. 只有"类别"字段不为空的物料才创建类别并添加到类别中
|
||||||
|
' 3. 类别对象保存CategorySelectCondition属性
|
||||||
|
' 4. 物料对象保存CategorySelectCondition和Code66属性
|
||||||
' ========================================
|
' ========================================
|
||||||
lastRow = wsPlatform.Cells(wsPlatform.Rows.Count, "C").End(xlUp).row
|
lastRow = wsPlatform.Cells(wsPlatform.Rows.Count, "C").End(xlUp).row
|
||||||
|
|
||||||
For i = 4 To lastRow ' 从第4行开始(跳过标题)
|
For i = 4 To lastRow ' 从第4行开始(跳过标题)
|
||||||
|
' 1. 创建物料对象并读取基础信息
|
||||||
Set mat = New clsMaterialItem
|
Set mat = New clsMaterialItem
|
||||||
mat.code = Trim(wsPlatform.Cells(i, "C").value & "") ' 物料代号
|
mat.code = Trim(wsPlatform.Cells(i, "C").value & "") ' 物料代号
|
||||||
mat.Name = Trim(wsPlatform.Cells(i, "D").value & "") ' 物料名称
|
mat.Name = Trim(wsPlatform.Cells(i, "D").value & "") ' 物料名称
|
||||||
@@ -66,55 +77,46 @@ Public Sub LoadData(wsConfig As Worksheet, wsPlatform As Worksheet)
|
|||||||
On Error GoTo 0
|
On Error GoTo 0
|
||||||
mat.Condition = Trim(wsPlatform.Cells(i, "F").value & "") ' 选择条件
|
mat.Condition = Trim(wsPlatform.Cells(i, "F").value & "") ' 选择条件
|
||||||
|
|
||||||
' 只保存代号非空的物料
|
' 2. 读取类别相关字段
|
||||||
|
Dim catName As String
|
||||||
|
Dim parentCatName As String
|
||||||
|
Dim catSelectCond As String
|
||||||
|
Dim code66 As String
|
||||||
|
|
||||||
|
catName = Trim(wsPlatform.Cells(i, "H").value & "") ' 类别
|
||||||
|
parentCatName = Trim(wsPlatform.Cells(i, "I").value & "") ' 上层类别
|
||||||
|
catSelectCond = Trim(wsPlatform.Cells(i, "J").value & "") ' 类别选用条件
|
||||||
|
code66 = Trim(wsPlatform.Cells(i, "K").value & "") ' 66代码
|
||||||
|
|
||||||
|
' 3. 保存类别相关属性到物料对象
|
||||||
|
mat.Category = catName
|
||||||
|
mat.ParentCategory = parentCatName
|
||||||
|
mat.CategorySelectCondition = catSelectCond
|
||||||
|
mat.Code66 = code66
|
||||||
|
|
||||||
|
' 4. 将物料保存到字典(所有物料都保存,包括无类别的)
|
||||||
If mat.code <> "" Then
|
If mat.code <> "" Then
|
||||||
Set dictAllMaterials(mat.code) = mat
|
Set dictAllMaterials(mat.code) = mat
|
||||||
End If
|
End If
|
||||||
|
|
||||||
|
' 5. 如果物料有类别,创建或更新类别对象,并将物料添加到类别
|
||||||
|
If catName <> "" Then
|
||||||
|
' 确保类别对象存在(如果类别不存在则创建)
|
||||||
|
If Not dictCategories.Exists(catName) Then
|
||||||
|
Set cat = New clsCategory
|
||||||
|
cat.categoryName = catName
|
||||||
|
cat.ParentCategoryName = parentCatName
|
||||||
|
cat.CategorySelectCondition = catSelectCond ' ⭐ 设置类别选用条件
|
||||||
|
Set dictCategories(catName) = cat
|
||||||
|
End If
|
||||||
|
|
||||||
|
' 将物料添加到类别
|
||||||
|
dictCategories(catName).AddMaterial mat
|
||||||
|
End If
|
||||||
Next i
|
Next i
|
||||||
|
|
||||||
' ========================================
|
' ========================================
|
||||||
' 第二步: 从领料配置加载类别信息并建立层级
|
' 第二步: 建立类别层级关系
|
||||||
' 说明:
|
|
||||||
' - 读取A列(代号)、C列(类别)、D列(上层类别)
|
|
||||||
' - 从第2行开始读取(第1行是标题)
|
|
||||||
' - 只有出现在此表中的物料才需要领料
|
|
||||||
' 处理逻辑:
|
|
||||||
' 1. 为每个类别创建clsCategory对象
|
|
||||||
' 2. 从dictAllMaterials中查找对应的物料信息
|
|
||||||
' 3. 将物料添加到对应的类别中
|
|
||||||
' ========================================
|
|
||||||
lastRow = wsConfig.Cells(wsConfig.Rows.Count, "A").End(xlUp).row
|
|
||||||
For i = 2 To lastRow ' 从第2行开始
|
|
||||||
Dim code As String, catName As String, parentCatName As String
|
|
||||||
code = Trim(wsConfig.Cells(i, "A").value & "") ' 物料代号
|
|
||||||
catName = Trim(wsConfig.Cells(i, "C").value & "") ' 类别名称
|
|
||||||
parentCatName = Trim(wsConfig.Cells(i, "D").value & "") ' 上层类别名称
|
|
||||||
|
|
||||||
' 跳过空行
|
|
||||||
If code = "" Then GoTo NextRow
|
|
||||||
|
|
||||||
' 确保类别对象存在(如果类别不存在则创建)
|
|
||||||
If Not dictCategories.Exists(catName) Then
|
|
||||||
Set cat = New clsCategory
|
|
||||||
cat.categoryName = catName
|
|
||||||
cat.ParentCategoryName = parentCatName
|
|
||||||
Set dictCategories(catName) = cat
|
|
||||||
End If
|
|
||||||
|
|
||||||
' 将物料添加到类别
|
|
||||||
' 注意: 必须先在dictAllMaterials中查找到完整的物料信息
|
|
||||||
If dictAllMaterials.Exists(code) Then
|
|
||||||
Set mat = dictAllMaterials(code)
|
|
||||||
mat.Category = catName ' 设置物料所属类别
|
|
||||||
mat.ParentCategory = parentCatName ' 设置物料的上层类别
|
|
||||||
dictCategories(catName).AddMaterial mat ' 将物料添加到类别对象中
|
|
||||||
End If
|
|
||||||
|
|
||||||
NextRow:
|
|
||||||
Next i
|
|
||||||
|
|
||||||
' ========================================
|
|
||||||
' 第三步: 建立类别层级关系
|
|
||||||
' 说明:
|
' 说明:
|
||||||
' - 遍历所有类别,根据ParentCategoryName建立父子关系
|
' - 遍历所有类别,根据ParentCategoryName建立父子关系
|
||||||
' - 如果类别有父类别,将自己添加到父类别的SubCategories中
|
' - 如果类别有父类别,将自己添加到父类别的SubCategories中
|
||||||
@@ -662,6 +664,56 @@ Public Function GetMaterialsByCategoryAndModel(modelStr As String, _
|
|||||||
Set GetMaterialsByCategoryAndModel = result
|
Set GetMaterialsByCategoryAndModel = result
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
|
' ========================================
|
||||||
|
' GetRequiredCategories 方法 (私有)
|
||||||
|
' 功能: 根据型号条件确定哪些类别是需要的
|
||||||
|
' 参数:
|
||||||
|
' conditions - 从型号提取的条件字典 (Dictionary对象)
|
||||||
|
' 包含如 lcfw, gclj, jycz 等条件变量
|
||||||
|
' 返回: Collection对象, 包含所有需要的 clsCategory 对象
|
||||||
|
'
|
||||||
|
' 判断逻辑:
|
||||||
|
' 遍历所有类别,对每个类别调用 IsRequiredForModel(conditions) 方法:
|
||||||
|
' - 如果类别选用条件为空 → 该类别总是需要,添加到结果集
|
||||||
|
' - 如果类别选用条件不为空 → 用 conditions 匹配
|
||||||
|
' - 匹配成功 → 添加到结果集
|
||||||
|
' - 匹配失败 → 不添加
|
||||||
|
'
|
||||||
|
' 示例:
|
||||||
|
' 假设型号条件为: lcfw=M16, gclj=M20
|
||||||
|
'
|
||||||
|
' 类别1: CategorySelectCondition = ""
|
||||||
|
' → IsRequiredForModel = True → 添加到结果集
|
||||||
|
'
|
||||||
|
' 类别2: CategorySelectCondition = "lcfw=M02"
|
||||||
|
' → IsRequiredForModel = False → 不添加
|
||||||
|
'
|
||||||
|
' 类别3: CategorySelectCondition = "gclj=M20"
|
||||||
|
' → IsRequiredForModel = True → 添加到结果集
|
||||||
|
'
|
||||||
|
' 用途:
|
||||||
|
' 在 GetValidMaterialsByModel 中,只检查需要的类别是否有物料匹配
|
||||||
|
' 不需要的类别不参与完整性检查
|
||||||
|
' ========================================
|
||||||
|
Private Function GetRequiredCategories(conditions As Object) As collection
|
||||||
|
Dim result As collection
|
||||||
|
Set result = New collection
|
||||||
|
|
||||||
|
' 遍历所有类别
|
||||||
|
Dim key As Variant
|
||||||
|
For Each key In dictCategories.Keys
|
||||||
|
Dim cat As clsCategory
|
||||||
|
Set cat = dictCategories(key)
|
||||||
|
|
||||||
|
' 检查该类别是否需要
|
||||||
|
If cat.IsRequiredForModel(conditions) Then
|
||||||
|
result.Add cat
|
||||||
|
End If
|
||||||
|
Next key
|
||||||
|
|
||||||
|
Set GetRequiredCategories = result
|
||||||
|
End Function
|
||||||
|
|
||||||
' ========================================
|
' ========================================
|
||||||
' ParseModelAndExtractConditions 方法
|
' ParseModelAndExtractConditions 方法
|
||||||
' 功能: 解析型号并返回条件字典(工具方法)
|
' 功能: 解析型号并返回条件字典(工具方法)
|
||||||
@@ -689,23 +741,41 @@ End Function
|
|||||||
' 参数:
|
' 参数:
|
||||||
' modelStr - 产品型号字符串
|
' modelStr - 产品型号字符串
|
||||||
' 返回: Collection对象
|
' 返回: Collection对象
|
||||||
' Collection中包含两个元素:
|
' Collection中包含三个元素:
|
||||||
' (1) "Materials" - Collection对象,包含所有匹配的 clsMaterialItem 对象
|
' (1) "Materials" - Collection对象,包含所有匹配的 clsMaterialItem 对象
|
||||||
' (2) "IsComplete" - Boolean值,指示物料是否完整
|
' (2) "IsComplete" - Boolean值,指示物料是否完整
|
||||||
' (3) "MissingCategories" - Collection对象, 包含所有缺失的类别名称字符串
|
' (3) "MissingCategories" - Collection对象, 包含所有缺失的类别名称字符串
|
||||||
'
|
'
|
||||||
' 完整性判断规则:
|
' ⭐ 完整性判断规则(重构后):
|
||||||
' - 对于每个需要领料的类别(顶层类别或其需要领取的子类别)
|
' 第一步: 确定哪些类别是该型号需要的
|
||||||
' - 必须有且仅有一个物料被匹配
|
' - 遍历所有类别,检查"类别选用条件"
|
||||||
' - 如果某个类别有0个或多于1个物料,则视为不完整
|
' - 类别选用条件为空 → 该类别总是需要
|
||||||
|
' - 类别选用条件不为空 → 用型号条件匹配,匹配成功才需要
|
||||||
|
'
|
||||||
|
' 第二步: 只检查需要的类别的完整性
|
||||||
|
' - 对于每个需要的类别(顶层类别或其需要领取的子类别)
|
||||||
|
' - 必须有且仅有一个物料被匹配
|
||||||
|
' - 如果某个需要的类别有0个或多于1个物料,则视为不完整
|
||||||
|
' - 不需要的类别不参与完整性检查
|
||||||
'
|
'
|
||||||
' 示例:
|
' 示例:
|
||||||
|
' 假设型号为 "YTHN-100.A0.532.M203.M16.Y3" (量程M16, 过程连接M20)
|
||||||
|
'
|
||||||
|
' 类别1: CategorySelectCondition = "" → 总是需要
|
||||||
|
' 类别2: CategorySelectCondition = "lcfw=M02" → 不需要(型号量程是M16)
|
||||||
|
' 类别3: CategorySelectCondition = "gclj=M20" → 需要(匹配成功)
|
||||||
|
'
|
||||||
|
' 只有类别1和类别3需要检查完整性,类别2不检查
|
||||||
|
'
|
||||||
|
' 调用示例:
|
||||||
' Dim result As Collection
|
' Dim result As Collection
|
||||||
' Set result = bomMgr.GetValidMaterialsByModel("YTHN-100.A0.532.M203.M16.Y3")
|
' Set result = bomMgr.GetValidMaterialsByModel("YTHN-100.A0.532.M203.M16.Y3")
|
||||||
' Dim materials As Collection
|
' Dim materials As Collection
|
||||||
' Dim isComplete As Boolean
|
' Dim isComplete As Boolean
|
||||||
|
' Dim missingCats As Collection
|
||||||
' Set materials = result("Materials")
|
' Set materials = result("Materials")
|
||||||
' isComplete = result("IsComplete")
|
' isComplete = result("IsComplete")
|
||||||
|
' Set missingCats = result("MissingCategories")
|
||||||
' ========================================
|
' ========================================
|
||||||
Public Function GetValidMaterialsByModel(modelStr As String) As collection
|
Public Function GetValidMaterialsByModel(modelStr As String) As collection
|
||||||
Dim result As New collection
|
Dim result As New collection
|
||||||
@@ -732,17 +802,21 @@ Public Function GetValidMaterialsByModel(modelStr As String) As collection
|
|||||||
' 条件匹配器
|
' 条件匹配器
|
||||||
Dim matcher As New clsConditionMatcher
|
Dim matcher As New clsConditionMatcher
|
||||||
|
|
||||||
' 遍历所有根类别,检查完整性
|
' ⭐ 第一步: 获取需要的类别列表
|
||||||
|
Dim requiredCats As collection
|
||||||
|
Set requiredCats = GetRequiredCategories(conditions)
|
||||||
|
|
||||||
|
' ⭐ 第二步: 只检查需要的类别的完整性
|
||||||
isComplete = True
|
isComplete = True
|
||||||
|
|
||||||
Dim rootCat As clsCategory
|
Dim reqCat As clsCategory
|
||||||
Dim i As Long
|
Dim i As Long
|
||||||
For i = 1 To rootCategories.Count
|
For i = 1 To requiredCats.Count
|
||||||
Set rootCat = rootCategories(i)
|
Set reqCat = requiredCats(i)
|
||||||
|
|
||||||
' 检查该类别及其子类别的完整性
|
' 检查该类别及其子类别的完整性
|
||||||
Dim catResult As Object
|
Dim catResult As Object
|
||||||
Set catResult = CheckCategoryCompleteness(rootCat, matcher, conditions)
|
Set catResult = CheckCategoryCompleteness(reqCat, matcher, conditions)
|
||||||
|
|
||||||
' 1. 合并匹配到的物料
|
' 1. 合并匹配到的物料
|
||||||
Dim mat As clsMaterialItem
|
Dim mat As clsMaterialItem
|
||||||
@@ -752,7 +826,7 @@ Public Function GetValidMaterialsByModel(modelStr As String) As collection
|
|||||||
allMaterials.Add mat
|
allMaterials.Add mat
|
||||||
Next mat
|
Next mat
|
||||||
|
|
||||||
' 2. 合并缺失的类别 (新增)
|
' 2. 合并缺失的类别
|
||||||
Dim missingCollection As collection
|
Dim missingCollection As collection
|
||||||
Set missingCollection = catResult("Missing")
|
Set missingCollection = catResult("Missing")
|
||||||
Dim missingCatName As Variant
|
Dim missingCatName As Variant
|
||||||
@@ -769,7 +843,7 @@ Public Function GetValidMaterialsByModel(modelStr As String) As collection
|
|||||||
' 返回结果
|
' 返回结果
|
||||||
result.Add allMaterials, "Materials"
|
result.Add allMaterials, "Materials"
|
||||||
result.Add isComplete, "IsComplete"
|
result.Add isComplete, "IsComplete"
|
||||||
result.Add allMissingCats, "MissingCategories" ' 新增返回项
|
result.Add allMissingCats, "MissingCategories"
|
||||||
|
|
||||||
Set GetValidMaterialsByModel = result
|
Set GetValidMaterialsByModel = result
|
||||||
End Function
|
End Function
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ Option Explicit
|
|||||||
|
|
||||||
Public categoryName As String
|
Public categoryName As String
|
||||||
Public ParentCategoryName As String
|
Public ParentCategoryName As String
|
||||||
Public materials As collection ' 存储 clsMaterialItem 对象
|
Public materials As collection ' 存储 clsMaterialItem 对象
|
||||||
Public SubCategories As collection ' 存储子类别 clsCategory 对象
|
Public SubCategories As collection ' 存储子类别 clsCategory 对象
|
||||||
Public IsLeafCategory As Boolean ' 是否叶子类别(无子类别)
|
Public IsLeafCategory As Boolean ' 是否叶子类别(无子类别)
|
||||||
|
Public CategorySelectCondition As String ' 类别选用条件(用于判断该类别是否需要)
|
||||||
|
|
||||||
Private Sub Class_Initialize()
|
Private Sub Class_Initialize()
|
||||||
Set materials = New collection
|
Set materials = New collection
|
||||||
@@ -37,4 +38,47 @@ End Function
|
|||||||
' 检查是否有子类别
|
' 检查是否有子类别
|
||||||
Public Function HasSubCategories() As Boolean
|
Public Function HasSubCategories() As Boolean
|
||||||
HasSubCategories = (SubCategories.Count > 0)
|
HasSubCategories = (SubCategories.Count > 0)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ========================================
|
||||||
|
' IsRequiredForModel 方法
|
||||||
|
' 功能: 根据型号条件判断该类别是否需要
|
||||||
|
' 参数:
|
||||||
|
' conditions - 从型号提取的条件字典 (Dictionary对象)
|
||||||
|
' 返回:
|
||||||
|
' True - 该类别需要
|
||||||
|
' False - 该类别不需要
|
||||||
|
'
|
||||||
|
' 判断逻辑:
|
||||||
|
' 1. 如果 CategorySelectCondition 为空字符串
|
||||||
|
' → 该类别总是需要(无条件限制)
|
||||||
|
' 2. 如果 CategorySelectCondition 不为空
|
||||||
|
' → 使用 clsConditionMatcher 匹配条件
|
||||||
|
' → 匹配成功 → 需要
|
||||||
|
' → 匹配失败 → 不需要
|
||||||
|
'
|
||||||
|
' 示例:
|
||||||
|
' 假设型号条件为: lcfw=M16, gclj=M20
|
||||||
|
'
|
||||||
|
' 情况1: CategorySelectCondition = ""
|
||||||
|
' → IsRequiredForModel(conditions) = True
|
||||||
|
'
|
||||||
|
' 情况2: CategorySelectCondition = "lcfw=M16"
|
||||||
|
' → IsRequiredForModel(conditions) = True (匹配)
|
||||||
|
'
|
||||||
|
' 情况3: CategorySelectCondition = "lcfw=M02"
|
||||||
|
' → IsRequiredForModel(conditions) = False (不匹配)
|
||||||
|
'
|
||||||
|
' 情况4: CategorySelectCondition = "gclj=M20 AND jycz=1"
|
||||||
|
' → IsRequiredForModel(conditions) = True (匹配)
|
||||||
|
' ========================================
|
||||||
|
Public Function IsRequiredForModel(conditions As Object) As Boolean
|
||||||
|
If Me.CategorySelectCondition = "" Then
|
||||||
|
' 选用条件为空,该类别总是需要
|
||||||
|
IsRequiredForModel = True
|
||||||
|
Else
|
||||||
|
' 需要用 conditions 去匹配 CategorySelectCondition
|
||||||
|
Dim matcher As New clsConditionMatcher
|
||||||
|
IsRequiredForModel = matcher.IsMatch(Me.CategorySelectCondition, conditions)
|
||||||
|
End If
|
||||||
End Function
|
End Function
|
||||||
@@ -4,9 +4,11 @@
|
|||||||
' ========================================
|
' ========================================
|
||||||
Option Explicit
|
Option Explicit
|
||||||
|
|
||||||
Public code As String ' 代号
|
Public code As String ' 代号
|
||||||
Public Name As String ' 名称
|
Public Name As String ' 名称
|
||||||
Public Quantity As Double ' 数量
|
Public Quantity As Double ' 数量
|
||||||
Public Condition As String ' 选择条件
|
Public Condition As String ' 选择条件
|
||||||
Public Category As String ' 类别
|
Public Category As String ' 类别
|
||||||
Public ParentCategory As String ' 上层类别
|
Public ParentCategory As String ' 上层类别
|
||||||
|
Public CategorySelectCondition As String ' 类别选用条件(用于判断该类别是否需要)
|
||||||
|
Public Code66 As String ' 66系统代码
|
||||||
530
reference_docs/clsBOMManager_LoadData流程分析-v2(整合后).md
Normal file
530
reference_docs/clsBOMManager_LoadData流程分析-v2(整合后).md
Normal file
@@ -0,0 +1,530 @@
|
|||||||
|
# clsBOMManager.LoadData 方法流程分析 (v2.0 - 整合后版本)
|
||||||
|
|
||||||
|
## 方法概述
|
||||||
|
|
||||||
|
`LoadData` 是 `clsBOMManager` 类的核心方法,负责从 [平台配置清单] 工作表加载 BOM 数据并构建完整的层级数据结构。
|
||||||
|
|
||||||
|
**v2.0 变更说明**:
|
||||||
|
- 移除了 [领料配置] 表的依赖
|
||||||
|
- 所有配置数据统一从 [平台配置清单] 读取
|
||||||
|
- 新增"类别选用条件"字段,支持动态判断类别是否需要
|
||||||
|
- 新增"66代码"字段
|
||||||
|
|
||||||
|
## 方法签名
|
||||||
|
|
||||||
|
```vba
|
||||||
|
Public Sub LoadData(wsPlatform As Worksheet)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 参数说明
|
||||||
|
|
||||||
|
| 参数 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `wsPlatform` | Worksheet | [平台配置清单]工作表,包含完整的物料信息和类别配置 |
|
||||||
|
|
||||||
|
## 整体流程图
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Start([开始 LoadData]) --> Step1[第一步: 从平台配置清单加载所有物料和类别信息]
|
||||||
|
Step1 --> Step2[第二步: 建立类别层级关系]
|
||||||
|
Step2 --> End([结束])
|
||||||
|
|
||||||
|
style Start fill:#e1f5e1
|
||||||
|
style End fill:#ffe1e1
|
||||||
|
style Step1 fill:#e1f0ff
|
||||||
|
style Step2 fill:#fff4e1
|
||||||
|
```
|
||||||
|
|
||||||
|
## 详细流程分析
|
||||||
|
|
||||||
|
### 第一步:从平台配置清单加载所有物料和类别信息
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
S1_Start([开始第一步]) --> S1_Init[初始化: 获取最后一行行号]
|
||||||
|
S1_Init --> S1_Loop{循环 i = 4 到 lastRow}
|
||||||
|
S1_Loop -->|i += 1| S1_ReadMat[读取物料基础信息<br/>C列:代号, D列:名称<br/>E列:数量, F列:选择条件]
|
||||||
|
S1_ReadMat --> S1_ReadCat[读取类别信息<br/>H列:类别, I列:上层类别<br/>J列:类别选用条件, K列:66代码]
|
||||||
|
S1_ReadCat --> S1_CreateMat[创建 clsMaterialItem 对象]
|
||||||
|
S1_CreateMat --> S1_SetMatProps[设置物料属性<br/>code, Name, Quantity, Condition<br/>Category, ParentCategory<br/>CategorySelectCondition, Code66]
|
||||||
|
S1_SetMatProps --> S1_SaveMat[保存到 dictAllMaterials]
|
||||||
|
S1_SaveMat --> S1_CheckCat{类别字段<br/>是否为空?}
|
||||||
|
S1_CheckCat -->|否, 有类别| S1_EnsureCat{类别对象<br/>是否存在?}
|
||||||
|
S1_CheckCat -->|是, 无类别| S1_Next
|
||||||
|
S1_EnsureCat -->|不存在| S1_CreateCat[创建新类别对象<br/>设置 categoryName, ParentCategoryName<br/>CategorySelectCondition]
|
||||||
|
S1_EnsureCat -->|已存在| S1_AddMat
|
||||||
|
S1_CreateCat --> S1_AddMat[将物料添加到类别]
|
||||||
|
S1_AddMat --> S1_Next
|
||||||
|
S1_Next --> S1_Loop
|
||||||
|
S1_Loop -->|i > lastRow| S1_End([第一步完成])
|
||||||
|
|
||||||
|
style S1_Start fill:#e1f5e1
|
||||||
|
style S1_End fill:#ffe1e1
|
||||||
|
style S1_CreateCat fill:#bbdefb
|
||||||
|
style S1_AddMat fill:#c8e6c9
|
||||||
|
style S1_CheckCat fill:#fff9c4
|
||||||
|
```
|
||||||
|
|
||||||
|
**数据来源**:[平台配置清单] 工作表
|
||||||
|
|
||||||
|
| 列 | 字段 | 说明 |
|
||||||
|
|----|------|------|
|
||||||
|
| C | code | 物料代号 |
|
||||||
|
| D | Name | 物料名称 |
|
||||||
|
| E | Quantity | 物料数量 |
|
||||||
|
| F | Condition | 选择条件 |
|
||||||
|
| H | Category | 类别名称 |
|
||||||
|
| I | ParentCategoryName | 上层类别名称 |
|
||||||
|
| J | CategorySelectCondition | 类别选用条件 |
|
||||||
|
| K | Code66 | 66系统代码 |
|
||||||
|
|
||||||
|
**数据结构**:
|
||||||
|
```
|
||||||
|
dictAllMaterials: Dictionary<String, clsMaterialItem>
|
||||||
|
├── "01011009557" → clsMaterialItem{
|
||||||
|
│ code: "01011009557"
|
||||||
|
│ Name: "径向低压接头部件"
|
||||||
|
│ Quantity: 1.0
|
||||||
|
│ Condition: "gclj=M20 AND jycz=1"
|
||||||
|
│ Category: "部件"
|
||||||
|
│ ParentCategory: ""
|
||||||
|
│ CategorySelectCondition: ""
|
||||||
|
│ Code66: "66021009557"
|
||||||
|
│ }
|
||||||
|
└── ...
|
||||||
|
|
||||||
|
dictCategories: Dictionary<String, clsCategory>
|
||||||
|
├── "部件" → clsCategory{
|
||||||
|
│ categoryName: "部件"
|
||||||
|
│ ParentCategoryName: ""
|
||||||
|
│ CategorySelectCondition: ""
|
||||||
|
│ materials: Collection<clsMaterialItem>
|
||||||
|
│ SubCategories: Collection<clsCategory>
|
||||||
|
│ }
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键代码**:
|
||||||
|
```vba
|
||||||
|
lastRow = wsPlatform.Cells(wsPlatform.Rows.Count, "C").End(xlUp).row
|
||||||
|
|
||||||
|
For i = 4 To lastRow
|
||||||
|
' 1. 创建物料对象并读取基础信息
|
||||||
|
Set mat = New clsMaterialItem
|
||||||
|
mat.code = Trim(wsPlatform.Cells(i, "C").value & "")
|
||||||
|
mat.Name = Trim(wsPlatform.Cells(i, "D").value & "")
|
||||||
|
mat.Quantity = CDbl(wsPlatform.Cells(i, "E").value)
|
||||||
|
mat.Condition = Trim(wsPlatform.Cells(i, "F").value & "")
|
||||||
|
|
||||||
|
' 2. 读取类别相关字段
|
||||||
|
catName = Trim(wsPlatform.Cells(i, "H").value & "")
|
||||||
|
parentCatName = Trim(wsPlatform.Cells(i, "I").value & "")
|
||||||
|
catSelectCond = Trim(wsPlatform.Cells(i, "J").value & "")
|
||||||
|
code66 = Trim(wsPlatform.Cells(i, "K").value & "")
|
||||||
|
|
||||||
|
' 3. 保存到物料对象
|
||||||
|
mat.Category = catName
|
||||||
|
mat.ParentCategory = parentCatName
|
||||||
|
mat.CategorySelectCondition = catSelectCond
|
||||||
|
mat.Code66 = code66
|
||||||
|
|
||||||
|
' 4. 保存到字典(所有物料都保存)
|
||||||
|
If mat.code <> "" Then
|
||||||
|
Set dictAllMaterials(mat.code) = mat
|
||||||
|
End If
|
||||||
|
|
||||||
|
' 5. 如果有类别,创建或更新类别对象
|
||||||
|
If catName <> "" Then
|
||||||
|
If Not dictCategories.Exists(catName) Then
|
||||||
|
Set cat = New clsCategory
|
||||||
|
cat.categoryName = catName
|
||||||
|
cat.ParentCategoryName = parentCatName
|
||||||
|
cat.CategorySelectCondition = catSelectCond
|
||||||
|
Set dictCategories(catName) = cat
|
||||||
|
End If
|
||||||
|
|
||||||
|
dictCategories(catName).AddMaterial mat
|
||||||
|
End If
|
||||||
|
Next i
|
||||||
|
```
|
||||||
|
|
||||||
|
**与 v1.0 的区别**:
|
||||||
|
|
||||||
|
| 特性 | v1.0 (旧版本) | v2.0 (新版本) |
|
||||||
|
|------|--------------|--------------|
|
||||||
|
| 数据源 | [平台配置清单] + [领料配置] | 仅 [平台配置清单] |
|
||||||
|
| 加载步骤 | 3步 (先物料,后类别,再层级) | 2步 (物料类别一起加载,再层级) |
|
||||||
|
| 参数 | wsConfig, wsPlatform | wsPlatform |
|
||||||
|
| 类别选用条件 | 不支持 | 支持 |
|
||||||
|
| 66代码 | 不支持 | 支持 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 第二步:建立类别层级关系
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
S2_Start([开始第二步]) --> S2_Loop{遍历 dictCategories<br/>所有键}
|
||||||
|
S2_Loop -->|下一个键| S2_GetCat[获取类别对象 cat]
|
||||||
|
S2_GetCat --> S2_CheckParent{ParentCategoryName<br/>是否为空?}
|
||||||
|
S2_CheckParent -->|否, 有父类别| S2_ParentExists{父类别<br/>存在?}
|
||||||
|
S2_CheckParent -->|是, 无父类别| S2_AddRoot[添加到 rootCategories<br/>作为根类别]
|
||||||
|
S2_ParentExists -->|是| S2_AddSub[添加到父类别的<br/>SubCategories 集合]
|
||||||
|
S2_ParentExists -->|否| S2_Orphan[父类别不存在<br/>跳过建立关系]
|
||||||
|
S2_AddRoot --> S2_Next
|
||||||
|
S2_AddSub --> S2_Next
|
||||||
|
S2_Orphan --> S2_Next
|
||||||
|
S2_Next --> S2_Loop
|
||||||
|
S2_Loop -->|遍历完成| S2_End([第二步完成])
|
||||||
|
|
||||||
|
style S2_Start fill:#e1f5e1
|
||||||
|
style S2_End fill:#ffe1e1
|
||||||
|
style S2_AddRoot fill:#ffccbc
|
||||||
|
style S2_AddSub fill:#b2dfdb
|
||||||
|
```
|
||||||
|
|
||||||
|
**数据结构**:
|
||||||
|
```
|
||||||
|
rootCategories: Collection<clsCategory>
|
||||||
|
├── "表壳" (根类别)
|
||||||
|
│ └── CategorySelectCondition: "" (总是需要)
|
||||||
|
├── "部件" (根类别)
|
||||||
|
│ └── CategorySelectCondition: "" (总是需要)
|
||||||
|
│ └── SubCategories:
|
||||||
|
│ ├── "接头" (子类别)
|
||||||
|
│ │ └── CategorySelectCondition: "gclj=M20" (条件性需要)
|
||||||
|
│ └── "弹性元件" (子类别)
|
||||||
|
│ └── CategorySelectCondition: "lcfw=M02" (条件性需要)
|
||||||
|
└── "机芯" (根类别)
|
||||||
|
└── CategorySelectCondition: "jycz=1" (条件性需要)
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键代码**:
|
||||||
|
```vba
|
||||||
|
For Each key In dictCategories.Keys
|
||||||
|
Set cat = dictCategories(key)
|
||||||
|
If cat.ParentCategoryName <> "" Then
|
||||||
|
If dictCategories.Exists(cat.ParentCategoryName) Then
|
||||||
|
Set parentCat = dictCategories(cat.ParentCategoryName)
|
||||||
|
parentCat.AddSubCategory cat
|
||||||
|
End If
|
||||||
|
Else
|
||||||
|
rootCategories.Add cat, cat.categoryName
|
||||||
|
End If
|
||||||
|
Next key
|
||||||
|
```
|
||||||
|
|
||||||
|
**说明**:此步骤与 v1.0 版本相同,未发生变化。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 数据结构总结
|
||||||
|
|
||||||
|
### 类的私有成员变量
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
class clsBOMManager {
|
||||||
|
-dictCategories: Dictionary
|
||||||
|
-dictAllMaterials: Dictionary
|
||||||
|
-rootCategories: Collection
|
||||||
|
+LoadData(wsPlatform)
|
||||||
|
+GetRootCategories() Collection
|
||||||
|
+GetCategory(categoryName) clsCategory
|
||||||
|
+GetRequiredCategories(conditions) Collection
|
||||||
|
+GetValidMaterialsByModel(modelStr) Collection
|
||||||
|
}
|
||||||
|
|
||||||
|
class clsCategory {
|
||||||
|
+categoryName: String
|
||||||
|
+ParentCategoryName: String
|
||||||
|
+CategorySelectCondition: String
|
||||||
|
+materials: Collection
|
||||||
|
+SubCategories: Collection
|
||||||
|
+IsLeafCategory: Boolean
|
||||||
|
+AddMaterial(mat)
|
||||||
|
+AddSubCategory(cat)
|
||||||
|
+IsRequiredForModel(conditions) Boolean
|
||||||
|
+HasSubCategories() Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
class clsMaterialItem {
|
||||||
|
+code: String
|
||||||
|
+Name: String
|
||||||
|
+Quantity: Double
|
||||||
|
+Condition: String
|
||||||
|
+Category: String
|
||||||
|
+ParentCategory: String
|
||||||
|
+CategorySelectCondition: String
|
||||||
|
+Code66: String
|
||||||
|
}
|
||||||
|
|
||||||
|
clsBOMManager "1" --> "*" clsCategory : 管理
|
||||||
|
clsCategory "1" --> "*" clsMaterialItem : 包含
|
||||||
|
clsCategory "1" --> "*" clsCategory : 父子关系
|
||||||
|
```
|
||||||
|
|
||||||
|
### 三个核心数据容器
|
||||||
|
|
||||||
|
| 数据容器 | 类型 | 键/索引 | 值 | 用途 |
|
||||||
|
|---------|------|---------|-----|------|
|
||||||
|
| `dictAllMaterials` | Dictionary | 物料代号 (String) | clsMaterialItem | 快速查找任意物料的完整信息 |
|
||||||
|
| `dictCategories` | Dictionary | 类别名称 (String) | clsCategory | 快速查找任意类别 |
|
||||||
|
| `rootCategories` | Collection | 索引 (Long) | clsCategory | 遍历完整的类别树结构 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 流程时序图
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Caller as 调用者
|
||||||
|
participant LoadData as LoadData方法
|
||||||
|
participant WSPlatform as [平台配置清单]
|
||||||
|
participant DictMat as dictAllMaterials
|
||||||
|
participant DictCat as dictCategories
|
||||||
|
participant RootCats as rootCategories
|
||||||
|
|
||||||
|
Caller->>LoadData: LoadData(wsPlatform)
|
||||||
|
|
||||||
|
Note over LoadData, WSPlatform: 第一步: 加载物料和类别信息
|
||||||
|
LoadData->>WSPlatform: 读取第4-末行
|
||||||
|
WSPlatform-->>LoadData: 返回物料和类别数据
|
||||||
|
loop 每一行数据
|
||||||
|
LoadData->>DictMat: 创建/添加物料 (包含新字段)
|
||||||
|
alt 类别字段不为空
|
||||||
|
LoadData->>DictCat: 类别存在?
|
||||||
|
alt 类别不存在
|
||||||
|
LoadData->>DictCat: 创建新类别(含类别选用条件)
|
||||||
|
end
|
||||||
|
LoadData->>DictCat: 添加物料到类别
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Note over LoadData, RootCats: 第二步: 构建层级树
|
||||||
|
loop 遍历所有类别
|
||||||
|
LoadData->>DictCat: 获取类别
|
||||||
|
alt 有父类别
|
||||||
|
LoadData->>DictCat: 添加到父类别的SubCategories
|
||||||
|
else 无父类别
|
||||||
|
LoadData->>RootCats: 添加为根类别
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
LoadData-->>Caller: 完成
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 重要业务规则
|
||||||
|
|
||||||
|
### 1. 物料筛选规则
|
||||||
|
- ✅ 类别字段不为空的物料 → **需要领料**,会被添加到类别中
|
||||||
|
- ❌ 类别字段为空的物料 → **不需要领料**,仅在 `dictAllMaterials` 中保存
|
||||||
|
|
||||||
|
### 2. 类别选用条件规则(v2.0 新增)
|
||||||
|
- ✅ 类别选用条件为空 → 该类别**总是需要**(无条件限制)
|
||||||
|
- ⚡ 类别选用条件不为空 → 需要用型号条件去匹配
|
||||||
|
- 匹配成功 → 该类别需要
|
||||||
|
- 匹配失败 → 该类别不需要
|
||||||
|
|
||||||
|
### 3. 类别层级规则
|
||||||
|
- 根类别:`ParentCategoryName` 为空字符串 `""`
|
||||||
|
- 子类别:`ParentCategoryName` 指向父类别名称
|
||||||
|
- 层级深度:无限制(支持任意深度的树形结构)
|
||||||
|
|
||||||
|
### 4. 数据一致性
|
||||||
|
- 所有物料必须从 [平台配置清单] 读取
|
||||||
|
- 类别字段和类别选用条件字段同时存在
|
||||||
|
- 一个物料只能属于一个类别
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 调用 LoadData
|
||||||
|
|
||||||
|
```vba
|
||||||
|
Dim bomMgr As New clsBOMManager
|
||||||
|
Dim wsPlatform As Worksheet
|
||||||
|
|
||||||
|
Set wsPlatform = ThisWorkbook.Worksheets("平台配置清单")
|
||||||
|
|
||||||
|
' 加载数据 (v2.0 只需要一个参数)
|
||||||
|
Call bomMgr.LoadData(wsPlatform)
|
||||||
|
|
||||||
|
' 获取根类别
|
||||||
|
Dim rootCats As Collection
|
||||||
|
Set rootCats = bomMgr.GetRootCategories()
|
||||||
|
|
||||||
|
' 遍历所有根类别
|
||||||
|
Dim i As Long
|
||||||
|
For i = 1 To rootCats.Count
|
||||||
|
Debug.Print "根类别: " & rootCats(i).categoryName
|
||||||
|
Debug.Print "类别选用条件: " & rootCats(i).CategorySelectCondition
|
||||||
|
Next i
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用类别选用条件
|
||||||
|
|
||||||
|
```vba
|
||||||
|
' 解析型号并提取条件
|
||||||
|
Dim conditions As Object
|
||||||
|
Set conditions = bomMgr.ParseModelAndExtractConditions("YTHN-100.A0.532.M203.M16.Y3")
|
||||||
|
|
||||||
|
' 获取需要的类别
|
||||||
|
Dim requiredCats As Collection
|
||||||
|
Set requiredCats = bomMgr.GetRequiredCategories(conditions)
|
||||||
|
|
||||||
|
' 遍历需要的类别
|
||||||
|
Dim cat As clsCategory
|
||||||
|
For Each cat In requiredCats
|
||||||
|
Debug.Print "需要的类别: " & cat.categoryName
|
||||||
|
Next cat
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 执行后的数据结构示例
|
||||||
|
|
||||||
|
假设加载后的数据结构如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
型号: YTHN-100.A0.532.M203.M16.Y3
|
||||||
|
提取的条件: lcfw=M16, gclj=M20, jycz=1, azxs=A0
|
||||||
|
|
||||||
|
dictAllMaterials:
|
||||||
|
{
|
||||||
|
"01011009557" → {
|
||||||
|
code: "01011009557",
|
||||||
|
Name: "径向低压接头部件",
|
||||||
|
Quantity: 1.0,
|
||||||
|
Condition: "gclj=M20 AND jycz=1 AND lcfw=M01",
|
||||||
|
Category: "部件",
|
||||||
|
ParentCategory: "",
|
||||||
|
CategorySelectCondition: "",
|
||||||
|
Code66: "66021009557"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dictCategories:
|
||||||
|
{
|
||||||
|
"部件" → {
|
||||||
|
categoryName: "部件",
|
||||||
|
ParentCategoryName: "",
|
||||||
|
CategorySelectCondition: "",
|
||||||
|
materials: [...],
|
||||||
|
SubCategories: ["接头", "弹性元件"]
|
||||||
|
},
|
||||||
|
"接头" → {
|
||||||
|
categoryName: "接头",
|
||||||
|
ParentCategoryName: "部件",
|
||||||
|
CategorySelectCondition: "gclj=M20", ← 类别选用条件
|
||||||
|
materials: [...],
|
||||||
|
SubCategories: []
|
||||||
|
},
|
||||||
|
"机芯" → {
|
||||||
|
categoryName: "机芯",
|
||||||
|
ParentCategoryName: "",
|
||||||
|
CategorySelectCondition: "jycz=1", ← 类别选用条件
|
||||||
|
materials: [...],
|
||||||
|
SubCategories: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
判断哪些类别需要:
|
||||||
|
- "部件" → CategorySelectCondition="" → 总是需要 ✓
|
||||||
|
- "接头" → CategorySelectCondition="gclj=M20" → 型号有gclj=M20 → 需要 ✓
|
||||||
|
- "机芯" → CategorySelectCondition="jycz=1" → 型号有jycz=1 → 需要 ✓
|
||||||
|
|
||||||
|
requiredCategories = ["部件", "接头", "机芯"]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
LoadData 方法本身不包含显式的错误处理(`On Error`),依赖以下机制:
|
||||||
|
|
||||||
|
1. **空值处理**:使用 `& ""` 确保字符串转换不会失败
|
||||||
|
2. **类型转换**:使用 `CDbl()` 时没有错误处理,假设数据格式正确
|
||||||
|
3. **跳过机制**:物料代号为空时,不保存到字典
|
||||||
|
4. **存在性检查**:使用 `Dictionary.Exists()` 避免键不存在错误
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 相关方法
|
||||||
|
|
||||||
|
LoadData 执行后,可以使用以下方法访问数据:
|
||||||
|
|
||||||
|
| 方法 | 说明 | v2.0变化 |
|
||||||
|
|------|------|---------|
|
||||||
|
| `GetRootCategories()` | 获取所有顶层类别 | 无变化 |
|
||||||
|
| `GetCategory(categoryName)` | 根据名称获取类别对象 | 无变化 |
|
||||||
|
| `GetRequiredCategories(conditions)` | ⭐ 获取需要的类别列表 | 新增 |
|
||||||
|
| `GetValidMaterialsByModel()` | 获取物料并检查完整性 | ⭐ 已重构,使用类别选用条件 |
|
||||||
|
| `GetMaterialsByModel()` | 根据型号获取物料 | 无变化 |
|
||||||
|
| `PrintCategoryTree()` | 打印类别树(调试用) | 无变化 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 迁移指南 (v1.0 → v2.0)
|
||||||
|
|
||||||
|
### 代码变更
|
||||||
|
|
||||||
|
**旧代码 (v1.0)**:
|
||||||
|
```vba
|
||||||
|
Dim wsConfig As Worksheet
|
||||||
|
Dim wsPlatform As Worksheet
|
||||||
|
Set wsConfig = ThisWorkbook.Worksheets("领料配置")
|
||||||
|
Set wsPlatform = ThisWorkbook.Worksheets("平台配置清单")
|
||||||
|
|
||||||
|
Call bomMgr.LoadData(wsConfig, wsPlatform) ' 两个参数
|
||||||
|
```
|
||||||
|
|
||||||
|
**新代码 (v2.0)**:
|
||||||
|
```vba
|
||||||
|
Dim wsPlatform As Worksheet
|
||||||
|
Set wsPlatform = ThisWorkbook.Worksheets("平台配置清单")
|
||||||
|
|
||||||
|
Call bomMgr.LoadData(wsPlatform) ' 一个参数
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据变更
|
||||||
|
|
||||||
|
**旧表结构**:
|
||||||
|
- [领料配置]: A列=代号, C列=类别, D列=上层类别
|
||||||
|
- [平台配置清单]: C列=代号, D列=名称, E列=数量, F列=选择条件
|
||||||
|
|
||||||
|
**新表结构**:
|
||||||
|
- [平台配置清单]: C列=代号, D列=名称, E列=数量, F列=选择条件
|
||||||
|
H列=类别, I列=上层类别, J列=类别选用条件, K列=66代码
|
||||||
|
|
||||||
|
### 逻辑变更
|
||||||
|
|
||||||
|
**完整性检查逻辑**:
|
||||||
|
- **v1.0**: 所有根类别都必须匹配到物料
|
||||||
|
- **v2.0**: 只检查需要的类别(通过类别选用条件判断)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
`LoadData` 方法(v2.0)通过两个阶段构建完整的 BOM 数据结构:
|
||||||
|
|
||||||
|
1. **数据加载**:从 [平台配置清单] 一次性加载所有物料和类别信息(包括新增的类别选用条件和66代码)
|
||||||
|
2. **结构构建**:构建类别的树形层级关系
|
||||||
|
|
||||||
|
v2.0 的主要改进:
|
||||||
|
- ✅ 简化数据源:从一个表读取所有数据
|
||||||
|
- ✅ 支持动态类别判断:通过类别选用条件
|
||||||
|
- ✅ 更精确的完整性检查:只检查需要的类别
|
||||||
|
- ✅ 支持66系统:新增66代码字段
|
||||||
|
|
||||||
|
最终形成的数据结构支持:
|
||||||
|
- 快速按代号查找物料
|
||||||
|
- 快速按类别查找物料
|
||||||
|
- 遍历完整的类别树
|
||||||
|
- 根据型号动态确定需要的类别
|
||||||
|
- 支持父类别/子类别的领料逻辑
|
||||||
11
reference_docs/平台配置清单.md
Normal file
11
reference_docs/平台配置清单.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
| | A | B | C | D | E | F | G | H | I | J | K |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| 1 | 代号 | YTHN-100 | 描述 | | 英文名称 | | | | | | |
|
||||||
|
| 2 | 名称 | (不锈钢)耐震压力表 | 负责人 | | 备注 | | | | | | |
|
||||||
|
| 3 | 行号 | 模块 | 代号 | 名称 | 数量 | 选择条件 | 备注 | 类别 | 上层类别 | 类别选用条件 | 66代码 |
|
||||||
|
| 4 | 10 | 316L部件 | 01011009557 | 径向低压接头部件 | 1.0 | gclj=M20 AND jycz=1 AND lcfw=M01 AND (azxs=A0 OR azxs=AT OR azxs=AH) | | 部件 | | | 66021009557 |
|
||||||
|
| 5 | 20 | 316L部件 | 01011009520 | 径向低压接头部件 | 1.0 | gclj=M20 AND jycz=1 AND lcfw=M02 AND (azxs=A0 OR azxs=AT OR azxs=AH) | | 部件 | | | 66021009520 |
|
||||||
|
| 6 | 30 | 316L部件 | 01011013536 | 径向低压接头部件 | 1.0 | gclj=M20 AND jycz=1 AND lcfw=M03 AND (azxs=A0 OR azxs=AT OR azxs=AH) | | 部件 | | | 66021013536 |
|
||||||
|
| 7 | 40 | 316L部件 | 01011013538 | 径向低压接头部件 | 1.0 | gclj=M20 AND jycz=1 AND lcfw=M04 AND (azxs=A0 OR azxs=AT OR azxs=AH) | | 部件 | | | 66021013538 |
|
||||||
|
| 8 | 50 | 316L部件 | 01011009522 | 径向低压接头部件 | 1.0 | gclj=M20 AND jycz=1 AND lcfw=M05 AND (azxs=A0 OR azxs=AT OR azxs=AH) | | 部件 | | | 66021009522 |
|
||||||
|
| 9 | 60 | 316L部件 | 01011013336 | 径向低压接头部件 | 1.0 | gclj=M20 AND jycz=1 AND lcfw=M06 AND (azxs=A0 OR azxs=AT OR azxs=AH) | | 部件 | | | 66021013336 |
|
||||||
650
reference_docs/重构计划-整合领料配置到平台配置清单.md
Normal file
650
reference_docs/重构计划-整合领料配置到平台配置清单.md
Normal file
@@ -0,0 +1,650 @@
|
|||||||
|
# 重构计划:整合 [领料配置] 到 [平台配置清单]
|
||||||
|
|
||||||
|
## 一、背景概述
|
||||||
|
|
||||||
|
### 1.1 重构目标
|
||||||
|
|
||||||
|
将原有的 [领料配置] 表格的功能整合到 [平台配置清单] 中,简化数据结构,统一管理。
|
||||||
|
|
||||||
|
### 1.2 变化原因
|
||||||
|
|
||||||
|
- 原有设计:物料基础信息在 [平台配置清单],类别配置在 [领料配置],数据分散
|
||||||
|
- 新设计:所有信息集中在 [平台配置清单] 一张表,便于维护
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、新工作表结构分析
|
||||||
|
|
||||||
|
### 2.1 新表结构
|
||||||
|
|
||||||
|
**[平台配置清单] 表结构**(第3行标题):
|
||||||
|
|
||||||
|
| 列 | 字段名 | 说明 | 示例 |
|
||||||
|
|---|--------|------|------|
|
||||||
|
| A | 行号 | 数据行序号 | 10, 20, 30 |
|
||||||
|
| B | 模块 | 模块分类 | 316L部件 |
|
||||||
|
| C | 代号 | 物料代号 | 01011009557 |
|
||||||
|
| D | 名称 | 物料名称 | 径向低压接头部件 |
|
||||||
|
| E | 数量 | 物料数量 | 1.0 |
|
||||||
|
| F | 选择条件 | 物料选用条件 | gclj=M20 AND jycz=1 |
|
||||||
|
| G | 备注 | 备注信息 | |
|
||||||
|
| **H** | **类别** | ⭐ 新增:物料所属类别 | 部件 |
|
||||||
|
| **I** | **上层类别** | ⭐ 新增:父类别名称 | |
|
||||||
|
| **J** | **类别选用条件** | ⭐ 新增:判断该类别是否需要 | lcfw=M02 |
|
||||||
|
| **K** | **66代码** | ⭐ 新增:66系统代码 | 66021009557 |
|
||||||
|
|
||||||
|
### 2.2 字段说明
|
||||||
|
|
||||||
|
#### 新增字段详解
|
||||||
|
|
||||||
|
1. **类别(H列)**
|
||||||
|
- 类型:字符串
|
||||||
|
- 说明:物料所属的类别名称
|
||||||
|
- 示例:`部件`、`表壳`、`接头`、`机芯`
|
||||||
|
- 为空:表示该物料不属于任何类别(不需要领料)
|
||||||
|
|
||||||
|
2. **上层类别(I列)**
|
||||||
|
- 类型:字符串
|
||||||
|
- 说明:父类别的名称,用于构建类别层级树
|
||||||
|
- 示例:`接头` 的上层类别是 `部件`
|
||||||
|
- 为空:表示该类别是根类别
|
||||||
|
|
||||||
|
3. **类别选用条件(J列)** ⭐ **关键字段**
|
||||||
|
- 类型:字符串(逻辑表达式)
|
||||||
|
- 说明:用于判断该类别是否需要被选用
|
||||||
|
- 格式:与"选择条件"字段相同的逻辑表达式
|
||||||
|
- 判断规则:
|
||||||
|
- **为空**:该类别总是需要(无条件)
|
||||||
|
- **不为空**:需要用型号提取的条件去匹配,匹配成功才需要
|
||||||
|
- 示例:
|
||||||
|
- `lcfw=M02`:只有当量程是 M02 时才需要这个类别
|
||||||
|
- `gclj=M20 AND jycz=1`:只有当过程连接是 M20 且接液材质是 1 时才需要
|
||||||
|
|
||||||
|
4. **66代码(K列)**
|
||||||
|
- 类型:字符串
|
||||||
|
- 说明:66系统使用的代码(具体用途待补充)
|
||||||
|
- 示例:`66021009557`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、核心业务逻辑变化
|
||||||
|
|
||||||
|
### 3.1 旧逻辑(重构前)
|
||||||
|
|
||||||
|
```
|
||||||
|
有效性检查规则:
|
||||||
|
- 所有根类别都必须匹配到至少1个物料
|
||||||
|
- 每个根类别(或其子类别)必须有且仅有1个物料匹配
|
||||||
|
- 如果有任何一个类别不满足,则物料集合不完整
|
||||||
|
```
|
||||||
|
|
||||||
|
**问题**:
|
||||||
|
- 无法表达"某些型号不需要某些类别"的情况
|
||||||
|
- 例如:某些型号可能不需要"机芯"类别
|
||||||
|
|
||||||
|
### 3.2 新逻辑(重构后)
|
||||||
|
|
||||||
|
```
|
||||||
|
有效性检查规则:
|
||||||
|
- 第一步:确定哪些类别是该型号需要的
|
||||||
|
- 遍历所有有物料的类别
|
||||||
|
- 如果"类别选用条件"为空 → 该类别总是需要
|
||||||
|
- 如果"类别选用条件"不为空 → 用型号条件去匹配,匹配成功才需要
|
||||||
|
|
||||||
|
- 第二步:只检查需要的类别是否有物料匹配
|
||||||
|
- 对于需要的类别,必须有且仅有1个物料匹配
|
||||||
|
- 不需要的类别不参与完整性检查
|
||||||
|
```
|
||||||
|
|
||||||
|
**示例场景**:
|
||||||
|
|
||||||
|
型号:`YTHN-100.A0.532.M203.M16.Y3` (量程 M16)
|
||||||
|
|
||||||
|
| 类别 | 类别选用条件 | 是否需要 | 说明 |
|
||||||
|
|------|-------------|---------|------|
|
||||||
|
| 表壳 | (空) | ✅ 需要 | 无条件,总是需要 |
|
||||||
|
| 部件 | lcfw=M02 | ❌ 不需要 | 型号量程是 M16,不满足条件 |
|
||||||
|
| 接头 | lcfw=M16 | ✅ 需要 | 型号量程是 M16,满足条件 |
|
||||||
|
| 机芯 | gclj=M20 | ❌ 不需要 | 型号过程连接不是 M20 |
|
||||||
|
|
||||||
|
**结果**:只需要检查 `表壳` 和 `接头` 类别的物料完整性
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、代码影响分析
|
||||||
|
|
||||||
|
### 4.1 需要修改的模块和方法
|
||||||
|
|
||||||
|
#### 1. clsBOMManager 类 ⭐ **核心改动**
|
||||||
|
|
||||||
|
**1.1 LoadData 方法**
|
||||||
|
|
||||||
|
```vba
|
||||||
|
' 旧签名
|
||||||
|
Public Sub LoadData(wsConfig As Worksheet, wsPlatform As Worksheet)
|
||||||
|
|
||||||
|
' 新签名
|
||||||
|
Public Sub LoadData(wsPlatform As Worksheet)
|
||||||
|
```
|
||||||
|
|
||||||
|
**变化**:
|
||||||
|
- ❌ 移除 `wsConfig` 参数(不再需要 [领料配置] 表)
|
||||||
|
- ✅ 所有数据从 [平台配置清单] 读取
|
||||||
|
- ✅ 读取列数增加:从 A-F 扩展到 A-K
|
||||||
|
|
||||||
|
**读取列对应**:
|
||||||
|
```vba
|
||||||
|
' 第一步:加载物料和类别信息(一次性完成)
|
||||||
|
For i = 4 To lastRow
|
||||||
|
' 基础信息
|
||||||
|
mat.code = Trim(wsPlatform.Cells(i, "C").value) ' 代号
|
||||||
|
mat.Name = Trim(wsPlatform.Cells(i, "D").value) ' 名称
|
||||||
|
mat.Quantity = CDbl(wsPlatform.Cells(i, "E").value) ' 数量
|
||||||
|
mat.Condition = Trim(wsPlatform.Cells(i, "F").value) ' 选择条件
|
||||||
|
|
||||||
|
' ⭐ 新增:类别相关字段
|
||||||
|
catName = Trim(wsPlatform.Cells(i, "H").value) ' 类别
|
||||||
|
parentCatName = Trim(wsPlatform.Cells(i, "I").value) ' 上层类别
|
||||||
|
catSelectCond = Trim(wsPlatform.Cells(i, "J").value) ' 类别选用条件
|
||||||
|
code66 = Trim(wsPlatform.Cells(i, "K").value) ' 66代码
|
||||||
|
Next i
|
||||||
|
```
|
||||||
|
|
||||||
|
**流程变化**:
|
||||||
|
```
|
||||||
|
旧流程(三步):
|
||||||
|
1. 从 [平台配置清单] 加载物料基础信息
|
||||||
|
2. 从 [领料配置] 加载类别信息
|
||||||
|
3. 建立类别层级关系
|
||||||
|
|
||||||
|
新流程(两步):
|
||||||
|
1. 从 [平台配置清单] 加载物料和类别信息(一次性)
|
||||||
|
2. 建立类别层级关系
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**1.2 GetValidMaterialsByModel 方法**
|
||||||
|
|
||||||
|
**变化**:
|
||||||
|
- ✅ 需要先判断哪些类别是该型号需要的
|
||||||
|
- ✅ 只检查需要的类别的完整性
|
||||||
|
|
||||||
|
**新逻辑**:
|
||||||
|
```vba
|
||||||
|
Public Function GetValidMaterialsByModel(modelStr As String) As collection
|
||||||
|
' 1. 解析型号并提取条件
|
||||||
|
Set conditions = ParseModelAndExtractConditions(modelStr)
|
||||||
|
|
||||||
|
' 2. ⭐ 新增:确定需要的类别列表
|
||||||
|
Dim requiredCategories As collection
|
||||||
|
Set requiredCategories = GetRequiredCategories(conditions)
|
||||||
|
|
||||||
|
' 3. 只检查需要的类别
|
||||||
|
For Each cat In requiredCategories
|
||||||
|
Set catResult = CheckCategoryCompleteness(cat, matcher, conditions)
|
||||||
|
' ...
|
||||||
|
Next cat
|
||||||
|
End Function
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**1.3 GetRequiredCategories 方法(新增)**
|
||||||
|
|
||||||
|
**功能**:根据型号条件,确定哪些类别是需要的
|
||||||
|
|
||||||
|
```vba
|
||||||
|
Private Function GetRequiredCategories(conditions As Object) As collection
|
||||||
|
' 遍历所有类别
|
||||||
|
' - 类别选用条件为空 → 添加到需要列表
|
||||||
|
' - 类别选用条件不为空 → 用 conditions 去匹配
|
||||||
|
' - 匹配成功 → 添加到需要列表
|
||||||
|
' - 匹配失败 → 不添加
|
||||||
|
End Function
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**1.4 CheckCategoryCompleteness 方法**
|
||||||
|
|
||||||
|
**变化**:
|
||||||
|
- 无需修改,因为调用者已经过滤了不需要的类别
|
||||||
|
- 逻辑保持不变:检查需要的类别是否有且仅有1个物料匹配
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 2. clsCategory 类 ⭐ **需要扩展**
|
||||||
|
|
||||||
|
**新增属性**:
|
||||||
|
|
||||||
|
```vba
|
||||||
|
Public CategorySelectCondition As String ' ⭐ 新增:类别选用条件
|
||||||
|
Public IsRequired As Boolean ' ⭐ 新增:是否需要(计算属性)
|
||||||
|
```
|
||||||
|
|
||||||
|
**新增方法**:
|
||||||
|
|
||||||
|
```vba
|
||||||
|
' 检查该类别是否需要(根据型号条件)
|
||||||
|
Public Function IsRequiredForModel(conditions As Object) As Boolean
|
||||||
|
If Me.CategorySelectCondition = "" Then
|
||||||
|
' 选用条件为空,总是需要
|
||||||
|
IsRequiredForModel = True
|
||||||
|
Else
|
||||||
|
' 需要用 conditions 去匹配 CategorySelectCondition
|
||||||
|
Dim matcher As New clsConditionMatcher
|
||||||
|
IsRequiredForModel = matcher.IsMatch(Me.CategorySelectCondition, conditions)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 3. clsMaterialItem 类 ⭐ **需要扩展**
|
||||||
|
|
||||||
|
**新增属性**:
|
||||||
|
|
||||||
|
```vba
|
||||||
|
Public CategorySelectCondition As String ' ⭐ 新增:类别选用条件
|
||||||
|
Public Code66 As String ' ⭐ 新增:66代码
|
||||||
|
```
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 物料需要保存所属类别的"类别选用条件"
|
||||||
|
- 便于后续判断该物料是否需要
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.2 不需要修改的方法
|
||||||
|
|
||||||
|
以下方法不受影响,无需修改:
|
||||||
|
|
||||||
|
- ✅ `GetRootCategories()` - 获取根类别
|
||||||
|
- ✅ `GetCategory(categoryName)` - 获取类别对象
|
||||||
|
- ✅ `GetMaterialsByModel()` - 根据型号获取物料(不涉及完整性检查)
|
||||||
|
- ✅ `GetMaterialsForPicking()` - 获取领料物料
|
||||||
|
- ✅ `ParseModelAndExtractConditions()` - 解析型号
|
||||||
|
- ✅ `FilterCategoryWithSubcategories()` - 类别筛选
|
||||||
|
- ✅ `CollectAllMaterials()` - 收集物料
|
||||||
|
- ✅ `PrintCategoryTree()` - 打印类别树
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、重构实施计划
|
||||||
|
|
||||||
|
### 5.1 实施步骤
|
||||||
|
|
||||||
|
#### 阶段一:数据模型扩展(1-2小时)
|
||||||
|
|
||||||
|
**任务清单**:
|
||||||
|
|
||||||
|
1. ✅ **修改 clsMaterialItem 类**
|
||||||
|
- [ ] 添加 `CategorySelectCondition` 属性
|
||||||
|
- [ ] 添加 `Code66` 属性
|
||||||
|
- [ ] 更新类注释
|
||||||
|
|
||||||
|
2. ✅ **修改 clsCategory 类**
|
||||||
|
- [ ] 添加 `CategorySelectCondition` 属性
|
||||||
|
- [ ] 添加 `IsRequiredForModel()` 方法
|
||||||
|
- [ ] 更新类注释
|
||||||
|
|
||||||
|
3. ✅ **添加测试用例**
|
||||||
|
- [ ] 测试类别选用条件的匹配逻辑
|
||||||
|
- [ ] 测试空条件和非空条件的情况
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 阶段二:LoadData 方法重构(2-3小时)
|
||||||
|
|
||||||
|
**任务清单**:
|
||||||
|
|
||||||
|
1. ✅ **修改方法签名**
|
||||||
|
```vba
|
||||||
|
' 旧
|
||||||
|
Public Sub LoadData(wsConfig As Worksheet, wsPlatform As Worksheet)
|
||||||
|
|
||||||
|
' 新
|
||||||
|
Public Sub LoadData(wsPlatform As Worksheet)
|
||||||
|
```
|
||||||
|
|
||||||
|
2. ✅ **重写加载逻辑**
|
||||||
|
- [ ] 移除第二步(从 [领料配置] 加载)
|
||||||
|
- [ ] 合并第一步和第二步(一次性从 [平台配置清单] 加载所有字段)
|
||||||
|
- [ ] 更新列索引映射:
|
||||||
|
- H列:类别
|
||||||
|
- I列:上层类别
|
||||||
|
- J列:类别选用条件
|
||||||
|
- K列:66代码
|
||||||
|
|
||||||
|
3. ✅ **更新类别创建逻辑**
|
||||||
|
- [ ] 创建类别时,设置 `CategorySelectCondition` 属性
|
||||||
|
- [ ] 创建物料时,设置 `CategorySelectCondition` 和 `Code66` 属性
|
||||||
|
|
||||||
|
4. ✅ **更新文档**
|
||||||
|
- [ ] 更新 `clsBOMManager_LoadData流程分析.md`
|
||||||
|
- [ ] 更新方法注释
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 阶段三:完整性检查逻辑重构(2-3小时)
|
||||||
|
|
||||||
|
**任务清单**:
|
||||||
|
|
||||||
|
1. ✅ **新增 GetRequiredCategories 方法**
|
||||||
|
```vba
|
||||||
|
Private Function GetRequiredCategories(conditions As Object) As collection
|
||||||
|
' 遍历所有类别,判断是否需要
|
||||||
|
' 返回需要的类别列表
|
||||||
|
End Function
|
||||||
|
```
|
||||||
|
|
||||||
|
2. ✅ **修改 GetValidMaterialsByModel 方法**
|
||||||
|
- [ ] 在检查完整性前,先调用 `GetRequiredCategories()`
|
||||||
|
- [ ] 只对需要的类别调用 `CheckCategoryCompleteness()`
|
||||||
|
- [ ] 更新方法注释
|
||||||
|
|
||||||
|
3. ✅ **测试完整性检查**
|
||||||
|
- [ ] 测试所有类别都需要的情况(向后兼容)
|
||||||
|
- [ ] 测试部分类别不需要的情况(新逻辑)
|
||||||
|
- [ ] 测试边界情况(所有类别都不需要)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 阶段四:测试与验证(2-3小时)
|
||||||
|
|
||||||
|
**任务清单**:
|
||||||
|
|
||||||
|
1. ✅ **单元测试**
|
||||||
|
- [ ] 测试 `LoadData` 加载新表结构
|
||||||
|
- [ ] 测试 `IsRequiredForModel()` 方法
|
||||||
|
- [ ] 测试 `GetRequiredCategories()` 方法
|
||||||
|
- [ ] 测试 `GetValidMaterialsByModel()` 新逻辑
|
||||||
|
|
||||||
|
2. ✅ **集成测试**
|
||||||
|
- [ ] 使用真实数据测试完整流程
|
||||||
|
- [ ] 验证结果与预期一致
|
||||||
|
|
||||||
|
3. ✅ **回归测试**
|
||||||
|
- [ ] 运行 `modModelParserTest` 中的所有测试
|
||||||
|
- [ ] 确保旧功能不受影响
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 阶段五:文档更新(1小时)
|
||||||
|
|
||||||
|
**任务清单**:
|
||||||
|
|
||||||
|
1. ✅ **更新 CLAUDE.md**
|
||||||
|
- [ ] 更新 [平台配置清单] 表结构说明
|
||||||
|
- [ ] 移除 [领料配置] 相关说明
|
||||||
|
- [ ] 更新"类别选用条件"业务逻辑说明
|
||||||
|
|
||||||
|
2. ✅ **更新参考文档**
|
||||||
|
- [ ] 更新 `clsBOMManager使用说明.md`
|
||||||
|
- [ ] 更新 `clsBOMManager_LoadData流程分析.md`
|
||||||
|
|
||||||
|
3. ✅ **添加迁移指南**
|
||||||
|
- [ ] 创建 `数据迁移指南.md`
|
||||||
|
- [ ] 说明如何将旧数据迁移到新结构
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5.2 风险评估
|
||||||
|
|
||||||
|
| 风险项 | 风险等级 | 缓解措施 |
|
||||||
|
|--------|---------|---------|
|
||||||
|
| 数据结构变化导致现有代码报错 | 🔴 高 | 充分的单元测试和回归测试 |
|
||||||
|
| 类别选用条件逻辑理解偏差 | 🟡 中 | 详细的业务场景测试 |
|
||||||
|
| 向后兼容性问题 | 🟡 中 | 保留旧版本的 LoadData 方法(重命名为 LoadDataV1) |
|
||||||
|
| 性能影响 | 🟢 低 | 新逻辑只增加一次类别过滤,影响极小 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5.3 时间估算
|
||||||
|
|
||||||
|
| 阶段 | 预计时间 | 负责人 |
|
||||||
|
|------|---------|--------|
|
||||||
|
| 阶段一:数据模型扩展 | 1-2 小时 | |
|
||||||
|
| 阶段二:LoadData 重构 | 2-3 小时 | |
|
||||||
|
| 阶段三:完整性检查重构 | 2-3 小时 | |
|
||||||
|
| 阶段四:测试与验证 | 2-3 小时 | |
|
||||||
|
| 阶段五:文档更新 | 1 小时 | |
|
||||||
|
| **总计** | **8-12 小时** | |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、数据迁移方案
|
||||||
|
|
||||||
|
### 6.1 从旧表到新表
|
||||||
|
|
||||||
|
**旧 [领料配置] 结构**:
|
||||||
|
| A列 | B列 | C列 | D列 |
|
||||||
|
|-----|-----|-----|-----|
|
||||||
|
| 物料代号 | 物料名称 | 类别 | 上层类别 |
|
||||||
|
|
||||||
|
**新 [平台配置清单] 结构**:
|
||||||
|
| ... | H列 | I列 | J列 | K列 |
|
||||||
|
|-----|-----|-----|-----|-----|
|
||||||
|
| ... | 类别 | 上层类别 | 类别选用条件 | 66代码 |
|
||||||
|
|
||||||
|
**迁移步骤**:
|
||||||
|
|
||||||
|
1. **在 [平台配置清单] 中添加新列**:
|
||||||
|
- H列:类别
|
||||||
|
- I列:上层类别
|
||||||
|
- J列:类别选用条件
|
||||||
|
- K列:66代码
|
||||||
|
|
||||||
|
2. **从 [领料配置] 复制数据**:
|
||||||
|
```
|
||||||
|
H2 = [领料配置]!A2 ' 物料代号 → 类别(需要匹配)
|
||||||
|
I2 = [领料配置]!C2 ' 类别
|
||||||
|
J2 = [领料配置]!D2 ' 上层类别
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **使用 VLOOKUP 匹配物料**:
|
||||||
|
```excel
|
||||||
|
H4 = VLOOKUP(C4, [领料配置]!A:D, 3, FALSE) ' 类别
|
||||||
|
I4 = VLOOKUP(C4, [领料配置]!A:D, 4, FALSE) ' 上层类别
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **手动填写类别选用条件和66代码**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、测试用例设计
|
||||||
|
|
||||||
|
### 7.1 类别选用条件测试
|
||||||
|
|
||||||
|
| 测试场景 | 类别选用条件 | 型号条件 | 预期结果 |
|
||||||
|
|---------|-------------|---------|---------|
|
||||||
|
| 无条件(总是需要) | (空) | 任意 | ✅ 需要 |
|
||||||
|
| 量程匹配 | lcfw=M02 | lcfw=M02 | ✅ 需要 |
|
||||||
|
| 量程不匹配 | lcfw=M02 | lcfw=M16 | ❌ 不需要 |
|
||||||
|
| 复杂条件匹配 | gclj=M20 AND jycz=1 | gclj=M20, jycz=1 | ✅ 需要 |
|
||||||
|
| 复杂条件不匹配 | gclj=M20 AND jycz=1 | gclj=M20, jycz=2 | ❌ 不需要 |
|
||||||
|
| OR 条件匹配 | lcfw=M02 OR lcfw=M16 | lcfw=M16 | ✅ 需要 |
|
||||||
|
|
||||||
|
### 7.2 完整性检查测试
|
||||||
|
|
||||||
|
| 测试场景 | 需要的类别 | 匹配结果 | 预期 IsComplete |
|
||||||
|
|---------|-----------|---------|-----------------|
|
||||||
|
| 全部匹配 | 表壳, 部件, 接头 | 全部有1个 | ✅ True |
|
||||||
|
| 缺失一个 | 表壳, 部件, 接头 | 部件缺失 | ❌ False |
|
||||||
|
| 部分类别不需要 | 表壳, 部件 | 表壳有, 部件无(但部件不需要) | ✅ True |
|
||||||
|
| 多于一个匹配 | 表壳, 部件 | 部件有2个 | ❌ False |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、向后兼容性策略
|
||||||
|
|
||||||
|
### 8.1 保留旧版本方法
|
||||||
|
|
||||||
|
```vba
|
||||||
|
' 旧版本(保留但不推荐使用)
|
||||||
|
Public Sub LoadDataV1(wsConfig As Worksheet, wsPlatform As Worksheet)
|
||||||
|
' 保留原有实现
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
' 新版本(推荐使用)
|
||||||
|
Public Sub LoadData(wsPlatform As Worksheet)
|
||||||
|
' 新实现
|
||||||
|
End Sub
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 迁移期支持
|
||||||
|
|
||||||
|
在迁移期间,两个版本同时存在:
|
||||||
|
- 新代码使用 `LoadData(wsPlatform)`
|
||||||
|
- 旧代码可以继续使用 `LoadDataV1(wsConfig, wsPlatform)`
|
||||||
|
|
||||||
|
迁移完成后,可以移除 `LoadDataV1` 方法。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 九、检查清单
|
||||||
|
|
||||||
|
### 重构前
|
||||||
|
|
||||||
|
- [ ] 备份现有代码
|
||||||
|
- [ ] 备份 [领料配置] 和 [平台配置清单] 表
|
||||||
|
- [ ] 确认测试环境可用
|
||||||
|
|
||||||
|
### 重构中
|
||||||
|
|
||||||
|
- [ ] 按阶段顺序执行
|
||||||
|
- [ ] 每个阶段完成后进行测试
|
||||||
|
- [ ] 及时更新文档
|
||||||
|
|
||||||
|
### 重构后
|
||||||
|
|
||||||
|
- [ ] 运行完整的测试套件
|
||||||
|
- [ ] 验证所有功能正常
|
||||||
|
- [ ] 更新用户文档
|
||||||
|
- [ ] 通知团队成员变更内容
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、后续优化建议
|
||||||
|
|
||||||
|
1. **性能优化**
|
||||||
|
- 缓存类别选用条件的匹配结果
|
||||||
|
- 避免重复解析型号
|
||||||
|
|
||||||
|
2. **功能扩展**
|
||||||
|
- 支持类别优先级(当多个类别都匹配时,优先选择哪个)
|
||||||
|
- 支持类别别名(一个类别多个名称)
|
||||||
|
|
||||||
|
3. **用户体验**
|
||||||
|
- 在 Excel 中添加类别选用条件的验证工具
|
||||||
|
- 提供类别选用条件的构建器(图形化界面)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录:关键代码示例
|
||||||
|
|
||||||
|
### A. 修改后的 LoadData 方法(伪代码)
|
||||||
|
|
||||||
|
```vba
|
||||||
|
Public Sub LoadData(wsPlatform As Worksheet)
|
||||||
|
Dim i As Long, lastRow As Long
|
||||||
|
Dim mat As clsMaterialItem
|
||||||
|
Dim cat As clsCategory
|
||||||
|
|
||||||
|
' 读取数据
|
||||||
|
lastRow = wsPlatform.Cells(wsPlatform.Rows.Count, "C").End(xlUp).row
|
||||||
|
|
||||||
|
For i = 4 To lastRow
|
||||||
|
' 1. 创建物料对象
|
||||||
|
Set mat = New clsMaterialItem
|
||||||
|
mat.code = Trim(wsPlatform.Cells(i, "C").value)
|
||||||
|
mat.Name = Trim(wsPlatform.Cells(i, "D").value)
|
||||||
|
mat.Quantity = CDbl(wsPlatform.Cells(i, "E").value)
|
||||||
|
mat.Condition = Trim(wsPlatform.Cells(i, "F").value)
|
||||||
|
|
||||||
|
' ⭐ 读取类别相关字段
|
||||||
|
Dim catName As String
|
||||||
|
Dim parentCatName As String
|
||||||
|
Dim catSelectCond As String
|
||||||
|
Dim code66 As String
|
||||||
|
|
||||||
|
catName = Trim(wsPlatform.Cells(i, "H").value & "")
|
||||||
|
parentCatName = Trim(wsPlatform.Cells(i, "I").value & "")
|
||||||
|
catSelectCond = Trim(wsPlatform.Cells(i, "J").value & "")
|
||||||
|
code66 = Trim(wsPlatform.Cells(i, "K").value & "")
|
||||||
|
|
||||||
|
' ⭐ 保存到物料对象
|
||||||
|
mat.Category = catName
|
||||||
|
mat.ParentCategory = parentCatName
|
||||||
|
mat.CategorySelectCondition = catSelectCond
|
||||||
|
mat.Code66 = code66
|
||||||
|
|
||||||
|
' 保存到字典
|
||||||
|
If mat.code <> "" Then
|
||||||
|
Set dictAllMaterials(mat.code) = mat
|
||||||
|
End If
|
||||||
|
|
||||||
|
' ⭐ 创建或更新类别对象
|
||||||
|
If catName <> "" Then
|
||||||
|
If Not dictCategories.Exists(catName) Then
|
||||||
|
Set cat = New clsCategory
|
||||||
|
cat.categoryName = catName
|
||||||
|
cat.ParentCategoryName = parentCatName
|
||||||
|
cat.CategorySelectCondition = catSelectCond ' ⭐ 新增
|
||||||
|
Set dictCategories(catName) = cat
|
||||||
|
End If
|
||||||
|
|
||||||
|
' 将物料添加到类别
|
||||||
|
dictCategories(catName).AddMaterial mat
|
||||||
|
End If
|
||||||
|
Next i
|
||||||
|
|
||||||
|
' 建立类别层级关系(逻辑不变)
|
||||||
|
' ...
|
||||||
|
End Sub
|
||||||
|
```
|
||||||
|
|
||||||
|
### B. 新增 GetRequiredCategories 方法(伪代码)
|
||||||
|
|
||||||
|
```vba
|
||||||
|
Private Function GetRequiredCategories(conditions As Object) As collection
|
||||||
|
Dim result As New collection
|
||||||
|
Dim matcher As New clsConditionMatcher
|
||||||
|
|
||||||
|
' 遍历所有类别
|
||||||
|
Dim key As Variant
|
||||||
|
For Each key In dictCategories.Keys
|
||||||
|
Dim cat As clsCategory
|
||||||
|
Set cat = dictCategories(key)
|
||||||
|
|
||||||
|
' 检查该类别是否需要
|
||||||
|
If cat.IsRequiredForModel(conditions) Then
|
||||||
|
result.Add cat
|
||||||
|
End If
|
||||||
|
Next key
|
||||||
|
|
||||||
|
Set GetRequiredCategories = result
|
||||||
|
End Function
|
||||||
|
```
|
||||||
|
|
||||||
|
### C. 新增 clsCategory.IsRequiredForModel 方法(伪代码)
|
||||||
|
|
||||||
|
```vba
|
||||||
|
Public Function IsRequiredForModel(conditions As Object) As Boolean
|
||||||
|
If Me.CategorySelectCondition = "" Then
|
||||||
|
' 选用条件为空,总是需要
|
||||||
|
IsRequiredForModel = True
|
||||||
|
Else
|
||||||
|
' 需要用 conditions 去匹配 CategorySelectCondition
|
||||||
|
Dim matcher As New clsConditionMatcher
|
||||||
|
IsRequiredForModel = matcher.IsMatch(Me.CategorySelectCondition, conditions)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**文档版本**:v1.0
|
||||||
|
**创建日期**:2026-01-29
|
||||||
|
**最后更新**:2026-01-29
|
||||||
Reference in New Issue
Block a user