diff --git a/.gitignore b/.gitignore index c0df636..3797fd4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ tmpclaude-* *workspace* *.png data/ -*.xlsm \ No newline at end of file +*.xlsm +*.xlsx \ No newline at end of file diff --git a/docs/BIPUploadModule流程图.md b/docs/BIPUploadModule流程图.md deleted file mode 100644 index eb80938..0000000 --- a/docs/BIPUploadModule流程图.md +++ /dev/null @@ -1,226 +0,0 @@ -# BIPUploadModule 流程图文档 - -## 模块概述 - -**模块名**: BIPUploadModule -**功能**: 处理产品订单数据,提取BOM后生成[BIP上传模板]格式数据 -**主入口**: ProcessOrdersToBIP - -## ProcessOrdersToBIP 流程图(简化版) - -```mermaid -flowchart TD - Start([开始处理]) --> Init["初始化
1. 获取工作表
2. 加载BOM库"] - - Init --> Loop["循环处理每个订单"] - - Loop --> Read["读取订单信息
- 生产订单号
- 产品型号
- 数量
- 产品编码
- 部件优先标志"] - - Read --> Parse["解析产品型号
提取规格参数"] - - Parse --> Extract["提取BOM物料
根据规格参数匹配物料库"] - - Extract --> CheckPriority{"部件优先=否?"} - CheckPriority -->|是| SkipComponent["排除'部件'类别
只提取子类别物料"] - CheckPriority -->|否| KeepAll["保留所有物料"] - SkipComponent --> Generate - KeepAll --> Generate["生成BIP上传数据
- 订单信息
- 物料清单
- 行号编码"] - - Generate --> Collect["收集到内存集合"] - - Collect --> NextOrder{"还有订单?"} - NextOrder -->|是| Loop - NextOrder -->|否| BatchWrite["批量写入[BIP上传模板]"] - - BatchWrite --> Format["格式化表格"] - - Format --> End([完成]) - - style Init fill:#e1f5e1 - style Parse fill:#fff3cd - style Extract fill:#d1ecf1 - style Generate fill:#d1ecf1 - style BatchWrite fill:#f8d7da - style End fill:#e1f5e1 -``` - ---- - -## ProcessSingleOrder 子流程(详细版) - -> 注:以下为技术人员提供详细流程图 - -```mermaid -flowchart TD - Start([开始 ProcessSingleOrder]) --> ClearExclude["清空排除类别
ClearExcludeCategories"] - - ClearExclude --> CheckPriority{"部件优先?"} - CheckPriority -->|否/0/FALSE| AddExclude["创建排除类别集合
添加: 部件"] - AddExclude --> SetExclude["设置排除类别"] - SetExclude --> ParseModel - CheckPriority -->|是| ParseModel["解析产品型号
ProductModelParser.Parse"] - - ParseModel --> CheckParse{"解析成功?"} - CheckParse -->|否| CreateError1["创建错误行数据
备注: 解析失败信息"] - CreateError1 --> AddError1["添加到outputData"] --> End1([返回]) - - CheckParse -->|是| ExtractBOM["提取BOM
BomExtractor.ExtractBom"] - ExtractBOM --> GetErrors["获取错误信息摘要"] - - GetErrors --> CheckMatch{"匹配到物料?"} - CheckMatch -->|否| SetError1["设置备注: 未匹配到任何物料"] - SetError1 --> CreateError2["创建空记录行数据"] - CreateError2 --> AddError2["添加到outputData"] --> End2([返回]) - - CheckMatch -->|是| InitLine["初始化行号索引 lineIndex = 1"] - InitLine --> StartLoop["开始循环物料"] - - StartLoop --> GetItem["获取物料项"] - GetItem --> InitNote["初始化备注 = 提取错误"] - - InitNote --> CheckItemError{"物料有错误?"} - CheckItemError -->|是| AppendError["追加物料错误信息"] - CheckItemError -->|否| CreateRow - AppendError --> CreateRow["创建BIP行数据
CreateBIPRowArray"] - - CreateRow --> AddRow["添加到outputData"] - AddRow --> IncLine["lineIndex++"] - IncLine --> NextLoop{"还有物料?"} - NextLoop -->|是| StartLoop - NextLoop -->|否| End3([返回]) -``` - -## WriteBatchData 批量写入流程 - -```mermaid -flowchart TD - Start([开始 WriteBatchData]) --> CheckData{"outputData为空?"} - CheckData -->|是| End1([直接返回]) - CheckData -->|否| GetRowCount["获取行数 rowCount"] - - GetRowCount --> CreateArray["创建二维数组
resultData1 to rowCount, 1 to 10"] - - CreateArray --> StartLoop["开始循环: i = 1 to rowCount"] - StartLoop --> GetRowArray["获取行数组 rowArray = outputDatai"] - - GetRowArray --> FillArray["填充二维数组
resultDatai, 1 to 10 = rowArray1 to 10"] - FillArray --> NextLoop{"i++ 还有数据?"} - NextLoop -->|是| StartLoop - NextLoop -->|否| WriteSheet["一次性写入工作表
A2单元格.Resize rowCount, 10"] - - WriteSheet --> End([完成]) -``` - -## 数据结构说明 - -### BIP上传模板字段 (10列) - -| 列号 | 字段名 | 说明 | 数据来源 | -|------|--------|------|----------| -| 1 | 来源单据号(生产订单号) | 订单唯一标识 | [产品订单] A列 | -| 2 | 产品编码 | 产品代码 | [产品订单] D列 | -| 3 | 生产数量 | 生产数量 | [产品订单] C列 | -| 4 | 行号 | BOM行号 = 7000 + 索引 | 自动生成(7001, 7002...) | -| 5 | 材料编码 | 物料66编码 | BOM提取结果 | -| 6 | 供应方式 | 固定值 | "一般发料" | -| 7 | 需用日期 | 日期 | 当天日期 Date | -| 8 | 发料组织 | 固定值 | "重庆布莱迪仪器仪表有限公司" | -| 9 | 计划出库数量 | 数量 | 与生产数量一致 | -| 10 | 备注 | 异常信息 | BOM提取错误/异常 | - -### [产品订单] 工作表结构 (5列) - -| 列号 | 字段名 | 说明 | 必填 | -|------|--------|------|------| -| A | 生产订单号 | 订单唯一标识 | 是 | -| B | 产品型号 | 产品完整型号 | 是 | -| C | 数量 | 生产数量 | 是 | -| D | 产品编码 | 产品代码 | 否 | -| E | 部件优先 | 是否优先提取部件类别 | 否 | - -## 关键特性 - -### 1. 性能优化 -- **批量写入**: 使用数组一次性写入所有数据,而非逐个单元格写入 -- **内存缓存**: 所有数据先收集到Collection对象,最后统一输出 -- **预期性能提升**: 10-100倍(取决于数据量) - -### 2. 部件优先功能 -- 当`部件优先` = "否"/"0"/"FALSE"时: - - 排除"部件"类别的物料 - - 只提取"部件"的子类别物料(如"接头"、"弹性元件"等) -- 当`部件优先` = "是"或其他值时: - - 正常提取所有物料,包括"部件"类别及其子类别 - -### 3. 错误处理 -- **必填字段验证**: 生产订单号、产品型号、数量 -- **解析失败处理**: 记录错误信息到备注列 -- **BOM提取异常**: 记录匹配失败和异常信息 -- **空行处理**: 自动跳过完全空白的行 - -### 4. 数据完整性 -- 每次运行前清空旧数据(保留表头) -- 自动创建[BIP上传模板]工作表(如不存在) -- 行号自动生成(7001, 7002, 7003...) -- 日期自动填充为当天日期 - -## 执行示例 - -### 正常流程示例 - -``` -输入: [产品订单] 3行数据 - - 订单001, 型号MD-100, 数量10, 编码P001, 部件优先=是 - - 订单002, 型号MD-200, 数量20, 编码P002, 部件优先=否 - - 订单003, 型号MD-300, 数量30, 编码P003, 部件优先=是 - -处理流程: - 1. 读取3行订单数据 - 2. 逐个解析产品型号 - 3. 根据部件优先设置提取BOM - 4. 订单001: 提取5种物料(包括部件) - 5. 订单002: 提取4种物料(排除部件,只提取子类别) - 6. 订单003: 提取6种物料(包括部件) - 7. 批量写入15行数据到[BIP上传模板] - 8. 显示完成信息: 处理3个订单,生成15行BOM数据 - -输出: [BIP上传模板] 15行数据 -``` - -### 异常处理示例 - -``` -输入: [产品订单] 包含异常数据 - - 订单A01, 型号INVALID, 数量5 ← 型号解析失败 - - 订单A02, 型号MD-100, 数量0 ← 未匹配到任何物料 - - 订单A03, 型号MD-200, 数量10 ← 正常提取3种物料 - -输出: - 第1行: 订单A01, ..., 备注: "解析失败: 无效的型号格式" - 第2行: 订单A02, ..., 备注: "未匹配到任何物料" - 第3-5行: 订单A03, 3种物料数据 -``` - -## 相关模块 - -### 依赖的类模块 -- **BomExtractor**: BOM提取器,负责从平台配置清单中提取匹配的物料 -- **ProductModelParser**: 产品型号解析器,解析型号字符串为结构化条件 -- **BomItem**: BOM物料项数据模型 -- **ConditionEvaluator**: 条件评估器,评估选择条件和类别条件 - -### 相关模块 -- **MainModule**: 主控模块,处理产品型号提取和BOM匹配 - - 类似的处理逻辑 - - 输出到[BOM提取结果]工作表 - - 支持部件优先功能 - - 使用相同的批量写入优化 - -## 版本历史 - -| 版本 | 日期 | 说明 | -|------|------|------| -| 1.0 | 2026-02-01 | 初始版本,实现基本的订单处理和BOM提取功能 | -| 1.1 | 2026-02-01 | 添加部件优先功能,支持排除特定类别 | -| 1.2 | 2026-02-01 | 性能优化,使用数组批量写入替代逐个单元格写入 | -| 1.3 | 2026-02-01 | 修复Mermaid流程图方括号转义问题 | diff --git a/docs/ComponentInventoryCheckModule流程图.md b/docs/ComponentInventoryCheckModule流程图.md deleted file mode 100644 index 414f882..0000000 --- a/docs/ComponentInventoryCheckModule流程图.md +++ /dev/null @@ -1,443 +0,0 @@ -# ComponentInventoryCheckModule 流程图文档 - -## 模块概述 - -**模块名**: ComponentInventoryCheckModule -**功能**: 部件库存核对模块 - 自动核对产品订单中"部件"类物料的库存情况 -**主入口**: CheckComponentInventory -**说明**: 当库存不足时,按订单顺序将超出部分的订单的"部件优先"字段标记为"否" - ---- - -## 业务流程图(面向非技术人员) - -```mermaid -flowchart TD - Start([开始核对部件库存]) --> Step1["1. 读取数据
读取产品订单表
读取库存表
读取物料配置表"] - - Step1 --> Step2["2. 分析订单
查看每个订单需要哪些部件
计算每个订单需要多少个部件"] - - Step2 --> Step3["3. 汇总需求
统计所有订单总共需要多少个部件
按部件种类分别统计"] - - Step3 --> Step4["4. 检查库存
对比库存数量和需求数量
找出库存不够的部件"] - - Step4 --> Step5["5. 分配库存
按订单从上到下依次分配
库存够的订单→保持不变
库存不够的订单→标记为'否'"] - - Step5 --> Step6["6. 完成处理
显示处理结果
多少订单能发货
多少订单缺部件"] - - Step6 --> End([完成]) - - style Step1 fill:#e1f5e1 - style Step2 fill:#d1ecf1 - style Step3 fill:#fff3cd - style Step4 fill:#ffe5b4 - style Step5 fill:#f8d7da - style Step6 fill:#e1f5e1 -``` - -### 业务流程说明 - -| 步骤 | 做什么 | 为什么 | -|------|--------|--------| -| **1. 读取数据** | 从Excel表格中读取订单、库存、配置信息 | 获取处理所需的所有数据 | -| **2. 分析订单** | 查看每个订单需要什么部件、多少个 | 了解每个订单的部件需求 | -| **3. 汇总需求** | 把所有订单的相同部件需求加在一起 | 算出总共需要多少部件 | -| **4. 检查库存** | 对比总需求和实际库存 | 判断库存是否够用 | -| **5. 分配库存** | 先来先得,库存不够的标记为"否" | 确定哪些订单能按时发货 | -| **6. 完成处理** | 显示统计结果 | 让用户了解处理情况 | - -### 举例说明 - -假设有3个订单,都需要同一个部件A: - -| 订单 | 需要部件A数量 | 库存分配过程 | 最终状态 | -|------|--------------|--------------|---------| -| 订单1 | 2个 | 库存剩5个,够用 | ✓ 保持原值 | -| 订单2 | 2个 | 库存剩3个,够用 | ✓ 保持原值 | -| 订单3 | 2个 | 库存剩1个,不够 | ✗ 标记为"否" | - -> **技术说明**:标记为"否"的订单,在生成BIP上传数据时会跳过部件类物料,优先保证子部件供应。 - ---- - -## CheckComponentInventory 主流程图(技术版) - -> 以下流程图面向技术人员,展示完整的错误处理和验证步骤。 - ---- - -## CheckComponentInventory 主流程图(详细版) - -```mermaid -flowchart TD - Start(["开始 CheckComponentInventory"]) --> GetSheet1["获取[产品订单]工作表"] - GetSheet1 --> CheckSheet1{工作表存在?} - CheckSheet1 -->|否| ShowMsg1["显示错误消息"] --> End1(["结束"]) - CheckSheet1 -->|是| GetSheet2["获取[现存量]工作表"] - - GetSheet2 --> CheckSheet2{工作表存在?} - CheckSheet2 -->|否| ShowMsg2["显示错误消息"] --> End2(["结束"]) - CheckSheet2 -->|是| GetSheet3["获取[平台配置清单]工作表"] - - GetSheet3 --> CheckSheet3{工作表存在?} - CheckSheet3 -->|否| ShowMsg3["显示错误消息"] --> End3(["结束"]) - CheckSheet3 -->|是| CheckData{订单数据存在?} - - CheckData -->|否| ShowMsg4["显示无数据消息"] --> End4(["结束"]) - CheckData -->|是| InitBOM["初始化BOM提取器
LoadBomData"] - - InitBOM --> CheckBOM{BOM加载成功?} - CheckBOM -->|否| ShowMsg5["显示BOM错误"] --> End5(["结束"]) - CheckBOM -->|是| LoadInv["LoadInventoryData
读取库存数据到字典"] - - LoadInv --> CheckInv{库存数据有效?} - CheckInv -->|否| ShowMsg6["显示无库存消息"] --> End6(["结束"]) - CheckInv -->|是| LoadOrders["LoadOrderData
读取订单数据"] - - LoadOrders --> CheckOrders{订单数据有效?} - CheckOrders -->|否| ShowMsg7["显示无订单消息"] --> End7(["结束"]) - CheckOrders -->|是| ParseAll["ParseAllOrdersBOM
解析所有订单的BOM"] - - ParseAll --> CalcDemand["CalculateComponentDemand
统计部件总需求"] - - CalcDemand --> CheckDemand{有部件需求?} - CheckDemand -->|否| ShowMsg8["显示无部件消息"] --> End8(["结束"]) - CheckDemand -->|是| Validate["ValidateInventory
验证库存数据"] - - Validate --> Allocate["AllocateInventory
按订单顺序分配库存"] - - Allocate --> ShowResult["显示结果统计
总订单数、包含部件订单数
库存充足/不足订单数"] - - ShowResult --> End9(["完成"]) - - style InitBOM fill:#e1f5e1 - style LoadInv fill:#d1ecf1 - style LoadOrders fill:#d1ecf1 - style ParseAll fill:#fff3cd - style CalcDemand fill:#fff3cd - style Validate fill:#ffe5b4 - style Allocate fill:#f8d7da - style End9 fill:#e1f5e1 -``` - ---- - -## AllocateInventory 库存分配详细流程 - -```mermaid -flowchart TD - AllocStart([开始 AllocateInventory]) --> InitStats[初始化统计信息
TotalOrders, OrdersWithComponent
OrdersSufficient, OrdersInsufficient, OrdersSkipped] - - InitStats --> LoopStart{遍历订单
i = 1 to orders.Count} - - LoopStart -->|有订单| GetOrder[获取订单 order = orders i] - GetOrder --> CheckParse{解析失败?} - CheckParse -->|ORDER_PARSE_ERR <> ""| Skip1[跳过订单
OrdersSkipped++] - CheckParse -->|无错误| CheckHasComp{包含部件?} - - CheckHasComp -->|ORDER_HAS_COMP = False| Skip2[跳过订单
OrdersSkipped++] - CheckHasComp -->|HasComponent = True| CheckQty{数量为0?} - - CheckQty -->|ORDER_QUANTITY = 0| Skip3[跳过订单
OrdersSkipped++] - CheckQty -->|数量 > 0| UpdateComp[OrdersWithComponent++] - - UpdateComp --> GetComp[获取部件库存信息
compInv = componentDemands compCode] - GetComp --> CalcReq[计算需求量
requiredQty = CompQty × OrderQty] - - CalcReq --> CheckStock{库存充足?
compInv.STOCK >= requiredQty} - - CheckStock -->|是| DeductSufficient[扣减库存
compInv.STOCK -= requiredQty
OrdersSufficient++] - CheckStock -->|否| MarkNo[标记E列为"否"
orderSheet.Cells Row, 5 = "否"
compInv.STOCK -= requiredQty
OrdersInsufficient++] - - DeductSufficient --> NextOrder[继续下一个订单] - MarkNo --> NextOrder - Skip1 --> NextOrder - Skip2 --> NextOrder - Skip3 --> NextOrder - - NextOrder --> LoopStart - - style UpdateComp fill:#e1f5e1 - style DeductSufficient fill:#e1f5e1 - style MarkNo fill:#f8d7da -``` - ---- - -## ParseOrderBOM 解析详细流程 - -```mermaid -flowchart TD - ParseStart([开始 ParseOrderBOM]) --> CreateParser[创建ProductModelParser] - CreateParser --> ParseModel[解析型号
parser.Parse model] - - ParseModel --> CheckParse{解析成功?} - CheckParse -->|否| SetError[设置解析错误
ORDER_PARSE_ERR = "解析失败: ..."] - SetError --> ParseEnd([结束返回]) - - CheckParse -->|是| ExtractBOM[提取BOM
bomExtractor.ExtractBom conditions] - - ExtractBOM --> LoopComp{遍历BOM项目
For Each item In matchedItems} - - LoopComp -->|有项目| CheckCat{item.category = "部件"?} - - CheckCat -->|否| NextItem[继续下一个项目] - CheckCat -->|是| SetCompInfo[设置部件信息
ORDER_COMP_CODE = item.Code66
ORDER_COMP_QTY = item.quantity
ORDER_HAS_COMP = True] - - SetCompInfo --> ParseEnd - NextItem --> LoopComp - - style SetCompInfo fill:#d1ecf1 - style SetError fill:#f8d7da -``` - ---- - -## 数据处理序列图 - -```mermaid -sequenceDiagram - participant Main as CheckComponentInventory - participant Loader as 数据加载器 - participant Parser as BOM解析器 - participant Calc as 需求计算器 - participant Validator as 库存验证器 - participant Allocator as 库存分配器 - - Main->>Loader: LoadInventoryData(ws) - Loader-->>Main: 库存字典 Dictionary(编码 -> 数量) - - Main->>Loader: LoadOrderData(ws) - Loader-->>Main: 订单集合 Collection(Of Dictionary) - - loop 每个订单 - Main->>Parser: ParseOrderBOM(order, bomExtractor) - Parser->>Parser: ProductModelParser.Parse(model) - Parser->>Parser: BomExtractor.ExtractBom(conditions) - Parser->>Parser: 查找 category="部件" 的物料 - Parser-->>Main: 设置订单部件信息 - end - - Main->>Calc: CalculateComponentDemand(orders) - Calc->>Calc: 累加每个订单的部件需求 - Note over Calc: 需求 = CompQty × OrderQty - Calc-->>Main: 部件需求字典 Dictionary(编码 -> 库存信息) - - Main->>Validator: ValidateInventory(demands, inventoryData) - Validator->>Validator: 检查每个部件是否存在 - Validator->>Validator: 计算是否短缺 - Validator-->>Main: 警告集合 Collection - - Main->>Allocator: AllocateInventory(orders, demands, sheet) - loop 按订单顺序遍历 - Allocator->>Allocator: 获取订单部件需求 - Allocator->>Allocator: 检查库存是否充足 - alt 库存充足 - Allocator->>Allocator: 扣减库存,保持原值 - Note over Allocator: OrdersSufficient++ - else 库存不足 - Allocator->>Allocator: 标记 E5 = "否" - Note over Allocator: OrdersInsufficient++ - end - end - Allocator-->>Main: 统计信息 Statistics - - Main-->>Main: 显示结果消息框 -``` - ---- - -## 数据结构说明 - -### 订单字典结构 (Dictionary Object) - -| 键名 | 常量 | 类型 | 说明 | -|------|------|------|------| -| RowNumber | ORDER_ROW | Long | 订单所在行号 | -| ProductModel | ORDER_MODEL | String | 产品型号 | -| Quantity | ORDER_QUANTITY | Double | 产品数量 | -| ComponentCode | ORDER_COMP_CODE | String | 部件66编码 | -| ComponentQty | ORDER_COMP_QTY | Double | 部件BOM数量 | -| HasComponent | ORDER_HAS_COMP | Boolean | 是否包含部件 | -| ParseError | ORDER_PARSE_ERR | String | 解析错误信息 | - -### 部件库存字典结构 (Dictionary Object) - -| 键名 | 常量 | 类型 | 说明 | -|------|------|------|------| -| ComponentCode | INV_CODE | String | 部件66编码 | -| TotalDemand | INV_DEMAND | Double | 总需求量 | -| AvailableStock | INV_STOCK | Double | 可用库存(动态扣减) | -| IsShortage | INV_SHORTAGE | Boolean | 是否短缺 | - -### 统计信息结构 (Statistics Type) - -| 字段 | 类型 | 说明 | -|------|------|------| -| TotalOrders | Long | 总订单数 | -| OrdersWithComponent | Long | 包含部件的订单数 | -| OrdersSufficient | Long | 库存充足订单数 | -| OrdersInsufficient | Long | 库存不足订单数 | -| OrdersSkipped | Long | 跳过订单数 | - ---- - -## 关键特性 - -### 1. 库存分配策略 -- **按订单顺序分配**: 从上到下遍历订单,先到先得 -- **动态库存扣减**: 库存充足时扣减,不足时标记为负数 -- **只标记不跳过**: 即使库存不足也继续处理后续订单 - -### 2. 部件识别机制 -- 通过 `BomExtractor` 提取完整BOM -- 遍历BOM项目,查找 `category = "部件"` 的物料 -- 只记录第一个匹配到的部件(假设每个产品只有一个主部件) - -### 3. 错误处理 -- **工作表缺失**: 提前验证,友好提示 -- **BOM加载失败**: 显示错误摘要,终止处理 -- **型号解析失败**: 记录错误,跳过该订单 -- **部件找不到库存**: 添加警告,继续处理(库存设为0) - -### 4. 数据完整性 -- 使用 `Dictionary` 实现库存的引用更新 -- 使用 `Collection` 保持订单顺序 -- 统计跳过的订单(解析失败、无部件、数量为0) - ---- - -## 执行示例 - -### 正常流程示例 - -``` -输入数据: - [产品订单] - 第2行: MD-100, 数量2, 部件A(编码661001, 用量1) - 第3行: MD-200, 数量2, 部件A(编码661001, 用量1) - 第4行: MD-300, 数量2, 部件A(编码661001, 用量1) - - [现存量] - 661001: 5 - -处理流程: - 1. 读取3个订单 - 2. 解析BOM,识别部件661001 - 3. 统计需求: 1×2 + 1×2 + 1×2 = 6 - 4. 验证库存: 6 > 5,短缺 - 5. 分配库存: - - 订单2: 需求2,库存5→3,充足 - - 订单3: 需求2,库存3→1,充足 - - 订单4: 需求2,库存1→-1,不足,标记E4="否" - -输出结果: - [产品订单] - 第2行: E列保持原值 - 第3行: E列保持原值 - 第4行: E列 = "否" - - 统计信息: - 处理订单数: 3 - 包含部件订单: 3 - 库存充足订单: 2 - 库存不足订单: 1 -``` - -### 异常处理示例 - -``` -输入数据: - [产品订单] - 第2行: INVALID-MODEL, 数量5 ← 型号解析失败 - 第3行: MD-100, 数量0 ← 数量为0 - 第4行: MD-200, 数量2, 部件669999 (库存中不存在) - - [现存量] - 661001: 10 - -输出结果: - 第2行: 跳过(解析失败) - 第3行: 跳过(数量为0) - 第4行: 标记E4="否",但添加警告 "部件 '669999' 在[现存量]中未找到" - - 统计信息: - 处理订单数: 3 - 包含部件订单: 1 - 库存充足订单: 0 - 库存不足订单: 1 - 跳过订单数: 2 - - 警告信息: - 部件 '669999' 在[现存量]中未找到 -``` - ---- - -## 工作表结构要求 - -### [产品订单] 工作表 - -| 列号 | 字段名 | 说明 | 必填 | -|------|--------|------|------| -| A | 生产订单号 | 订单唯一标识 | 否 | -| B | 产品型号 | 产品完整型号 | 是 | -| C | 数量 | 生产数量 | 是 | -| D | 产品编码 | 产品代码 | 否 | -| E | 部件优先 | 是否优先提取部件类别(输出字段) | - | - -- **表头**: 第1行 -- **数据起始**: 第2行 -- **输出位置**: E列(第5列) - -### [现存量] 工作表 - -| 列号 | 字段名 | 说明 | -|------|--------|------| -| B | 物料编码 | 对应BOM中的66代码 | -| J | 结存主数量 | 库存数量 | - -- **表头**: 第3行 -- **数据起始**: 第4行 - -### [平台配置清单] 工作表 - -标准的BOM配置清单,用于提取物料信息。 - ---- - -## 相关模块 - -### 依赖的类模块 -- **BomExtractor**: BOM提取器,负责从平台配置清单中提取匹配的物料 -- **ProductModelParser**: 产品型号解析器,解析型号字符串为结构化条件 -- **BomItem**: BOM物料项数据模型 - -### 相关模块 -- **BIPUploadModule**: 生成BIP上传模板 - - 读取"部件优先"字段 - - 当值为"否"时排除"部件"类别物料 - - 与本模块功能互补 - ---- - -## 算法复杂度 - -| 操作 | 时间复杂度 | 说明 | -|------|-----------|------| -| LoadInventoryData | O(n) | n = 库存行数 | -| LoadOrderData | O(m) | m = 订单行数 | -| ParseAllOrdersBOM | O(m × p) | p = 平均BOM物料数 | -| CalculateComponentDemand | O(m) | 遍历订单统计需求 | -| ValidateInventory | O(k) | k = 不同部件数 | -| AllocateInventory | O(m) | 遍历订单分配库存 | - -**总体复杂度**: O(m × p),主要由BOM解析决定 - ---- - -## 版本历史 - -| 版本 | 日期 | 说明 | -|------|------|------| -| 1.0 | 2026-02-03 | 初始版本,实现部件库存核对功能 | diff --git a/docs/执行计划-部件数量核对.md b/docs/执行计划-部件数量核对.md deleted file mode 100644 index 9726a3d..0000000 --- a/docs/执行计划-部件数量核对.md +++ /dev/null @@ -1,391 +0,0 @@ -# 执行计划:部件数量核对功能 - -## 一、需求概述 - -### 1.1 功能描述 -自动核对产品订单中"部件"类物料的库存情况,当库存不足时,按订单顺序将超出部分的订单的"部件优先"字段标记为"否"。 - -### 1.2 数据源 - -#### [产品订单]工作表 -- **表头位置**:第1行 -- **数据起始行**:第2行 -- **关键列**: - - B列:产品型号 - - C列:产品数量 - - E列:部件优先(输出字段) - -#### [现存量]工作表 -- **表头位置**:第3行 -- **数据起始行**:第4行 -- **关键列**: - - B列:物料编码(对应BOM中的66代码) - - J列:结存主数量(库存数量) - ---- - -## 二、业务逻辑 - -### 2.1 核心流程 - -``` -1. 读取所有订单 → 2. 解析型号提取BOM → 3. 识别部件物料 -↓ -4. 统计部件总需求 → 5. 查询库存 → 6. 库存充足性检查 -↓ -7. 按订单顺序分配库存 → 8. 修改部件优先字段 -``` - -### 2.2 计算规则 - -#### 部件总需求量 -``` -部件总需求 = Σ(订单i的BOM中部件数量 × 订单i的产品数量) -``` - -#### 示例 -``` -订单1:产品数量=2,部件A的BOM数量=1 → 需求 = 1 × 2 = 2 -订单2:产品数量=2,部件A的BOM数量=1 → 需求 = 1 × 2 = 2 -订单3:产品数量=2,部件A的BOM数量=1 → 需求 = 1 × 2 = 2 --------------------------------------------------------- -部件A总需求 = 2 + 2 + 2 = 6 -部件A库存 = 5 -库存不足 = 6 - 5 = 1 -``` - -### 2.3 分配策略 - -**原则**:按订单自上而下的顺序分配库存 - -| 订单 | 需求量 | 分配前库存 | 分配后库存 | 部件优先 | -|------|--------|-----------|-----------|---------| -| 订单1 | 2 | 5 | 3 | 保持不变 | -| 订单2 | 2 | 3 | 1 | 保持不变 | -| 订单3 | 2 | 1 | -1(不足) | **改为"否"** | - ---- - -## 三、处理规则 - -### 3.1 正常情况 -- 订单包含"部件"物料,库存充足 → 保持原值 -- 订单包含"部件"物料,库存不足 → 改为"否" -- 订单不包含"部件"物料 → **保持原值不变** - -### 3.2 异常情况 -- **部件在[现存量]中找不到** → 报错提示,终止处理 -- **型号解析失败** → 跳过该订单,记录错误 -- **未匹配到任何物料** → 跳过该订单(无部件) - -### 3.3 字段修改规则 -- **基于现有值进行修改**(不清空原有值) -- **只修改E列"部件优先"字段** -- 修改格式:`是`、`否`、`1`、`0`、`TRUE`、`FALSE` 均可识别 - ---- - -## 四、数据结构设计 - -### 4.1 订单数据结构 -```vba -Type OrderInfo - RowNumber As Long ' 行号 - ProductModel As String ' 产品型号 - Quantity As Long ' 产品数量 - ComponentCode As String ' 部件66编码 - ComponentQty As Double ' 部件BOM数量 - HasComponent As Boolean ' 是否包含部件 - ParseError As String ' 解析错误信息 -End Type -``` - -### 4.2 部件库存结构 -```vba -Type ComponentInventory - ComponentCode As String ' 部件66编码 - TotalDemand As Double ' 总需求量 - AvailableStock As Double ' 可用库存 - IsShortage As Boolean ' 是否短缺 -End Type -``` - ---- - -## 五、实现方案 - -### 5.1 新建模块 - -**文件名**:`ComponentInventoryCheckModule.bas` - -**主要过程**: -```vba -Public Sub CheckComponentInventory() - ' 主入口程序 -End Sub -``` - -### 5.2 核心函数 - -#### 5.2.1 读取订单数据 -```vba -Private Function LoadOrderData(ws As Worksheet) As Collection - ' 返回 Collection(Of OrderInfo) - ' 读取[产品订单]的B、C列数据 -End Function -``` - -#### 5.2.2 读取库存数据 -```vba -Private Function LoadInventoryData(ws As Worksheet) As Object - ' 返回 Dictionary(物料编码 -> 库存数量) - ' 读取[现存量]的B、J列数据 -End Function -``` - -#### 5.2.3 解析订单BOM -```vba -Private Sub ParseOrderBOM(orderInfo As OrderInfo, _ - bomExtractor As BomExtractor, _ - parser As ProductModelParser) - ' 解析型号,提取BOM - ' 识别"部件"类别物料 - ' 填充 orderInfo.ComponentCode 和 orderInfo.ComponentQty -End Sub -``` - -#### 5.2.4 统计部件需求 -```vba -Private Function CalculateComponentDemand( _ - orders As Collection) As Object - ' 返回 Dictionary(部件编码 -> ComponentInventory) - ' 累加所有订单的部件需求量 -End Function -``` - -#### 5.2.5 检查库存充足性 -```vba -Private Sub ValidateInventory( _ - componentDemands As Object, _ - inventoryData As Object) - ' 检查每个部件的库存是否充足 - ' 如果找不到或不足,报错提示 -End Sub -``` - -#### 5.2.6 分配库存并标记 -```vba -Private Sub AllocateInventory( _ - orders As Collection, _ - componentDemands As Object, _ - orderSheet As Worksheet) - ' 按订单顺序分配库存 - ' 库存不足时,修改E列"部件优先"为"否" -End Sub -``` - ---- - -## 六、详细实现步骤 - -### 6.1 主流程(CheckComponentInventory) - -``` -1. 获取工作表对象 - ├─ [产品订单]工作表 - ├─ [现存量]工作表 - └─ [平台配置清单]工作表 - -2. 初始化BOM提取器 - └─ 加载BOM数据 - -3. 读取库存数据到字典 - └─ Dictionary(66编码 -> 库存数量) - -4. 读取订单数据 - └─ Collection(Of OrderInfo) - -5. 解析所有订单的BOM - └─ 识别部件,填充ComponentCode和ComponentQty - -6. 统计部件总需求 - └─ Dictionary(部件编码 -> ComponentInventory) - -7. 验证库存 - └─ 检查部件是否存在于[现存量]中 - -8. 按订单顺序分配库存 - └─ 修改E列"部件优先"字段 - -9. 输出结果统计 - └─ MsgBox显示处理结果 -``` - -### 6.2 分配算法(伪代码) - -```vba -For Each order In orders - If order.HasComponent Then - Dim demand As ComponentInventory - demand = componentDemands(order.ComponentCode) - - Dim requiredQty As Double - requiredQty = order.ComponentQty * order.Quantity - - If demand.AvailableStock >= requiredQty Then - ' 库存充足,保持原值 - demand.AvailableStock = demand.AvailableStock - requiredQty - Else - ' 库存不足,标记为"否" - orderSheet.Cells(order.RowNumber, 5).Value = "否" - demand.AvailableStock = demand.AvailableStock - requiredQty - End If - End If -Next order -``` - ---- - -## 七、边界情况处理 - -### 7.1 无部件的订单 -``` -条件:订单的BOM中没有"类别=部件"的物料 -处理:跳过该订单,E列保持原值 -``` - -### 7.2 多个订单使用不同部件 -``` -订单1:部件A -订单2:部件B -处理:分别统计A和B的需求,独立核算库存 -``` - -### 7.3 库存为0的情况 -``` -条件:[现存量]中某部件的结存主数量 = 0 -处理:所有需要该部件的订单都标记为"否" -``` - -### 7.4 产品数量为0的情况 -``` -条件:订单的C列产品数量 = 0 -处理:该订单的部件需求 = 0,不影响库存 -``` - -### 7.5 部件优先字段原有值 -``` -可能的值:"是"、"否"、1、0、TRUE、FALSE、空 -处理:基于现有值修改,不清空 -``` - ---- - -## 八、测试用例 - -### 8.1 基本功能测试 - -| 测试场景 | 订单数 | 部件 | 总需求 | 库存 | 预期结果 | -|---------|--------|------|--------|------|---------| -| 库存充足 | 3 | A(1) | 6 | 10 | 全部保持原值 | -| 库存不足 | 3 | A(1) | 6 | 5 | 第3个订单改为"否" | -| 库存刚好 | 3 | A(1) | 6 | 6 | 全部保持原值 | -| 无部件订单 | 2 | - | - | - | 保持原值 | -| 库存为0 | 2 | A(1) | 4 | 0 | 全部改为"否" | - -### 8.2 异常情况测试 - -| 测试场景 | 预期行为 | -|---------|---------| -| 部件在[现存量]中不存在 | 报错提示,终止处理 | -| 型号解析失败 | 跳过该订单,记录错误 | -| [产品订单]为空 | 提示无数据,退出 | -| [现存量]为空 | 报错提示,退出 | - -### 8.3 边界值测试 - -| 测试场景 | 输入值 | 预期结果 | -|---------|--------|---------| -| 产品数量为1 | 所有订单数量=1 | 正常计算 | -| 产品数量为大数 | 订单数量=1000 | 正常计算 | -| 部件BOM数量为小数 | 0.5 | 正确计算总需求 | -| 库存数量为小数 | 2.5 | 正确判断库存 | - ---- - -## 九、用户界面设计 - -### 9.1 执行入口 - -建议在主界面添加按钮: -``` -[部件库存核对] -``` - -### 9.2 结果提示 - -执行完成后显示: -``` -✓ 部件库存核对完成! - -处理订单数:10 -包含部件订单:8 -库存充足订单:6 -库存不足订单:2 - -耗时:0.50秒 -``` - -### 9.3 错误提示 - -格式: -``` -✗ 部件库存核对失败! - -错误信息: -- 订单第5行:部件 '661234' 在[现存量]中未找到 - -请检查数据后重试。 -``` - ---- - -## 十、实施步骤 - -1. **创建新模块** - - 文件名:`ComponentInventoryCheckModule.bas` - - 位置:`VBA/Modules/` - -2. **实现核心函数** - - LoadOrderData - - LoadInventoryData - - ParseOrderBOM - - CalculateComponentDemand - - ValidateInventory - - AllocateInventory - -3. **实现主流程** - - CheckComponentInventory - -4. **测试验证** - - 基本功能测试 - - 异常情况测试 - - 边界值测试 - -5. **集成到主界面** - - 添加按钮或菜单项 - ---- - -## 十一、风险评估 - -| 风险 | 影响 | 缓解措施 | -|------|------|---------| -| 库存数据不准确 | 导致错误的分配结果 | 执行前提示用户确认库存数据 | -| 订单量大导致性能问题 | 处理时间长 | 优化算法,使用批量操作 | -| BOM解析失败 | 无法识别部件 | 记录错误日志,跳过该订单 | -| 部件编码不一致 | 无法匹配库存 | 严格验证,报错提示 | - ---- - -**是否批准此执行计划?确认后我将开始代码实现。** diff --git a/docs/执行计划-附加功能支持.md b/docs/执行计划-附加功能支持.md deleted file mode 100644 index 6ac4b72..0000000 --- a/docs/执行计划-附加功能支持.md +++ /dev/null @@ -1,199 +0,0 @@ -# 执行计划:添加附加功能(fjgn)支持 - -## 一、需求概述 - -### 1.1 新增条件字段 -- **条件代码**:`fjgn` -- **条件名称**:`附加功能` -- **提取位置**:从型号表头的[仪表特性]字段中提取(第6个位置,索引5) - -### 1.2 型号结构 -``` -[型号]-[公称外径].[安装形式].[壳体形式].[过程连接&接液材质].[量程范围].[仪表特性] -``` - -### 1.3 仪表特性解析规则 -- **充油类型**:位于最后,格式为 `Y`+一位数字(如 Y3) -- **附加功能**:在充油类型前面的内容 - - 分隔符可能是 `,` 或 `.`(如 `N2,N3` 或 `N2.N3`) - - 可能为空(如仪表特性只有 `Y3`) - -### 1.4 示例 -| 型号 | 仪表特性 | 充油类型 | 附加功能 | -|------|----------|----------|----------| -| PYTHN-100.A0.541.M201.M06.N2,N3.Y3 | N2,N3.Y3 | Y3 | N2,N3 | -| YTHN-100.A0.531.M201.M08.Y3 | Y3 | Y3 | (空) | - ---- - -## 二、条件评估规则 - -### 2.1 fjgn 条件的特殊处理 - -| 条件表达式 | 型号中的 fjgn | 评估结果 | 说明 | -|-----------|--------------|---------|------| -| fjgn=N1 | N1,N2 | ✅ True | 包含 N1 | -| fjgn=N1 | N2,N3 | ❌ False | 不包含 N1 | -| fjgn=N1 | (空) | ❌ False | 空值不包含任何值 | -| fjgn!=N1 | N1,N2 | ❌ False | 包含 N1,不满足!= | -| fjgn!=N1 | N2,N3 | ✅ True | 不包含 N1 | -| fjgn!=N1 | (空) | ✅ True | 空值不包含 N1 | - -### 2.2 匹配逻辑 -- **等值匹配(fjgn=XX)**:fjgn 字符串中包含指定值即为真 -- **不等匹配(fjgn!=XX)**:fjgn 字符串中不包含指定值即为真 -- **空值处理**:空字符串不包含任何值 - ---- - -## 三、代码修改方案 - -### 3.1 ProductModelParser.cls - -**修改位置**:`ParseHeader()` 方法 - -**新增内容**: -```vba -' 仪表特性 - 第6个位置(索引5),可能不存在 -If UBound(dotParts) >= 5 Then - Dim instrumentFeature As String - instrumentFeature = Trim(dotParts(5)) - - ' 提取附加功能 - Dim fjgn As String - fjgn = ExtractAdditionalFeatures(instrumentFeature) - - pConditions.Add "fjgn", fjgn -Else - ' 如果没有仪表特性字段,fjgn为空 - pConditions.Add "fjgn", "" -End If -``` - -**新增方法**:`ExtractAdditionalFeatures()` -```vba -Private Function ExtractAdditionalFeatures(instrumentFeature As String) As String - ' 1. 检查是否以Y+数字结尾(充油类型) - Dim lastTwoChars As String - If Len(instrumentFeature) >= 2 Then - lastTwoChars = Right(instrumentFeature, 2) - If UCase(Left(lastTwoChars, 1)) = "Y" And IsNumeric(Right(lastTwoChars, 1)) Then - ' 去掉充油类型 - instrumentFeature = Left(instrumentFeature, Len(instrumentFeature) - 2) - instrumentFeature = Trim(instrumentFeature) - End If - End If - - ' 2. 处理可能的分隔符(,或.) - ' 将可能的.替换为,,统一处理 - instrumentFeature = Replace(instrumentFeature, ".", ",") - - ' 3. 去除可能的后缀分隔符 - If Right(instrumentFeature, 1) = "," Then - instrumentFeature = Left(instrumentFeature, Len(instrumentFeature) - 1) - End If - - ExtractAdditionalFeatures = Trim(instrumentFeature) -End Function -``` - ---- - -### 3.2 BIPUploadModule.bas - -**修改位置**:第12行常量定义 - -**修改内容**: -```vba -' 修改前 -Private Const CONDITION_CONFIG = "azxs,安装形式|bkxs,表壳形式|gclj,过程连接|jycz,接液材质|lcfw,量程范围" - -' 修改后 -Private Const CONDITION_CONFIG = "azxs,安装形式|bkxs,表壳形式|gclj,过程连接|jycz,接液材质|lcfw,量程范围|fjgn,附加功能" -``` - ---- - -### 3.3 ConditionEvaluator.cls - -**修改位置**:`EvaluateSingleCondition()` 方法 - -**修改内容**:在现有的 `=` 和 `!=` 运算符处理后,添加 fjgn 特殊处理 - -```vba -' 在现有的 actualValue = Conditions(varName) 之后添加 -actualValue = Conditions(varName) - -' fjgn 字段特殊处理(多值匹配) -If varName = "fjgn" Then - If operator = "=" Then - ' fjgn=N1:检查actualValue中是否包含value - EvaluateSingleCondition = InStr(actualValue, value) > 0 - ElseIf operator = "!=" Then - ' fjgn!=N1:检查actualValue中是否不包含value - EvaluateSingleCondition = InStr(actualValue, value) = 0 - End If - Exit Function -End If - -' 其他字段使用原有逻辑 -EvaluateSingleCondition = (actualValue = value) -``` - ---- - -## 四、测试用例 - -### 4.1 解析测试 - -| 测试型号 | 预期 fjgn | 预期其他条件 | -|---------|----------|-------------| -| PYTHN-100.A0.541.M201.M06.N2,N3.Y3 | N2,N3 | azxs=A0, bkxs=541, gclj=M20, jycz=1, lcfw=M06 | -| YTHN-100.A0.531.M201.M08.Y3 | (空) | azxs=A0, bkxs=531, gclj=M20, jycz=1, lcfw=M08 | -| BP-088.2312.M08.0A3.N1.N2.Y2 | N1,N2 | azxs=23, bkxs=12, gclj=M08, jycz=0, lcfw=A3 | -| BP-088.2312.M08.0A3.Y2 | (空) | azxs=23, bkxs=12, gclj=M08, jycz=0, lcfw=A3 | - -### 4.2 条件评估测试 - -| fjgn值 | 选用条件 | 预期结果 | -|-------|---------|---------| -| N1,N2 | fjgn=N1 | True | -| N1,N2 | fjgn=N3 | False | -| (空) | fjgn=N1 | False | -| N1,N2 | fjgn!=N1 | False | -| N1,N2 | fjgn!=N3 | True | -| (空) | fjgn!=N1 | True | - ---- - -## 五、实施步骤 - -1. **修改 ProductModelParser.cls** - - 在 `ParseHeader()` 方法中添加仪表特性提取逻辑 - - 新增 `ExtractAdditionalFeatures()` 方法 - -2. **修改 BIPUploadModule.bas** - - 更新 `CONDITION_CONFIG` 常量 - -3. **修改 ConditionEvaluator.cls** - - 在 `EvaluateSingleCondition()` 方法中添加 fjgn 特殊处理逻辑 - -4. **测试验证** - - 测试型号解析是否正确 - - 测试条件评估是否符合预期 - - 测试边界情况(空值、多个附加功能等) - ---- - -## 六、风险评估 - -| 风险 | 影响 | 缓解措施 | -|------|------|---------| -| 旧型号缺少仪表特性字段 | 解析失败 | 判断字段是否存在,不存在时 fjgn 为空 | -| 充油类型识别错误 | 提取错误 | 严格匹配 Y+一位数字的格式 | -| 分隔符不一致 | 解析错误 | 统一将 `.` 替换为 `,` 处理 | -| 条件评估逻辑错误 | 匹配错误 | 充分测试各种边界情况 | - ---- - -**是否批准此执行计划?确认后我将开始代码实现。**