feat: 创建物控学习笔记仓库 v1.0.0
This commit is contained in:
863
物控学习笔记/Excel_VBA_基础.md
Normal file
863
物控学习笔记/Excel_VBA_基础.md
Normal file
@@ -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
|
||||||
833
物控学习笔记/README.md
Normal file
833
物控学习笔记/README.md
Normal file
@@ -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"""
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
body {{ font-family: Arial, sans-serif; }}
|
||||||
|
table {{ border-collapse: collapse; width: 100%; }}
|
||||||
|
th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
|
||||||
|
th {{ background-color: #f2f2f2; }}
|
||||||
|
.alert-high {{ background-color: #ffcccc; }}
|
||||||
|
.alert-medium {{ background-color: #fff3cd; }}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>库存预警报告</h2>
|
||||||
|
<p>生成时间:{pd.Timestamp.now().strftime("%Y-%m-%d %H:%M")}</p>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>类型</th>
|
||||||
|
<th>物料编码</th>
|
||||||
|
<th>物料名称</th>
|
||||||
|
<th>数值</th>
|
||||||
|
<th>状态</th>
|
||||||
|
</tr>
|
||||||
|
"""
|
||||||
|
|
||||||
|
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"""
|
||||||
|
<tr class="{row_class}">
|
||||||
|
<td>{row['类型']}</td>
|
||||||
|
<td>{row['物料编码']}</td>
|
||||||
|
<td>{row['物料名称']}</td>
|
||||||
|
<td>{value}</td>
|
||||||
|
<td>需要处理</td>
|
||||||
|
</tr>
|
||||||
|
"""
|
||||||
|
|
||||||
|
html += """
|
||||||
|
</table>
|
||||||
|
<p>请及时处理预警事项!</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user