Compare commits

..

14 Commits

Author SHA1 Message Date
Misaka_Company
c597a6cf75 feat(error-analysis): add configurable header row for report layout
Add OUTPUT_HEADER_ROW constant (row 10) to allow space above error report:
- Preserves rows 1-9 for title, instructions, or summary information
- Error report header now starts at row 10
- Only clears data from header row downwards (preserves upper content)
- Removed AutoFit to maintain custom column widths

This provides flexibility for report formatting and allows
additional context to be added above the error analysis data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 16:48:39 +08:00
Misaka_Company
1e995b5485 feat(error-analysis): split unmatched parameters into name and value columns
Enhance error analysis report to display unmatched parameters in separate columns:
- "未匹配参数" (parameter name) - shows which parameter caused the mismatch
- "未匹配参数值" (parameter value) - shows the actual value that didn't match

This separation provides clearer visibility into specific parameter conflicts
and makes troubleshooting BOM matching issues easier.

Changes:
- Modified CreateErrorRowArray to accept separate param and value arguments
- Updated FindUnmatchedParameter to return key:value pairs
- Enhanced output formatting to highlight both columns in red/bold
- Updated header to include new "未匹配参数值" column

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:05:00 +08:00
Misaka_Company
d4f8e77c66 🎨 style: standardize parameter naming to camelCase
Refactor parameter and property names from PascalCase to camelCase
for consistent naming conventions across VBA modules.

Changes:
- Conditions → conditions (property and parameters)
- Update all references across 7 modules
- Maintain functional behavior while improving code readability

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:47:17 +08:00
Misaka_Company
7d59e489b1 feat(BIP): add weighted parameter error analysis algorithm
Implement intelligent BOM error analysis module that:
- Extracts and analyzes failed order matching attempts
- Uses feature-weight algorithm (azxs=10000, bkxs=1000, etc.) for accurate parameter conflict detection
- Automatically traces unmatched parameters to root cause
- Splits multi-error results into separate rows for detailed analysis
- Exposes BomExtractor data pool via GetAllItems() for external analysis

This solves fuzzy tie problems that caused parameter false positives in previous matching logic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:41:58 +08:00
Misaka_Company
658f8a7160 refactor(BIP): skip invalid orders and remove remarks column
- Add logic to skip orders where [是否领料] = "否"
- Remove remarks column from BIP output (10 columns → 9 columns)
- Simplify error handling by removing detailed error notes
- Update data reading range to include column G (是否领料)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:15:02 +08:00
Misaka_Company
04099d25bc docs: add extraction remarks data source documentation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 09:58:29 +08:00
Misaka_Company
9b543ab466 refactor: split order validation and processing into separate functions
- Add OrderValidationModule for checking order material validity
- Modify Sheet9 to separate validation (CommandButton1) from processing (CommandButton5)
- Support filtered data validation with performance optimization

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 09:57:48 +08:00
Misaka_Company
36c00befa5 refactor: support filtered data processing and optimize performance
- Add clear data button functionality to Sheet9
- Refactor AccessDataModule to safely handle filtered data with memory array optimization
- Refactor BIPUploadModule to process only visible rows with screen updating optimization
- Refactor ComponentInventoryCheckModule to support filtered data and improve performance
- Refactor MainModule to handle filtered data and remove '代号' field
- Add RestoreAppStatus helper for better application state management
- Improve overall performance by using memory arrays instead of cell-by-cell operations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 13:12:31 +08:00
Misaka_Company
596a2d0ad2 chore: clean up whitespace and update gitignore
- Remove trailing whitespace in BIPUploadModule.bas
- Add missing newline at end of BomItem.cls
- Update .gitignore: rename Claude to AI Agent, add .sisyphus/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 17:23:59 +08:00
Misaka_Company
7e2710da3f feat(BIP): dynamic row number from platform config column L 2026-03-12 14:48:54 +08:00
Misaka_Company
c119d2370b feat(BomItem): add BipRowNumberBase property from column L 2026-03-12 14:40:40 +08:00
Misaka_Company
1747af046b feat: add Access data integration and total queue number column
- Add new AccessDataModule for fetching data from Access database based on total queue number
- Add CommandButton3_Click handler in Sheet9 for Access data fetch
- Add support for new "总排号" (Total Queue Number) column at column A
- Adjust all column indices to accommodate the new column (shifted by +1)
- Standardize code style: Collection, Count, Quantity, ProductModel, Description
- Fix inventory check result to write to correct column (F instead of E)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 13:53:28 +08:00
Misaka_Company
787b3f56ab docs: add AGENTS.md documentation files
Add comprehensive AGENTS.md documentation files across the project structure to document the agent architecture and capabilities.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 14:05:56 +08:00
Misaka_Company
318d1c31f7 chore: update .gitignore to include .xlsx files 2026-03-11 09:57:50 +08:00
20 changed files with 5025 additions and 2 deletions

7
.gitignore vendored
View File

@@ -6,11 +6,16 @@ log
*.spec
temp
# Claude 临时文件
# AI Agent 临时文件
.claude/
.sisyphus/
tmpclaude-*
*.log
*workspace*
*.png
data/
*.xlsm
*.xlsx

131
AGENTS.md Normal file
View File

@@ -0,0 +1,131 @@
# AutoBOM Knowledge Base
**Generated:** 2026-03-11
**Stack:** VBA (Excel) + Python automation
## OVERVIEW
VBA-based BOM extraction system that parses product model strings and matches components from a platform configuration list. Core workflow: parse model → extract conditions → match BOM items → export results.
## STRUCTURE
```
AutoBOM/
├── VBA/ # Core VBA modules
│ ├── ClassModules/ # Data models + logic classes
│ ├── Modules/ # Entry points + workflows
│ └── DocumentModules/ # Sheet-specific code
├── docs/ # Flowcharts + execution plans
├── reference_docs/ # External product references
└── *.xlsm/*.xlsx # Excel workbooks with embedded macros
```
## WHERE TO LOOK
| Task | Location | Notes |
|------|----------|-------|
| Parse product model | `VBA/ClassModules/ProductModelParser.cls` | Extracts conditions from model string |
| Match BOM items | `VBA/ClassModules/BomExtractor.cls` | Core matching logic with assembly hierarchy |
| BOM data model | `VBA/ClassModules/BomItem.cls` | Row structure for platform configuration |
| Condition evaluation | `VBA/ClassModules/ConditionEvaluator.cls` | Evaluates selection conditions |
| Main workflow | `VBA/Modules/MainModule.bas` | `ProcessProductModels()` entry point |
| BIP upload | `VBA/Modules/BIPUploadModule.bas` | Upload results to BIP system |
| Component check | `VBA/Modules/ComponentInventoryCheckModule.bas` | Inventory verification |
| Excel→Markdown | `.claude/skills/excel-to-markdown/scripts/excel_to_markdown.py` | Python utility |
## CODE MAP
### Core Classes
| Symbol | Type | Location | Role |
|--------|------|----------|------|
| `BomExtractor` | Class | `ClassModules/BomExtractor.cls` | Extracts matching BOM items based on conditions |
| `BomItem` | Class | `ClassModules/BomItem.cls` | Data model for BOM row (11 fields) |
| `ProductModelParser` | Class | `ClassModules/ProductModelParser.cls` | Parses model string like `Y-100-M203.316SS` |
| `ConditionEvaluator` | Class | `ClassModules/ConditionEvaluator.cls` | Evaluates condition expressions |
| `MainModule` | Module | `Modules/MainModule.bas` | Orchestrates end-to-end workflow |
### Key Functions
| Function | Location | Description |
|----------|----------|-------------|
| `ProcessProductModels()` | `MainModule.bas` | Main entry point |
| `ExtractBom(conditions)` | `BomExtractor.cls` | Returns matched `Collection` of `BomItem` |
| `Parse(modelString)` | `ProductModelParser.cls` | Extracts `azxs`, `bkxs`, `gclj`, `jycz`, `lcfw`, `fjgn` |
| `Evaluate(condition, conditions)` | `ConditionEvaluator.cls` | Returns `Boolean` match result |
## CONVENTIONS
### VBA Structure
- **ClassModules**: Domain logic classes (`*.cls`)
- **Modules**: Procedural workflows (`*.bas`)
- **DocumentModules**: Sheet-specific event handlers (`Sheet9.cls`)
### Model String Format
```
[Header]-[Spec1].[Spec2].[Spec3].[Spec4].[Spec5]|[Detail1]|[Detail2]
Example: Y-100-M203.316SS.L100.N2
```
### Condition Config
```vba
Private Const CONDITION_CONFIG = "azxs安装形式|bkxs表壳形式|gclj过程连接|jycz接液材质|lcfw量程范围|fjgn附加功能"
```
## ANTI-PATTERNS (THIS PROJECT)
- **DO NOT** modify row indexing in `BomExtractor.LoadBomData()` — starts at row 4 (header is row 3)
- **DO NOT** change `BomItem` field order — hardcoded column mapping in `LoadFromRow()`
- **DO NOT** remove `On Error Resume Next` in `BomItem.LoadFromRow()` — handles type conversion failures
- **NEVER** skip assembly logic in `ApplyAssemblyLogic()` — handles parent/child component hierarchy
- **NEVER** hardcode workbook names — use `ThisWorkbook` reference
## UNIQUE STYLES
### Assembly Logic (Parent/Child Override)
If parent category matches (e.g., "部件") AND all children match (e.g., "接头", "弹性元件"), parent overrides children in output. Controlled by `ApplyAssemblyLogic()`.
### Batch Output Pattern
Results collected in `Collection` → converted to 2D array → single `Range.Value` write for performance.
### Condition Expression Syntax
```vba
' Format: "field=value" or "field1=value1|field2=value2"
' Example: "azxs=M|azxs=L" (vertical OR horizontal)
```
## COMMANDS
### Python Virtual Environment
```bash
# Activate (Windows)
.venv\Scripts\activate
# Run Excel→Markdown converter
python .claude/skills/excel-to-markdown/scripts/excel_to_markdown.py input.xlsx -o output.md
```
### Excel Macros
```
1. Open *.xlsm workbook
2. Press Alt+F11 to open VBA editor
3. Run: MainModule.ProcessProductModels
```
## NOTES
### Workbook Requirements
- Input sheet: `产品订单` (product orders)
- BOM sheet: `平台配置清单` (platform configuration)
- Output sheet: `BOM 提取结果` (auto-created)
### Git Configuration
`.gitignore` excludes `*.xlsm`, `*.xlsx`, `*.png`, `.venv/`, `build/`, `dist/`. Binary Excel files not tracked.
### ConditionEvaluator Dependencies
VBA `Scripting.Dictionary` required (Windows only). Not compatible with Mac Excel.
### Error Handling
- Parse failures → `extractNote = "解析失败:..."`
- No matches → `extractNote = "未匹配到任何物料"`
- Multi-match → logged but outputs all (data quality flag)

95
VBA/AGENTS.md Normal file
View File

@@ -0,0 +1,95 @@
# VBA Module Knowledge Base
**Scope:** `VBA/` directory - Core automation logic
## OVERVIEW
VBA modules for BOM extraction: ClassModules (domain logic), Modules (workflows), DocumentModules (sheet events).
## STRUCTURE
```
VBA/
├── ClassModules/ # 4 classes: BomExtractor, BomItem, ProductModelParser, ConditionEvaluator
├── Modules/ # 4 modules: MainModule, BIPUploadModule, ComponentInventoryCheckModule, TestModule
├── DocumentModules/ # Sheet9.cls (event handlers)
└── vba_metadata.json # Module metadata
```
## WHERE TO LOOK
| Task | Location | Notes |
|------|----------|-------|
| Main entry point | `Modules/MainModule.bas` | `ProcessProductModels()` |
| BOM extraction | `ClassModules/BomExtractor.cls` | `ExtractBom()` with assembly logic |
| Model parsing | `ClassModules/ProductModelParser.cls` | `Parse()` extracts 6 conditions |
| Condition logic | `ClassModules/ConditionEvaluator.cls` | Expression evaluation |
| BIP upload | `Modules/BIPUploadModule.bas` | Upload to BIP system |
| Inventory check | `Modules/ComponentInventoryCheckModule.bas` | Component verification |
## CODE MAP
| Symbol | Type | Location | Role |
|--------|------|----------|------|
| `ProcessProductModels()` | Sub | `MainModule.bas` | Main entry point |
| `ExtractBom()` | Function | `BomExtractor.cls` | Core matching logic |
| `Parse()` | Function | `ProductModelParser.cls` | Model string parser |
| `Evaluate()` | Function | `ConditionEvaluator.cls` | Condition evaluator |
| `LoadFromRow()` | Sub | `BomItem.cls` | Row data loader |
## CONVENTIONS
### Module Organization
- **ClassModules**: `*.cls` files with `Option Explicit`, public methods, private state
- **Modules**: `*.bas` files with public subs, helper functions
- **DocumentModules**: Sheet-specific event handlers (e.g., `Sheet9.cls`)
### Error Handling Pattern
```vba
On Error GoTo ErrorHandler
' ... logic ...
Exit Sub
ErrorHandler:
' Handle error
```
### Data Loading
- `BomExtractor.LoadBomData()` starts at row 4 (row 3 is header)
- `BomItem.LoadFromRow()` uses `On Error Resume Next` for type conversions
## ANTI-PATTERNS (THIS PROJECT)
- **NEVER** change row indexing in `LoadBomData()` — hardcoded to start at row 4
- **NEVER** reorder `BomItem` fields — column mapping is hardcoded (11 fields)
- **NEVER** remove `On Error Resume Next` in `LoadFromRow()` — handles type conversion
- **NEVER** bypass `ApplyAssemblyLogic()` — parent/child override is critical
- **NEVER** hardcode workbook names — always use `ThisWorkbook`
## UNIQUE STYLES
### Assembly Logic (总成逻辑)
Parent category overrides children if:
1. Parent matches exactly 1 item
2. All children match at least 1 item
3. Result: parent output, children hidden
### Batch Output Pattern
```vba
' Collect in Collection
' Convert to 2D array
' Single Range.Value write
```
### Condition Syntax
- Format: `"field=value|field2=value2"` (pipe = OR)
- Example: `"azxs=M|azxs=L"` (vertical OR horizontal installation)
## NOTES
### Scripting.Dictionary Dependency
All classes use `CreateObject("Scripting.Dictionary")` - Windows only, not Mac-compatible.
### Workbook Sheets Required
- `产品订单` - Input orders
- `平台配置清单` - BOM configuration (starts at row 4)
- `BOM 提取结果` - Auto-created output

148
VBA/ClassModules/AGENTS.md Normal file
View File

@@ -0,0 +1,148 @@
# ClassModules Knowledge Base
**Scope:** `VBA/ClassModules/` - Domain logic classes
## OVERVIEW
4 classes: `ProductModelParser` (parse model strings), `BomItem` (data model), `ConditionEvaluator` (evaluate conditions), `BomExtractor` (core matching logic).
## STRUCTURE
```
ClassModules/
├── ProductModelParser.cls # Parse model strings → extract 6 conditions
├── BomItem.cls # BOM data model (11 fields)
├── ConditionEvaluator.cls # Evaluate condition expressions
└── BomExtractor.cls # Core matching + assembly logic
```
## WHERE TO LOOK
| Task | Location | Notes |
|------|----------|-------|
| Parse model string | `ProductModelParser.cls` | `Parse()` method |
| Extract conditions | `ProductModelParser.cls` | Returns `azxs`, `bkxs`, `gclj`, `jycz`, `lcfw`, `fjgn` |
| Load BOM row | `BomItem.cls` | `LoadFromRow()` with type conversion |
| Evaluate condition | `ConditionEvaluator.cls` | `Evaluate(condition, conditions)` |
| Match BOM items | `BomExtractor.cls` | `ExtractBom()` with 4-step process |
| Assembly logic | `BomExtractor.cls` | `ApplyAssemblyLogic()` parent/child override |
## CODE MAP
### ProductModelParser
| Method | Role |
|--------|------|
| `Parse(modelString)` | Entry point, validates format |
| `ParseHeader()` | Splits model string by `-` and `.` |
| `ExtractConnectionAndMaterial()` | Separates `gclj` and `jycz` from code |
| `ExtractAdditionalFeatures()` | Removes oil-fill type (Y+digit) from `fjgn` |
### BomItem
| Property | Type | Column |
|----------|------|--------|
| `RowNumber` | Long | A |
| `Module` | String | B |
| `code` | String | C |
| `Name` | String | D |
| `quantity` | Double | E |
| `SelectCondition` | String | F |
| `Remark` | String | G |
| `category` | String | H |
| `ParentCategory` | String | I |
| `CategoryCondition` | String | J |
| `Code66` | String | K |
### BomExtractor
| Method | Role |
|--------|------|
| `LoadBomData()` | Loads from row 4 (header row 3) |
| `ExtractBom(conditions)` | 4-step: determine categories → match → apply assembly → validate |
| `ApplyAssemblyLogic()` | Parent overrides children if all match |
| `ValidateResult()` |双向覆盖检查 (parent/child coverage) |
## CONVENTIONS
### Class Structure
```vba
Option Explicit
' Private state
Private pPropertyName As Type
' Initialize
Private Sub Class_Initialize()
Set pProperty = New Collection
End Sub
' Public methods
Public Function MethodName() As ReturnType
On Error GoTo ErrorHandler
' Logic
Exit Function
ErrorHandler:
' Handle
End Function
```
### Condition Dictionary
All condition methods accept `Scripting.Dictionary` with keys: `azxs`, `bkxs`, `gclj`, `jycz`, `lcfw`, `fjgn`.
### Error Collection Pattern
```vba
Private pErrorMessages As Collection
Public Function GetErrorSummary() As String
' Join all errors with "; "
End Function
```
## ANTI-PATTERNS (THIS PROJECT)
- **NEVER** change `BomItem.LoadFromRow()` column indices — hardcoded to match platform configuration sheet
- **NEVER** remove `On Error Resume Next` in `LoadFromRow()` — type conversions fail gracefully
- **NEVER** skip validation in `ValidateResult()` —双向覆盖检查 prevents false negatives
- **DO NOT** modify `BomExtractor.LoadBomData()` row start — must be row 4 (row 3 is header)
## UNIQUE STYLES
### Assembly Logic Algorithm
```vba
' 1. Build parent→child map from CategoryHierarchy
' 2. Count matches per category
' 3. If parent=1 AND all children≥1 → parent covers children
' 4. Output parent only, hide children
```
### Model String Format
```
[Header]-[Spec1].[Spec2].[Spec3].[Spec4].[Spec5]|[Detail1]|[Detail2]
Example: Y-100-M203.316SS.L100.N2
Parsed conditions:
- azxs (安装形式): M (vertical)
- bkxs (表壳形式): 3
- gclj (过程连接): M20
- jycz (接液材质): 3 (316SS)
- lcfw (量程范围): 0.L100
- fjgn (附加功能): N2 (from .316SS.L100.N2)
```
### Condition Expression Syntax
```vba
' Single: "field=value"
' OR: "field1=value1|field2=value2"
' Example: "azxs=M|azxs=L" → vertical OR horizontal
```
## NOTES
### Category Hierarchy
Built from `ParentCategory` field (column I). Example:
- "接头" → "部件"
- "弹性元件" → "部件"
- Result: if "部件" matches AND both children match → output "部件" only
### Type Conversion
`BomItem.LoadFromRow()` uses `CLng()`, `CStr()`, `CDbl()` with `On Error Resume Next` — invalid conversions become 0 or empty string.
### Scripting.Dictionary
Windows-only. All dictionary usage via `CreateObject("Scripting.Dictionary")`.

View File

@@ -0,0 +1,490 @@
'=====================================================================
' 类名: BomExtractor
' 功能: BOM提取器,从平台配置清单中提取匹配的物料
'=====================================================================
Option Explicit
Private pWorksheet As Worksheet
Private pConditionEvaluator As ConditionEvaluator
Private pAllItems As Collection ' 所有BOM项
Private pMatchedItems As Collection ' 匹配的BOM项
Private pRequiredCategories As Collection ' 需要的类别
Private pCategoryHierarchy As Object ' 类别层次结构 Dictionary(子类别->父类别)
Private pErrorMessages As Collection
Private pExcludeCategories As Collection ' 需要排除的类别
'=====================================================================
' 方法: Class_Initialize
' 功能: 初始化类
'=====================================================================
Private Sub Class_Initialize()
Set pConditionEvaluator = New ConditionEvaluator
Set pAllItems = New Collection
Set pMatchedItems = New Collection
Set pRequiredCategories = New Collection
Set pCategoryHierarchy = CreateObject("Scripting.Dictionary")
Set pErrorMessages = New Collection
Set pExcludeCategories = New Collection
End Sub
'=====================================================================
' 方法: SetWorksheet
' 功能: 设置BOM数据源工作表
' 参数: ws - 工作表对象
'=====================================================================
Public Sub SetWorksheet(ws As Worksheet)
Set pWorksheet = ws
End Sub
'=====================================================================
' 方法: LoadBomData
' 功能: 加载BOM数据
' 返回: Boolean - 成功返回True
'=====================================================================
Public Function LoadBomData() As Boolean
On Error GoTo ErrorHandler
If pWorksheet Is Nothing Then
pErrorMessages.Add "未设置工作表"
LoadBomData = False
Exit Function
End If
'
Set pAllItems = New Collection
Set pCategoryHierarchy = CreateObject("Scripting.Dictionary")
' 从第4行开始读取(第3行是表头)
Dim lastRow As Long
lastRow = pWorksheet.Cells(pWorksheet.Rows.count, 1).End(xlUp).row
Dim i As Long
Dim item As BomItem
For i = 4 To lastRow
'
If Trim(pWorksheet.Cells(i, 1).value) <> "" Then
Set item = New BomItem
item.LoadFromRow pWorksheet, i
' 只添加有效物料(类别不为空)
If item.IsValidItem Then
pAllItems.Add item
' 构建类别层次结构
If item.HasParentCategory Then
If Not pCategoryHierarchy.Exists(item.category) Then
pCategoryHierarchy.Add item.category, item.ParentCategory
End If
End If
Else
' ,
pAllItems.Add item
End If
End If
Next i
LoadBomData = True
Exit Function
ErrorHandler:
pErrorMessages.Add "加载BOM数据异常: " & Err.Description
LoadBomData = False
End Function
'=====================================================================
' 方法: SetExcludeCategories
' 功能: 设置需要排除的类别
' 参数: categories - 类别集合
'=====================================================================
Public Sub SetExcludeCategories(categories As Collection)
Set pExcludeCategories = categories
End Sub
'=====================================================================
' 方法: ClearExcludeCategories
' 功能: 清空排除类别列表
'=====================================================================
Public Sub ClearExcludeCategories()
Set pExcludeCategories = New Collection
End Sub
'=====================================================================
' 方法: ClearErrorMessages
' 功能: 清空错误信息列表
'=====================================================================
Public Sub ClearErrorMessages()
Set pErrorMessages = New Collection
End Sub
'=====================================================================
' 方法: ExtractBom
' 功能: 根据产品条件提取BOM
' 参数: productConditions - 产品条件字典
' 返回: Collection - 匹配的BOM项集合
'=====================================================================
Public Function ExtractBom(productConditions As Object) As Collection
On Error GoTo ErrorHandler
' 清空结果
Set pMatchedItems = New Collection
Set pRequiredCategories = New Collection
Set pErrorMessages = New Collection
' 第一步:确定需要的类别
DetermineRequiredCategories productConditions
' 第二步:匹配物料
MatchItems productConditions
' 第三步:应用总成逻辑(父类别优先)
ApplyAssemblyLogic
' :
ValidateResult
Set ExtractBom = pMatchedItems
Exit Function
ErrorHandler:
pErrorMessages.Add "提取BOM异常: " & Err.Description
Set ExtractBom = pMatchedItems
End Function
'=====================================================================
' 方法: DetermineRequiredCategories
' 功能: 确定需要的类别
' 参数: productConditions - 产品条件字典
'=====================================================================
Private Sub DetermineRequiredCategories(productConditions As Object)
Dim item As BomItem
Dim uniqueCategories As Object
Set uniqueCategories = CreateObject("Scripting.Dictionary")
' 遍历所有有效物料,获取唯一类别
For Each item In pAllItems
If item.IsValidItem Then
' 检查是否在排除列表中
Dim isExcluded As Boolean
isExcluded = False
Dim excludeCat As Variant
For Each excludeCat In pExcludeCategories
If item.category = CStr(excludeCat) Then
isExcluded = True
Exit For
End If
Next excludeCat
' 如果不在排除列表中,继续处理
If Not isExcluded Then
'
Dim categoryRequired As Boolean
If Trim(item.CategoryCondition) = "" Then
' 无条件,必需类别
categoryRequired = True
Else
' 有条件,评估条件
categoryRequired = pConditionEvaluator.Evaluate(item.CategoryCondition, productConditions)
End If
If categoryRequired Then
If Not uniqueCategories.Exists(item.category) Then
uniqueCategories.Add item.category, True
pRequiredCategories.Add item.category
End If
End If
End If
End If
Next item
End Sub
'=====================================================================
' 方法: MatchItems
' 功能: 匹配物料
' 修改说明: 当未匹配到物料(Count=0)时不再立即报错,而是留给 ValidateResult
' 进行综合判断(因为可能存在父子覆盖或散件满足的情况)。
'=====================================================================
Private Sub MatchItems(productConditions As Object)
Dim item As BomItem
Dim category As Variant
' 遍历每个需要的类别
For Each category In pRequiredCategories
Dim categoryMatches As Collection
Set categoryMatches = New Collection
' 查找该类别下所有匹配的物料
For Each item In pAllItems
If item.category = category Then
'
Dim matched As Boolean
If Trim(item.SelectCondition) = "" Then
' 无选择条件,无条件匹配
matched = True
Else
' 有选择条件,评估
matched = pConditionEvaluator.Evaluate(item.SelectCondition, productConditions)
End If
If matched Then
item.IsMatched = True
categoryMatches.Add item
End If
End If
Next item
' 检查匹配结果
If categoryMatches.count = 0 Then
' ---------------------------------------------------------
' CHANGE: 这里不再立即报错
' 理由: 未匹配到可能是正常的(例如:父类别缺失但子类别齐全,或者子类别被父类别覆盖)
' 具体的缺失检查移交到 ValidateResult 方法中统一处理
' ---------------------------------------------------------
ElseIf categoryMatches.count = 1 Then
' 正常:匹配到1条
pMatchedItems.Add categoryMatches(1)
Else
' : ()
Dim multiMsg As String
multiMsg = "类别[" & category & "]匹配到多条物料(" & categoryMatches.count & "条)"
pErrorMessages.Add multiMsg
' 临时处理:输出所有匹配的
Dim tempItem As BomItem
For Each tempItem In categoryMatches
tempItem.MatchError = multiMsg
pMatchedItems.Add tempItem
Next tempItem
End If
Next category
End Sub
'=====================================================================
' 方法: ApplyAssemblyLogic
' 功能: 应用总成逻辑(父类别优先)
' 修改说明: 重构了算法,解决了以下问题:
' 1. 当某类别匹配到多条物料时,能够保留所有匹配项,而不是只输出第一条。
' 2. 解决了因列表顺序不同导致子类别可能未被正确覆盖的潜在隐患。
'=====================================================================
Private Sub ApplyAssemblyLogic()
' 1. ->
Dim parentToChildren As Object
Set parentToChildren = CreateObject("Scripting.Dictionary")
Dim parentCat As Variant
Dim childCat As Variant
Dim key As Variant
For Each key In pCategoryHierarchy.Keys
childCat = CStr(key)
parentCat = pCategoryHierarchy(key)
If Not parentToChildren.Exists(parentCat) Then
Set parentToChildren(parentCat) = CreateObject("Scripting.Dictionary")
End If
parentToChildren(parentCat)(childCat) = True
Next key
' 2.
Dim categoryCounts As Object
Set categoryCounts = CreateObject("Scripting.Dictionary")
Dim item As BomItem
For Each item In pMatchedItems
If Not categoryCounts.Exists(item.category) Then
categoryCounts(item.category) = 0
End If
categoryCounts(item.category) = categoryCounts(item.category) + 1
Next item
' 3. "总成优先"
' 1
Dim coveredCategories As Object
Set coveredCategories = CreateObject("Scripting.Dictionary")
Dim satisfiedParentItems As Collection
Set satisfiedParentItems = New Collection
For Each parentCat In parentToChildren.Keys
' 只有当该父类别确实有匹配物料时才进行检查
If categoryCounts.Exists(parentCat) Then
' 条件1: 父类别只匹配到1条 (如果匹配多条,存在歧义,不应用覆盖逻辑,而是全部输出以供排查)
If categoryCounts(parentCat) = 1 Then
' 条件2: 所有子类别都匹配到(至少1条)
Dim childrenMatched As Boolean
childrenMatched = True
For Each childCat In parentToChildren(parentCat).Keys
If Not categoryCounts.Exists(childCat) Then
childrenMatched = False
Exit For
End If
Next childCat
If childrenMatched Then
' 满足总成条件: 找到那个父类别项
Dim pItem As BomItem
For Each item In pMatchedItems
If item.category = parentCat Then
satisfiedParentItems.Add item
Exit For
End If
Next item
' 标记覆盖的类别(父类别自己和所有子类别都标记为已处理)
' 这样做的目的是在步骤4中我们会先添加 satisfiedParentItems
' coveredCategories "父类覆盖子类""父类不重复添加"
coveredCategories(parentCat) = True
For Each childCat In parentToChildren(parentCat).Keys
coveredCategories(childCat) = True
Next childCat
End If
End If
End If
Next parentCat
' 4. 构建新的结果集
Dim newMatchedItems As Collection
Set newMatchedItems = New Collection
' 4.1 先添加满足条件的父类别项 (总成)
For Each item In satisfiedParentItems
newMatchedItems.Add item
Next item
' 4.2 再添加未被覆盖的其他项 (散件 或 有问题的多条匹配项)
For Each item In pMatchedItems
' "被覆盖"
' 关键点这里不再去重如果同一个Category有5条记录这5条都会因为不在coveredCategories中而被添加
If Not coveredCategories.Exists(item.category) Then
newMatchedItems.Add item
End If
Next item
' 更新结果
Set pMatchedItems = newMatchedItems
End Sub
'=====================================================================
' 方法: ValidateResult
' 功能: 验证提取结果
' 修改说明: 实现了双向覆盖检查:
' 1. 子类别缺失,但父类别存在 -> 视为正常 (总成优先)
' 2. 父类别缺失,但所有必需子类别都存在 -> 视为正常 (散件满足)
'=====================================================================
Private Sub ValidateResult()
'
Dim category As Variant
Dim categoryMatched As Object
Set categoryMatched = CreateObject("Scripting.Dictionary")
' 统计已匹配的类别
Dim item As BomItem
For Each item In pMatchedItems
If Not categoryMatched.Exists(item.category) Then
categoryMatched(item.category) = 0
End If
categoryMatched(item.category) = categoryMatched(item.category) + 1
Next item
' 检查未匹配的类别
For Each category In pRequiredCategories
' 如果结果集中不存在该必需类别
If Not categoryMatched.Exists(category) Then
Dim isResolved As Boolean
isResolved = False
' ---------------------------------------------------------
' 检查 1: 被父类别覆盖 (总成逻辑)
' 场景: 匹配到了部件(父),自动隐藏了接头(子),接头不应报错
' ---------------------------------------------------------
If pCategoryHierarchy.Exists(category) Then
Dim parentCat As String
parentCat = pCategoryHierarchy(category)
If categoryMatched.Exists(parentCat) Then
isResolved = True
End If
End If
' ---------------------------------------------------------
' 检查 2: 被子类别覆盖 (散件逻辑)
' 场景: 部件(父)没匹配到(或被移除),但接头(子)和弹性元件(子)都齐了,部件不应报错
' ---------------------------------------------------------
If Not isResolved Then
Dim hasRequiredChildren As Boolean
Dim allChildrenMatched As Boolean
hasRequiredChildren = False
allChildrenMatched = True
' "必需"category
Dim reqCat As Variant
For Each reqCat In pRequiredCategories
' 如果 reqCat 是当前 category 的子类别
If pCategoryHierarchy.Exists(reqCat) Then
If pCategoryHierarchy(reqCat) = category Then
hasRequiredChildren = True
' 检查这个子类别是否在结果集中
If Not categoryMatched.Exists(reqCat) Then
allChildrenMatched = False
Exit For ' "满足"
End If
End If
End If
Next reqCat
' 只有当存在必需子类别,且它们全都匹配时,才算通过
If hasRequiredChildren And allChildrenMatched Then
isResolved = True
End If
End If
' ---------------------------------------------------------
' 最终判断
' ---------------------------------------------------------
If Not isResolved Then
pErrorMessages.Add "必需类别[" & category & "]未匹配"
End If
End If
Next category
End Sub
'=====================================================================
' 方法: GetErrorMessages
' 功能: 获取错误信息集合
' 返回: Collection
'=====================================================================
Public Function GetErrorMessages() As Collection
Set GetErrorMessages = pErrorMessages
End Function
'=====================================================================
' 方法: GetErrorSummary
' 功能: 获取错误信息摘要
' 返回: String
'=====================================================================
Public Function GetErrorSummary() As String
If pErrorMessages.count = 0 Then
GetErrorSummary = ""
Else
Dim result As String
Dim msg As Variant
For Each msg In pErrorMessages
result = result & CStr(msg) & "; "
Next msg
GetErrorSummary = result
End If
End Function
'=====================================================================
' 方法: GetAllItems
' 功能: 获取所有加载的BOM物料 (暴露数据池供异常分析溯源算法使用)
' 返回: Collection
'=====================================================================
Public Function GetAllItems() As Collection
Set GetAllItems = pAllItems
End Function

View File

@@ -0,0 +1,89 @@
'=====================================================================
' 类名BomItem
' 功能BOM 物料项数据模型
'=====================================================================
Option Explicit
' 物料属性
Public RowNumber As Long ' 行号
Public Module As String ' 模块
Public code As String ' 代号
Public Name As String ' 名称
Public Quantity As Double ' 数量
Public SelectCondition As String ' 选择条件
Public Remark As String ' 备注
Public category As String ' 类别
Public ParentCategory As String ' 上层类别
Public CategoryCondition As String ' 类别选用条件
Public Code66 As String ' 66 代码
Public BipRowNumberBase As Long ' BIP 行号基数
' 匹配状态
Public IsMatched As Boolean ' 是否匹配
Public MatchError As String ' 匹配错误信息
'=====================================================================
' 方法Class_Initialize
' 功能:初始化类
'=====================================================================
Private Sub Class_Initialize()
IsMatched = False
MatchError = ""
BipRowNumberBase = 7000
End Sub
'=====================================================================
' 方法LoadFromRow
' 功能:从工作表行加载数据
' 参数ws - 工作表对象
' row - 行号
'=====================================================================
Public Sub LoadFromRow(ws As Worksheet, row As Long)
On Error Resume Next
Me.RowNumber = CLng(ws.Cells(row, 1).value) ' A 列:行号
Me.Module = CStr(ws.Cells(row, 2).value) ' B 列:模块
Me.code = CStr(ws.Cells(row, 3).value) ' C 列:代号
Me.Name = CStr(ws.Cells(row, 4).value) ' D 列:名称
Me.Quantity = CDbl(ws.Cells(row, 5).value) ' E 列:数量
Me.SelectCondition = CStr(ws.Cells(row, 6).value) ' F 列:选择条件
Me.Remark = CStr(ws.Cells(row, 7).value) ' G 列:备注
Me.category = CStr(ws.Cells(row, 8).value) ' H 列:类别
Me.ParentCategory = CStr(ws.Cells(row, 9).value) ' I 列:上层类别
Me.CategoryCondition = CStr(ws.Cells(row, 10).value) ' J 列:类别选用条件
Me.Code66 = CStr(ws.Cells(row, 11).value) ' K 列66 代码
Me.BipRowNumberBase = CLng(ws.Cells(row, 12).value) ' L 列BIP 行号基数
On Error GoTo 0
End Sub
'=====================================================================
' 方法IsValidItem
' 功能:判断是否为有效物料 (类别字段不为空)
' 返回Boolean
'=====================================================================
Public Function IsValidItem() As Boolean
IsValidItem = (Trim(Me.category) <> "")
End Function
'=====================================================================
' 方法HasParentCategory
' 功能:判断是否有父类别
' 返回Boolean
'=====================================================================
Public Function HasParentCategory() As Boolean
HasParentCategory = (Trim(Me.ParentCategory) <> "")
End Function
'=====================================================================
' 方法ToString
' 功能:转换为字符串描述
' 返回String
'=====================================================================
Public Function ToString() As String
ToString = "行号:" & Me.RowNumber & _
" | 类别:" & Me.category & _
" | 代号:" & Me.code & _
" | 名称:" & Me.Name
End Function

View File

@@ -0,0 +1,224 @@
'=====================================================================
' 类名: ConditionEvaluator
' 功能: 解析和评估条件表达式
'=====================================================================
Option Explicit
'=====================================================================
' 方法: Evaluate
' 功能: 评估条件表达式
' 参数: expression - 条件表达式字符串
' productConditions - 产品条件字典(Dictionary对象)
' 返回: Boolean - True表示条件满足,False表示不满足
' 说明: 支持AND、OR、!=运算符和括号嵌套
' 特殊规则:如果表达式中要求!=某值,而产品条件中不存在该变量,视为满足条件
'=====================================================================
Public Function Evaluate(expression As String, productConditions As Object) As Boolean
On Error GoTo ErrorHandler
'
If Trim(expression) = "" Then
Evaluate = True
Exit Function
End If
' 递归解析表达式
Evaluate = EvaluateExpression(Trim(expression), productConditions)
Exit Function
ErrorHandler:
' 解析错误时返回False
Evaluate = False
End Function
'=====================================================================
' 方法: EvaluateExpression
' 功能: 递归评估表达式
' 参数: expr - 表达式
' conditions - 条件字典
' 返回: Boolean
'=====================================================================
Private Function EvaluateExpression(expr As String, conditions As Object) As Boolean
expr = Trim(expr)
'
If Left(expr, 1) = "(" And Right(expr, 1) = ")" Then
If IsMatchedParentheses(expr) Then
expr = Mid(expr, 2, Len(expr) - 2)
expr = Trim(expr)
End If
End If
' OR()
Dim orResult As Variant
orResult = SplitByOperator(expr, " OR ", conditions)
If Not IsEmpty(orResult) Then
EvaluateExpression = orResult
Exit Function
End If
' AND
Dim andResult As Variant
andResult = SplitByOperator(expr, " AND ", conditions)
If Not IsEmpty(andResult) Then
EvaluateExpression = andResult
Exit Function
End If
' 处理单个条件
EvaluateExpression = EvaluateSingleCondition(expr, conditions)
End Function
'=====================================================================
' 方法: SplitByOperator
' 功能: 按指定运算符分割并评估表达式
' 参数: expr - 表达式
' operator - (" OR " " AND ")
' conditions - 条件字典
' 返回: Variant - 评估结果或Empty
'=====================================================================
Private Function SplitByOperator(expr As String, operator As String, conditions As Object) As Variant
Dim pos As Long
Dim leftPart As String
Dim rightPart As String
Dim depth As Long
Dim i As Long
Dim char As String
'
depth = 0
For i = 1 To Len(expr) - Len(operator) + 1
char = Mid(expr, i, 1)
If char = "(" Then
depth = depth + 1
ElseIf char = ")" Then
depth = depth - 1
ElseIf depth = 0 Then
' 检查是否匹配运算符
If Mid(expr, i, Len(operator)) = operator Then
leftPart = Trim(Left(expr, i - 1))
rightPart = Trim(Mid(expr, i + Len(operator)))
'
If operator = " OR " Then
SplitByOperator = EvaluateExpression(leftPart, conditions) Or _
EvaluateExpression(rightPart, conditions)
ElseIf operator = " AND " Then
SplitByOperator = EvaluateExpression(leftPart, conditions) And _
EvaluateExpression(rightPart, conditions)
End If
Exit Function
End If
End If
Next i
' 未找到运算符
SplitByOperator = Empty
End Function
'=====================================================================
' 方法: EvaluateSingleCondition
' 功能: 评估单个条件(如 azxs=A0 或 azxs!=AH)
' 参数: condition - 单个条件字符串
' conditions - 条件字典
' 返回: Boolean
'=====================================================================
Private Function EvaluateSingleCondition(condition As String, conditions As Object) As Boolean
Dim varName As String
Dim operator As String
Dim value As String
Dim actualValue As String
condition = Trim(condition)
' !=
If InStr(condition, "!=") > 0 Then
Dim parts() As String
parts = Split(condition, "!=")
If UBound(parts) >= 1 Then
varName = Trim(parts(0))
value = Trim(parts(1))
' 特殊规则:如果产品条件中不存在该变量,视为满足!=条件
If Not conditions.Exists(varName) Then
EvaluateSingleCondition = True
Else
actualValue = conditions(varName)
' fjgn
If varName = "fjgn" Then
' fjgn!=N1检查actualValue中是否不包含value
EvaluateSingleCondition = (InStr(actualValue, value) = 0)
Else
' 其他字段使用精确匹配
EvaluateSingleCondition = (actualValue <> value)
End If
End If
Exit Function
End If
End If
' =
If InStr(condition, "=") > 0 Then
Dim eqParts() As String
eqParts = Split(condition, "=")
If UBound(eqParts) >= 1 Then
varName = Trim(eqParts(0))
value = Trim(eqParts(1))
If Not conditions.Exists(varName) Then
EvaluateSingleCondition = False
Else
actualValue = conditions(varName)
' fjgn
If varName = "fjgn" Then
' fjgn=N1检查actualValue中是否包含value
EvaluateSingleCondition = (InStr(actualValue, value) > 0)
Else
' 其他字段使用精确匹配
EvaluateSingleCondition = (actualValue = value)
End If
End If
Exit Function
End If
End If
' 无法解析的条件返回False
EvaluateSingleCondition = False
End Function
'=====================================================================
' 方法: IsMatchedParentheses
' 功能: 检查字符串最外层括号是否匹配
' 参数: str - 字符串
' 返回: Boolean
'=====================================================================
Private Function IsMatchedParentheses(str As String) As Boolean
If Left(str, 1) <> "(" Or Right(str, 1) <> ")" Then
IsMatchedParentheses = False
Exit Function
End If
Dim depth As Long
Dim i As Long
depth = 0
For i = 1 To Len(str)
If Mid(str, i, 1) = "(" Then
depth = depth + 1
ElseIf Mid(str, i, 1) = ")" Then
depth = depth - 1
End If
' ,
If depth = 0 And i < Len(str) Then
IsMatchedParentheses = False
Exit Function
End If
Next i
IsMatchedParentheses = (depth = 0)
End Function

View File

@@ -0,0 +1,303 @@
'=====================================================================
' 类名: ProductModelParser
' 功能: 解析产品型号并提取物料选择条件
'=====================================================================
Option Explicit
Private pFullModel As String
Private pHeaderModel As String
Private pConditions As Object ' Dictionary
Private pErrorMessage As String
'=====================================================================
' 属性: FullModel - 完整产品型号
'=====================================================================
Public Property Get FullModel() As String
FullModel = pFullModel
End Property
Public Property Let FullModel(value As String)
pFullModel = value
End Property
'=====================================================================
' 属性: HeaderModel - 表头型号
'=====================================================================
Public Property Get HeaderModel() As String
HeaderModel = pHeaderModel
End Property
'=====================================================================
' 属性: Conditions - 提取的条件字典
'=====================================================================
Public Property Get conditions() As Object
Set conditions = pConditions
End Property
'=====================================================================
' 属性: ErrorMessage - 错误信息
'=====================================================================
Public Property Get ErrorMessage() As String
ErrorMessage = pErrorMessage
End Property
'=====================================================================
' 方法: Class_Initialize
' 功能: 初始化类
'=====================================================================
Private Sub Class_Initialize()
Set pConditions = CreateObject("Scripting.Dictionary")
pErrorMessage = ""
End Sub
'=====================================================================
' 方法: Parse
' 功能: 解析产品型号
' 参数: modelString - 完整产品型号字符串
' 返回: Boolean - True表示解析成功,False表示失败
'=====================================================================
Public Function Parse(modelString As String) As Boolean
On Error GoTo ErrorHandler
pFullModel = Trim(modelString)
pConditions.RemoveAll
pErrorMessage = ""
' (|)
Dim parts() As String
parts = Split(pFullModel, "|")
If UBound(parts) < 0 Then
pErrorMessage = "型号格式错误:缺少表头部分"
Parse = False
Exit Function
End If
pHeaderModel = Trim(parts(0))
'
If Not ParseHeader() Then
Parse = False
Exit Function
End If
Parse = True
Exit Function
ErrorHandler:
pErrorMessage = "解析异常: " & Err.Description
Parse = False
End Function
'=====================================================================
' 方法: ParseHeader
' 功能: 解析表头型号结构
' 返回: Boolean - True表示解析成功
' 说明: 表头结构 [型号]-[公称外径].[安装形式].[壳体形式].[过程连接&接液材质].[量程范围].[仪表特性]
'=====================================================================
Private Function ParseHeader() As Boolean
On Error GoTo ErrorHandler
'
Dim dashParts() As String
dashParts = Split(pHeaderModel, "-")
If UBound(dashParts) < 1 Then
pErrorMessage = "表头格式错误:缺少'-'分隔符"
ParseHeader = False
Exit Function
End If
' (.)
Dim dotParts() As String
dotParts = Split(dashParts(1), ".")
' :5()
If UBound(dotParts) < 4 Then
pErrorMessage = "表头结构不完整:缺少必要字段"
ParseHeader = False
Exit Function
End If
' 提取各个条件
' - 2(1)
Dim azxs As String
azxs = Trim(dotParts(1))
pConditions.Add "azxs", azxs
' - 3(2)
Dim bkxs As String
bkxs = Trim(dotParts(2))
pConditions.Add "bkxs", bkxs
' - 4(3)
Dim connectionCode As String
connectionCode = Trim(dotParts(3))
Dim gclj As String
Dim jycz As String
If Not ExtractConnectionAndMaterial(connectionCode, gclj, jycz) Then
ParseHeader = False
Exit Function
End If
pConditions.Add "gclj", gclj
pConditions.Add "jycz", jycz
' - 5(4)
Dim lcfw As String
lcfw = Trim(dotParts(4))
pConditions.Add "lcfw", lcfw
' 仪表特性 - 第6个位置(索引5)及之后的所有部分
' .(N3.N2.Y3)5
Dim fjgn As String
If UBound(dotParts) >= 5 Then
Dim instrumentFeature As String
Dim i As Long
instrumentFeature = ""
' 5.
For i = 5 To UBound(dotParts)
If instrumentFeature = "" Then
instrumentFeature = dotParts(i)
Else
instrumentFeature = instrumentFeature & "." & dotParts(i)
End If
Next i
instrumentFeature = Trim(instrumentFeature)
fjgn = ExtractAdditionalFeatures(instrumentFeature)
Else
' fjgn
fjgn = ""
End If
pConditions.Add "fjgn", fjgn
ParseHeader = True
Exit Function
ErrorHandler:
pErrorMessage = "解析表头异常: " & Err.Description
ParseHeader = False
End Function
'=====================================================================
' 方法: ExtractConnectionAndMaterial
' 功能: 从过程连接代码中提取过程连接和接液材质
' 参数: code - 过程连接代码(如M203)
' outConnection - 输出:过程连接(如M20)
' outMaterial - 输出:接液材质(如3)
' 返回: Boolean - True表示提取成功
' 说明: 材质代码为最后一位数字,其余为螺纹代码
'=====================================================================
Private Function ExtractConnectionAndMaterial(code As String, _
ByRef outConnection As String, _
ByRef outMaterial As String) As Boolean
On Error GoTo ErrorHandler
If Len(code) < 2 Then
pErrorMessage = "过程连接代码格式错误:长度不足"
ExtractConnectionAndMaterial = False
Exit Function
End If
' 材质代码是最后一位数字
Dim lastChar As String
lastChar = Right(code, 1)
'
If Not IsNumeric(lastChar) Then
pErrorMessage = "过程连接代码格式错误:最后一位不是数字"
ExtractConnectionAndMaterial = False
Exit Function
End If
outMaterial = lastChar
outConnection = Left(code, Len(code) - 1)
ExtractConnectionAndMaterial = True
Exit Function
ErrorHandler:
pErrorMessage = "提取过程连接和材质异常: " & Err.Description
ExtractConnectionAndMaterial = False
End Function
'=====================================================================
' 方法: GetConditionValue
' 功能: 获取指定条件的值
' 参数: conditionName - 条件名称
' 返回: String - 条件值,如果不存在返回空字符串
'=====================================================================
Public Function GetConditionValue(conditionName As String) As String
If pConditions.Exists(conditionName) Then
GetConditionValue = pConditions(conditionName)
Else
GetConditionValue = ""
End If
End Function
'=====================================================================
' 方法: GetAllConditions
' 功能: 获取所有条件的描述文本
' 返回: String - 条件描述文本
'=====================================================================
Public Function GetAllConditions() As String
Dim result As String
Dim key As Variant
result = ""
For Each key In pConditions.Keys
result = result & key & "=" & pConditions(key) & "; "
Next key
GetAllConditions = result
End Function
'=====================================================================
' 方法: ExtractAdditionalFeatures
' 功能: 从仪表特性中提取附加功能
' : instrumentFeature - ("N2,N3.Y3""Y3")
' 返回: String - 附加功能字符串,多个功能用逗号分隔
' 说明:
' 1. 识别并去除充油类型(位于最后格式为Y+一位数字)
' 2. 统一分隔符处理(将.替换为,)
' 3. 去除可能的后缀分隔符
'=====================================================================
Private Function ExtractAdditionalFeatures(instrumentFeature As String) As String
On Error GoTo ErrorHandler
Dim result As String
result = Trim(instrumentFeature)
' 1. 检查是否以Y+数字结尾(充油类型)
If Len(result) >= 2 Then
Dim lastTwoChars As String
lastTwoChars = Right(result, 2)
' Y+
If UCase(Left(lastTwoChars, 1)) = "Y" And IsNumeric(Right(lastTwoChars, 1)) Then
' 去掉充油类型
result = Left(result, Len(result) - 2)
result = Trim(result)
End If
End If
' 2. 处理可能的分隔符(,或.
' .,
result = Replace(result, ".", ",")
' 3.
If Len(result) > 0 And Right(result, 1) = "," Then
result = Left(result, Len(result) - 1)
End If
ExtractAdditionalFeatures = Trim(result)
Exit Function
ErrorHandler:
'
ExtractAdditionalFeatures = ""
End Function

View File

@@ -0,0 +1,32 @@
'=====================================================================
' 主按钮点击事件
' 功能: 执行BOM提取和BIP上传
'=====================================================================
Private Sub CommandButton1_Click()
Call ProcessProductModels
Call ValidateOrderMaterials
End Sub
'=====================================================================
' 部件库存核对按钮点击事件
' 功能: 执行部件库存核对,标记库存不足的订单
'=====================================================================
Private Sub CommandButton2_Click()
Call CheckComponentInventory
End Sub
'=====================================================================
' 数据提取按钮点击事件
' 功能:基于总排号查询相关字段数据
'=====================================================================
Private Sub CommandButton3_Click()
Call FetchDataFromAccess
End Sub
Private Sub CommandButton4_Click()
ThisWorkbook.Worksheets("产品订单").Range("A2:G10000").ClearContents
End Sub
Private Sub CommandButton5_Click()
Call ProcessOrdersToBIP
End Sub

142
VBA/Modules/AGENTS.md Normal file
View File

@@ -0,0 +1,142 @@
# Modules Knowledge Base
**Scope:** `VBA/Modules/` - Procedural workflows and entry points
## OVERVIEW
4 modules: `MainModule` (main workflow), `BIPUploadModule` (upload results), `ComponentInventoryCheckModule` (inventory check), `TestModule` (testing utilities).
## STRUCTURE
```
Modules/
├── MainModule.bas # Main entry point + orchestration
├── BIPUploadModule.bas # Upload to BIP system
├── ComponentInventoryCheckModule.bas # Component inventory verification
└── TestModule.bas # Testing utilities
```
## WHERE TO LOOK
| Task | Location | Notes |
|------|----------|-------|
| Run BOM extraction | `MainModule.bas` | `ProcessProductModels()` |
| Upload results | `BIPUploadModule.bas` | BIP system integration |
| Check inventory | `ComponentInventoryCheckModule.bas` | Component count verification |
| Test parsing | `TestModule.bas` | Unit test utilities |
## CODE MAP
### MainModule
| Function | Role |
|----------|------|
| `ProcessProductModels()` | Main entry point, orchestrates workflow |
| `ProcessSingleModel()` | Process one model, collect output |
| `CreateOutputRowArray()` | Build row data array |
| `WriteBatchData()` | Bulk write to worksheet |
| `GetInputSheet()` | Get `产品订单` sheet |
| `GetBomSheet()` | Get `平台配置清单` sheet |
| `CreateOutputSheet()` | Create/reset `BOM 提取结果` |
### BIPUploadModule
| Function | Role |
|----------|------|
| `UploadToBIP()` | Upload BOM results to BIP system |
### ComponentInventoryCheckModule
| Function | Role |
|----------|------|
| `CheckComponentInventory()` | Verify component counts match |
## CONVENTIONS
### Module Structure
```vba
Option Explicit
' Constants
Private Const CONDITION_CONFIG = "azxs安装形式|bkxs表壳形式|..."
' Public entry points
Public Sub MainEntryPoint()
On Error GoTo ErrorHandler
' Orchestration
Exit Sub
ErrorHandler:
MsgBox "Error: " & Err.Description
End Sub
' Private helpers
Private Sub HelperFunction()
' Implementation
End Sub
```
### Workflow Pattern
1. Get input sheet (`产品订单`)
2. Get BOM sheet (`平台配置清单`)
3. Create output sheet (`BOM 提取结果`)
4. Initialize `BomExtractor`, load data
5. Loop through models, call `ProcessSingleModel()`
6. Batch write results
7. Format output, show message
### Batch Output Pattern
```vba
' Collect in Collection
Dim outputData As Collection
Set outputData = New Collection
' Add arrays
outputData.Add CreateOutputRowArray(...)
' Convert to 2D array
ReDim resultData(1 To rowCount, 1 To colCount)
' Fill array...
' Single write
ws.Range("A2").Resize(rowCount, colCount).Value = resultData
```
## ANTI-PATTERNS (THIS PROJECT)
- **NEVER** hardcode sheet names — use `GetInputSheet()`, `GetBomSheet()` helpers
- **NEVER** write row-by-row — always batch write via 2D array
- **NEVER** skip error handling — all public subs use `On Error GoTo ErrorHandler`
- **DO NOT** change output column order — must match header definition in `WriteOutputHeader()`
## UNIQUE STYLES
### Condition Config Constant
```vba
Private Const CONDITION_CONFIG = "azxs安装形式|bkxs表壳形式|gclj过程连接|jycz接液材质|lcfw量程范围|fjgn附加功能"
```
Format: `code,label|code,label|...` — parsed dynamically for header generation.
### Component Priority Flag
From column E of input sheet:
- "是" (or "1", "TRUE") → include all categories
- "否" (or "0", "FALSE") → exclude "部件" (components) category
### Output Row Structure
```
Column 1: 生产订单号 (order number)
Column 2: 产品型号 (full model)
Columns 3-8: 6 conditions (azxs, bkxs, gclj, jycz, lcfw, fjgn)
Columns 9-16: BOM fields (行号模块代号名称数量类别66 代码,备注)
```
## NOTES
### Output Sheet Formatting
- Row 1: Bold, gray background (RGB 217,217,217), centered
- Data starts at row 2
- AutoFit columns (commented out in current code)
### Error Messages
- Parse failure: `"解析失败:" + error`
- No match: `"未匹配到任何物料"`
- Multi-match: `"类别[X] 匹配到多条物料(N 条)"` — outputs all but flags
### Performance
Batch write via `Range.Value = resultData` is 10-100x faster than cell-by-cell writes for large datasets.

View File

@@ -0,0 +1,172 @@
'=====================================================================
' 模块名: AccessDataModule
' 功能: 连接Access数据库根据[总排号]提取数据并填充到[产品订单]工作表
' 特性: [安全极速版] 完美解决筛选状态下全量数组写回导致的错位 Bug
'=====================================================================
Option Explicit
'=====================================================================
' 配置区域 (请根据你的实际情况修改以下常量)
'=====================================================================
' Access数据库文件的完整路径
Private Const DB_PATH = "\\192.168.110.114\生产进度表\2025年数据\生产合同数据.accdb"
' Access中目标数据表的名称
Private Const TARGET_TABLE = "26年压力表合同数据"
'=====================================================================
' 过程: FetchDataFromAccess
' 功能: 主控程序,执行数据提取和回填逻辑
'=====================================================================
Public Sub FetchDataFromAccess()
On Error GoTo ErrorHandler
Dim startTime As Double
startTime = Timer
Dim ws As Worksheet
Set ws = GetOrderSheet()
If ws Is Nothing Then
MsgBox "未找到[产品订单]工作表,请检查工作表名称。", vbCritical
Exit Sub
End If
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.count, 1).End(xlUp).row
If lastRow < 2 Then
MsgBox "[产品订单]工作表中没有需要处理的数据。", vbInformation
Exit Sub
End If
' 1. 获取A列中所有筛选后的可见单元格
Dim visibleRange As Range
On Error Resume Next
Set visibleRange = ws.Range("A2:A" & lastRow).SpecialCells(xlCellTypeVisible)
On Error GoTo ErrorHandler
If visibleRange Is Nothing Then
MsgBox "当前筛选状态下没有可见的数据。", vbInformation
Exit Sub
End If
' 2. 仅收集可见行中的总排号
Dim cell As Range
Dim queueNums As String
Dim currentNum As String
For Each cell In visibleRange
currentNum = Trim(cell.value)
If currentNum <> "" Then
queueNums = queueNums & "'" & currentNum & "',"
End If
Next cell
If queueNums = "" Then
MsgBox "可见数据中没有找到有效的总排号。", vbInformation
Exit Sub
End If
queueNums = Left(queueNums, Len(queueNums) - 1)
' 3. 连接Access查询并装入字典 (内存极速匹配)
Dim cn As Object, rs As Object
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
Dim connStr As String
connStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DB_PATH & ";"
cn.Open connStr
Dim sql As String
sql = "SELECT 总排号, 生产订单号, 产品型号, 数量, 成品物料码 " & _
"FROM [" & TARGET_TABLE & "] " & _
"WHERE 总排号 IN (" & queueNums & ")"
rs.Open sql, cn, 1, 1
Dim dbDict As Object
Set dbDict = CreateObject("Scripting.Dictionary")
If Not rs.EOF Then
rs.MoveFirst
Do Until rs.EOF
Dim key As String
key = Trim(rs.Fields("总排号").value)
If Not dbDict.Exists(key) Then
dbDict.Add key, Array( _
rs.Fields("生产订单号").value, _
rs.Fields("产品型号").value, _
rs.Fields("数量").value, _
rs.Fields("成品物料码").value _
)
End If
rs.MoveNext
Loop
End If
rs.Close
cn.Close
Set rs = Nothing
Set cn = Nothing
' 4. 【核心修复】安全且极速地回写可见数据
Dim matchCount As Long
matchCount = 0
' 关闭屏幕刷新、自动计算和事件触发,拉满单行写入性能
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
For Each cell In visibleRange
currentNum = Trim(cell.value)
If dbDict.Exists(currentNum) Then
Dim dbRecord As Variant
dbRecord = dbDict(currentNum)
' 【神级优化点】将4个字段装入一个微型一维数组利用 Resize 一次性写入 B 到 E 列
' 这样每一行只需要 1 次单元格操作,而不是 4 次!性能无限逼近全量数组写回。
cell.Offset(0, 1).Resize(1, 4).value = Array(dbRecord(0), dbRecord(1), dbRecord(2), dbRecord(3))
matchCount = matchCount + 1
End If
Next cell
' 恢复应用状态
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Set dbDict = Nothing
Dim elapsedTime As Double
elapsedTime = Timer - startTime
MsgBox "数据提取完成!" & vbCrLf & _
"成功匹配并更新了 " & matchCount & " 条筛选记录。" & vbCrLf & _
"用时: " & Format(elapsedTime, "0.00") & " 秒", vbInformation
Exit Sub
ErrorHandler:
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
On Error Resume Next
If Not rs Is Nothing Then If rs.State = 1 Then rs.Close
If Not cn Is Nothing Then If cn.State = 1 Then cn.Close
On Error GoTo 0
MsgBox "提取Access数据时发生异常: " & Err.Description, vbCritical
End Sub
'=====================================================================
' 函数: GetOrderSheet
' 功能: 获取[产品订单]工作表
' 返回: Worksheet - 工作表对象
'=====================================================================
Private Function GetOrderSheet() As Worksheet
On Error Resume Next
Set GetOrderSheet = ThisWorkbook.Worksheets("产品订单")
On Error GoTo 0
End Function

View File

@@ -0,0 +1,452 @@
'=====================================================================
' 模块名: BIPUploadModule
' 功能: 处理产品订单数据提取BOM后生成[BIP上传模板]格式数据
'=====================================================================
Option Explicit
'=====================================================================
' 常量定义
'=====================================================================
' 提取条件配置
Private Const CONDITION_CONFIG = "azxs安装形式 |bkxs表壳形式 |gclj过程连接 |jycz接液材质 |lcfw量程范围 |fjgn附加功能"
'=====================================================================
' 过程: ProcessOrdersToBIP
' 功能: 处理产品订单数据生成BIP上传格式
' 说明: 主入口程序,从[产品订单]读取数据,输出到[BIP上传模板]
'=====================================================================
Public Sub ProcessOrdersToBIP()
On Error GoTo ErrorHandler
Dim startTime As Double
startTime = Timer
Application.ScreenUpdating = False
' 准备工作表对象
Dim orderSheet As Worksheet
Dim bipSheet As Worksheet
Dim bomSheet As Worksheet
' 获取[产品订单]工作表
Set orderSheet = GetOrderSheet()
If orderSheet Is Nothing Then
Application.ScreenUpdating = True
MsgBox "未找到[产品订单]工作表!", vbCritical
Exit Sub
End If
' 获取[BIP上传模板]工作表
Set bipSheet = GetBIPUploadSheet()
' 获取BOM库工作表
Set bomSheet = GetBomSheet()
If bomSheet Is Nothing Then
Application.ScreenUpdating = True
MsgBox "未找到[平台配置清单]工作表!", vbCritical
Exit Sub
End If
' 初始化BOM提取器
Dim BomExtractor As BomExtractor
Set BomExtractor = New BomExtractor
BomExtractor.SetWorksheet bomSheet
If Not BomExtractor.LoadBomData Then
Application.ScreenUpdating = True
MsgBox "加载BOM数据失败:" & BomExtractor.GetErrorSummary, vbCritical
Exit Sub
End If
' 清空BIP上传模板数据保留表头
ClearBIPSheetData bipSheet
' 写入BIP上传模板表头
WriteBIPHeader bipSheet
' 获取订单数据行数
Dim lastRow As Long
lastRow = orderSheet.Cells(orderSheet.Rows.count, 1).End(xlUp).row
' 如果只有表头或没有数据
If lastRow < 2 Then
Application.ScreenUpdating = True
MsgBox "[产品订单]工作表中没有数据!", vbExclamation
Exit Sub
End If
' 【性能核心】全量读入源数据
Dim sourceDataArr As Variant
sourceDataArr = orderSheet.Range("A2:G" & lastRow).value
' 【筛选核心】获取可见区域
Dim visibleRange As Range
On Error Resume Next
Set visibleRange = orderSheet.Range("A2:A" & lastRow).SpecialCells(xlCellTypeVisible)
On Error GoTo ErrorHandler
If visibleRange Is Nothing Then
Application.ScreenUpdating = True
MsgBox "当前筛选状态下没有可见的数据。", vbInformation
Exit Sub
End If
' 收集所有输出数据
Dim outputData As Collection
Set outputData = New Collection
Dim cell As Range
Dim arrIndex As Long
Dim processedCount As Long
Dim orderCount As Long
Dim skippedCount As Long
processedCount = 0
orderCount = 0
skippedCount = 0
' 遍历筛选出来的可见单元格
For Each cell In visibleRange
' 计算内存数组索引
arrIndex = cell.row - 1
' 读取订单数据
Dim totalQueueNum As String
Dim orderNumber As String
Dim ProductModel As String
Dim Quantity As String
Dim productCode As String
Dim componentPriority As String
Dim isIssueMaterial As String
totalQueueNum = Trim(sourceDataArr(arrIndex, 1)) ' A列总排号
orderNumber = Trim(sourceDataArr(arrIndex, 2)) ' B列生产订单号
ProductModel = Trim(sourceDataArr(arrIndex, 3)) ' C列产品型号
Quantity = Trim(sourceDataArr(arrIndex, 4)) ' D列数量
productCode = Trim(sourceDataArr(arrIndex, 5)) ' E列产品编码
componentPriority = Trim(sourceDataArr(arrIndex, 6)) ' F列部件优先
isIssueMaterial = Trim(sourceDataArr(arrIndex, 7)) ' G列是否领料
' 【拦截逻辑】忽略[是否领料]为"否"的订单
If isIssueMaterial = "否" Then
skippedCount = skippedCount + 1
GoTo ContinueLoop
End If
' 跳过空行
If orderNumber = "" And ProductModel = "" Then
GoTo ContinueLoop
End If
' 验证必填字段
If orderNumber = "" Then
MsgBox "工作表第" & cell.row & "行:生产订单号为空,跳过该行!", vbExclamation
GoTo ContinueLoop
End If
If ProductModel = "" Then
MsgBox "工作表第" & cell.row & "行:产品型号为空,跳过该行!", vbExclamation
GoTo ContinueLoop
End If
If Quantity = "" Then
MsgBox "工作表第" & cell.row & "行:数量为空,跳过该行!", vbExclamation
GoTo ContinueLoop
End If
orderCount = orderCount + 1
' 处理单个订单,收集输出数据
ProcessSingleOrder orderNumber, ProductModel, Quantity, productCode, _
componentPriority, BomExtractor, outputData
processedCount = processedCount + 1
ContinueLoop:
Next cell
' 批量写入数据到工作表
If outputData.count > 0 Then
WriteBatchData bipSheet, outputData
End If
' 格式化BIP上传模板
FormatBIPSheet bipSheet
Dim elapsedTime As Double
elapsedTime = Timer - startTime
Application.ScreenUpdating = True
MsgBox "处理完成!" & vbCrLf & _
"处理有效订单数: " & orderCount & vbCrLf & _
"忽略无效订单数: " & skippedCount & vbCrLf & _
"生成BIP行数: " & outputData.count & vbCrLf & _
"用时: " & Format(elapsedTime, "0.00") & "秒", vbInformation
' 激活BIP上传模板
bipSheet.Activate
Exit Sub
ErrorHandler:
Application.ScreenUpdating = True
MsgBox "处理异常: " & Err.Description, vbCritical
End Sub
'=====================================================================
' 过程: ProcessSingleOrder
' 功能: 处理单个订单提取BOM并将数据添加到输出集合
' 参数: orderNumber - 生产订单号
' ProductModel - 产品型号
' Quantity - 生产数量
' productCode - 产品编码
' componentPriority - 部件优先标志("是"或"否"
' BomExtractor - BOM提取器对象
' outputData - 输出数据集合
'=====================================================================
Private Sub ProcessSingleOrder(orderNumber As String, _
ProductModel As String, _
Quantity As String, _
productCode As String, _
componentPriority As String, _
BomExtractor As BomExtractor, _
outputData As Collection)
On Error Resume Next
' 根据部件优先设置排除类别
BomExtractor.ClearExcludeCategories
If UCase(componentPriority) = "否" Or componentPriority = "0" Or componentPriority = "FALSE" Then
Dim excludeCats As New Collection
excludeCats.Add "部件"
BomExtractor.SetExcludeCategories excludeCats
End If
' 解析产品型号
Dim parser As ProductModelParser
Set parser = New ProductModelParser
If Not parser.Parse(ProductModel) Then
' 解析失败,添加一行空物料记录
outputData.Add CreateBIPRowArray(orderNumber, productCode, Quantity, 1, "")
Exit Sub
End If
' 提取BOM
Dim matchedItems As Collection
Set matchedItems = BomExtractor.ExtractBom(parser.conditions)
' 输出结果
If matchedItems.count = 0 Then
' 没有匹配项,添加一行空物料记录
outputData.Add CreateBIPRowArray(orderNumber, productCode, Quantity, 1, "")
Else
' 输出每个匹配的物料
Dim item As BomItem
Dim lineIndex As Long
lineIndex = 1
' 创建字典跟踪每个 BIP 行号基数的当前序号
Dim bipRowBaseDict As Object
Set bipRowBaseDict = CreateObject("Scripting.Dictionary")
For Each item In matchedItems
' 计算实际行号:行号 = BIP 行号基数 + 组内序号 (从 1 开始)
Dim baseValue As Long
baseValue = item.BipRowNumberBase
Dim currentIndex As Long
If bipRowBaseDict.Exists(baseValue) Then
currentIndex = bipRowBaseDict(baseValue) + 1
Else
currentIndex = 1
End If
bipRowBaseDict(baseValue) = currentIndex
Dim actualRowNumber As Long
actualRowNumber = baseValue + currentIndex
' 创建 BIP 行数据并添加到集合 (已移除备注参数)
outputData.Add CreateBIPRowArray(orderNumber, productCode, Quantity, _
actualRowNumber, item.Code66)
lineIndex = lineIndex + 1
Next item
End If
End Sub
'=====================================================================
' 函数CreateBIPRowArray
' 功能:创建 BIP 上传模板一行数据的数组
' 参数orderNumber - 生产订单号
' productCode - 产品编码
' Quantity - 生产数量
' actualRowNumber - 实际行号BIP 行号基数 + 组内序号)
' materialCode - 材料编码66 编码)
' 返回Variant() - 包含 9 个元素的数组
'=====================================================================
Private Function CreateBIPRowArray(orderNumber As String, _
productCode As String, _
Quantity As String, _
actualRowNumber As Long, _
materialCode As String) As Variant()
Dim rowData(1 To 9) As Variant
rowData(1) = orderNumber ' 来源单据号(生产订单号)
rowData(2) = productCode ' 产品编码
rowData(3) = Quantity ' 生产数量
rowData(4) = actualRowNumber ' 行号
rowData(5) = materialCode ' 材料编码66 编码)
rowData(6) = "一般发料" ' 供应方式(固定值)
rowData(7) = Date ' 需用日期(当天日期)
rowData(8) = "重庆布莱迪仪器仪表有限公司" ' 发料组织(固定值)
rowData(9) = Quantity ' 计划出库数量(与生产数量一致)
CreateBIPRowArray = rowData
End Function
'=====================================================================
' 过程: WriteBatchData
' 功能: 批量写入数据到工作表
' 参数: ws - 工作表对象
' outputData - 输出数据集合,每个元素是一个一维数组
'=====================================================================
Private Sub WriteBatchData(ws As Worksheet, outputData As Collection)
' 如果没有数据,直接返回
If outputData.count = 0 Then
Exit Sub
End If
' 创建二维数组
Dim rowCount As Long
rowCount = outputData.count
Dim resultData() As Variant
ReDim resultData(1 To rowCount, 1 To 9)
' 填充数据到二维数组
Dim i As Long
Dim rowArray As Variant
For i = 1 To rowCount
rowArray = outputData(i)
resultData(i, 1) = rowArray(1)
resultData(i, 2) = rowArray(2)
resultData(i, 3) = rowArray(3)
resultData(i, 4) = rowArray(4)
resultData(i, 5) = rowArray(5)
resultData(i, 6) = rowArray(6)
resultData(i, 7) = rowArray(7)
resultData(i, 8) = rowArray(8)
resultData(i, 9) = rowArray(9)
Next i
' 一次性写入工作表从第2行开始
ws.Range("A2").Resize(rowCount, 9).value = resultData
End Sub
'=====================================================================
' 过程: WriteBIPHeader
' 功能: 写入BIP上传模板表头
' 参数: ws - 工作表对象
'=====================================================================
Private Sub WriteBIPHeader(ws As Worksheet)
' 第1行主表头
ws.Cells(1, 1).value = "来源单据号(生产订单号)"
ws.Cells(1, 2).value = "产品编码"
ws.Cells(1, 3).value = "生产数量"
ws.Cells(1, 4).value = "行号"
ws.Cells(1, 5).value = "材料编码"
ws.Cells(1, 6).value = "供应方式"
ws.Cells(1, 7).value = "需用日期"
ws.Cells(1, 8).value = "发料组织"
ws.Cells(1, 9).value = "计划出库数量"
End Sub
'=====================================================================
' 函数: GetOrderSheet
' 功能: 获取[产品订单]工作表
' 返回: Worksheet - 工作表对象
'=====================================================================
Private Function GetOrderSheet() As Worksheet
On Error Resume Next
Set GetOrderSheet = ThisWorkbook.Worksheets("产品订单")
On Error GoTo 0
End Function
'=====================================================================
' 函数: GetBIPUploadSheet
' 功能: 获取或创建[BIP上传模板]工作表
' 返回: Worksheet - 工作表对象
'=====================================================================
Private Function GetBIPUploadSheet() As Worksheet
Dim wsName As String
wsName = "BIP上传模板"
On Error Resume Next
Set GetBIPUploadSheet = ThisWorkbook.Worksheets(wsName)
On Error GoTo 0
If GetBIPUploadSheet Is Nothing Then
' 创建新工作表
Set GetBIPUploadSheet = ThisWorkbook.Worksheets.Add(After:=ThisWorkbook.Worksheets(ThisWorkbook.Worksheets.count))
GetBIPUploadSheet.Name = wsName
End If
End Function
'=====================================================================
' 函数: GetBomSheet
' 功能: 获取BOM工作表
' 返回: Worksheet - BOM工作表对象
'=====================================================================
Private Function GetBomSheet() As Worksheet
On Error Resume Next
Set GetBomSheet = ThisWorkbook.Worksheets("平台配置清单")
On Error GoTo 0
End Function
'=====================================================================
' 过程: ClearBIPSheetData
' 功能: 清空BIP上传模板的数据保留表头
' 参数: ws - 工作表对象
'=====================================================================
Private Sub ClearBIPSheetData(ws As Worksheet)
' 清空从第2行开始的所有数据
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.count, 1).End(xlUp).row
If lastRow > 1 Then
ws.Rows("2:" & lastRow).ClearContents
End If
End Sub
'=====================================================================
' 过程: FormatBIPSheet
' 功能: 格式化BIP上传模板工作表
' 参数: ws - 工作表对象
'=====================================================================
Private Sub FormatBIPSheet(ws As Worksheet)
On Error Resume Next
' 设置表头格式
With ws.Rows(1)
.Font.Bold = True
.Interior.Color = RGB(217, 217, 217)
.HorizontalAlignment = xlCenter
End With
' 设置所有单元格居中对齐
With ws.UsedRange
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlCenter
End With
' 自动调整列宽
ws.Columns.AutoFit
' 设置日期列格式
ws.Columns(7).NumberFormat = "yyyy/mm/dd"
On Error GoTo 0
End Sub

View File

@@ -0,0 +1,513 @@
'=====================================================================
' 模块名: ComponentInventoryCheckModule
' 功能: 部件库存核推模块 - 自动核对产品订单中"部件"类物料的库存情况
' 特性: [已重构] 支持仅对筛选后的数据进行处理,采用内存极速读取
'=====================================================================
Option Explicit
'=====================================================================
' 数据结构定义 - 使用字典以支持引用更新
'=====================================================================
' 订单字典键
Private Const ORDER_ROW As String = "RowNumber"
Private Const ORDER_MODEL As String = "ProductModel"
Private Const ORDER_QUANTITY As String = "Quantity"
Private Const ORDER_COMP_CODE As String = "ComponentCode"
Private Const ORDER_COMP_QTY As String = "ComponentQty"
Private Const ORDER_HAS_COMP As String = "HasComponent"
Private Const ORDER_PARSE_ERR As String = "ParseError"
' 部件库存字典键
Private Const INV_CODE As String = "ComponentCode"
Private Const INV_DEMAND As String = "TotalDemand"
Private Const INV_STOCK As String = "AvailableStock"
Private Const INV_SHORTAGE As String = "IsShortage"
' 统计信息结构
Private Type Statistics
TotalOrders As Long ' 总订单数
OrdersWithComponent As Long ' 包含部件的订单数
OrdersSufficient As Long ' 库存充足订单数
OrdersInsufficient As Long ' 库存不足订单数
OrdersSkipped As Long ' 跳过订单数
End Type
'=====================================================================
' 主入口程序
'=====================================================================
Public Sub CheckComponentInventory()
On Error GoTo ErrorHandler
Dim startTime As Double
startTime = Timer
' 提升性能:关闭屏幕更新和自动计算
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' 获取工作表对象
Dim orderSheet As Worksheet
Dim inventorySheet As Worksheet
Dim bomSheet As Worksheet
Set orderSheet = GetOrderSheet()
If orderSheet Is Nothing Then
RestoreAppStatus
MsgBox "未找到[产品订单]工作表!", vbExclamation
Exit Sub
End If
Set inventorySheet = GetInventorySheet()
If inventorySheet Is Nothing Then
RestoreAppStatus
MsgBox "未找到[现存量]工作表!", vbExclamation
Exit Sub
End If
Set bomSheet = GetBomSheet()
If bomSheet Is Nothing Then
RestoreAppStatus
MsgBox "未找到[平台配置清单]工作表!", vbExclamation
Exit Sub
End If
' 检查订单数据 (调整为按C列:产品型号获取最后一行)
Dim lastRow As Long
lastRow = orderSheet.Cells(orderSheet.Rows.count, 3).End(xlUp).row
If lastRow < 2 Then
RestoreAppStatus
MsgBox "[产品订单]工作表没有数据!", vbExclamation
Exit Sub
End If
' 【核心重构】获取筛选后的可见单元格区域 (A列)
Dim visibleRange As Range
On Error Resume Next
Set visibleRange = orderSheet.Range("A2:A" & lastRow).SpecialCells(xlCellTypeVisible)
On Error GoTo ErrorHandler
If visibleRange Is Nothing Then
RestoreAppStatus
MsgBox "当前筛选状态下没有可见的数据。", vbInformation
Exit Sub
End If
' 初始化BOM提取器
Dim BomExtractor As BomExtractor
Set BomExtractor = New BomExtractor
BomExtractor.SetWorksheet bomSheet
If Not BomExtractor.LoadBomData Then
RestoreAppStatus
MsgBox "加载BOM数据失败:" & BomExtractor.GetErrorSummary, vbCritical
Exit Sub
End If
' 读取库存数据到字典
Dim inventoryData As Object
Set inventoryData = LoadInventoryData(inventorySheet)
If inventoryData.count = 0 Then
RestoreAppStatus
MsgBox "[现存量]工作表没有有效数据!", vbExclamation
Exit Sub
End If
' 【核心重构】传递可见区域和总行数,仅读取可见订单数据
Dim orders As Collection
Set orders = LoadOrderData(orderSheet, visibleRange, lastRow)
If orders.count = 0 Then
RestoreAppStatus
MsgBox "可见区域中没有有效的订单数据!", vbExclamation
Exit Sub
End If
' 解析所有订单的BOM
ParseAllOrdersBOM orders, BomExtractor
' 统计部件总需求
Dim componentDemands As Object
Set componentDemands = CalculateComponentDemand(orders)
If componentDemands.count = 0 Then
RestoreAppStatus
MsgBox "筛选的订单中没有包含'部件'类别物料,无需处理库存!", vbInformation
Exit Sub
End If
' 验证库存
Dim validationWarnings As Collection
Set validationWarnings = ValidateInventory(componentDemands, inventoryData)
' 按订单顺序分配库存并标记
Dim stats As Statistics
AllocateInventory orders, componentDemands, orderSheet, stats
' 恢复应用状态
RestoreAppStatus
' 输出结果统计
Dim elapsedTime As Double
elapsedTime = Timer - startTime
Dim resultMsg As String
resultMsg = "部件库存核对完成!" & vbCrLf & vbCrLf
resultMsg = resultMsg & "处理筛选订单数: " & stats.TotalOrders & vbCrLf
resultMsg = resultMsg & "包含部件订单: " & stats.OrdersWithComponent & vbCrLf
resultMsg = resultMsg & "库存充足订单: " & stats.OrdersSufficient & vbCrLf
resultMsg = resultMsg & "库存不足订单: " & stats.OrdersInsufficient & vbCrLf
If stats.OrdersSkipped > 0 Then
resultMsg = resultMsg & "跳过订单数: " & stats.OrdersSkipped & vbCrLf
End If
resultMsg = resultMsg & vbCrLf & "耗时: " & Format(elapsedTime, "0.00") & "秒"
' 显示警告信息(如果有)
If validationWarnings.count > 0 Then
resultMsg = resultMsg & vbCrLf & vbCrLf & "警告信息:" & vbCrLf
resultMsg = resultMsg & JoinCollection(validationWarnings, vbCrLf)
End If
MsgBox resultMsg, vbInformation
Exit Sub
ErrorHandler:
RestoreAppStatus
MsgBox "部件库存核对异常: " & Err.Description, vbCritical
End Sub
'=====================================================================
' 辅助过程: RestoreAppStatus
' 功能: 恢复Excel应用程序的状态
'=====================================================================
Private Sub RestoreAppStatus()
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
'=====================================================================
' 函数: LoadOrderData
' 功能: 读取订单数据
' 参数: ws - [产品订单]工作表
' 返回: Collection - 每个元素是字典对象,包含订单信息
'=====================================================================
Private Function LoadOrderData(ws As Worksheet, visibleRange As Range, lastRow As Long) As Collection
Set LoadOrderData = New Collection
' 全量读入内存数组提升速度
Dim sourceDataArr As Variant
sourceDataArr = ws.Range("A2:F" & lastRow).value
Dim cell As Range
Dim arrIndex As Long
' 仅遍历可见的单元格
For Each cell In visibleRange
Dim model As String
Dim qty As Variant
' 数组索引 = Excel行号 - 1
arrIndex = cell.row - 1
' 从内存数组中提取数据
model = Trim(sourceDataArr(arrIndex, 3)) ' C列: 产品型号
qty = sourceDataArr(arrIndex, 4) ' D列: 产品数量
' 跳过空行
If model <> "" Then
Dim order As Object
Set order = CreateObject("Scripting.Dictionary")
' 记录真实的Excel行号用于后续库存不足时精准写入F列
order.Add ORDER_ROW, CLng(cell.row)
order.Add ORDER_MODEL, CStr(model)
order.Add ORDER_QUANTITY, CDbl(IIf(IsNull(qty) Or IsEmpty(qty), 0, qty))
order.Add ORDER_COMP_CODE, ""
order.Add ORDER_COMP_QTY, 0
order.Add ORDER_HAS_COMP, False
order.Add ORDER_PARSE_ERR, ""
LoadOrderData.Add order
End If
Next cell
End Function
'=====================================================================
' 函数: LoadInventoryData
' 功能: 读取库存数据
' 参数: ws - [现存量]工作表
' 返回: Dictionary(物料编码 -> 库存数量)
'=====================================================================
Private Function LoadInventoryData(ws As Worksheet) As Object
Set LoadInventoryData = CreateObject("Scripting.Dictionary")
' 从第4行开始读取(第3行是表头)
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.count, 2).End(xlUp).row
Dim i As Long
For i = 4 To lastRow
Dim code As String
Dim qty As Variant
code = Trim(ws.Cells(i, 2).value) ' B列: 物料编码
qty = ws.Cells(i, 10).value ' J列: 结存主数量
If code <> "" And Not IsEmpty(qty) Then
If Not LoadInventoryData.Exists(code) Then
LoadInventoryData.Add code, CDbl(qty)
End If
End If
Next i
End Function
'=====================================================================
' 过程: ParseAllOrdersBOM
' 功能: 解析所有订单的BOM
' 参数: orders - 订单集合(每个元素是字典)
' bomExtractor - BOM提取器
'=====================================================================
Private Sub ParseAllOrdersBOM(orders As Collection, BomExtractor As BomExtractor)
Dim i As Long
For i = 1 To orders.count
Dim order As Object
Set order = orders(i)
ParseOrderBOM order, BomExtractor
Next i
End Sub
'=====================================================================
' 过程: ParseOrderBOM
' 功能: 解析单个订单的BOM,识别部件类别物料
' 参数: orderInfo - 订单信息字典(ByRef)
' bomExtractor - BOM提取器
'=====================================================================
Private Sub ParseOrderBOM(ByRef orderInfo As Object, BomExtractor As BomExtractor)
On Error Resume Next
' 解析型号
Dim parser As ProductModelParser
Set parser = New ProductModelParser
If Not parser.Parse(orderInfo(ORDER_MODEL)) Then
orderInfo(ORDER_PARSE_ERR) = "解析失败: " & parser.ErrorMessage
Exit Sub
End If
' 提取BOM
Dim matchedItems As Collection
Set matchedItems = BomExtractor.ExtractBom(parser.conditions)
' 查找"部件"类别物料
Dim item As BomItem
For Each item In matchedItems
If item.category = "部件" Then
orderInfo(ORDER_COMP_CODE) = item.Code66
orderInfo(ORDER_COMP_QTY) = item.Quantity
orderInfo(ORDER_HAS_COMP) = True
Exit For
End If
Next item
End Sub
'=====================================================================
' 函数: CalculateComponentDemand
' 功能: 统计部件总需求
' 参数: orders - 订单集合
' 返回: Dictionary(部件编码 -> 库存信息字典)
'=====================================================================
Private Function CalculateComponentDemand(orders As Collection) As Object
Dim demands As Object
Set demands = CreateObject("Scripting.Dictionary")
Dim i As Long
For i = 1 To orders.count
Dim order As Object
Set order = orders(i)
If order(ORDER_HAS_COMP) Then
Dim demand As Double
demand = order(ORDER_COMP_QTY) * order(ORDER_QUANTITY)
Dim compCode As String
compCode = order(ORDER_COMP_CODE)
If demands.Exists(compCode) Then
Dim compInv As Object
Set compInv = demands(compCode)
compInv(INV_DEMAND) = compInv(INV_DEMAND) + demand
Else
Dim newComp As Object
Set newComp = CreateObject("Scripting.Dictionary")
newComp.Add INV_CODE, compCode
newComp.Add INV_DEMAND, demand
newComp.Add INV_STOCK, 0
newComp.Add INV_SHORTAGE, False
demands.Add compCode, newComp
End If
End If
Next i
Set CalculateComponentDemand = demands
End Function
'=====================================================================
' 函数: ValidateInventory
' 功能: 验证库存数据
' 参数: componentDemands - 部件需求字典
' inventoryData - 库存数据字典
' 返回: Collection - 警告信息集合(找不到的部件)
'=====================================================================
Private Function ValidateInventory(componentDemands As Object, _
inventoryData As Object) As Collection
Set ValidateInventory = New Collection
Dim code As Variant
For Each code In componentDemands.Keys
Dim compInv As Object
Set compInv = componentDemands(code)
' 检查库存中是否存在该部件
If Not inventoryData.Exists(compInv(INV_CODE)) Then
compInv(INV_STOCK) = 0
compInv(INV_SHORTAGE) = True
ValidateInventory.Add "部件 '" & compInv(INV_CODE) & "' 在[现存量]中未找到"
Else
compInv(INV_STOCK) = inventoryData(compInv(INV_CODE))
If compInv(INV_DEMAND) > compInv(INV_STOCK) Then
compInv(INV_SHORTAGE) = True
End If
End If
Next code
End Function
'=====================================================================
' 过程: AllocateInventory
' 功能: 按订单顺序分配库存并标记
' 参数: orders - 订单集合
' componentDemands - 部件需求字典
' orderSheet - 订单工作表
' stats - 统计信息(ByRef)
'=====================================================================
Private Sub AllocateInventory(orders As Collection, _
componentDemands As Object, _
orderSheet As Worksheet, _
ByRef stats As Statistics)
' 初始化统计
stats.TotalOrders = orders.count
stats.OrdersWithComponent = 0
stats.OrdersSufficient = 0
stats.OrdersInsufficient = 0
stats.OrdersSkipped = 0
Dim i As Long
For i = 1 To orders.count
Dim order As Object
Set order = orders(i)
' 跳过解析失败的订单
If order(ORDER_PARSE_ERR) <> "" Then
stats.OrdersSkipped = stats.OrdersSkipped + 1
GoTo NextOrder
End If
' 跳过没有部件的订单
If Not order(ORDER_HAS_COMP) Then
stats.OrdersSkipped = stats.OrdersSkipped + 1
GoTo NextOrder
End If
' 跳过数量为0的订单
If order(ORDER_QUANTITY) = 0 Then
stats.OrdersSkipped = stats.OrdersSkipped + 1
GoTo NextOrder
End If
stats.OrdersWithComponent = stats.OrdersWithComponent + 1
' 获取部件库存信息
Dim compInv As Object
Set compInv = componentDemands(order(ORDER_COMP_CODE))
' 计算需求量
Dim requiredQty As Double
requiredQty = order(ORDER_COMP_QTY) * order(ORDER_QUANTITY)
' 检查库存是否充足
If compInv(INV_STOCK) >= requiredQty Then
' 库存充足,扣减库存,保持原值
compInv(INV_STOCK) = compInv(INV_STOCK) - requiredQty
stats.OrdersSufficient = stats.OrdersSufficient + 1
Else
' --- 因为已经保存了真正的行号 ORDER_ROW, 在关闭屏幕刷新的情况下,这里直接写入是非常快的 ---
orderSheet.Cells(order(ORDER_ROW), 6).value = "否"
compInv(INV_STOCK) = compInv(INV_STOCK) - requiredQty
stats.OrdersInsufficient = stats.OrdersInsufficient + 1
End If
NextOrder:
Next i
End Sub
'=====================================================================
' 函数: GetOrderSheet
' 功能: 获取[产品订单]工作表
' 返回: Worksheet
'=====================================================================
Private Function GetOrderSheet() As Worksheet
On Error Resume Next
Set GetOrderSheet = ThisWorkbook.Worksheets("产品订单")
On Error GoTo 0
End Function
'=====================================================================
' 函数: GetInventorySheet
' 功能: 获取[现存量]工作表
' 返回: Worksheet
'=====================================================================
Private Function GetInventorySheet() As Worksheet
On Error Resume Next
Set GetInventorySheet = ThisWorkbook.Worksheets("现存量")
On Error GoTo 0
End Function
'=====================================================================
' 函数: GetBomSheet
' 功能: 获取[平台配置清单]工作表
' 返回: Worksheet
'=====================================================================
Private Function GetBomSheet() As Worksheet
On Error Resume Next
Set GetBomSheet = ThisWorkbook.Worksheets("平台配置清单")
On Error GoTo 0
End Function
'=====================================================================
' 函数: JoinCollection
' 功能: 将集合内容连接为字符串
' 参数: coll - 集合
' separator - 分隔符
' 返回: String
'=====================================================================
Private Function JoinCollection(coll As Collection, separator As String) As String
Dim result As String
result = ""
Dim item As Variant
Dim isFirst As Boolean
isFirst = True
For Each item In coll
If Not isFirst Then
result = result & separator
End If
result = result & CStr(item)
isFirst = False
Next item
JoinCollection = result
End Function

View File

@@ -0,0 +1,586 @@
'=====================================================================
' 模块名: ErrorAnalysisModule
' 功能: BOM匹配异常分析模块仅提取报错订单拆分多行并自动回溯"未匹配参数"
' 特性: 采用"特征权重算法"解决模糊平局(Tie)导致的参数误报问题
'=====================================================================
Option Explicit
' 提取条件配置
Private Const CONDITION_CONFIG = "azxs,安装形式|bkxs,表壳形式|gclj,过程连接|jycz,接液材质|lcfw,量程范围|fjgn,附加功能"
' 指定输出[BOM匹配异常报表]的表头所在行号
Private Const OUTPUT_HEADER_ROW As Long = 10
'=====================================================================
' 过程: GenerateErrorAnalysisReport
' 功能: 批量处理产品型号输出BOM匹配异常报表
'=====================================================================
Public Sub GenerateErrorAnalysisReport()
On Error GoTo ErrorHandler
Dim startTime As Double
startTime = Timer
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' 获取工作表
Dim orderSheet As Worksheet
Dim bomSheet As Worksheet
Dim outputSheet As Worksheet
Set orderSheet = GetOrderSheet()
If orderSheet Is Nothing Then
RestoreAppStatus
MsgBox "未找到[产品订单]工作表!", vbCritical
Exit Sub
End If
Set bomSheet = GetBomSheet()
If bomSheet Is Nothing Then
RestoreAppStatus
MsgBox "未找到[平台配置清单]工作表!", vbCritical
Exit Sub
End If
' 初始化BOM提取器
Dim BomExtractor As BomExtractor
Set BomExtractor = New BomExtractor
BomExtractor.SetWorksheet bomSheet
If Not BomExtractor.LoadBomData Then
RestoreAppStatus
MsgBox "加载BOM数据失败:" & BomExtractor.GetErrorSummary, vbCritical
Exit Sub
End If
' 获取或创建输出表
Set outputSheet = CreateErrorOutputSheet()
WriteOutputHeader outputSheet
' 获取筛选后的订单数据
Dim lastRow As Long
lastRow = orderSheet.Cells(orderSheet.Rows.count, 3).End(xlUp).row
If lastRow < 2 Then
RestoreAppStatus
MsgBox "[产品订单]工作表中没有数据!", vbExclamation
Exit Sub
End If
Dim sourceDataArr As Variant
sourceDataArr = orderSheet.Range("A2:F" & lastRow).value
Dim visibleRange As Range
On Error Resume Next
Set visibleRange = orderSheet.Range("A2:A" & lastRow).SpecialCells(xlCellTypeVisible)
On Error GoTo ErrorHandler
If visibleRange Is Nothing Then
RestoreAppStatus
MsgBox "当前筛选状态下没有可见的数据。", vbInformation
Exit Sub
End If
' 初始化正则表达式引擎 (只初始化一次,提速)
Dim regEx As Object
Set regEx = CreateObject("VBScript.RegExp")
regEx.Global = True
regEx.IgnoreCase = True
' 匹配如 azxs=A0, fjgn!=N1 这样的条件结构
regEx.Pattern = "(azxs|bkxs|gclj|jycz|lcfw|fjgn)\s*(!=|=)\s*([A-Za-z0-9_]+)"
Dim outputData As Collection
Set outputData = New Collection
Dim cell As Range
Dim arrIndex As Long
Dim totalProcessed As Long
Dim errorOrdersCount As Long
Dim errorRowsCount As Long
totalProcessed = 0
errorOrdersCount = 0
errorRowsCount = 0
' 遍历可见订单
For Each cell In visibleRange
arrIndex = cell.row - 1
Dim totalQueueNum As String
Dim orderNumber As String
Dim modelString As String
Dim componentPriority As String
totalQueueNum = Trim(sourceDataArr(arrIndex, 1))
orderNumber = Trim(sourceDataArr(arrIndex, 2))
modelString = Trim(sourceDataArr(arrIndex, 3))
componentPriority = Trim(sourceDataArr(arrIndex, 6))
If modelString <> "" Then
totalProcessed = totalProcessed + 1
' 解析并匹配BOM
Dim parser As ProductModelParser
Set parser = New ProductModelParser
Dim hasError As Boolean
hasError = False
Dim errors As String
errors = ""
If Not parser.Parse(modelString) Then
hasError = True
errors = "型号解析失败: " & parser.ErrorMessage
Else
' 提取逻辑
BomExtractor.ClearExcludeCategories
If UCase(componentPriority) = "否" Or componentPriority = "0" Or componentPriority = "FALSE" Then
Dim excludeCats As New Collection
excludeCats.Add "部件"
BomExtractor.SetExcludeCategories excludeCats
End If
Dim matchedItems As Collection
Set matchedItems = BomExtractor.ExtractBom(parser.conditions)
errors = BomExtractor.GetErrorSummary()
If errors <> "" Or matchedItems.count = 0 Then
hasError = True
If errors = "" And matchedItems.count = 0 Then
errors = "完全未匹配到物料"
End If
End If
' 深度检查BOM行自身的报错如"匹配到多条"
Dim item As BomItem
For Each item In matchedItems
If item.MatchError <> "" Then
hasError = True
errors = errors & item.category & ":" & item.MatchError & ";"
End If
Next item
End If
' 如果存在错误,拆分为多行并寻找未匹配参数
If hasError Then
errorOrdersCount = errorOrdersCount + 1
Dim errArray() As String
errArray = Split(errors, ";")
Dim i As Long
For i = LBound(errArray) To UBound(errArray)
Dim singleError As String
singleError = Trim(errArray(i))
If singleError <> "" Then
Dim unmatchedValues As String
unmatchedValues = "无法精准定位:无法精准定位"
' 如果是型号解析失败,跳过溯源
If InStr(singleError, "型号解析失败") = 0 And InStr(singleError, "完全未匹配到物料") = 0 Then
Dim targetCategory As String
targetCategory = ExtractCategoryName(singleError)
If targetCategory <> "" Then
' 核心:调用带权重的重合度算法定位冲突参数(通过 | 分隔,内部用 : 分隔键值)
unmatchedValues = FindUnmatchedParameter(targetCategory, parser.conditions, BomExtractor.GetAllItems(), regEx)
End If
End If
' ---> 拆分未匹配参数,避免糅合在一起
Dim unmatchArr() As String
unmatchArr = Split(unmatchedValues, "|")
Dim j As Long
For j = LBound(unmatchArr) To UBound(unmatchArr)
Dim singleUnmatch As String
singleUnmatch = Trim(unmatchArr(j))
If singleUnmatch <> "" Then
Dim uParam As String
Dim uValue As String
Dim colonPos As Long
colonPos = InStr(singleUnmatch, ":")
' 拆分键和值
If colonPos > 0 Then
uParam = Left(singleUnmatch, colonPos - 1)
uValue = Mid(singleUnmatch, colonPos + 1)
Else
uParam = singleUnmatch
uValue = singleUnmatch
End If
outputData.Add CreateErrorRowArray(totalQueueNum, orderNumber, modelString, parser.conditions, uParam, uValue, singleError)
errorRowsCount = errorRowsCount + 1
End If
Next j
End If
Next i
End If
End If
Next cell
' 批量写入数据
If outputData.count > 0 Then
WriteBatchData outputSheet, outputData
Else
MsgBox "太棒了所选订单均完美匹配BOM未发现任何异常。", vbInformation
End If
' 格式化表格
FormatOutputSheet outputSheet
RestoreAppStatus
Dim elapsedTime As Double
elapsedTime = Timer - startTime
MsgBox "异常分析完成!" & vbCrLf & _
"共检查订单: " & totalProcessed & vbCrLf & _
"发现异常订单: " & errorOrdersCount & vbCrLf & _
"生成异常明细: " & errorRowsCount & " 行" & vbCrLf & _
"用时: " & Format(elapsedTime, "0.00") & "秒", vbInformation
outputSheet.Activate
Exit Sub
ErrorHandler:
RestoreAppStatus
MsgBox "异常分析发生错误: " & Err.Description, vbCritical
End Sub
'=====================================================================
' 核心算法: FindUnmatchedParameter (带权重的最大特征重合度算法)
' 功能: 分析BOM库找出与当前订单特征最相似的物料并提取冲突(未匹配)的参数值
'=====================================================================
Private Function FindUnmatchedParameter(category As String, productConds As Object, allBomItems As Collection, regEx As Object) As String
' 使用 Long 类型,因为加入权重后得分会超过 Integer 上限
Dim maxScore As Long
maxScore = -1
Dim bestConflictKeys As String
bestConflictKeys = ""
Dim item As BomItem
' 遍历BOM库中同类别的所有物料
For Each item In allBomItems
If item.category = category And Trim(item.SelectCondition) <> "" Then
Dim allowed As Object
Set allowed = CreateObject("Scripting.Dictionary")
Dim forbidden As Object
Set forbidden = CreateObject("Scripting.Dictionary")
' 使用正则提取该物料的所有约束条件 (如 azxs=A0)
Dim matches As Object
Set matches = regEx.Execute(item.SelectCondition)
Dim match As Object
For Each match In matches
Dim k As String, op As String, v As String
k = match.SubMatches(0)
op = Trim(match.SubMatches(1))
v = Trim(match.SubMatches(2))
If op = "=" Then
If Not allowed.Exists(k) Then allowed(k) = "|"
allowed(k) = allowed(k) & v & "|"
ElseIf op = "!=" Or op = "<>" Then
If Not forbidden.Exists(k) Then forbidden(k) = "|"
forbidden(k) = forbidden(k) & v & "|"
End If
Next match
' 合并出现过的所有参数键
Dim allRuleKeys As Object
Set allRuleKeys = CreateObject("Scripting.Dictionary")
Dim vKey As Variant
For Each vKey In allowed.Keys: allRuleKeys(vKey) = True: Next vKey
For Each vKey In forbidden.Keys: allRuleKeys(vKey) = True: Next vKey
Dim currentScore As Long
currentScore = 0
Dim currentConflicts As String
currentConflicts = ""
' 计算该物料与实际订单参数的重合度得分
Dim keyVar As Variant
For Each keyVar In allRuleKeys.Keys
Dim keyStr As String
keyStr = CStr(keyVar)
Dim prodVal As String
If productConds.Exists(keyStr) Then prodVal = productConds(keyStr) Else prodVal = ""
Dim isMatch As Boolean
isMatch = False
If allowed.Exists(keyStr) Then
' 如果实际值包含在允许值中,则得分
If InStr(allowed(keyStr), "|" & prodVal & "|") > 0 Then
isMatch = True
End If
ElseIf forbidden.Exists(keyStr) Then
' 如果没有允许值限制,只有禁止值限制,且实际值不在禁止值中,则得分
If InStr(forbidden(keyStr), "|" & prodVal & "|") = 0 Then
isMatch = True
End If
End If
If isMatch Then
' 【核心修改】引入特征权重,让系统具备业务直觉
currentScore = currentScore + GetFeatureWeight(keyStr)
Else
currentConflicts = currentConflicts & keyStr & ","
End If
Next keyVar
' 更新最高得分记录
If currentScore > maxScore Then
maxScore = currentScore
bestConflictKeys = currentConflicts
ElseIf currentScore = maxScore And currentScore > 0 Then
' 如果权重得分依然相同,合并所有可能的冲突原因
Dim keysArray() As String
keysArray = Split(currentConflicts, ",")
Dim cKey As Variant
For Each cKey In keysArray
If Trim(cKey) <> "" And InStr(bestConflictKeys, cKey & ",") = 0 Then
bestConflictKeys = bestConflictKeys & cKey & ","
End If
Next cKey
End If
End If
Next item
' 将最高分的冲突Key翻译为实际的参数值
If bestConflictKeys <> "" Then
Dim resultStr As String
resultStr = ""
Dim finalKeys() As String
finalKeys = Split(bestConflictKeys, ",")
Dim fKey As Variant
For Each fKey In finalKeys
If Trim(fKey) <> "" Then
Dim actVal As String
If productConds.Exists(fKey) Then actVal = productConds(fKey) Else actVal = "无值"
' 使用 | 作为条目分隔符,使用 : 分隔键和值
Dim pairStr As String
pairStr = fKey & ":" & actVal
If resultStr = "" Then
resultStr = pairStr
Else
If InStr("|" & resultStr & "|", "|" & pairStr & "|") = 0 Then
resultStr = resultStr & "|" & pairStr
End If
End If
End If
Next fKey
If resultStr <> "" Then
FindUnmatchedParameter = resultStr
Else
FindUnmatchedParameter = "无法精准定位:无法精准定位"
End If
Else
FindUnmatchedParameter = "无法精准定位:无法精准定位"
End If
End Function
'=====================================================================
' 辅助函数: GetFeatureWeight
' 功能: 获取字段的匹配权重,严格保证高优先级特征的决定性
'=====================================================================
Private Function GetFeatureWeight(keyStr As String) As Long
Select Case LCase(Trim(keyStr))
Case "azxs"
GetFeatureWeight = 10000 ' 安装形式 - 决定物理结构,最重要
Case "bkxs"
GetFeatureWeight = 1000 ' 表壳形式
Case "gclj"
GetFeatureWeight = 100 ' 过程连接
Case "jycz"
GetFeatureWeight = 50 ' 接液材质
Case "lcfw"
GetFeatureWeight = 10 ' 量程范围
Case "fjgn"
GetFeatureWeight = 1 ' 附加功能
Case Else
GetFeatureWeight = 0
End Select
End Function
'=====================================================================
' 辅助函数: ExtractCategoryName
' 功能: 从报错文本如 "必需类别[部件]未匹配" 中提取出 "部件"
'=====================================================================
Private Function ExtractCategoryName(errorMsg As String) As String
Dim startPos As Long
Dim endPos As Long
startPos = InStr(errorMsg, "[")
endPos = InStr(errorMsg, "]")
If startPos > 0 And endPos > startPos Then
ExtractCategoryName = Mid(errorMsg, startPos + 1, endPos - startPos - 1)
Else
ExtractCategoryName = ""
End If
End Function
'=====================================================================
' 过程: WriteOutputHeader
'=====================================================================
Private Sub WriteOutputHeader(ws As Worksheet)
Dim col As Long
col = 1
ws.Cells(OUTPUT_HEADER_ROW, col).value = "总排号": col = col + 1
ws.Cells(OUTPUT_HEADER_ROW, col).value = "生产订单号": col = col + 1
ws.Cells(OUTPUT_HEADER_ROW, col).value = "产品型号": col = col + 1
Dim configs() As String
configs = Split(CONDITION_CONFIG, "|")
Dim i As Long
For i = LBound(configs) To UBound(configs)
ws.Cells(OUTPUT_HEADER_ROW, col).value = Trim(Split(configs(i), ",")(1))
col = col + 1
Next i
ws.Cells(OUTPUT_HEADER_ROW, col).value = "未匹配参数": col = col + 1
ws.Cells(OUTPUT_HEADER_ROW, col).value = "未匹配参数值": col = col + 1
ws.Cells(OUTPUT_HEADER_ROW, col).value = "提取备注": col = col + 1
End Sub
'=====================================================================
' 函数: CreateErrorRowArray
' 功能: 构建输出的一行数据
'=====================================================================
Private Function CreateErrorRowArray(totalQueueNum As String, orderNumber As String, _
modelStr As String, conditions As Object, _
unmatchedParam As String, unmatchedValue As String, errorNote As String) As Variant()
Dim configs() As String
configs = Split(CONDITION_CONFIG, "|")
Dim totalCols As Long
totalCols = 3 + UBound(configs) - LBound(configs) + 1 + 3
ReDim rowData(1 To totalCols) As Variant
Dim col As Long
col = 1
rowData(col) = totalQueueNum: col = col + 1
rowData(col) = orderNumber: col = col + 1
rowData(col) = modelStr: col = col + 1
Dim i As Long
For i = LBound(configs) To UBound(configs)
Dim key As String
key = Trim(Split(configs(i), ",")(0))
If conditions.Exists(key) Then
rowData(col) = conditions(key)
Else
rowData(col) = ""
End If
col = col + 1
Next i
rowData(col) = unmatchedParam: col = col + 1
rowData(col) = unmatchedValue: col = col + 1
rowData(col) = errorNote: col = col + 1
CreateErrorRowArray = rowData
End Function
'=====================================================================
' 过程: WriteBatchData
'=====================================================================
Private Sub WriteBatchData(ws As Worksheet, outputData As Collection)
Dim firstRow As Variant
firstRow = outputData(1)
Dim rowCount As Long
Dim colCount As Long
rowCount = outputData.count
colCount = UBound(firstRow) - LBound(firstRow) + 1
Dim resultData() As Variant
ReDim resultData(1 To rowCount, 1 To colCount)
Dim i As Long, j As Long
Dim rowArray As Variant
For i = 1 To rowCount
rowArray = outputData(i)
For j = 1 To colCount
resultData(i, j) = rowArray(j)
Next j
Next i
' 数据从表头的下一行开始写入
ws.Cells(OUTPUT_HEADER_ROW + 1, 1).Resize(rowCount, colCount).value = resultData
End Sub
'=====================================================================
' 辅助过程
'=====================================================================
Private Function GetOrderSheet() As Worksheet
On Error Resume Next
Set GetOrderSheet = ThisWorkbook.Worksheets("产品订单")
If GetOrderSheet Is Nothing Then Set GetOrderSheet = ActiveSheet
On Error GoTo 0
End Function
Private Function GetBomSheet() As Worksheet
On Error Resume Next
Set GetBomSheet = ThisWorkbook.Worksheets("平台配置清单")
On Error GoTo 0
End Function
Private Function CreateErrorOutputSheet() As Worksheet
Dim wsName As String
wsName = "BOM匹配异常报表"
On Error Resume Next
Set CreateErrorOutputSheet = ThisWorkbook.Worksheets(wsName)
On Error GoTo 0
If CreateErrorOutputSheet Is Nothing Then
Set CreateErrorOutputSheet = ThisWorkbook.Worksheets.Add
CreateErrorOutputSheet.Name = wsName
Else
' 只清空表头及其以下的数据,保留表头以上的可能存在的内容
CreateErrorOutputSheet.Rows(OUTPUT_HEADER_ROW & ":" & CreateErrorOutputSheet.Rows.count).Clear
End If
End Function
Private Sub FormatOutputSheet(ws As Worksheet)
On Error Resume Next
With ws.Rows(OUTPUT_HEADER_ROW)
.Font.Bold = True
.Interior.Color = RGB(244, 176, 132) ' 橙色背景,突出异常属性
.HorizontalAlignment = xlCenter
End With
' 将"未匹配参数"列和"未匹配参数值"列加粗显示,颜色标红
Dim unmatchValCol As Long
unmatchValCol = ws.Cells(OUTPUT_HEADER_ROW, ws.Columns.count).End(xlToLeft).Column - 1
Dim unmatchParamCol As Long
unmatchParamCol = unmatchValCol - 1
If unmatchParamCol > 0 Then
ws.Columns(unmatchParamCol).Font.Color = RGB(255, 0, 0)
ws.Columns(unmatchParamCol).Font.Bold = True
ws.Columns(unmatchValCol).Font.Color = RGB(255, 0, 0)
ws.Columns(unmatchValCol).Font.Bold = True
End If
On Error GoTo 0
End Sub
Private Sub RestoreAppStatus()
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub

476
VBA/Modules/MainModule.bas Normal file
View File

@@ -0,0 +1,476 @@
'=====================================================================
' 模块名: MainModule
' 功能: 主控模块,处理产品型号提取和BOM匹配的上层逻辑
' 特性: [已重构] 支持仅对筛选后的数据进行处理,采用内存极速读取
'=====================================================================
Option Explicit
'=====================================================================
' 常量定义
'=====================================================================
' 提取条件配置(可灵活扩展)
Private Const CONDITION_CONFIG = "azxs,安装形式|bkxs,表壳形式|gclj,过程连接|jycz,接液材质|lcfw,量程范围|fjgn,附加功能"
'=====================================================================
' 过程: ProcessProductModels
' 功能: 批量处理产品型号并输出结果
' 说明: 这是主入口程序
'=====================================================================
Public Sub ProcessProductModels()
On Error GoTo ErrorHandler
Dim startTime As Double
startTime = Timer
' 关闭屏幕刷新提升速度
Application.ScreenUpdating = False
' 准备输入输出
Dim inputSheet As Worksheet
Dim outputSheet As Worksheet
Dim bomSheet As Worksheet
' 获取工作表
Set inputSheet = GetInputSheet()
If inputSheet Is Nothing Then
Application.ScreenUpdating = True
MsgBox "未找到输入工作表,请确保工作簿中有包含订单数据的工作表", vbCritical
Exit Sub
End If
' 获取BOM库工作表
Set bomSheet = GetBomSheet()
If bomSheet Is Nothing Then
Application.ScreenUpdating = True
MsgBox "未找到'平台配置清单'工作表,请确保BOM数据存在", vbCritical
Exit Sub
End If
' 创建或获取输出工作表
Set outputSheet = CreateOutputSheet()
' 初始化BOM提取器
Dim BomExtractor As BomExtractor
Set BomExtractor = New BomExtractor
BomExtractor.SetWorksheet bomSheet
If Not BomExtractor.LoadBomData Then
Application.ScreenUpdating = True
MsgBox "加载BOM数据失败:" & BomExtractor.GetErrorSummary, vbCritical
Exit Sub
End If
Dim lastRow As Long
lastRow = inputSheet.Cells(inputSheet.Rows.count, 1).End(xlUp).row
If lastRow < 2 Then
Application.ScreenUpdating = True
MsgBox "[产品订单]工作表中没有数据!", vbExclamation
Exit Sub
End If
' 【性能核心】将输入数据全量读入内存数组
Dim sourceDataArr As Variant
sourceDataArr = inputSheet.Range("A2:F" & lastRow).value
' 【筛选核心】获取可见的单元格区域
Dim visibleRange As Range
On Error Resume Next
Set visibleRange = inputSheet.Range("A2:A" & lastRow).SpecialCells(xlCellTypeVisible)
On Error GoTo ErrorHandler
If visibleRange Is Nothing Then
Application.ScreenUpdating = True
MsgBox "当前筛选状态下没有可见的数据。", vbInformation
Exit Sub
End If
' 写入输出表头
WriteOutputHeader outputSheet
' 收集所有输出数据
Dim outputData As Collection
Set outputData = New Collection
Dim cell As Range
Dim arrIndex As Long
Dim modelString As String
Dim processedCount As Long
processedCount = 0
' 仅遍历筛选出来的可见行
For Each cell In visibleRange
Dim totalQueueNum As String
Dim orderNumber As String
Dim componentPriority As String
' 将工作表行号映射到数组索引
arrIndex = cell.row - 1
' 从内存数组中极速读取对应字段
totalQueueNum = Trim(sourceDataArr(arrIndex, 1)) ' A列总排号
orderNumber = Trim(sourceDataArr(arrIndex, 2)) ' B列生产订单号
modelString = Trim(sourceDataArr(arrIndex, 3)) ' C列产品型号
componentPriority = Trim(sourceDataArr(arrIndex, 6)) ' F列部件优先
If modelString <> "" Then
' 处理单个型号,收集数据
ProcessSingleModel totalQueueNum, orderNumber, modelString, componentPriority, BomExtractor, outputData
processedCount = processedCount + 1
End If
Next cell
' 批量写入数据到工作表
If outputData.count > 0 Then
WriteBatchData outputSheet, outputData
End If
' 格式化输出表
FormatOutputSheet outputSheet
Dim elapsedTime As Double
elapsedTime = Timer - startTime
Application.ScreenUpdating = True
MsgBox "处理完成!" & vbCrLf & _
"处理筛选型号数: " & processedCount & vbCrLf & _
"用时: " & Format(elapsedTime, "0.00") & "秒", vbInformation
' 激活输出表
outputSheet.Activate
Exit Sub
ErrorHandler:
Application.ScreenUpdating = True
MsgBox "处理异常: " & Err.Description, vbCritical
End Sub
'=====================================================================
' 过程: ProcessSingleModel
' 功能: 处理单个产品型号,将数据添加到输出集合
' 参数: orderNumber - 生产订单号
' modelString - 产品型号字符串
' componentPriority - 部件优先标志("是"或"否"
' bomExtractor - BOM提取器对象
' outputData - 输出数据集合
'=====================================================================
Private Sub ProcessSingleModel(totalQueueNum As String, _
orderNumber As String, _
modelString As String, _
componentPriority As String, _
BomExtractor As BomExtractor, _
outputData As Collection)
On Error Resume Next
' 根据部件优先设置排除类别
BomExtractor.ClearExcludeCategories
If UCase(componentPriority) = "否" Or componentPriority = "0" Or componentPriority = "FALSE" Then
Dim excludeCats As New Collection
excludeCats.Add "部件"
BomExtractor.SetExcludeCategories excludeCats
End If
' 解析产品型号
Dim parser As ProductModelParser
Set parser = New ProductModelParser
Dim extractNote As String
extractNote = ""
If Not parser.Parse(modelString) Then
' 解析失败
extractNote = "解析失败: " & parser.ErrorMessage
outputData.Add CreateOutputRowArray(totalQueueNum, orderNumber, modelString, parser.conditions, extractNote, Nothing)
Exit Sub
End If
' 提取BOM
Dim matchedItems As Collection
Set matchedItems = BomExtractor.ExtractBom(parser.conditions)
' 获取错误信息
Dim bomErrors As String
bomErrors = BomExtractor.GetErrorSummary
If bomErrors <> "" Then
extractNote = bomErrors
End If
' 输出结果
If matchedItems.count = 0 Then
' 没有匹配项
If extractNote = "" Then
extractNote = "未匹配到任何物料"
End If
outputData.Add CreateOutputRowArray(totalQueueNum, orderNumber, modelString, parser.conditions, extractNote, Nothing)
Else
' 输出每个匹配的物料
Dim item As BomItem
Dim isFirst As Boolean
isFirst = True
For Each item In matchedItems
Dim itemNote As String
itemNote = extractNote
' 添加物料特定的错误
If item.MatchError <> "" Then
If itemNote <> "" Then itemNote = itemNote & "; "
itemNote = itemNote & item.MatchError
End If
If isFirst Then
' 首行保留总排号和订单号
outputData.Add CreateOutputRowArray(totalQueueNum, orderNumber, modelString, parser.conditions, itemNote, item)
isFirst = False
Else
' 同一个型号的后续BOM项总排号和订单号留空以保持报表整洁
outputData.Add CreateOutputRowArray("", "", modelString, parser.conditions, itemNote, item)
End If
Next item
End If
End Sub
'=====================================================================
' 过程: WriteOutputHeader
' 功能: 写入输出表头
' 参数: ws - 工作表对象
'=====================================================================
Private Sub WriteOutputHeader(ws As Worksheet)
Dim col As Long
col = 1
' 新增总排号表头
ws.Cells(1, col).value = "总排号": col = col + 1
ws.Cells(1, col).value = "生产订单号": col = col + 1
ws.Cells(1, col).value = "产品型号": col = col + 1
' 写入条件字段表头
Dim conditions() As String
Dim labels() As String
GetConditionConfig conditions, labels
Dim i As Long
For i = LBound(conditions) To UBound(conditions)
ws.Cells(1, col).value = labels(i)
col = col + 1
Next i
' BOM字段表头
ws.Cells(1, col).value = "行号": col = col + 1
ws.Cells(1, col).value = "模块": col = col + 1
' ws.Cells(1, col).Value = "代号": col = col + 1 <-- 已移除
ws.Cells(1, col).value = "名称": col = col + 1
ws.Cells(1, col).value = "数量": col = col + 1
ws.Cells(1, col).value = "类别": col = col + 1
ws.Cells(1, col).value = "66代码": col = col + 1
ws.Cells(1, col).value = "提取备注": col = col + 1
End Sub
'=====================================================================
' 函数: CreateOutputRowArray
' 功能: 创建输出行数据的数组
' 参数: orderNumber - 生产订单号
' FullModel - 完整型号
' Conditions - 条件字典
' note - 备注
' item - BOM项(可为Nothing)
' 返回: Variant() - 行数据数组
'=====================================================================
Private Function CreateOutputRowArray(totalQueueNum As String, _
orderNumber As String, _
FullModel As String, _
conditions As Object, _
note As String, _
item As BomItem) As Variant()
' 获取条件配置
Dim condNames() As String
Dim labels() As String
GetConditionConfig condNames, labels
' 计算总列数3 (排号+订单+型号) + 条件数 + 7 (BOM字段减去代号后剩6个 + 1个备注)
Dim totalCols As Long
totalCols = 3 + (UBound(condNames) - LBound(condNames) + 1) + 7
' 创建数组
ReDim rowData(1 To totalCols) As Variant
Dim col As Long
col = 1
' 基础信息
rowData(col) = totalQueueNum: col = col + 1
rowData(col) = orderNumber: col = col + 1
rowData(col) = FullModel: col = col + 1
' 写入条件值
Dim i As Long
For i = LBound(condNames) To UBound(condNames)
If conditions.Exists(condNames(i)) Then
rowData(col) = conditions(condNames(i))
Else
rowData(col) = ""
End If
col = col + 1
Next i
' 写入BOM数据
If Not item Is Nothing Then
rowData(col) = item.RowNumber: col = col + 1
rowData(col) = item.Module: col = col + 1
' rowData(col) = item.code: col = col + 1 <-- 已移除
rowData(col) = item.Name: col = col + 1
rowData(col) = item.Quantity: col = col + 1
rowData(col) = item.category: col = col + 1
rowData(col) = item.Code66: col = col + 1
Else
' 跳过BOM字段 (原本是7个字段去掉代号后变成6个字段)
col = col + 6
End If
' 备注
rowData(col) = note
CreateOutputRowArray = rowData
End Function
'=====================================================================
' 过程: WriteBatchData
' 功能: 批量写入数据到工作表
' 参数: ws - 工作表对象
' outputData - 输出数据集合
'=====================================================================
Private Sub WriteBatchData(ws As Worksheet, outputData As Collection)
' 如果没有数据,直接返回
If outputData.count = 0 Then
Exit Sub
End If
' 获取第一行数据来确定列数
Dim firstRow As Variant
firstRow = outputData(1)
Dim rowCount As Long
Dim colCount As Long
rowCount = outputData.count
colCount = UBound(firstRow) - LBound(firstRow) + 1
' 创建二维数组
Dim resultData() As Variant
ReDim resultData(1 To rowCount, 1 To colCount)
' 填充数据到二维数组
Dim i As Long
Dim j As Long
Dim rowArray As Variant
For i = 1 To rowCount
rowArray = outputData(i)
For j = 1 To colCount
resultData(i, j) = rowArray(j)
Next j
Next i
' 一次性写入工作表从第2行开始
ws.Range("A2").Resize(rowCount, colCount).value = resultData
End Sub
'=====================================================================
' 过程: GetConditionConfig
' 功能: 获取条件配置
' 参数: outNames - 输出条件名称数组
' outLabels - 输出条件标签数组
'=====================================================================
Private Sub GetConditionConfig(ByRef outNames() As String, ByRef outLabels() As String)
Dim configs() As String
configs = Split(CONDITION_CONFIG, "|")
ReDim outNames(LBound(configs) To UBound(configs))
ReDim outLabels(LBound(configs) To UBound(configs))
Dim i As Long
Dim parts() As String
For i = LBound(configs) To UBound(configs)
parts = Split(configs(i), ",")
outNames(i) = Trim(parts(0))
outLabels(i) = Trim(parts(1))
Next i
End Sub
'=====================================================================
' 函数: GetInputSheet
' 功能: 获取输入工作表
' 返回: Worksheet - 输入工作表对象
'=====================================================================
Private Function GetInputSheet() As Worksheet
' 这里假设输入数据在当前活动工作表或名为"订单"的工作表
On Error Resume Next
Set GetInputSheet = ThisWorkbook.Worksheets("产品订单")
If GetInputSheet Is Nothing Then
Set GetInputSheet = ActiveSheet
End If
On Error GoTo 0
End Function
'=====================================================================
' 函数: GetBomSheet
' 功能: 获取BOM工作表
' 返回: Worksheet - BOM工作表对象
'=====================================================================
Private Function GetBomSheet() As Worksheet
On Error Resume Next
Set GetBomSheet = ThisWorkbook.Worksheets("平台配置清单")
On Error GoTo 0
End Function
'=====================================================================
' 函数: CreateOutputSheet
' 功能: 创建或获取输出工作表
' 返回: Worksheet - 输出工作表对象
'=====================================================================
Private Function CreateOutputSheet() As Worksheet
Dim wsName As String
wsName = "BOM提取结果"
On Error Resume Next
Set CreateOutputSheet = ThisWorkbook.Worksheets(wsName)
On Error GoTo 0
If CreateOutputSheet Is Nothing Then
Set CreateOutputSheet = ThisWorkbook.Worksheets.Add
CreateOutputSheet.Name = wsName
Else
' 清空现有数据
CreateOutputSheet.Cells.Clear
End If
End Function
'=====================================================================
' 过程: FormatOutputSheet
' 功能: 格式化输出工作表
' 参数: ws - 工作表对象
'=====================================================================
Private Sub FormatOutputSheet(ws As Worksheet)
On Error Resume Next
' 设置表头格式
With ws.Rows(1)
.Font.Bold = True
.Interior.Color = RGB(217, 217, 217)
.HorizontalAlignment = xlCenter
End With
' ' 自动调整列宽
' ws.Columns.AutoFit
'
' ' 冻结首行
' ws.Rows(2).Select
' 'ActiveWindow.FreezePanes = True
' ws.Cells(1, 1).Select
On Error GoTo 0
End Sub

View File

@@ -0,0 +1,217 @@
'=====================================================================
' 模块名: OrderValidationModule
' 功能: 订单物料有效性检查模块
' 说明: 检查[产品订单]中可见行的产品型号是否能成功提取BOM。
' 如果发生任何提取错误或无法匹配物料则在G列[是否领料]写入"否"。
' 特性: 采用内存极速读取,仅对筛选后的数据进行处理。
'=====================================================================
Option Explicit
'=====================================================================
' 过程: ValidateOrderMaterials
' 功能: 批量检查可见订单的BOM提取有效性
'=====================================================================
Public Sub ValidateOrderMaterials()
On Error GoTo ErrorHandler
Dim startTime As Double
startTime = Timer
' 提升性能:关闭屏幕更新和自动计算
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' 获取工作表
Dim orderSheet As Worksheet
Dim bomSheet As Worksheet
Set orderSheet = GetOrderSheet()
If orderSheet Is Nothing Then
RestoreAppStatus
MsgBox "未找到[产品订单]工作表!", vbCritical
Exit Sub
End If
Set bomSheet = GetBomSheet()
If bomSheet Is Nothing Then
RestoreAppStatus
MsgBox "未找到[平台配置清单]工作表!", vbCritical
Exit Sub
End If
' 初始化BOM提取器
Dim BomExtractor As BomExtractor
Set BomExtractor = New BomExtractor
BomExtractor.SetWorksheet bomSheet
If Not BomExtractor.LoadBomData Then
RestoreAppStatus
MsgBox "加载BOM数据失败:" & BomExtractor.GetErrorSummary, vbCritical
Exit Sub
End If
' 写入G列表头
orderSheet.Cells(1, 7).value = "是否领料"
Dim lastRow As Long
lastRow = orderSheet.Cells(orderSheet.Rows.count, 3).End(xlUp).row
If lastRow < 2 Then
RestoreAppStatus
MsgBox "[产品订单]工作表中没有需要处理的数据!", vbExclamation
Exit Sub
End If
' 【性能核心】将输入数据全量读入内存数组 (读取A到F列即可)
Dim sourceDataArr As Variant
sourceDataArr = orderSheet.Range("A2:F" & lastRow).value
' 【筛选核心】获取可见的单元格区域 (A列)
Dim visibleRange As Range
On Error Resume Next
Set visibleRange = orderSheet.Range("A2:A" & lastRow).SpecialCells(xlCellTypeVisible)
On Error GoTo ErrorHandler
If visibleRange Is Nothing Then
RestoreAppStatus
MsgBox "当前筛选状态下没有可见的数据。", vbInformation
Exit Sub
End If
Dim cell As Range
Dim arrIndex As Long
Dim modelString As String
Dim componentPriority As String
Dim processedCount As Long
Dim invalidCount As Long
processedCount = 0
invalidCount = 0
' 仅遍历筛选出来的可见行
For Each cell In visibleRange
' 将工作表行号映射到数组索引 (数据从第2行开始所以数组索引 = 行号 - 1)
arrIndex = cell.row - 1
' 从内存数组中极速读取所需的关键字段
modelString = Trim(sourceDataArr(arrIndex, 3)) ' C列产品型号
componentPriority = Trim(sourceDataArr(arrIndex, 6)) ' F列部件优先
If modelString <> "" Then
processedCount = processedCount + 1
' 调用校验逻辑判断是否存在BOM提取错误
If IsInvalidOrderBOM(modelString, componentPriority, BomExtractor) Then
' 如果无效/有报错直接在对应行的第7列(G列)写入"否"
' 正常订单不做任何处理,保留原样
orderSheet.Cells(cell.row, 7).value = "否"
invalidCount = invalidCount + 1
End If
End If
Next cell
' 恢复应用状态
RestoreAppStatus
Dim elapsedTime As Double
elapsedTime = Timer - startTime
MsgBox "有效性检查完成!" & vbCrLf & _
"共检查了 " & processedCount & " 个筛选订单。" & vbCrLf & _
"发现并标记了 " & invalidCount & " 个无效/报错订单。" & vbCrLf & _
"用时: " & Format(elapsedTime, "0.00") & " 秒", vbInformation
Exit Sub
ErrorHandler:
RestoreAppStatus
MsgBox "检查订单物料有效性时发生异常: " & Err.Description, vbCritical
End Sub
'=====================================================================
' 函数: IsInvalidOrderBOM
' 功能: 模拟BOM提取过程判定该订单是否存在错误
' 参数: modelString - 产品型号
' componentPriority - 部件优先标识
' BomExtractor - 已初始化的BOM提取器对象
' 返回: Boolean - 只要发生任何错误或未匹配到物料,则返回 True
'=====================================================================
Private Function IsInvalidOrderBOM(modelString As String, _
componentPriority As String, _
BomExtractor As BomExtractor) As Boolean
On Error Resume Next
' 默认认为它是有效的,直到发现错误
IsInvalidOrderBOM = False
' 1. 根据部件优先设置排除类别
BomExtractor.ClearExcludeCategories
If UCase(componentPriority) = "否" Or componentPriority = "0" Or componentPriority = "FALSE" Then
Dim excludeCats As New Collection
excludeCats.Add "部件"
BomExtractor.SetExcludeCategories excludeCats
End If
' 2. 解析产品型号
Dim parser As ProductModelParser
Set parser = New ProductModelParser
If Not parser.Parse(modelString) Then
' 解析失败,属于无效订单
IsInvalidOrderBOM = True
Exit Function
End If
' 3. 提取BOM
Dim matchedItems As Collection
Set matchedItems = BomExtractor.ExtractBom(parser.conditions)
' 4. 检查 BOM 提取器全局错误日志
If BomExtractor.GetErrorSummary <> "" Then
IsInvalidOrderBOM = True
Exit Function
End If
' 5. 检查是否完全没有匹配到物料
If matchedItems.count = 0 Then
IsInvalidOrderBOM = True
Exit Function
End If
' 6. 深度检查:遍历提取出的每一项,看是否存在子项报错
Dim item As BomItem
For Each item In matchedItems
If item.MatchError <> "" Then
IsInvalidOrderBOM = True
Exit Function
End If
Next item
On Error GoTo 0
End Function
'=====================================================================
' 辅助过程: RestoreAppStatus
' 功能: 恢复Excel应用程序的状态
'=====================================================================
Private Sub RestoreAppStatus()
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
'=====================================================================
' 辅助函数: 获取所需工作表
'=====================================================================
Private Function GetOrderSheet() As Worksheet
On Error Resume Next
Set GetOrderSheet = ThisWorkbook.Worksheets("产品订单")
On Error GoTo 0
End Function
Private Function GetBomSheet() As Worksheet
On Error Resume Next
Set GetBomSheet = ThisWorkbook.Worksheets("平台配置清单")
On Error GoTo 0
End Function

336
VBA/Modules/TestModule.bas Normal file
View File

@@ -0,0 +1,336 @@
'=====================================================================
' 模块名: TestModule
' 功能: 单元测试模块
'=====================================================================
Option Explicit
'=====================================================================
' 过程: RunAllTests
' 功能: 运行所有测试
'=====================================================================
Public Sub RunAllTests()
Debug.Print "=========================================="
Debug.Print "开始运行所有测试"
Debug.Print "时间: " & Now
Debug.Print "=========================================="
Debug.Print ""
' 运行各个测试
TestProductModelParser
TestConditionEvaluator
TestBomExtractor
Debug.Print ""
Debug.Print "=========================================="
Debug.Print "所有测试完成"
Debug.Print "=========================================="
MsgBox "所有测试完成,请查看立即窗口查看结果", vbInformation
End Sub
'=====================================================================
' 过程: TestProductModelParser
' 功能: 测试产品型号解析器
'=====================================================================
Public Sub TestProductModelParser()
Debug.Print ">>> 测试 ProductModelParser"
Debug.Print ""
Dim parser As ProductModelParser
Set parser = New ProductModelParser
' 测试用例1: 正常型号
Debug.Print "测试用例1: 正常型号"
Dim testModel1 As String
testModel1 = "YTHN-100.A0.531.M203.M06.Y3|BP-088.2312.M06.0A3"
If parser.Parse(testModel1) Then
Debug.Print " 解析成功"
Debug.Print " 表头型号: " & parser.HeaderModel
Debug.Print " 条件:"
Debug.Print " azxs = " & parser.GetConditionValue("azxs")
Debug.Print " bkxs = " & parser.GetConditionValue("bkxs")
Debug.Print " gclj = " & parser.GetConditionValue("gclj")
Debug.Print " jycz = " & parser.GetConditionValue("jycz")
Debug.Print " lcfw = " & parser.GetConditionValue("lcfw")
' 验证结果
AssertEquals "azxs", "A0", parser.GetConditionValue("azxs")
AssertEquals "bkxs", "531", parser.GetConditionValue("bkxs")
AssertEquals "gclj", "M20", parser.GetConditionValue("gclj")
AssertEquals "jycz", "3", parser.GetConditionValue("jycz")
AssertEquals "lcfw", "M06", parser.GetConditionValue("lcfw")
Else
Debug.Print " 解析失败: " & parser.ErrorMessage
End If
Debug.Print ""
' 测试用例2: 不同材质代码
Debug.Print "测试用例2: 不同材质代码"
Dim testModel2 As String
testModel2 = "YTHN-100.BZ.531.M201.M09.Y3|BP-088.2312.M37.0A3"
If parser.Parse(testModel2) Then
Debug.Print " 解析成功"
Debug.Print " gclj = " & parser.GetConditionValue("gclj")
Debug.Print " jycz = " & parser.GetConditionValue("jycz")
AssertEquals "gclj", "M20", parser.GetConditionValue("gclj")
AssertEquals "jycz", "1", parser.GetConditionValue("jycz")
Else
Debug.Print " 解析失败: " & parser.ErrorMessage
End If
Debug.Print ""
' 测试用例3: 带附件的型号
Debug.Print "测试用例3: 带附件的型号"
Dim testModel3 As String
testModel3 = "YTHN-100.A0.531.G123.M04.Y3|BP-088.2312.B09.0A3"
If parser.Parse(testModel3) Then
Debug.Print " 解析成功"
Debug.Print " gclj = " & parser.GetConditionValue("gclj")
Debug.Print " jycz = " & parser.GetConditionValue("jycz")
AssertEquals "gclj", "G12", parser.GetConditionValue("gclj")
AssertEquals "jycz", "3", parser.GetConditionValue("jycz")
Else
Debug.Print " 解析失败: " & parser.ErrorMessage
End If
Debug.Print ""
Debug.Print "<<< ProductModelParser 测试完成"
Debug.Print ""
End Sub
'=====================================================================
' 过程: TestConditionEvaluator
' 功能: 测试条件评估器
'=====================================================================
Public Sub TestConditionEvaluator()
Debug.Print ">>> 测试 ConditionEvaluator"
Debug.Print ""
Dim evaluator As ConditionEvaluator
Set evaluator = New ConditionEvaluator
' 创建测试条件字典
Dim conditions As Object
Set conditions = CreateObject("Scripting.Dictionary")
conditions.Add "azxs", "A0"
conditions.Add "bkxs", "531"
conditions.Add "gclj", "M20"
conditions.Add "jycz", "3"
conditions.Add "lcfw", "M06"
' 测试用例1: 简单等式
Debug.Print "测试用例1: 简单等式"
Dim expr1 As String
expr1 = "azxs=A0"
Debug.Print " 表达式: " & expr1
Debug.Print " 结果: " & evaluator.Evaluate(expr1, conditions)
AssertTrue "简单等式", evaluator.Evaluate(expr1, conditions)
Debug.Print ""
' 测试用例2: AND运算
Debug.Print "测试用例2: AND运算"
Dim expr2 As String
expr2 = "azxs=A0 AND bkxs=531"
Debug.Print " 表达式: " & expr2
Debug.Print " 结果: " & evaluator.Evaluate(expr2, conditions)
AssertTrue "AND运算", evaluator.Evaluate(expr2, conditions)
Debug.Print ""
' 测试用例3: OR运算
Debug.Print "测试用例3: OR运算"
Dim expr3 As String
expr3 = "azxs=AT OR azxs=A0"
Debug.Print " 表达式: " & expr3
Debug.Print " 结果: " & evaluator.Evaluate(expr3, conditions)
AssertTrue "OR运算", evaluator.Evaluate(expr3, conditions)
Debug.Print ""
' 测试用例4: !=运算
Debug.Print "测试用例4: !=运算"
Dim expr4 As String
expr4 = "azxs!=AH"
Debug.Print " 表达式: " & expr4
Debug.Print " 结果: " & evaluator.Evaluate(expr4, conditions)
AssertTrue "!=运算", evaluator.Evaluate(expr4, conditions)
Debug.Print ""
' 测试用例5: 复杂嵌套
Debug.Print "测试用例5: 复杂嵌套"
Dim expr5 As String
expr5 = "(azxs=A0 OR azxs=AT) AND (bkxs=531 OR bkxs=541)"
Debug.Print " 表达式: " & expr5
Debug.Print " 结果: " & evaluator.Evaluate(expr5, conditions)
AssertTrue "复杂嵌套", evaluator.Evaluate(expr5, conditions)
Debug.Print ""
' 测试用例6: 不存在的变量(!=情况)
Debug.Print "测试用例6: 不存在的变量(!=情况)"
Dim expr6 As String
expr6 = "tsyq!=SCRJ"
Debug.Print " 表达式: " & expr6
Debug.Print " 结果: " & evaluator.Evaluate(expr6, conditions)
AssertTrue "不存在的变量!=", evaluator.Evaluate(expr6, conditions)
Debug.Print ""
' 测试用例7: 实际BOM条件
Debug.Print "测试用例7: 实际BOM条件"
Dim expr7 As String
expr7 = "gclj=M20 AND jycz=1 AND lcfw=M01 AND (azxs=A0 OR azxs=AT OR azxs=AH)"
Debug.Print " 表达式: " & expr7
Debug.Print " 结果: " & evaluator.Evaluate(expr7, conditions)
' 这个应该是False,因为jycz=3,不是1
AssertFalse "实际BOM条件(应该False)", evaluator.Evaluate(expr7, conditions)
Debug.Print ""
Debug.Print "<<< ConditionEvaluator 测试完成"
Debug.Print ""
End Sub
'=====================================================================
' 过程: TestBomExtractor
' 功能: 测试BOM提取器(需要实际的工作表数据)
'=====================================================================
Public Sub TestBomExtractor()
Debug.Print ">>> 测试 BomExtractor"
Debug.Print ""
On Error Resume Next
Dim bomSheet As Worksheet
Set bomSheet = ThisWorkbook.Worksheets("平台配置清单")
If bomSheet Is Nothing Then
Debug.Print "警告: 未找到'平台配置清单'工作表,跳过BomExtractor测试"
Debug.Print ""
Exit Sub
End If
On Error GoTo 0
Dim extractor As BomExtractor
Set extractor = New BomExtractor
extractor.SetWorksheet bomSheet
If Not extractor.LoadBomData Then
Debug.Print "加载BOM数据失败: " & extractor.GetErrorSummary
Debug.Print ""
Exit Sub
End If
Debug.Print "BOM数据加载成功"
Debug.Print ""
' 测试用例: 提取BOM
Debug.Print "测试用例: 提取BOM"
Dim testConditions As Object
Set testConditions = CreateObject("Scripting.Dictionary")
testConditions.Add "azxs", "A0"
testConditions.Add "bkxs", "531"
testConditions.Add "gclj", "M20"
testConditions.Add "jycz", "1"
testConditions.Add "lcfw", "M01"
Dim matchedItems As Collection
Set matchedItems = extractor.ExtractBom(testConditions)
Debug.Print " 匹配到 " & matchedItems.count & " 个物料"
If matchedItems.count > 0 Then
Debug.Print " 匹配的物料:"
Dim item As BomItem
Dim i As Long
i = 1
For Each item In matchedItems
Debug.Print " " & i & ". " & item.ToString
i = i + 1
Next item
End If
Dim errors As String
errors = extractor.GetErrorSummary
If errors <> "" Then
Debug.Print " 错误信息: " & errors
End If
Debug.Print ""
Debug.Print "<<< BomExtractor 测试完成"
Debug.Print ""
End Sub
'=====================================================================
' 辅助测试函数
'=====================================================================
Private Sub AssertEquals(testName As String, expected As String, actual As String)
If expected = actual Then
Debug.Print " PASS: " & testName
Else
Debug.Print " FAIL: " & testName & " (期望:" & expected & ", 实际:" & actual & ")"
End If
End Sub
Private Sub AssertTrue(testName As String, value As Boolean)
If value Then
Debug.Print " PASS: " & testName
Else
Debug.Print " FAIL: " & testName & " (期望:True, 实际:False)"
End If
End Sub
Private Sub AssertFalse(testName As String, value As Boolean)
If Not value Then
Debug.Print " PASS: " & testName
Else
Debug.Print " FAIL: " & testName & " (期望:False, 实际:True)"
End If
End Sub
'=====================================================================
' 过程: TestWithProvidedModels
' 功能: 使用提供的测试型号进行测试
'=====================================================================
Public Sub TestWithProvidedModels()
Debug.Print "=========================================="
Debug.Print "使用提供的测试型号进行测试"
Debug.Print "=========================================="
Debug.Print ""
Dim testModels() As String
testModels = Split( _
"YTHN-100.A0.531.G123.M04.Y3|BP-088.2312.B09.0A3," & _
"YTHN-100.BZ.531.M201.M09.Y3|BP-088.2312.M37.0A3," & _
"YTHN-100.BZ.531.M201.M08.Y3|BP-088.2312.M08.0A3," & _
"YTHN-100.A0.531.M201.M08.Y3|BP-088.2312.M08.0B3," & _
"YTHN-100.A0.531.M203.M06.Y3|BP-088.2312.M06.0A3|LSG-1.14x2.M20F.M20.3^HDJ.M20F.BW.14×2×60.3^TSFJ^WHP.70X20X1.3," & _
"YTHN-100.A0.531.M203.P21.Y3|BP-088.2312.M39.0A3|HDJ.M20F.BW.14×2×60.3^LSG-1.14x2.M20F.M20.3^TSFJ^WHP.70X20X1.3," & _
"YTHN-100.A0.531.M201.M03.N1.Y3|BP-088.2312.M31.0A4," & _
"YTHN-100.A0.531.M201.M04.Y3|BP-088.2312.M32.0A3," & _
"YTHN-100.A0.531.Z121.M07.Y3|BP-088.2312.M07.0A3," & _
"YTHN-100.A0.531.Z121.M08.Y3|BP-088.2312.M08.0A3", _
",")
Dim parser As ProductModelParser
Set parser = New ProductModelParser
Dim i As Long
For i = LBound(testModels) To UBound(testModels)
Debug.Print "型号 " & (i + 1) & ": " & testModels(i)
If parser.Parse(testModels(i)) Then
Debug.Print " 解析成功"
Debug.Print " 表头: " & parser.HeaderModel
Debug.Print " 条件: " & parser.GetAllConditions
Else
Debug.Print " 解析失败: " & parser.ErrorMessage
End If
Debug.Print ""
Next i
Debug.Print "=========================================="
Debug.Print "测试完成"
Debug.Print "=========================================="
End Sub

53
VBA/vba_metadata.json Normal file
View File

@@ -0,0 +1,53 @@
{
"source_file": "C:\\Users\\Administrator\\Desktop\\新BOM\\AutoBOM\\YTHN-100.xlsm",
"modules": {
"Sheet9.cls": {
"name": "Sheet9",
"type": "DocumentModules",
"attributes": {},
"file": "DocumentModules\\Sheet9.cls"
},
"MainModule.bas": {
"name": "MainModule",
"type": "Modules",
"attributes": {},
"file": "Modules\\MainModule.bas"
},
"TestModule.bas": {
"name": "TestModule",
"type": "Modules",
"attributes": {},
"file": "Modules\\TestModule.bas"
},
"BomExtractor.cls": {
"name": "BomExtractor",
"type": "ClassModules",
"attributes": {},
"file": "ClassModules\\BomExtractor.cls"
},
"BomItem.cls": {
"name": "BomItem",
"type": "ClassModules",
"attributes": {},
"file": "ClassModules\\BomItem.cls"
},
"ConditionEvaluator.cls": {
"name": "ConditionEvaluator",
"type": "ClassModules",
"attributes": {},
"file": "ClassModules\\ConditionEvaluator.cls"
},
"ProductModelParser.cls": {
"name": "ProductModelParser",
"type": "ClassModules",
"attributes": {},
"file": "ClassModules\\ProductModelParser.cls"
},
"BIPUploadModule.bas": {
"name": "BIPUploadModule",
"type": "Modules",
"attributes": {},
"file": "Modules\\BIPUploadModule.bas"
}
}
}

View File

@@ -0,0 +1,444 @@
# 提取备注字段数据来源分析
**文档生成时间**: 2026-03-13
**入口函数**: `MainModule.ProcessProductModels()`
**输出位置**: `BOM 提取结果` 工作表的"提取备注"列(最后一列)
---
## 核心数据流图
```mermaid
flowchart TD
A[ProcessProductModels<br/>主入口] --> B[ProcessSingleModel<br/>处理单个型号]
B --> C{解析产品型号<br/>parser.Parse}
C -->|解析失败 | D["extractNote = <br/>解析失败: + ErrorMessage"]
C -->|解析成功 | E[BomExtractor.ExtractBom<br/>提取 BOM]
E --> F[DetermineRequiredCategories<br/>确定必需类别]
F --> G[MatchItems<br/>匹配物料]
G --> H{匹配数量?}
H -->|0 条 | I[不立即报错<br/>移交 ValidateResult]
H -->|1 条 | J[正常添加到结果集]
H -->|多条 | K["记录错误到 pErrorMessages<br/>类别 X 匹配到多条物料 N 条"]
K --> L[设置 item.MatchError<br/>并添加所有匹配项]
I --> M[ValidateResult<br/>双向覆盖检查]
L --> M
J --> M
M --> N{必需类别存在?}
N -->|被子类覆盖 | O[视为正常<br/>不报错]
N -->|被父类覆盖 | O
N -->|确实缺失 | P["记录错误<br/>必需类别 X 未匹配"]
O --> Q[GetErrorSummary<br/>汇总错误]
P --> Q
Q --> R{bomErrors 为空?}
R -->|非空 | S[extractNote = bomErrors]
R -->|空 | T[extractNote 保持空]
S --> U{matchedItems 为空?}
T --> U
U -->|是 | V{extractNote 为空?}
U -->|否 | W[遍历每个 item]
V -->|是 | X["extractNote = <br/>未匹配到任何物料"]
V -->|否 | Y[保持现有 extractNote]
X --> Z1[CreateOutputRowArray<br/>创建输出行]
Y --> Z1
W --> AA[For Each item<br/>itemNote = extractNote]
AA --> AB{item.MatchError<br/>非空?}
AB -->|是 | AC["拼接itemNote += <br/>; + MatchError"]
AB -->|否 | AD[保持 itemNote]
AC --> AE[CreateOutputRowArray<br/>创建输出行]
AD --> AE
Z1 --> AF[输出到 BOM 提取结果<br/>提取备注列]
AE --> AF
style D fill:#ff6b6b
style K fill:#ffa94d
style P fill:#ff6b6b
style X fill:#51cf66
style AF fill:#339af0,color:#fff
```
---
## 详细数据来源
### 来源 1: 产品型号解析失败
**位置**: `MainModule.bas` 第 184-188 行
**触发条件**: `ProductModelParser.Parse()` 返回 `False`
**错误来源**: `ProductModelParser.ErrorMessage`
```vba
' MainModule.bas:184-188
If Not parser.Parse(modelString) Then
' 解析失败
extractNote = "解析失败:" & parser.ErrorMessage
outputData.Add CreateOutputRowArray(..., extractNote, Nothing)
Exit Sub
End If
```
**可能的错误消息**(来自 `ProductModelParser.cls`:
| 错误场景 | 错误消息示例 | 源码位置 |
|---------|-------------|---------|
| 缺少表头部分 | `型号格式错误:缺少表头部分` | Parse() L73 |
| 缺少'-'分隔符 | `表头格式错误:缺少'-'分隔符` | ParseHeader() L108 |
| 表头结构不完整 | `表头结构不完整:缺少必要字段` | ParseHeader() L114 |
| 过程连接代码格式错误 | `过程连接代码格式错误:长度不足` | ExtractConnectionAndMaterial() L205 |
| 最后一位不是数字 | `过程连接代码格式错误:最后一位不是数字` | ExtractConnectionAndMaterial() L213 |
| 解析异常 | `解析表头异常:[VBA 错误描述]` | ParseHeader() ErrorHandler L131 |
---
### 来源 2: BOM 提取器错误汇总
**位置**: `MainModule.bas` 第 197-200 行
**触发条件**: `BomExtractor.GetErrorSummary()` 返回非空字符串
**错误来源**: `BomExtractor.pErrorMessages` 集合
```vba
' MainModule.bas:197-200
Dim bomErrors As String
bomErrors = BomExtractor.GetErrorSummary
If bomErrors <> "" Then
extractNote = bomErrors
End If
```
**错误汇总逻辑**`BomExtractor.cls` L471-481:
```vba
Public Function GetErrorSummary() As String
If pErrorMessages.Count = 0 Then
GetErrorSummary = ""
Else
Dim result As String
Dim msg As Variant
For Each msg In pErrorMessages
result = result & CStr(msg) & "; " ' 使用"; " 连接
Next msg
GetErrorSummary = result
End If
End Function
```
---
### 来源 3: 多匹配错误
**位置**: `BomExtractor.cls` 第 247-258 行
**触发条件**: 同一类别匹配到多条物料
**错误消息**: `类别 [X] 匹配到多条物料 (N 条)`
```vba
' BomExtractor.cls:247-258
ElseIf categoryMatches.Count = 1 Then
' 正常:匹配到 1 条
pMatchedItems.Add categoryMatches(1)
Else
' 异常:匹配到多条
Dim multiMsg As String
multiMsg = "类别 [" & category & "] 匹配到多条物料 (" & categoryMatches.Count & "条)"
pErrorMessages.Add multiMsg
' 临时处理:输出所有匹配的
Dim tempItem As BomItem
For Each tempItem In categoryMatches
tempItem.MatchError = multiMsg ' ← 设置到 item
pMatchedItems.Add tempItem
Next tempItem
End If
```
**特性**:
- 错误同时添加到 `pErrorMessages`(进入 GetErrorSummary
- 同时设置到 `item.MatchError`(逐行附加)
- 输出所有匹配项,但每条都带警告
---
### 来源 4: 必需类别缺失
**位置**: `BomExtractor.cls` 第 449-451 行
**触发条件**: ValidateResult 检测到必需类别未匹配且无覆盖
**错误消息**: `必需类别 [X] 未匹配`
```vba
' BomExtractor.cls:449-451
If Not isResolved Then
pErrorMessages.Add "必需类别 [" & category & "] 未匹配"
End If
```
**双向覆盖检查逻辑**:
```mermaid
flowchart LR
A[必需类别 X 缺失] --> B{检查 1: 有父类?}
B -->|是 | C{父类别已匹配?}
C -->|是 | D[被父类覆盖<br/>不报错]
C -->|否 | E[检查 2]
B -->|否 | E
E --> F{检查 2: 有子类?}
F -->|是 | G{所有子类都匹配?}
G -->|是 | H[被子类覆盖<br/>不报错]
G -->|否 | I[确实缺失<br/>报错]
F -->|否 | I
D --> J[Continue]
H --> J
I --> K[添加到 pErrorMessages]
style D fill:#51cf66
style H fill:#51cf66
style K fill:#ff6b6b
```
**覆盖场景示例**:
| 场景 | 父类别 | 子类别 1 | 子类别 2 | 结果 |
|------|--------|---------|---------|------|
| 总成优先 | ✅ 部件 (1 条) | ✅ 接头 (2 条) | ✅ 弹性元件 (1 条) | 输出"部件",子类不报错 |
| 散件满足 | ❌ 部件 (缺失) | ✅ 接头 (2 条) | ✅ 弹性元件 (1 条) | 输出子类,父类不报错 |
| 确实缺失 | ❌ 部件 (缺失) | ❌ 接头 (缺失) | ✅ 弹性元件 (1 条) | 报错:"必需类别 [接头] 未匹配" |
---
### 来源 5: 无匹配物料
**位置**: `MainModule.bas` 第 203-208 行
**触发条件**: `matchedItems.Count = 0``extractNote` 为空
**错误消息**: `未匹配到任何物料`
```vba
' MainModule.bas:203-208
If matchedItems.Count = 0 Then
' 没有匹配项
If extractNote = "" Then
extractNote = "未匹配到任何物料"
End If
outputData.Add CreateOutputRowArray(..., extractNote, Nothing)
```
**注意**: 如果已有其他错误(如解析错误),则不会覆盖。
---
### 来源 6: 物料级 MatchError
**位置**: `MainModule.bas` 第 215-223 行
**触发条件**: `BomItem.MatchError` 非空
**错误来源**: 由 `BomExtractor.MatchItems()` 设置(见来源 3
```vba
' MainModule.bas:215-223
For Each item In matchedItems
Dim itemNote As String
itemNote = extractNote
' 添加物料特定的错误
If item.MatchError <> "" Then
If itemNote <> "" Then itemNote = itemNote & "; "
itemNote = itemNote & item.MatchError
End If
' ... 添加到输出
Next item
```
**特性**:
- 每条 BOM 物料单独输出时附加
- 使用 `"; "` 分隔符拼接
- 首行继承全局 `extractNote`,后续行只继承不含物料级错误
---
## 错误消息拼接规则
```mermaid
sequenceDiagram
participant M as MainModule
participant P as ProductModelParser
participant E as BomExtractor
participant I as BomItem
M->>P: Parse modelString
alt 解析失败
P-->>M: 返回 ErrorMessage
Note over M: extractNote = 解析失败 + ErrorMessage
M->>M: 输出单行后结束
else 解析成功
M->>E: ExtractBom conditions
Note over E: 收集错误到 pErrorMessages
Note over E: 设置 item.MatchError
E-->>M: GetErrorSummary
alt bomErrors 非空
Note over M: extractNote = bomErrors
else bomErrors 为空
Note over M: extractNote 保持空
end
alt matchedItems = 0
Note over M: 设为 未匹配到任何物料
M->>M: 输出单行后结束
else matchedItems > 0
loop For Each item
M->>M: itemNote = extractNote
alt item.MatchError 非空
Note over M: itemNote += MatchError
end
M->>M: CreateOutputRowArray
end
end
end
Note over M: 输出到提取备注列
```
**拼接规则总结**:
| 场景 | 拼接方式 | 示例 |
|------|---------|------|
| 全局错误汇总 | 分号连接 | 错误 1; 错误 2 |
| 物料级附加 | 检查非空后加分号 | extractNote + MatchError |
| 多类别缺失 | 逐个添加到集合后汇总 | 必需类别 A 未匹配; 必需类别 B 未匹配 |
| 解析失败 + 其他 | 解析失败时立即退出不叠加 | 解析失败:... |
---
## 输出列定义
**位置**: `MainModule.bas` 第 270 行
```vba
' MainModule.bas:242-270
Private Sub WriteOutputHeader(ws As Worksheet)
' ... 前面的列 ...
ws.Cells(1, col).Value = "66 代码": col = col + 1
ws.Cells(1, col).Value = "提取备注": col = col + 1 ' ← 最后一列
End Sub
```
**数据写入**: `CreateOutputRowArray()` 函数的最后一个元素
```vba
' MainModule.bas:283-339
Private Function CreateOutputRowArray(..., note As String, ...) As Variant()
' ... 填充前面的列 ...
' 备注(最后一列)
rowData(col) = note
CreateOutputRowArray = rowData
End Function
```
---
## 完整错误消息类型汇总表
| 错误类型 | 错误消息模板 | 触发条件 | 源码位置 | 是否可叠加 |
|---------|-------------|---------|---------|----------|
| **解析错误** | `解析失败:[具体原因]` | Parse() 失败 | MainModule.bas:186 | ❌ 单独输出 |
| **多匹配** | `类别 [X] 匹配到多条物料 (N 条)` | 同类别匹配>1 | BomExtractor.cls:250 | ✅ 可叠加 |
| **必需类别缺失** | `必需类别 [X] 未匹配` | ValidateResult 检测缺失 | BomExtractor.cls:450 | ✅ 可叠加 |
| **无匹配** | `未匹配到任何物料` | matchedItems.Count = 0 | MainModule.bas:207 | ❌ 仅当无其他错误 |
| **物料级多匹配** | `类别 [X] 匹配到多条物料 (N 条)` | item.MatchError | MainModule.bas:221 | ✅ 逐行附加 |
---
## 典型输出示例
### 示例 1: 解析失败
```
产品型号Y-100-M203.316SS
提取备注:解析失败:表头结构不完整:缺少必要字段
```
### 示例 2: 正常匹配(无错误)
```
产品型号Y-100-M203.316SS.L100.N2
提取备注:(空)
```
### 示例 3: 多匹配错误
```
产品型号Y-100-M203.316SS.L100.N2
提取备注:类别 [接液材质] 匹配到多条物料 (3 条); 类别 [量程范围] 匹配到多条物料 (2 条);
```
### 示例 4: 必需类别缺失
```
产品型号Y-100-M203.316SS.L100.N2
提取备注:必需类别 [安装形式] 未匹配; 必需类别 [表壳形式] 未匹配;
```
### 示例 5: 无匹配
```
产品型号INVALID-MODEL
提取备注:未匹配到任何物料
```
### 示例 6: 物料级错误附加
```
产品型号Y-100-M203.316SS.L100.N2
行号 | 类别 | 提取备注
-----|------|----------
1 | 接头 | 类别 [接液材质] 匹配到多条物料 (3 条); 类别 [接液材质] 匹配到多条物料 (3 条);
2 | 弹性元件 | 类别 [接液材质] 匹配到多条物料 (3 条);
```
---
## 关键代码路径索引
| 功能 | 文件 | 行号范围 |
|------|------|---------|
| 主入口 | MainModule.bas | 20-150 |
| 单型号处理 | MainModule.bas | 161-235 |
| 输出表头定义 | MainModule.bas | 242-271 |
| 行数据创建 | MainModule.bas | 283-339 |
| 型号解析 | ProductModelParser.cls | 60-91 |
| BOM 提取 | BomExtractor.cls | 130-152 |
| 匹配物料 | BomExtractor.cls | 208-261 |
| 总成逻辑 | BomExtractor.cls | 270-367 |
| 结果验证 | BomExtractor.cls | 376-455 |
| 错误汇总 | BomExtractor.cls | 471-482 |
| 物料数据模型 | BomItem.cls | 1-89 |
---
## 设计特点
**优点**:
1. 错误信息分层清晰(解析层、匹配层、验证层)
2. 支持多错误叠加,不丢失任何警告
3. 双向覆盖检查避免误报(总成/散件场景)
4. 物料级错误逐行附加,便于定位问题
⚠️ **注意事项**:
1. 解析失败时立即退出,不执行后续 BOM 提取
2. 多匹配错误会输出所有匹配项(数据不确定时保留全部)
3. 错误消息使用 `"; "` 分隔,末尾可能有多余分隔符
4. "未匹配到任何物料"仅在无其他错误时显示
---
**文档结束**

View File

@@ -0,0 +1,115 @@
| 代号 | Y-100 | 描述 | | 英文名称 | | | | | | | | |
|------|---------|--------------|------------|------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----|------|------|--------|-------------|---------|-----------------|
| 名称 | 压力表 | 负责人 | | 备注 | | | | | | | | |
| 行号 | 模块 | 代号 | 名称 | 数量 | 选择条件 | 备注 | 类别 | 上层类别 | 类别选用条件 | 66代码 | BIP行号基数 | 修改备注 |
| 10 | 表壳 | 01091004644 | 轴向表壳(黑色) | 1 | (azxs=B0 OR azxs=BT) AND (bkxs=200 OR bkxs=210) | | 表壳 | | | 66071004644 | 7100 | |
| 20 | 表壳 | 01091004647 | 轴向表壳(抛光) | 1 | azxs=B0 AND bkxs=500 | | 表壳 | | | 66071004647 | 7100 | |
| 30 | 表壳 | 01091004643 | 径向表壳(黑色) | 1 | (azxs=A0 OR azxs=AT) AND (bkxs=200 OR bkxs=210) | | 表壳 | | | 66071004643 | 7100 | |
| 40 | 表壳 | 01091004648 | 径向表壳(抛光) | 1 | azxs=A0 AND bkxs=500 | | 表壳 | | | 66071004648 | 7100 | |
| 50 | 表壳 | 01081013918 | 带后边表壳(黑色) | 1 | azxs=AH AND (bkxs=200 OR bkxs=210) | | 表壳 | | | 66051013918 | 7100 | |
| 60 | 罩壳 | 01091004768 | 罩壳(抛光) | 1 | azxs=A0 AND bkxs=500 | | 罩壳 | | | 66071004768 | 7100 | |
| 70 | 罩壳 | 01091005032 | 罩壳(抛光) | 1 | azxs=B0 AND bkxs=500 | | 罩壳 | | | 66071005032 | 7100 | 改为 66071005032 |
| 80 | 罩壳 | 01091004624 | 罩壳(黑色) | 1 | azxs=AT AND bkxs=200 | | 罩壳 | | | 66071004624 | 7100 | |
| 90 | 罩壳 | 01091004623 | 罩壳(亮) | 1 | azxs=A0 AND bkxs=210 | | 罩壳 | | | 66071004623 | 7100 | |
| 100 | 罩壳 | 01091004621 | 罩壳(亮) | 1 | azxs=BT AND bkxs=210 | | 罩壳 | | | 66071004621 | 7100 | |
| 110 | 罩壳 | 01091004620 | 罩壳(黑色) | 1 | azxs=BT AND bkxs=200 | | 罩壳 | | | 66071004620 | 7100 | |
| 120 | 罩壳 | 01091004619 | 罩壳(黑色) | 1 | azxs=B0 AND bkxs=200 | | 罩壳 | | | 66071004619 | 7100 | |
| 130 | 罩壳 | 01091004618 | 罩壳(黑色) | 1 | azxs=A0 AND bkxs=200 | | 罩壳 | | | 66071004618 | 7100 | |
| 140 | 罩壳 | 01091004617 | 罩壳(亮) | 1 | azxs=B0 AND bkxs=210 | | 罩壳 | | | 66071004617 | 7100 | |
| 150 | 玻璃 | 01111001334 | 表玻璃 | 1 | fjgn!=N1 | | 玻璃 | | | 66201001334 | 7100 | |
| 160 | 定位型玻璃部件 | 01111001335 | 表玻璃 | 1 | fjgn=N1 | | 玻璃 | | | 66201001202 | 7100 | 改为 66201001202 |
| 170 | 定位型玻璃部件 | 01091004453 | 100 红色定位指针 | 1 | fjgn= N1 | | | | | 66071004453 | 7100 | |
| 180 | 定位型玻璃部件 | 01091004451 | 100 绿色定位指针 | 1 | fjgn=N1 | | | | | 66071004451 | 7100 | |
| 190 | 定位型玻璃部件 | 01081014387 | 定位钉 | 1 | fjgn=N1 | | | | | 66051014387 | 7100 | |
| 200 | 定位型玻璃部件 | 01201002619 | 固定套 | 1 | fjgn=N1 | | | | | 66991922619 | 7100 | |
| 210 | 定位型玻璃部件 | 01121002259 | 橡胶垫<2> | 1 | fjgn=N1 | | | | | 66181002259 | 7100 | |
| 220 | 定位型玻璃部件 | 01121002258 | 橡胶垫<1> | 1 | fjgn=N1 | | | | | 66181002258 | 7100 | |
| 230 | 定位型玻璃部件 | 01140003351 | O型圈 | 1 | fjgn=N1 | | | | | 66220003351 | 7100 | |
| 240 | 定位型玻璃部件 | 01140003353 | O型圈 | 1 | fjgn=N1 | | | | | 66220003353 | 7100 | |
| 250 | 衬圈 | 01121002271 | 衬圈 | 1 | | | 衬圈 | | | 66181002271 | 7100 | |
| 260 | 表壳螺钉 | 01140002660 | 表壳螺钉 | 3 | | | | | | 260 | | |
| 270 | 罩壳螺钉 | 01140002701 | 罩壳螺钉 | 2 | | | | | | 270 | | |
| 280 | 铅封螺钉 | 01081008107 | 铅封螺钉 | 1 | | | | | | 66051008107 | | |
| 290 | 表盘螺钉 | 01140002695 | 表盘螺钉 | 2 | | | | | | 290 | | |
| 300 | 盘止钉 | 01081005277 | 盘止钉 | 1 | lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11 OR lcfw=M12 OR lcfw=M13 OR lcfw=M14 OR lcfw=M15 OR lcfw=M16 OR lcfw=M30 | | | | | 300 | | |
| 310 | 接头部件 | 01011019173 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND (lcfw=M01 OR lcfw=K78) AND gclj=M20 | | 部件 | | | 66021019173 | 7200 | |
| 320 | 接头部件 | 01011019174 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M02 AND gclj=M20 | | 部件 | | | 66021019174 | 7200 | |
| 330 | 接头部件 | 01011019175 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M03 AND gclj=M20 | | 部件 | | | 66021019175 | 7200 | |
| 340 | 接头部件 | 01011019176 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M04 AND gclj=M20 | | 部件 | | | 66021019176 | 7200 | |
| 350 | 接头部件 | 01011019177 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M05 AND gclj=M20 | | 部件 | | | 66021019177 | 7200 | |
| 360 | 接头部件 | 01011019178 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M06 AND gclj=M20 | | 部件 | | | 66021019178 | 7200 | |
| 370 | 接头部件 | 01011019179 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M07 AND gclj=M20 | | 部件 | | | 66021019179 | 7200 | |
| 380 | 接头部件 | 01011019180 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M08 AND gclj=M20 | | 部件 | | | 66021019180 | 7200 | |
| 390 | 接头部件 | 01011019181 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M09 AND gclj=M20 | | 部件 | | | 66021019181 | 7200 | |
| 400 | 接头部件 | 01011019182 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M10 AND gclj=M20 | | 部件 | | | 66021019182 | 7200 | |
| 410 | 接头部件 | 01011019183 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M11 AND gclj=M20 | | 部件 | | | 66021019183 | 7200 | |
| 420 | 接头部件 | 01011019184 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M12 AND gclj=M20 | | 部件 | | | 66021019184 | 7200 | |
| 430 | 接头部件 | 01011019185 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M13 AND gclj=M20 | | 部件 | | | 66021019185 | 7200 | |
| 440 | 接头部件 | 01011019186 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M14 AND gclj=M20 | | 部件 | | | 66021019186 | 7200 | |
| 450 | 接头部件 | 01011019187 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M15 AND gclj=M20 | | 部件 | | | 66021019187 | 7200 | |
| 460 | 接头部件 | 01011019188 | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M16 AND gclj=M20 | | 部件 | | | 66021019188 | 7200 | |
| 470 | 接头部件 | 01011019189 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND (lcfw=M02 OR lcfw=K107) AND gclj=M20 | | 部件 | | | 66021019189 | 7200 | |
| 480 | 接头部件 | 01011019190 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M03 AND gclj=M20 | | 部件 | | | 66021019190 | 7200 | |
| 490 | 接头部件 | 01011019191 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M04 AND gclj=M20 | | 部件 | | | 66021019191 | 7200 | |
| 500 | 接头部件 | 01011019192 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M05 AND gclj=M20 | | 部件 | | | 66021019192 | 7200 | |
| 510 | 接头部件 | 01011019193 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M06 AND gclj=M20 | | 部件 | | | 66021019193 | 7200 | |
| 520 | 接头部件 | 01011019194 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M07 AND gclj=M20 | | 部件 | | | 66021019194 | 7200 | |
| 530 | 接头部件 | 01011019195 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M08 AND gclj=M20 | | 部件 | | | 66021019195 | 7200 | |
| 540 | 接头部件 | 01011019196 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M09 AND gclj=M20 | | 部件 | | | 66021019196 | 7200 | |
| 550 | 接头部件 | 01011019197 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M10 AND gclj=M20 | | 部件 | | | 66021019197 | 7200 | |
| 560 | 接头部件 | 01011019198 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M11 AND gclj=M20 | | 部件 | | | 66021019198 | 7200 | |
| 570 | 接头部件 | 01011019199 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M12 AND gclj=M20 | | 部件 | | | 66021019199 | 7200 | |
| 580 | 接头部件 | 01011019200 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M13 AND gclj=M20 | | 部件 | | | 66021019200 | 7200 | |
| 590 | 接头部件 | 01011019201 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M14 AND gclj=M20 | | 部件 | | | 66021019201 | 7200 | |
| 600 | 接头部件 | 01011019172 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M15 AND gclj=M20 | | | | | 66021019172 | 7200 | 删除 |
| 610 | 接头部件 | 01011019202 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M01 AND gclj=M20 | | 部件 | | | 66021019202 | 7200 | |
| 620 | 接头部件 | 01011019203 | 轴向部件 | 1 | (azxs=B0 OR azxs=BT) AND lcfw=M16 AND gclj=M20 | | 部件 | | | 66021019203 | 7200 | |
| 630 | 接头 | 01081013687 | 径向低压接头 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND gclj=M14 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11 ) | | 接头 | 部件 | | 66051013687 | 7200 | |
| 640 | 接头 | 01081013686 | 径向低压接头 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND gclj=Z14 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11) | | 接头 | 部件 | | 66051013686 | 7200 | |
| 650 | 接头 | 01081013685 | 径向低压接头 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND gclj=G14 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11) | | 接头 | 部件 | | 66051013685 | 7200 | |
| 660 | 接头 | 01081013684 | 径向低压接头 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND gclj=G12 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11) | | 接头 | 部件 | | 66051013684 | 7200 | |
| 670 | 接头 | 01081013683 | 径向低压接头 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND gclj=G38 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11) | | 接头 | 部件 | | 66051013683 | 7200 | |
| 680 | 接头 | 01081013682 | 径向低压接头 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND gclj=R12 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11) | | 接头 | 部件 | | 66051013682 | 7200 | |
| 690 | 接头 | 01081013681 | 径向低压接头 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND gclj=Z12 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11) | | 接头 | 部件 | | 66051013681 | 7200 | |
| 700 | 接头 | 01081014361 | 径向高压接头 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND gclj=Z14 AND (lcfw=M12 OR lcfw=M13 OR lcfw=M14 OR lcfw=M15 OR lcfw=M16) | | 接头 | 部件 | | 66码缺失 | 7200 | |
| 710 | 接头 | 01081013694 | 径向高压接头 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND gclj=G12 AND (lcfw=M12 OR lcfw=M13 OR lcfw=M14 OR lcfw=M15 OR lcfw=M16) | | 接头 | 部件 | | 66051013694 | 7200 | |
| 720 | 接头 | 01081013692 | 径向高压接头 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND gclj=M14 AND (lcfw=M12 OR lcfw=M13 OR lcfw=M14 OR lcfw=M15 OR lcfw=M16) | | 接头 | 部件 | | 66051013692 | 7200 | |
| 730 | 接头 | 01081013691 | 径向高压接头 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND gclj=R12 AND (lcfw=M12 OR lcfw=M13 OR lcfw=M14 OR lcfw=M15 OR lcfw=M16) | | 接头 | 部件 | | 66051013691 | 7200 | |
| 740 | 接头 | 01081013690 | 径向高压接头 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND gclj=Z12 AND (lcfw=M12 OR lcfw=M13 OR lcfw=M14 OR lcfw=M15 OR lcfw=M16) | | 接头 | 部件 | | 66051013690 | 7200 | |
| 750 | 接头 | 01081013702 | 下轴向低压接头 | 1 | (azxs=B0 OR azxs=BT) AND gclj=R12 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11 OR lcfw=K107 ) | | 接头 | 部件 | | 66051013702 | 7200 | |
| 760 | 接头 | 01081013701 | 下轴向低压接头 | 1 | (azxs=B0 OR azxs=BT) AND gclj=M14 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11 OR lcfw=K107) | | 接头 | 部件 | | 66051013701 | 7200 | |
| 770 | 接头 | 01081013700 | 下轴向低压接头 | 1 | (azxs=B0 OR azxs=BT) AND gclj=Z12 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11 OR lcfw=K107) | | 接头 | 部件 | | 66051013700 | 7200 | |
| 780 | 接头 | 01081013698 | 下轴向低压接头 | 1 | (azxs=B0 OR azxs=BT) AND gclj=G14 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11 OR lcfw=K107) | | 接头 | 部件 | | 66051013698 | 7200 | |
| 790 | 接头 | 01081013697 | 下轴向低压接头 | 1 | (azxs=B0 OR azxs=BT) AND gclj=Z14 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11 OR lcfw=K107) | | 接头 | 部件 | | 66051013697 | 7200 | |
| 800 | 接头 | 01081013696 | 下轴向低压接头 | 1 | (azxs=B0 OR azxs=BT) AND gclj=G12 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11 OR lcfw=K107) | | 接头 | 部件 | | 66051013696 | 7200 | |
| 810 | 接头 | 01081013709 | 下轴向高压接头 | 1 | (azxs=B0 OR azxs=BT) AND gclj=M14 AND (lcfw=M12 OR lcfw=M13 OR lcfw=M14 OR lcfw=M15 OR lcfw=M16) | | 接头 | 部件 | | 66051013709 | 7200 | |
| 820 | 接头 | 01081013707 | 下轴向高压接头 | 1 | (azxs=B0 OR azxs=BT) AND gclj=G14 AND (lcfw=M12 OR lcfw=M13 OR lcfw=M14 OR lcfw=M15 OR lcfw=M16) | | 接头 | 部件 | | 66051013707 | 7200 | |
| 830 | 接头 | 01081013706 | 下轴向高压接头 | 1 | (azxs=B0 OR azxs=BT) AND gclj=Z14 AND (lcfw=M12 OR lcfw=M13 OR lcfw=M14 OR lcfw=M15 OR lcfw=M16) | | 接头 | 部件 | | 66051013706 | 7200 | |
| 840 | 接头 | 01081013705 | 下轴向高压接头 | 1 | (azxs=B0 OR azxs=BT) AND gclj=G12 AND (lcfw=M12 OR lcfw=M13 OR lcfw=M14 OR lcfw=M15 OR lcfw=M16) | | 接头 | 部件 | | 66051013705 | 7200 | |
| 850 | 接头 | 01081013704 | 下轴向高压接头 | 1 | (azxs=B0 OR azxs=BT) AND gclj=R12 AND (lcfw=M12 OR lcfw=M13 OR lcfw=M14 OR lcfw=M15 OR lcfw=M16) | | 接头 | 部件 | | 66051013704 | 7200 | |
| 860 | 弹性元件 | 01041005171 | 弹簧管 | 1 | lcfw=M01 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005171 | 7200 | |
| 870 | 弹性元件 | 01041005169 | 弹簧管 | 1 | lcfw=M02 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005169 | 7200 | |
| 880 | 弹性元件 | 01041005170 | 弹簧管 | 1 | lcfw=M03 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005170 | 7200 | |
| 890 | 弹性元件 | 01041005453 | 弹簧管 | 1 | lcfw=M04 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005453 | 7200 | |
| 900 | 弹性元件 | 01041005235 | 弹簧管 | 1 | lcfw=M05 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005235 | 7200 | |
| 910 | 弹性元件 | 01041005114 | 弹簧管 | 1 | lcfw=M06 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005114 | 7200 | |
| 920 | 弹性元件 | 01041005230 | 弹簧管 | 1 | lcfw=M07 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005230 | 7200 | |
| 930 | 弹性元件 | 01041005233 | 弹簧管 | 1 | lcfw=M08 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005233 | 7200 | |
| 940 | 弹性元件 | 01041005229 | 弹簧管 | 1 | lcfw=M09 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005229 | 7200 | |
| 950 | 弹性元件 | 01041005172 | 弹簧管 | 1 | lcfw=M10 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005172 | 7200 | |
| 960 | 弹性元件 | 01041005452 | 弹簧管 | 1 | lcfw=M11 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005452 | 7200 | |
| 970 | 弹性元件 | 01041005005 | 螺旋管 | 1 | lcfw=M12 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005005 | 7200 | |
| 980 | 弹性元件 | 01041005006 | 螺旋管 | 1 | lcfw=M13 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005006 | 7200 | |
| 990 | 弹性元件 | 01041005007 | 螺旋管 | 1 | lcfw=M14 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005007 | 7200 | |
| 1000 | 弹性元件 | 01041005008 | 螺旋管 | 1 | lcfw=M15 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005008 | 7200 | |
| 1010 | 弹性元件 | 01041005009 | 螺旋管 | 1 | lcfw=M16 AND gclj!=M20 | | 弹性元件 | 部件 | | 66131005009 | 7200 | |
| 1020 | 封口片 | 01091003680 | 高压封口片 | 1 | (lcfw=M12 OR lcfw=M13 OR lcfw=M14 OR lcfw=M15 OR lcfw=M16)AND gclj!=M20 | | | | | 66071003680 | | |
| 1030 | 封口片 | 01091003663 | 低压封口片 | 1 | (lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11)AND gclj!=M20 | | | | | 66071003663 | | |
| 1040 | 连接螺钉 | 01081008105 | 连接螺钉 | 1 | | | | | | 66051008105 | | |
| 1050 | 垫片 | 01091004354 | 垫片 | 1 | lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11 OR lcfw=K107 | | | | | 66071004354 | | |
| 1060 | 机芯螺钉 | 01140003413 | 机芯螺钉 | 2 | | | | | | 66220003413 | | |
| 1070 | 机芯部件 | 01101000574 | 机芯 | 1 | lcfw=M01 OR lcfw=M02 OR lcfw=M03 OR lcfw=M04 OR lcfw=K107 | | 机芯 | | | 66151000574 | 7100 | 删除 OR lcfw=M12 |
| 1080 | 机芯部件 | 01101000575 | 机芯 | 1 | lcfw=M05 OR lcfw=M06 OR lcfw=M07 OR lcfw=M08 OR lcfw=M09 OR lcfw=M10 OR lcfw=M11 OR lcfw=M13 OR lcfw=M14 OR lcfw=M15 OR lcfw=M16 OR lcfw=M12 | | 机芯 | | | 66151000575 | 7100 | 增加 OR lcfw=M12 |
| 1090 | 接头部件 | | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M05 AND gclj=G12 | | 部件 | | | 66021019206 | 7200 | 新增物料 |
| 1100 | 接头部件 | | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M09 AND gclj=Z12 | | 部件 | | | 66021019212 | 7200 | 新增物料 |
| 1120 | 接头部件 | | 径向部件 | 1 | (azxs=A0 OR azxs=AT OR azxs=AH) AND lcfw=M08 AND gclj=G12 | | 部件 | | | 66021019208 | 7200 | 新增物料 |