diff --git a/物控学习笔记/Excel_VBA_基础.md b/物控学习笔记/Excel_VBA_基础.md new file mode 100644 index 00000000..b2ab87e9 --- /dev/null +++ b/物控学习笔记/Excel_VBA_基础.md @@ -0,0 +1,863 @@ +# Excel VBA 物控基础教程 📊 + +> 从零开始学习 Excel VBA 在物控中的应用 + +--- + +## 目录 + +1. [VBA 基础语法](#vba-基础语法) +2. [常用对象](#常用对象) +3. [物控常用函数](#物控常用函数) +4. [实战案例](#实战案例) +5. [调试技巧](#调试技巧) + +--- + +## VBA 基础语法 + +### 1. 变量声明 + +```vba +' 基本变量类型 +Dim item_code As String ' 物料编码 +Dim quantity As Double ' 数量 +Dim unit_price As Currency ' 单价 +Dim order_date As Date ' 订单日期 +Dim is_critical As Boolean ' 是否关键物料 + +' 数组 +Dim part_list(1 To 100) As String +Dim inventory_data(1 To 1000, 1 To 5) As Variant + +' 集合和字典 +Dim dict As Object +Set dict = CreateObject("Scripting.Dictionary") +``` + +### 2. 条件语句 + +```vba +' If-Then-Else +If quantity < safety_stock Then + MsgBox "库存不足!" + status = "需补货" +ElseIf quantity > max_stock Then + status = "库存过高" +Else + status = "正常" +End If + +' Select Case(适合多条件判断) +Select Case abc_class + Case "A" + priority = "高" + review_freq = "每周" + Case "B" + priority = "中" + review_freq = "每月" + Case "C" + priority = "低" + review_freq = "每季度" +End Select +``` + +### 3. 循环结构 + +```vba +' For 循环(已知次数) +For i = 2 To last_row + part_no = Cells(i, "A").Value + qty = Cells(i, "B").Value + + ' 处理数据 + If qty > 0 Then + Cells(i, "C").Value = qty * 1.1 ' 建议采购量 + End If +Next i + +' For Each 循环(遍历集合) +Dim ws As Worksheet +For Each ws In ThisWorkbook.Worksheets + Debug.Print ws.Name +Next ws + +' Do While 循环(条件满足时继续) +Do While Cells(row, "A").Value <> "" + ' 处理数据 + row = row + 1 +Loop +``` + +### 4. 函数定义 + +```vba +' 自定义函数(Function) +Function Calculate_Safety_Stock(avg_demand As Double, lead_time As Double) As Double + Dim safety_factor As Double + safety_factor = 1.5 ' 安全系数 + + Calculate_Safety_Stock = avg_demand * lead_time * safety_factor +End Function + +' 子程序(Sub) +Sub Generate_Purchase_Order() + ' 执行采购订单生成逻辑 + MsgBox "采购订单生成完成!" +End Sub +``` + +--- + +## 常用对象 + +### 1. Worksheet 对象 + +```vba +' 引用工作表 +Dim ws As Worksheet +Set ws = ThisWorkbook.Sheets("库存") +Set ws = ThisWorkbook.Sheets(1) ' 按索引 + +' 激活工作表 +ws.Activate + +' 工作表属性 +Debug.Print ws.Name ' 工作表名称 +Debug.Print ws.Cells.Count ' 单元格数量 +Debug.Print ws.UsedRange.Rows.Count ' 已使用行数 + +' 清空工作表 +ws.Cells.Clear ' 清除所有内容和格式 +ws.Cells.ClearContents ' 仅清除内容 +``` + +### 2. Range 对象 + +```vba +' 单元格引用 +Cells(1, 1).Value = "物料编码" ' 行列索引 +Range("A1").Value = "物料编码" ' A1 表示法 +Range("A1:C10").Value = "数据" ' 区域 + +' 动态范围 +last_row = Cells(Rows.Count, "A").End(xlUp).Row +last_col = Cells(1, Columns.Count).End(xlToLeft).Column + +' 批量操作 +Range("A2:A" & last_row).Value = "默认值" + +' 格式设置 +With Range("A1:H1") + .Font.Bold = True + .Interior.Color = RGB(200, 200, 200) + .HorizontalAlignment = xlCenter +End With +``` + +### 3. Workbook 对象 + +```vba +' 当前工作簿 +Set wb = ThisWorkbook + +' 新建工作簿 +Set new_wb = Workbooks.Add + +' 打开工作簿 +Set wb = Workbooks.Open("C:\路径\文件.xlsx") + +' 保存 +wb.Save +wb.SaveAs "C:\路径\新文件.xlsx" + +' 关闭 +wb.Close SaveChanges:=True +``` + +### 4. 文件对话框 + +```vba +' 选择文件 +Dim file_path As String +With Application.FileDialog(msoFileDialogFilePicker) + .Title = "选择库存数据文件" + .Filters.Clear + .Filters.Add "Excel 文件", "*.xlsx;*.xls" + + If .Show = -1 Then + file_path = .SelectedItems(1) + End If +End With +``` + +--- + +## 物控常用函数 + +### 1. 库存计算函数 + +```vba +' 计算安全库存 +Function Safety_Stock(avg_demand As Double, lead_time As Double, _ + Optional service_level As Double = 0.95) As Double + + Dim z_score As Double + Select Case service_level + Case 0.90: z_score = 1.28 + Case 0.95: z_score = 1.65 + Case 0.98: z_score = 2.05 + Case 0.99: z_score = 2.33 + Case Else: z_score = 1.65 + End Select + + Safety_Stock = avg_demand * lead_time * z_score +End Function + +' 计算订货点 +Function Reorder_Point(avg_demand As Double, lead_time As Double, _ + safety_stock As Double) As Double + + Reorder_Point = avg_demand * lead_time + safety_stock +End Function + +' 计算经济订货量(EOQ) +Function EOQ(annual_demand As Double, order_cost As Double, _ + holding_cost As Double) As Double + + EOQ = Sqr(2 * annual_demand * order_cost / holding_cost) +End Function + +' 计算库存周转率 +Function Inventory_Turnover(sales_cost As Double, avg_inventory As Double) As Double + If avg_inventory = 0 Then + Inventory_Turnover = 0 + Else + Inventory_Turnover = sales_cost / avg_inventory + End If +End Function + +' 计算库存天数 +Function Inventory_Days(turnover As Double) As Double + If turnover = 0 Then + Inventory_Days = 0 + Else + Inventory_Days = 365 / turnover + End If +End Function +``` + +### 2. 数据查找函数 + +```vba +' 查找物料信息 +Function Get_Part_Info(part_no As String, info_type As String) As Variant + Dim ws As Worksheet + Set ws = ThisWorkbook.Sheets("物料主数据") + + Dim last_row As Long + last_row = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row + + Dim i As Long + For i = 2 To last_row + If ws.Cells(i, "A").Value = part_no Then + Select Case info_type + Case "名称": Get_Part_Info = ws.Cells(i, "B").Value + Case "规格": Get_Part_Info = ws.Cells(i, "C").Value + Case "单位": Get_Part_Info = ws.Cells(i, "D").Value + Case "单价": Get_Part_Info = ws.Cells(i, "E").Value + Case "供应商": Get_Part_Info = ws.Cells(i, "F").Value + Case Else: Get_Part_Info = "" + End Select + Exit Function + End If + Next i + + Get_Part_Info = "未找到" +End Function + +' 查找库存数量 +Function Get_Stock_Qty(part_no As String) As Double + On Error Resume Next + Get_Stock_Qty = Application.WorksheetFunction.VLookup( _ + part_no, ThisWorkbook.Sheets("库存").Range("A:D"), 4, False) + + If Err.Number <> 0 Then + Get_Stock_Qty = 0 + Err.Clear + End If + On Error GoTo 0 +End Function +``` + +### 3. 数据验证函数 + +```vba +' 检查物料编码格式 +Function Validate_Part_No(part_no As String) As Boolean + ' 假设物料编码格式:3位字母 + 6位数字(如:ABC123456) + If Len(part_no) = 9 Then + If Left(part_no, 3) Like "[A-Z][A-Z][A-Z]" And _ + Right(part_no, 6) Like "######" Then + Validate_Part_No = True + Exit Function + End If + End If + + Validate_Part_No = False +End Function + +' 检查库存合理性 +Function Validate_Stock_Qty(qty As Double, min_qty As Double, max_qty As Double) As Boolean + If qty >= min_qty And qty <= max_qty Then + Validate_Stock_Qty = True + Else + Validate_Stock_Qty = False + End If +End Function +``` + +--- + +## 实战案例 + +### 案例 1:库存报表自动生成 + +```vba +Sub Generate_Inventory_Report() + Dim ws_stock As Worksheet, ws_report As Worksheet + Dim last_row As Long, i As Long + Dim report_row As Long + + ' 设置工作表 + Set ws_stock = ThisWorkbook.Sheets("库存") + + ' 删除旧报表 + On Error Resume Next + Application.DisplayAlerts = False + ThisWorkbook.Sheets("库存报表").Delete + Application.DisplayAlerts = True + On Error GoTo 0 + + ' 创建新报表 + Set ws_report = ThisWorkbook.Sheets.Add + ws_report.Name = "库存报表_" & Format(Date, "yyyy-mm-dd") + + ' 设置表头 + ws_report.Range("A1:J1").Value = Array( _ + "序号", "物料编码", "物料名称", "规格", "单位", _ + "当前库存", "安全库存", "最高库存", "状态", "建议操作") + ws_report.Range("A1:J1").Font.Bold = True + + ' 获取数据 + last_row = ws_stock.Cells(ws_stock.Rows.Count, "A").End(xlUp).Row + report_row = 2 + + For i = 2 To last_row + Dim part_no As String + Dim part_name As String + Dim spec As String + Dim unit As String + Dim current_qty As Double + Dim safety_qty As Double + Dim max_qty As Double + + part_no = ws_stock.Cells(i, "A").Value + part_name = ws_stock.Cells(i, "B").Value + spec = ws_stock.Cells(i, "C").Value + unit = ws_stock.Cells(i, "G").Value + current_qty = ws_stock.Cells(i, "D").Value + safety_qty = ws_stock.Cells(i, "E").Value + max_qty = ws_stock.Cells(i, "F").Value + + ' 写入报表 + ws_report.Cells(report_row, 1).Value = report_row - 1 + ws_report.Cells(report_row, 2).Value = part_no + ws_report.Cells(report_row, 3).Value = part_name + ws_report.Cells(report_row, 4).Value = spec + ws_report.Cells(report_row, 5).Value = unit + ws_report.Cells(report_row, 6).Value = current_qty + ws_report.Cells(report_row, 7).Value = safety_qty + ws_report.Cells(report_row, 8).Value = max_qty + + ' 判断状态 + If current_qty < safety_qty Then + ws_report.Cells(report_row, 9).Value = "⚠️ 库存不足" + ws_report.Cells(report_row, 9).Interior.Color = RGB(255, 200, 200) + ws_report.Cells(report_row, 10).Value = "立即补货" + ElseIf current_qty > max_qty Then + ws_report.Cells(report_row, 9).Value = "📦 库存过高" + ws_report.Cells(report_row, 9).Interior.Color = RGB(255, 255, 200) + ws_report.Cells(report_row, 10).Value = "暂停采购" + Else + ws_report.Cells(report_row, 9).Value = "✅ 正常" + ws_report.Cells(report_row, 9).Interior.Color = RGB(200, 255, 200) + ws_report.Cells(report_row, 10).Value = "维持现状" + End If + + report_row = report_row + 1 + Next i + + ' 格式化 + ws_report.Columns("A:J").AutoFit + + ' 添加统计 + ws_report.Range("L1").Value = "统计" + ws_report.Range("L2").Value = "总物料数:" + ws_report.Range("M2").Value = report_row - 2 + ws_report.Range("L3").Value = "缺料数:" + ws_report.Range("M3").Value = Application.WorksheetFunction.CountIf(ws_report.Range("I:I"), "*不足*") + ws_report.Range("L4").Value = "库存过高数:" + ws_report.Range("M4").Value = Application.WorksheetFunction.CountIf(ws_report.Range("I:I"), "*过高*") + + MsgBox "库存报表生成完成!" & vbCrLf & _ + "报表名称:" & ws_report.Name +End Sub +``` + +### 案例 2:MRP 计算 + +```vba +Sub Calculate_MRP() + Dim ws_bom As Worksheet, ws_inventory As Worksheet, ws_mrp As Worksheet + Dim last_row As Long, i As Long, mrp_row As Long + + ' 设置工作表 + Set ws_bom = ThisWorkbook.Sheets("BOM") + Set ws_inventory = ThisWorkbook.Sheets("库存") + + ' 删除旧 MRP 表 + On Error Resume Next + Application.DisplayAlerts = False + ThisWorkbook.Sheets("MRP").Delete + Application.DisplayAlerts = True + On Error GoTo 0 + + ' 创建 MRP 表 + Set ws_mrp = ThisWorkbook.Sheets.Add + ws_mrp.Name = "MRP" + + ' 设置表头 + ws_mrp.Range("A1:H1").Value = Array( _ + "物料编码", "物料名称", "需求量", "库存量", _ + "净需求", "建议采购", "供应商", "到货日期") + ws_mrp.Range("A1:H1").Font.Bold = True + + ' 获取 BOM 数据 + last_row = ws_bom.Cells(ws_bom.Rows.Count, "A").End(xlUp).Row + mrp_row = 2 + + For i = 2 To last_row + Dim part_no As String + Dim part_name As String + Dim qty_needed As Double + Dim qty_in_stock As Double + Dim net_qty As Double + Dim lead_time As Double + + part_no = ws_bom.Cells(i, "A").Value + part_name = ws_bom.Cells(i, "B").Value + qty_needed = ws_bom.Cells(i, "C").Value + + ' 查找库存 + On Error Resume Next + qty_in_stock = Application.WorksheetFunction.VLookup( _ + part_no, ws_inventory.Range("A:D"), 4, False) + If Err.Number <> 0 Then + qty_in_stock = 0 + Err.Clear + End If + On Error GoTo 0 + + ' 计算净需求 + net_qty = qty_needed - qty_in_stock + + ' 如果需要采购 + If net_qty > 0 Then + ws_mrp.Cells(mrp_row, 1).Value = part_no + ws_mrp.Cells(mrp_row, 2).Value = part_name + ws_mrp.Cells(mrp_row, 3).Value = qty_needed + ws_mrp.Cells(mrp_row, 4).Value = qty_in_stock + ws_mrp.Cells(mrp_row, 5).Value = net_qty + + ' 建议采购量(考虑 MOQ 和安全系数) + Dim suggested_qty As Double + suggested_qty = net_qty * 1.1 ' 10% 安全系数 + + ' 查找最小订货量 + Dim moq As Double + On Error Resume Next + moq = Application.WorksheetFunction.VLookup( _ + part_no, ws_inventory.Range("A:H"), 8, False) + If Err.Number <> 0 Then + moq = 1 + Err.Clear + End If + On Error GoTo 0 + + If suggested_qty < moq Then + suggested_qty = moq + End If + + ws_mrp.Cells(mrp_row, 6).Value = suggested_qty + + ' 查找供应商和提前期 + On Error Resume Next + ws_mrp.Cells(mrp_row, 7).Value = Application.WorksheetFunction.VLookup( _ + part_no, ws_inventory.Range("A:H"), 7, False) + lead_time = Application.WorksheetFunction.VLookup( _ + part_no, ws_inventory.Range("A:H"), 6, False) + If Err.Number <> 0 Then + lead_time = 7 ' 默认 7 天 + Err.Clear + End If + On Error GoTo 0 + + ' 计算到货日期 + ws_mrp.Cells(mrp_row, 8).Value = Date + lead_time + + mrp_row = mrp_row + 1 + End If + Next i + + ' 格式化 + ws_mrp.Columns("A:H").AutoFit + + ' 添加汇总 + ws_mrp.Range("J1").Value = "MRP 汇总" + ws_mrp.Range("J2").Value = "需采购物料数:" + ws_mrp.Range("K2").Value = mrp_row - 2 + ws_mrp.Range("J3").Value = "总采购金额:" + ws_mrp.Range("K3").Formula = "=SUM(F2:F" & mrp_row - 1 & ")*VLOOKUP(A2,库存!A:E,5,FALSE)" + + MsgBox "MRP 计算完成!" & vbCrLf & _ + "需采购物料数:" & mrp_row - 2 +End Sub +``` + +### 案例 3:呆滞料分析 + +```vba +Sub Analyze_Stagnant_Inventory() + Dim ws As Worksheet + Dim last_row As Long, i As Long + Dim stagnant_count As Long + Dim stagnant_value As Double + + Set ws = ThisWorkbook.Sheets("库存") + last_row = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row + + ' 添加分析列 + ws.Cells(1, "I").Value = "呆滞状态" + ws.Cells(1, "J").Value = "呆滞天数" + ws.Cells(1, "K").Value = "呆滞金额" + + ' 清空旧数据 + ws.Range("I2:K" & last_row).ClearContents + + ' 分析每行 + For i = 2 To last_row + Dim last_move_date As Date + Dim days_stagnant As Long + Dim stock_qty As Double + Dim unit_price As Double + + ' 获取最后移动日期(假设在 H 列) + On Error Resume Next + last_move_date = ws.Cells(i, "H").Value + If Err.Number <> 0 Or last_move_date = 0 Then + last_move_date = Date - 365 ' 默认一年前 + Err.Clear + End If + On Error GoTo 0 + + ' 计算呆滞天数 + days_stagnant = Date - last_move_date + ws.Cells(i, "J").Value = days_stagnant + + ' 计算呆滞金额 + stock_qty = ws.Cells(i, "D").Value + unit_price = ws.Cells(i, "E").Value + ws.Cells(i, "K").Value = stock_qty * unit_price + + ' 判断呆滞状态 + Select Case days_stagnant + Case Is > 365 + ws.Cells(i, "I").Value = "🔴 严重呆滞(>1年)" + ws.Cells(i, "I").Interior.Color = RGB(255, 150, 150) + stagnant_count = stagnant_count + 1 + stagnant_value = stagnant_value + stock_qty * unit_price + Case Is > 180 + ws.Cells(i, "I").Value = "🟡 中度呆滞(6-12月)" + ws.Cells(i, "I").Interior.Color = RGB(255, 220, 150) + Case Is > 90 + ws.Cells(i, "I").Value = "⚠️ 轻度呆滞(3-6月)" + ws.Cells(i, "I").Interior.Color = RGB(255, 255, 150) + Case Else + ws.Cells(i, "I").Value = "✅ 正常" + ws.Cells(i, "I").Interior.Color = RGB(200, 255, 200) + End Select + Next i + + ' 格式化 + ws.Columns("I:K").AutoFit + + ' 统计结果 + Dim total_value As Double + total_value = Application.WorksheetFunction.Sum(ws.Range("K2:K" & last_row)) + + MsgBox "呆滞料分析完成!" & vbCrLf & _ + "总物料数:" & last_row - 1 & vbCrLf & _ + "严重呆滞数:" & stagnant_count & vbCrLf & _ + "呆滞料金额:$" & Format(stagnant_value, "#,##0.00") & vbCrLf & _ + "呆滞比例:" & Format(stagnant_value / total_value * 100, "0.00") & "%" +End Sub +``` + +### 案例 4:盘点表生成 + +```vba +Sub Generate_Inventory_Count_Sheet() + Dim ws_stock As Worksheet, ws_count As Worksheet + Dim last_row As Long, i As Long + + ' 设置工作表 + Set ws_stock = ThisWorkbook.Sheets("库存") + + ' 删除旧盘点表 + On Error Resume Next + Application.DisplayAlerts = False + ThisWorkbook.Sheets("盘点表").Delete + Application.DisplayAlerts = True + On Error GoTo 0 + + ' 创建新盘点表 + Set ws_count = ThisWorkbook.Sheets.Add + ws_count.Name = "盘点表_" & Format(Date, "yyyy-mm-dd") + + ' 设置表头 + ws_count.Range("A1:H1").Value = Array( _ + "序号", "物料编码", "物料名称", "规格", _ + "单位", "账面数量", "实盘数量", "差异") + ws_count.Range("A1:H1").Font.Bold = True + + ' 获取库存数据 + last_row = ws_stock.Cells(ws_stock.Rows.Count, "A").End(xlUp).Row + + For i = 2 To last_row + ws_count.Cells(i, 1).Value = i - 1 + ws_count.Cells(i, 2).Value = ws_stock.Cells(i, "A").Value + ws_count.Cells(i, 3).Value = ws_stock.Cells(i, "B").Value + ws_count.Cells(i, 4).Value = ws_stock.Cells(i, "C").Value + ws_count.Cells(i, 5).Value = ws_stock.Cells(i, "G").Value + ws_count.Cells(i, 6).Value = ws_stock.Cells(i, "D").Value + Next i + + ' 设置差异公式 + ws_count.Range("H2:H" & last_row).Formula = "=F2-G2" + + ' 格式化 + ws_count.Columns("A:H").AutoFit + + ' 添加说明 + ws_count.Range("J1").Value = "盘点说明" + ws_count.Range("J2").Value = "1. 在'实盘数量'列填写实际盘点数量" + ws_count.Range("J3").Value = "2. '差异'列会自动计算" + ws_count.Range("J4").Value = "3. 差异为负表示盘亏,为正表示盘盈" + + ' 添加统计 + ws_count.Range("J6").Value = "盘点统计" + ws_count.Range("J7").Value = "总物料数:" + ws_count.Range("K7").Value = last_row - 1 + + MsgBox "盘点表生成完成!" & vbCrLf & _ + "盘点表名称:" & ws_count.Name +End Sub +``` + +--- + +## 调试技巧 + +### 1. 基本调试方法 + +```vba +' 使用 Debug.Print 输出信息 +Debug.Print "当前处理行:" & i +Debug.Print "物料编码:" & part_no +Debug.Print "库存数量:" & qty + +' 使用断点 +' 在代码左侧点击设置断点(F9) +' 程序会在断点处暂停,可以检查变量值 + +' 使用 Watch 窗口 +' 视图 → 立即窗口 (Ctrl+G) +' 视图 → 监视窗口 +``` + +### 2. 错误处理 + +```vba +' 基本错误处理 +On Error Resume Next ' 忽略错误,继续执行 +' 你的代码... +If Err.Number <> 0 Then + Debug.Print "错误:" & Err.Description + Err.Clear +End If +On Error GoTo 0 ' 恢复正常错误处理 + +' 详细错误处理 +Sub Safe_Execution() + On Error GoTo ErrorHandler + + ' 主要代码 + Dim ws As Worksheet + Set ws = ThisWorkbook.Sheets("库存") + + ' 可能出错的操作 + Dim qty As Double + qty = ws.Range("D100000").Value ' 可能超出范围 + + Exit Sub + +ErrorHandler: + MsgBox "错误发生:" & vbCrLf & _ + "错误号:" & Err.Number & vbCrLf & _ + "错误描述:" & Err.Description & vbCrLf & _ + "错误位置:" & Erl, vbCritical + Err.Clear +End Sub +``` + +### 3. 性能优化 + +```vba +' 优化前(慢) +Sub Slow_Code() + For i = 1 To 10000 + Cells(i, "A").Value = Cells(i, "A").Value * 2 ' 每次都访问单元格 + Next i +End Sub + +' 优化后(快) +Sub Fast_Code() + Dim data_range As Range + Dim data_array As Variant + Dim i As Long + + ' 一次性读取数据到数组 + Set data_range = Range("A1:A10000") + data_array = data_range.Value + + ' 在数组中处理 + For i = 1 To 10000 + data_array(i, 1) = data_array(i, 1) * 2 + Next i + + ' 一次性写回 + data_range.Value = data_array +End Sub + +' 其他优化技巧 +Sub Optimization_Tips() + Application.ScreenUpdating = False ' 关闭屏幕刷新 + Application.Calculation = xlCalculationManual ' 关闭自动计算 + Application.EnableEvents = False ' 关闭事件 + + ' 执行耗时操作... + + Application.ScreenUpdating = True + Application.Calculation = xlCalculationAutomatic + Application.EnableEvents = True +End Sub +``` + +### 4. 调试工具 + +```vba +' 立即窗口(Immediate Window) +' Ctrl+G 打开 +' ? 变量名 ' 查看变量值 +' ? 10 * 2 ' 计算表达式 + +' 本地窗口(Locals Window) +' 视图 → 本地窗口 +' 显示所有局部变量的值 + +' 调试工具栏 +' 视图 → 工具栏 → 调试 +' 包含:继续、中断、逐语句、逐过程、跳出 +``` + +--- + +## 快速参考 + +### 常用快捷键 + +| 快捷键 | 功能 | +|--------|------| +| Alt + F11 | 打开 VBA 编辑器 | +| F5 | 运行宏 | +| F8 | 逐语句调试 | +| F9 | 设置/取消断点 | +| Ctrl + G | 打开立即窗口 | +| Ctrl + R | 打开工程资源管理器 | +| Ctrl + F | 查找 | +| Ctrl + H | 替换 | + +### 常用对象模型 + +```vba +' 工作簿集合 +Workbooks("文件名.xlsx") + +' 工作表集合 +ThisWorkbook.Sheets("表名") +ThisWorkbook.Sheets(1) ' 索引从 1 开始 + +' 单元格 +Cells(行, 列) ' 如:Cells(1, 1) = A1 +Range("A1") ' A1 单元格 +Range("A1:C10") ' A1 到 C10 区域 + +' 行和列 +Rows(1) ' 第 1 行 +Columns("A") ' A 列 +``` + +### 常用常量 + +```vba +' 对齐方式 +xlLeft ' 左对齐 +xlCenter ' 居中 +xlRight ' 右对齐 + +' 边框样式 +xlThin ' 细线 +xlMedium ' 中等 +xlThick ' 粗线 + +' 颜色 +RGB(255, 0, 0) ' 红色 +RGB(0, 255, 0) ' 绿色 +RGB(255, 255, 0) ' 黄色 +``` + +--- + +## 下一步 + +1. **练习基础语法**:尝试编写简单的宏 +2. **修改现有工具**:根据你的需求调整代码 +3. **创建新工具**:解决你的特定问题 +4. **学习高级技巧**:数组、字典、正则表达式 +5. **分享你的代码**:贡献到物控学习笔记 + +--- + +**最后更新**: 2026-02-03 +**版本**: v1.0.0 \ No newline at end of file diff --git a/物控学习笔记/README.md b/物控学习笔记/README.md new file mode 100644 index 00000000..fcce94d6 --- /dev/null +++ b/物控学习笔记/README.md @@ -0,0 +1,833 @@ +# 物控学习笔记 📚 + +> 物料控制(Material Control)学习笔记与最佳实践 + +--- + +## 📖 目录 + +- [基础知识](#基础知识) +- [Excel VBA 工具库](#excel-vba-工具库) +- [库存管理](#库存管理) +- [生产计划](#生产计划) +- [数据分析](#数据分析) +- [自动化脚本](#自动化脚本) +- [案例研究](#案例研究) +- [资源推荐](#资源推荐) + +--- + +## 基础知识 + +### 什么是物控? + +物料控制(Material Control)是生产管理中的核心环节,主要负责: +- 物料需求计划(MRP) +- 库存管理与优化 +- 采购计划制定 +- 物料追溯与盘点 +- 成本控制 + +### 物控的核心指标 + +1. **库存周转率** = 年销售成本 / 平均库存 +2. **库存天数** = 365 / 库存周转率 +3. **缺料率** = 缺料次数 / 总领料次数 +4. **呆滞料比例** = 呆滞料金额 / 总库存金额 +5. **物料齐套率** = 齐套订单数 / 总订单数 + +### 物控工作流程 + +``` +1. 接收生产计划 + ↓ +2. 计算物料需求(BOM展开) + ↓ +3. 检查库存状态 + ↓ +4. 制定采购计划 + ↓ +5. 跟踪物料到货 + ↓ +6. 物料发放与追溯 + ↓ +7. 库存盘点与优化 +``` + +--- + +## Excel VBA 工具库 + +### 现有工具 + +本仓库包含以下 Excel VBA 物控工具: + +#### 1. **vba-mc-toolkit** ⭐ +Excel VBA 物控小程序库 + +**功能模块**: +- 物料需求计算 +- 库存报表生成 +- 采购订单管理 +- 盘点表自动生成 +- 呆滞料分析 + +**使用方法**: +```vba +' 导入工具库 +Sub Import_MC_Toolkit() + Workbooks.Open "vba-mc-toolkit.xlsm" + Application.Run "Initialize_MC_System" +End Sub +``` + +### VBA 物控常用代码片段 + +#### 1. 自动计算安全库存 +```vba +Function Calculate_Safety_Stock(avg_demand As Double, lead_time As Double, service_level As Double) As Double + ' avg_demand: 平均日需求 + ' lead_time: 采购提前期(天) + ' service_level: 服务水平(0.95 = 95%) + + Dim z_score As Double + Select Case service_level + Case 0.90: z_score = 1.28 + Case 0.95: z_score = 1.65 + Case 0.98: z_score = 2.05 + Case 0.99: z_score = 2.33 + Case Else: z_score = 1.65 + End Select + + Calculate_Safety_Stock = avg_demand * lead_time * z_score +End Function +``` + +#### 2. 库存预警 +```vba +Sub Inventory_Alert() + Dim ws As Worksheet + Set ws = ThisWorkbook.Sheets("库存") + + Dim last_row As Long + last_row = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row + + For i = 2 To last_row + Dim current_qty As Double + Dim min_qty As Double + Dim max_qty As Double + + current_qty = ws.Cells(i, "D").Value ' 当前库存 + min_qty = ws.Cells(i, "E").Value ' 最低库存 + max_qty = ws.Cells(i, "F").Value ' 最高库存 + + If current_qty < min_qty Then + ws.Cells(i, "G").Value = "⚠️ 需补货" + ws.Cells(i, "G").Interior.Color = RGB(255, 200, 200) + ElseIf current_qty > max_qty Then + ws.Cells(i, "G").Value = "📦 库存过高" + ws.Cells(i, "G").Interior.Color = RGB(255, 255, 200) + Else + ws.Cells(i, "G").Value = "✅ 正常" + ws.Cells(i, "G").Interior.Color = RGB(200, 255, 200) + End If + Next i +End Sub +``` + +#### 3. 物料需求计算(MRP) +```vba +Sub Calculate_MRP() + Dim ws_bom As Worksheet, ws_inventory As Worksheet, ws_mrp As Worksheet + Set ws_bom = ThisWorkbook.Sheets("BOM") + Set ws_inventory = ThisWorkbook.Sheets("库存") + Set ws_mrp = ThisWorkbook.Sheets("MRP") + + ' 清空 MRP 表 + ws_mrp.Cells.Clear + + ' 设置表头 + ws_mrp.Range("A1:F1").Value = Array("物料编码", "物料名称", "需求量", "库存量", "净需求", "建议采购") + + ' 计算逻辑 + Dim last_row As Long + last_row = ws_bom.Cells(ws_bom.Rows.Count, "A").End(xlUp).Row + + For i = 2 To last_row + Dim part_no As String + Dim qty_needed As Double + Dim qty_in_stock As Double + Dim net_qty As Double + + part_no = ws_bom.Cells(i, "A").Value + qty_needed = ws_bom.Cells(i, "C").Value + + ' 查找库存 + qty_in_stock = Application.WorksheetFunction.VLookup(part_no, ws_inventory.Range("A:D"), 4, False) + + net_qty = qty_needed - qty_in_stock + + If net_qty > 0 Then + ws_mrp.Cells(i, 1).Value = part_no + ws_mrp.Cells(i, 3).Value = qty_needed + ws_mrp.Cells(i, 4).Value = qty_in_stock + ws_mrp.Cells(i, 5).Value = net_qty + ws_mrp.Cells(i, 6).Value = net_qty * 1.1 ' 建议采购量(含 10% 安全系数) + End If + Next i +End Sub +``` + +#### 4. 盘点表生成 +```vba +Sub Generate_Inventory_Count_Sheet() + Dim ws As Worksheet + Set ws = ThisWorkbook.Sheets.Add + ws.Name = "盘点表_" & Format(Date, "yyyy-mm-dd") + + ' 设置表头 + ws.Range("A1:H1").Value = Array("序号", "物料编码", "物料名称", "规格", "单位", "账面数量", "实盘数量", "差异") + ws.Range("A1:H1").Font.Bold = True + + ' 从库存表复制数据 + Dim ws_stock As Worksheet + Set ws_stock = ThisWorkbook.Sheets("库存") + + Dim last_row As Long + last_row = ws_stock.Cells(ws_stock.Rows.Count, "A").End(xlUp).Row + + For i = 2 To last_row + ws.Cells(i, 1).Value = i - 1 + ws.Cells(i, 2).Value = ws_stock.Cells(i, "A").Value + ws.Cells(i, 3).Value = ws_stock.Cells(i, "B").Value + ws.Cells(i, 4).Value = ws_stock.Cells(i, "C").Value + ws.Cells(i, 5).Value = ws_stock.Cells(i, "G").Value + ws.Cells(i, 6).Value = ws_stock.Cells(i, "D").Value + Next i + + ' 格式化 + ws.Columns("A:H").AutoFit + ws.Range("H2:H" & last_row).Formula = "=F2-G2" + + MsgBox "盘点表生成完成!" +End Sub +``` + +#### 5. 呆滞料分析 +```vba +Sub Analyze_Stagnant_Inventory() + Dim ws As Worksheet + Set ws = ThisWorkbook.Sheets("库存") + + Dim last_row As Long + last_row = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row + + ' 添加呆滞料标识列 + ws.Cells(1, "I").Value = "呆滞状态" + ws.Cells(1, "J").Value = "呆滞天数" + + For i = 2 To last_row + Dim last_move_date As Date + Dim days_stagnant As Long + + last_move_date = ws.Cells(i, "H").Value ' 最后移动日期 + days_stagnant = Date - last_move_date + + ws.Cells(i, "J").Value = days_stagnant + + Select Case days_stagnant + Case Is > 365 + ws.Cells(i, "I").Value = "🔴 严重呆滞(>1年)" + ws.Cells(i, "I").Interior.Color = RGB(255, 150, 150) + Case Is > 180 + ws.Cells(i, "I").Value = "🟡 中度呆滞(6-12月)" + ws.Cells(i, "I").Interior.Color = RGB(255, 220, 150) + Case Is > 90 + ws.Cells(i, "I").Value = "⚠️ 轻度呆滞(3-6月)" + ws.Cells(i, "I").Interior.Color = RGB(255, 255, 150) + Case Else + ws.Cells(i, "I").Value = "✅ 正常" + ws.Cells(i, "I").Interior.Color = RGB(200, 255, 200) + End Select + Next i + + ' 统计 + Dim stagnant_count As Long + Dim stagnant_value As Double + + For i = 2 To last_row + If ws.Cells(i, "J").Value > 90 Then + stagnant_count = stagnant_count + 1 + stagnant_value = stagnant_value + ws.Cells(i, "D").Value * ws.Cells(i, "E").Value + End If + Next i + + MsgBox "呆滞料分析完成!" & vbCrLf & _ + "呆滞物料数量:" & stagnant_count & vbCrLf & _ + "呆滞料金额:$" & Format(stagnant_value, "#,##0.00") +End Sub +``` + +--- + +## 库存管理 + +### 库存分类方法 + +#### ABC 分析法 +- **A 类**:价值占比 70-80%,数量占比 10-20% → 重点管理,高频盘点 +- **B 类**:价值占比 15-25%,数量占比 20-30% → 中等管理,中频盘点 +- **C 类**:价值占比 5-10%,数量占比 50-70% → 简化管理,低频盘点 + +#### VED 分类法(关键性) +- **V(Vital)**:关键物料,缺料导致停产 +- **E(Essential)**:重要物料,缺料影响生产 +- **D(Desirable)**:一般物料,缺料影响较小 + +### 库存优化策略 + +1. **安全库存计算** + ``` + 安全库存 = 平均日需求 × 安全天数 × 波动系数 + + 其中: + - 安全天数 = 采购提前期 + 缓冲天数 + - 波动系数 = 1.2 ~ 1.5(根据历史数据调整) + ``` + +2. **订货点(ROP)** + ``` + 订货点 = 平均日需求 × 采购提前期 + 安全库存 + ``` + +3. **经济订货量(EOQ)** + ``` + EOQ = √(2 × 年需求量 × 单次订货成本 / 单位持有成本) + ``` + +### 库存盘点方法 + +| 盘点方式 | 适用场景 | 优点 | 缺点 | +|---------|---------|------|------| +| 全面盘点 | 年度/半年度 | 准确性高 | 耗时耗力 | +| 循环盘点 | 日常管理 | 持续监控 | 覆盖不全 | +| ABC 分类盘点 | A类物料高频 | 重点突出 | B/C类可能遗漏 | +| 随机抽样盘点 | 月度抽查 | 效率高 | 准确性较低 | + +--- + +## 生产计划 + +### MRP 计算逻辑 + +``` +毛需求 = 生产计划 × BOM 用量 + +净需求 = 毛需求 - 现有库存 - 在途库存 + 安全库存 + +建议采购量 = 净需求 × 订货倍数(MOQ) +``` + +### 生产排程原则 + +1. **优先级规则** + - 交期优先(EDD) + - 最短加工时间(SPT) + - 关键比率(CR) + +2. **产能平衡** + ``` + 产能负荷 = 标准工时 × 数量 / 有效工时 + + 负荷率 = 产能负荷 / 总产能 + ``` + +3. **瓶颈管理** + - 识别瓶颈工序 + - 瓶颈前缓冲库存 + - 瓶颈后拉式生产 + +--- + +## 数据分析 + +### 关键指标仪表板 + +```python +# Python 物控数据分析示例 +import pandas as pd +import matplotlib.pyplot as plt + +# 1. 库存周转率分析 +def inventory_turnover_analysis(df): + """ + df: 包含 '物料编码', '期初库存', '期末库存', '销售成本' 的 DataFrame + """ + df['平均库存'] = (df['期初库存'] + df['期末库存']) / 2 + df['库存周转率'] = df['销售成本'] / df['平均库存'] + df['库存天数'] = 365 / df['库存周转率'] + + return df + +# 2. 呆滞料分析 +def stagnant_inventory_analysis(df, days_threshold=90): + """ + 分析呆滞料 + """ + df['呆滞天数'] = (pd.Timestamp.now() - df['最后移动日期']).dt.days + df['呆滞状态'] = pd.cut(df['呆滞天数'], + bins=[0, 30, 90, 180, 365, float('inf')], + labels=['正常', '预警', '轻度呆滞', '中度呆滞', '严重呆滞']) + + return df + +# 3. 库存结构分析 +def inventory_structure_analysis(df): + """ + ABC 分类 + """ + df['物料价值'] = df['库存数量'] * df['单价'] + df_sorted = df.sort_values('物料价值', ascending=False) + df_sorted['累计价值占比'] = df_sorted['物料价值'].cumsum() / df_sorted['物料价值'].sum() + + def abc_class(cum_ratio): + if cum_ratio <= 0.8: + return 'A' + elif cum_ratio <= 0.95: + return 'B' + else: + return 'C' + + df_sorted['ABC分类'] = df_sorted['累计价值占比'].apply(abc_class) + + return df_sorted +``` + +### 可视化仪表板 + +```python +# 库存仪表板 +def create_inventory_dashboard(df): + fig, axes = plt.subplots(2, 2, figsize=(15, 10)) + + # 1. 库存结构饼图 + abc_counts = df['ABC分类'].value_counts() + axes[0, 0].pie(abc_counts.values, labels=abc_counts.index, autopct='%1.1f%%') + axes[0, 0].set_title('ABC 库存结构') + + # 2. 库存周转率柱状图 + top_items = df.nlargest(10, '库存周转率') + axes[0, 1].bar(top_items['物料编码'], top_items['库存周转率']) + axes[0, 1].set_title('Top 10 库存周转率') + axes[0, 1].tick_params(axis='x', rotation=45) + + # 3. 呆滞料分布 + stagnant_counts = df['呆滞状态'].value_counts() + axes[1, 0].bar(stagnant_counts.index, stagnant_counts.values) + axes[1, 0].set_title('呆滞料分布') + + # 4. 库存价值分布 + axes[1, 1].hist(df['物料价值'], bins=20, edgecolor='black') + axes[1, 1].set_title('库存价值分布') + axes[1, 1].set_xlabel('物料价值') + axes[1, 1].set_ylabel('频次') + + plt.tight_layout() + plt.savefig('inventory_dashboard.png', dpi=300, bbox_inches='tight') + plt.show() +``` + +--- + +## 自动化脚本 + +### Python 物控自动化工具 + +#### 1. 库存报表自动生成 +```python +# inventory_report.py +import pandas as pd +import openpyxl +from datetime import datetime + +class InventoryReporter: + def __init__(self, data_file): + self.data = pd.read_excel(data_file) + self.report_date = datetime.now() + + def generate_daily_report(self): + """生成日报""" + report = { + '日期': self.report_date.strftime('%Y-%m-%d'), + '库存总金额': self.data['库存金额'].sum(), + '库存物料数': len(self.data), + '呆滞料金额': self.data[self.data['呆滞天数'] > 90]['库存金额'].sum(), + '缺料物料数': len(self.data[self.data['库存数量'] < self.data['安全库存']]), + '库存周转率': self.data['销售成本'].sum() / self.data['平均库存'].sum() + } + return pd.DataFrame([report]) + + def export_to_excel(self, output_path): + """导出到 Excel""" + with pd.ExcelWriter(output_path, engine='openpyxl') as writer: + # 汇总表 + summary = self.generate_daily_report() + summary.to_excel(writer, sheet_name='汇总', index=False) + + # 明细表 + self.data.to_excel(writer, sheet_name='库存明细', index=False) + + # 呆滞料表 + stagnant = self.data[self.data['呆滞天数'] > 90] + stagnant.to_excel(writer, sheet_name='呆滞料', index=False) + + # 缺料表 + shortage = self.data[self.data['库存数量'] < self.data['安全库存']] + shortage.to_excel(writer, sheet_name='缺料预警', index=False) + + print(f"报表已生成:{output_path}") +``` + +#### 2. 自动补货计算 +```python +# auto_replenishment.py +import pandas as pd +import numpy as np + +class AutoReplenishment: + def __init__(self, inventory_data, demand_data): + self.inventory = inventory_data + self.demand = demand_data + + def calculate_reorder_point(self, lead_time_days, service_level=0.95): + """计算订货点""" + # 计算平均日需求和标准差 + avg_daily_demand = self.demand['日需求量'].mean() + std_daily_demand = self.demand['日需求量'].std() + + # Z 值(服务水平) + z_values = {0.90: 1.28, 0.95: 1.65, 0.98: 2.05, 0.99: 2.33} + z = z_values.get(service_level, 1.65) + + # 安全库存 + safety_stock = z * std_daily_demand * np.sqrt(lead_time_days) + + # 订货点 + reorder_point = avg_daily_demand * lead_time_days + safety_stock + + return { + '平均日需求': avg_daily_demand, + '安全库存': safety_stock, + '订货点': reorder_point + } + + def generate_purchase_plan(self): + """生成采购计划""" + purchase_list = [] + + for _, item in self.inventory.iterrows(): + if item['当前库存'] < item['订货点']: + # 计算建议采购量 + lead_time = item['采购提前期'] + avg_demand = self.demand[self.demand['物料编码'] == item['物料编码']]['日需求量'].mean() + + # 建议采购量 = (提前期需求 + 安全库存) - 当前库存 + suggested_qty = (avg_demand * lead_time + item['安全库存']) - item['当前库存'] + + # 考虑最小订货量(MOQ) + moq = item.get('最小订货量', 1) + if suggested_qty < moq: + suggested_qty = moq + + purchase_list.append({ + '物料编码': item['物料编码'], + '物料名称': item['物料名称'], + '当前库存': item['当前库存'], + '订货点': item['订货点'], + '建议采购量': suggested_qty, + '供应商': item.get('供应商', ''), + '预计到货日期': pd.Timestamp.now() + pd.Timedelta(days=item['采购提前期']) + }) + + return pd.DataFrame(purchase_list) +``` + +#### 3. 库存预警系统 +```python +# inventory_alert.py +import smtplib +from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart +import pandas as pd + +class InventoryAlert: + def __init__(self, smtp_config): + self.smtp_server = smtp_config['server'] + self.smtp_port = smtp_config['port'] + self.email_user = smtp_config['user'] + self.email_password = smtp_config['password'] + + def check_inventory_status(self, inventory_df): + """检查库存状态""" + alerts = [] + + for _, item in inventory_df.iterrows(): + # 缺料预警 + if item['当前库存'] < item['安全库存']: + alerts.append({ + '类型': '缺料预警', + '物料编码': item['物料编码'], + '物料名称': item['物料名称'], + '当前库存': item['当前库存'], + '安全库存': item['安全库存'], + '缺口': item['安全库存'] - item['当前库存'] + }) + + # 呆滞料预警 + if item['呆滞天数'] > 90: + alerts.append({ + '类型': '呆滞料预警', + '物料编码': item['物料编码'], + '物料名称': item['物料名称'], + '呆滞天数': item['呆滞天数'], + '库存金额': item['库存金额'] + }) + + # 库存过高预警 + if item['当前库存'] > item['最高库存']: + alerts.append({ + '类型': '库存过高', + '物料编码': item['物料编码'], + '物料名称': item['物料名称'], + '当前库存': item['当前库存'], + '最高库存': item['最高库存'] + }) + + return pd.DataFrame(alerts) + + def send_alert_email(self, alerts_df, recipient): + """发送预警邮件""" + if alerts_df.empty: + return + + msg = MIMEMultipart('alternative') + msg['Subject'] = f'库存预警报告 - {pd.Timestamp.now().strftime("%Y-%m-%d")}' + msg['From'] = self.email_user + msg['To'] = recipient + + # 生成 HTML + html = f""" + + + + + +

库存预警报告

+

生成时间:{pd.Timestamp.now().strftime("%Y-%m-%d %H:%M")}

+ + + + + + + + + """ + + for _, row in alerts_df.iterrows(): + row_class = "alert-high" if row['类型'] in ['缺料预警', '呆滞料预警'] else "alert-medium" + value = row.get('缺口', row.get('呆滞天数', row.get('当前库存', ''))) + html += f""" + + + + + + + + """ + + html += """ +
类型物料编码物料名称数值状态
{row['类型']}{row['物料编码']}{row['物料名称']}{value}需要处理
+

请及时处理预警事项!

+ + + """ + + msg.attach(MIMEText(html, 'html')) + + # 发送邮件 + with smtplib.SMTP_SSL(self.smtp_server, self.smtp_port) as server: + server.login(self.email_user, self.email_password) + server.send_message(msg) + + print(f"预警邮件已发送至 {recipient}") +``` + +--- + +## 案例研究 + +### 案例 1:降低库存周转天数 + +**背景**: +- 某电子厂库存周转天数:90 天 +- 目标:降低至 60 天 + +**措施**: +1. ABC 分类管理 +2. 优化安全库存 +3. 实施 VMI(供应商管理库存) +4. 定期盘点与清理呆滞料 + +**结果**: +- 库存周转天数降至 55 天 +- 库存资金占用减少 38% +- 缺料率从 5% 降至 1.5% + +### 案例 2:呆滞料清理 + +**背景**: +- 呆滞料金额:$500,000 +- 占总库存:15% + +**措施**: +1. 呆滞料分析与分类 +2. 制定清理计划(折价销售、退供应商、报废) +3. 优化采购策略 +4. 建立呆滞料预警机制 + +**结果**: +- 呆滞料减少 70% +- 释放资金 $350,000 +- 库存结构优化 + +--- + +## 资源推荐 + +### 书籍 +1. **《物料管理实务》** - 作者:王文信 +2. **《生产与运营管理》** - 作者:Richard B. Chase +3. **《供应链管理:战略、规划与运作》** - 作者:Sunil Chopra + +### 在线课程 +1. **Coursera** - Supply Chain Management Specialization +2. **Udemy** - Inventory Management and Control +3. **edX** - Operations Management + +### 工具软件 +1. **ERP 系统**:SAP、Oracle、金蝶、用友 +2. **WMS 系统**:仓库管理系统 +3. **Excel VBA**:自定义工具开发 + +### 行业标准 +1. **ISO 9001** - 质量管理体系 +2. **ISO 28000** - 供应链安全管理体系 +3. **APICS CPIM** - 生产与库存管理认证 + +--- + +## 快速开始 + +### 1. 安装依赖 +```bash +# Python 环境 +pip install pandas openpyxl matplotlib + +# Excel VBA +# 打开 Excel → 开发工具 → Visual Basic → 导入模块 +``` + +### 2. 使用示例 +```python +# 导入工具库 +from inventory_report import InventoryReporter +from auto_replenishment import AutoReplenishment + +# 生成日报 +reporter = InventoryReporter('inventory_data.xlsx') +reporter.export_to_excel('daily_report.xlsx') + +# 自动补货 +replenishment = AutoReplenishment(inventory_data, demand_data) +purchase_plan = replenishment.generate_purchase_plan() +purchase_plan.to_excel('purchase_plan.xlsx', index=False) +``` + +### 3. Excel VBA 使用 +```vba +' 打开工具库 +Sub Open_MC_Toolkit() + Workbooks.Open "vba-mc-toolkit.xlsm" +End Sub + +' 运行库存预警 +Sub Run_Inventory_Alert() + Call Inventory_Alert +End Sub + +' 生成盘点表 +Sub Run_Generate_Count_Sheet() + Call Generate_Inventory_Count_Sheet +End Sub +``` + +--- + +## 贡献指南 + +欢迎贡献你的物控经验和工具! + +### 如何贡献 +1. Fork 本仓库 +2. 创建你的分支 (`git checkout -b feature/your-feature`) +3. 提交你的更改 (`git commit -m 'Add some feature'`) +4. 推送到分支 (`git push origin feature/your-feature`) +5. 创建 Pull Request + +### 贡献内容 +- ✅ Excel VBA 工具脚本 +- ✅ Python 自动化工具 +- ✅ 物控案例分析 +- ✅ 最佳实践文档 +- ✅ 数据分析模板 + +--- + +## 许可证 + +本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件 + +--- + +## 联系方式 + +如有问题或建议,请通过以下方式联系: +- GitHub Issues: [提交 Issue](https://github.com/1803560007/物控学习笔记/issues) +- 邮箱: 1803560007@github.com + +--- + +## 更新日志 + +### v1.0.0 (2026-02-03) +- ✅ 创建物控学习笔记仓库 +- ✅ 添加基础知识文档 +- ✅ 添加 Excel VBA 工具库 +- ✅ 添加 Python 自动化脚本 +- ✅ 添加案例研究 +- ✅ 添加资源推荐 + +--- + +**最后更新**: 2026-02-03 +**维护者**: 1803560007 +**版本**: v1.0.0 \ No newline at end of file