Implement inventory-based component selection logic that accumulates demand
across orders and falls back to sub-components (joint + element) when stock
is insufficient.
**Changes:**
- M04_Config: Add inventory worksheet configuration constants
- INVENTORY_SHEET_NAME = "现存量"
- INVENTORY_HEADER_ROW = 3 (headers on row 3)
- INVENTORY_COL_CODE = "B" (material code)
- INVENTORY_COL_QTY = "J" (stock quantity)
- M08_ComponentProcessor: Implement inventory tracking and verification
- Add module-level variables: g_InventoryDict, g_AccumulatedDemandDict
- Add LoadInventoryData() to load stock from [现存量] worksheet
- Add InitComponentProcessorWithInventory() for initialization with inventory
- Rewrite CheckComponentInventory() with actual inventory logic:
* Calculate cumulative demand = orderQty × bomQty + previousAccumulated
* Compare with available stock
* Return True if stock sufficient, False otherwise
* Update accumulated demand after each order
- Update ProcessComponentRecord() to accept orderQty parameter
- M09_BOMExtractor: Integrate inventory check into main workflow
- Modify ReadInputModels() to read 3 columns (orderNo, model, qty)
- Initialize component processor with inventory support
- Extract order quantity from column C and pass through call chain
- Update ProcessSingleModel() and MatchAllMaterialTypesWithValidation()
signatures to accept orderQty parameter
**Logic Example:**
- 3 orders (PO-001, PO-003, PO-006) use component A
- Each order: quantity=2, BOM qty=1, stock=5
- PO-001: cumulative=2, stock 5>=2 ✓ → return component
- PO-003: cumulative=4, stock 5>=4 ✓ → return component
- PO-006: cumulative=6, stock 5<6 ✗ → return sub-components
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
8.2 KiB
库存校验功能集成总结
实施日期
2026-02-24
功能概述
在 RunBOMExtraction 过程中集成了库存校验功能,针对部件物料进行库存检查。根据订单顺序累加需求量,当库存充足时使用部件,不足时使用子件(接头+弹性元件)。
修改的文件
1. M04_Config.bas
修改内容: 添加现存量工作表配置常量
' 现存量工作表配置常量
Public Const INVENTORY_SHEET_NAME As String = "现存量"
Public Const INVENTORY_HEADER_ROW As Long = 3 ' 表头在第3行
Public Const INVENTORY_COL_CODE As String = "B" ' B列 = 物料编码
Public Const INVENTORY_COL_QTY As String = "J" ' J列 = 库存数量
2. M08_ComponentProcessor.bas
修改内容:
2.1 添加库存追踪模块级变量
' 模块级变量 - 库存追踪
Private g_InventoryDict As Object ' 部件编码 -> 现存量
Private g_AccumulatedDemandDict As Object ' 部件编码 -> 累计需求量
2.2 新增 LoadInventoryData 函数
- 从[现存量]工作表加载库存数据
- 表头在第3行,从第4行开始读取
- B列 = 物料编码,J列 = 库存数量
- 如果未找到工作表,记录警告并使用空字典(所有库存视为0)
2.3 新增 InitComponentProcessorWithInventory 函数
- 初始化部件处理器,同时加载库存数据
- 创建累计需求量字典
- 调用
LoadInventoryData加载库存
2.4 重写 CheckComponentInventory 函数
输入参数:
wsComponent- "部件"工作表rowNum- 匹配到的行号headerMap- 表头映射orderQty- 订单数量(新增)
逻辑流程:
- 提取部件编码
- 获取BOM需求量(部件工作表中的数量列)
- 检查库存数据是否存在
- 计算累计需求量 = 订单数量 × BOM需求量 + 之前累计需求
- 比较库存和需求:
- 库存 >= 累计需求 → 返回 True,更新累计需求量
- 库存 < 累计需求 → 返回 False,记录错误
2.5 修改 ProcessComponentRecord 函数签名
新增参数: orderQty As Long
修改调用: 传递 orderQty 给 CheckComponentInventory
3. M09_BOMExtractor.bas
修改内容:
3.1 修改 ReadInputModels 函数
当前实现: 读取2列(生产订单号、产品型号)
注意: 计划中提到读取3列(包括数量),但当前代码仍使用 ws.Range(ws.Cells(2, 1), ws.Cells(lastRow, colIdx)).Value,需要确保 colIdx 变量包含第3列(数量列)。
建议修改:
' 确保读取到第3列(数量)
ReadInputModels = ws.Range(ws.Cells(2, 1), ws.Cells(lastRow, 3)).Value
3.2 修改 RunBOMExtraction 主循环
修改点1: 提取数量列
Dim orderQty As Long
orderQty = CLng(inputModels(i, 3)) ' Column C: 数量
修改点2: 初始化部件处理器时传递库存工作簿
Dim invWorkbook As Workbook
Set invWorkbook = ThisWorkbook ' 现存量在主工作簿中
M08_ComponentProcessor.InitComponentProcessorWithInventory g_Logger, invWorkbook
修改点3: 传递数量给 ProcessSingleModel
Set modelResults = ProcessSingleModel(modelString, g_BOMWorkbook, g_Logger, productionOrderNo, orderQty)
3.3 修改 ProcessSingleModel 函数签名
新增参数: orderQty As Long
修改调用: 传递 orderQty 给 MatchAllMaterialTypesWithValidation
3.4 修改 MatchAllMaterialTypesWithValidation 函数签名
新增参数: orderQty As Long
修改调用: 传递 orderQty 给 ProcessComponentRecord
数据流程
输入数据
[产品型号]工作表:
| 生产订单号 | 产品型号 | 数量 |
|---|---|---|
| PO-001 | YTHN-... | 2 |
| PO-003 | YTHN-... | 2 |
| PO-006 | YTHN-... | 2 |
[现存量]工作表: (第3行表头) | ... | 物料编码 | ... | 库存数量 | | ... | COMP001 | ... | 5 |
处理逻辑
-
初始化阶段:
InitComponentProcessorWithInventory加载库存数据到g_InventoryDict- 创建空的
g_AccumulatedDemandDict用于追踪累计需求
-
订单处理阶段(按顺序):
- PO-001: 累计需求 = 2×1 + 0 = 2,库存5 >= 2 ✓ → 返回部件
- PO-003: 累计需求 = 2×1 + 2 = 4,库存5 >= 4 ✓ → 返回部件
- PO-006: 累计需求 = 2×1 + 4 = 6,库存5 < 6 ✗ → 返回子件(接头+弹性元件)
-
错误记录:
- 库存不足时记录错误(Blocking Error)
- 未找到库存数据时记录警告(Non-blocking Warning)
输出结果
BOM提取结果:
| 生产订单号 | 原始产品型号 | 物料类型 | 物料名称 | 物料编码 | 物料数量 | 提取备注 |
|---|---|---|---|---|---|---|
| PO-001 | YTHN-... | 部件 | 部件A | COMP001 | 1 | |
| PO-003 | YTHN-... | 部件 | 部件A | COMP001 | 1 | |
| PO-006 | YTHN-... | 接头 | 接头B | JOINT01 | 1 | 部件无库存,使用子件 |
| PO-006 | YTHN-... | 弹性元件 | 元件C | ELEM01 | 1 | 部件无库存,使用子件 |
测试要点
功能测试
-
正常库存场景:
- 准备测试数据:3个订单,库存=5
- 验证前2个订单返回部件
- 验证第3个订单返回子件
-
边界测试:
- 库存=0,所有订单应返回子件
- 库存充足(>=累计需求),所有订单返回部件
- 库存恰好等于累计需求,应返回部件
-
异常测试:
- 现存量工作表不存在 → 记录警告,所有订单返回子件
- 部件编码在现存量中不存在 → 记录警告,该订单返回子件
- 数量列为空或非数字 → 应有错误处理
数据验证
-
累计需求计算:
- 验证累计需求 = 订单数量 × BOM需求量 + 之前累计
- 验证每次处理后累计需求量正确更新
-
错误报告:
- 检查错误报告工作表是否生成
- 验证库存不足错误正确记录
- 验证警告正确记录
注意事项
关键假设
-
[现存量]工作表结构:
- 表头在第3行
- 数据从第4行开始
- B列 = 物料编码
- J列 = 库存数量
-
[产品型号]工作表结构:
- A列 = 生产订单号
- B列 = 产品型号
- C列 = 数量
-
BOM库[部件]工作表:
- 必须包含"编码"列(部件编码)
- 必须包含"数量"列(BOM需求量)
已知限制
-
库存检查时机:
- 库存在初始化时加载一次
- 处理过程中库存不更新(不考虑库存增加)
-
错误处理:
- 未找到库存数据时返回 False(使用子件)
- 不会中断整个流程,继续处理下一个订单
-
数量列处理:
- 当前代码假设数量列总是存在且为数字
- 如果数量列为空或非数字,可能导致运行时错误
后续改进建议
-
增强错误处理:
- 在
ReadInputModels中验证数量列是否存在 - 处理数量列为空或非数字的情况
- 在
-
性能优化:
- 如果库存数据很大,考虑只加载需要的部件编码
- 添加日志记录库存使用情况
-
功能扩展:
- 支持库存实时更新(如果需要)
- 支持按批次或其他维度分组计算需求
- 添加库存预留功能(预留库存给特定订单)
相关文档
- BOM提取系统架构:
CLAUDE.md - 库存校验计划:原始计划文档
- 测试用例:需要单独创建
实施验证清单
- M04_Config.bas 添加常量
- M08_ComponentProcessor.bas 添加变量
- M08_ComponentProcessor.bas 实现 LoadInventoryData
- M08_ComponentProcessor.bas 重写 CheckComponentInventory
- M08_ComponentProcessor.bas 添加 InitComponentProcessorWithInventory
- M08_ComponentProcessor.bas 修改 ProcessComponentRecord 签名
- M09_BOMExtractor.bas 修改 RunBOMExtraction(初始化、提取数量)
- M09_BOMExtractor.bas 修改 ProcessSingleModel(添加参数)
- M09_BOMExtractor.bas 修改 MatchAllMaterialTypesWithValidation(添加参数)
- 验证 ReadInputModels 读取3列(需要确认)
- 端到端测试
- 错误场景测试