如何用vba计算多层bom物料需求

excel表单状态(如图)

A列:客户
B列:成品代码
c列:bom一级
D列:bom二级一直往后,最多6层(bom层级6列)
诉求
用vba根据成品需求,求对应的物料需求(如成品的下阶是半品,半品下阶是材料,半品还有委外层级),考虑良率,bom用量

img

该回答引用ChatGPT

假设成品代码在第2行,物料编码在第4列,物料数量在第5列,BOM层级为6级。

以下是实现过程:

1.先将BOM中的物料编码和物料数量分别存入一个二维数组中。代码如下:

Dim arrBom As Variant
Dim i As Long, j As Long
Dim lastRow As Long

lastRow = Cells(Rows.Count, 1).End(xlUp).Row
arrBom = Range(Cells(1, 1), Cells(lastRow, 7)).Value

For i = 2 To UBound(arrBom, 1)
    For j = 3 To UBound(arrBom, 2) Step 2
        If arrBom(i, j) <> "" Then
            ReDim Preserve arrMaterial(1 To UBound(arrMaterial, 1), 1 To UBound(arrMaterial, 2) + 1)
            arrMaterial(1, UBound(arrMaterial, 2)) = arrBom(i, j)
            arrMaterial(2, UBound(arrMaterial, 2)) = arrBom(i, j + 1)
        End If
    Next j
Next i

2、然后根据成品代码,递归查找物料需求。代码如下:

Sub GetMaterialRequirement(ByVal strProductCode As String, ByVal dblProductQty As Double)
    Dim arrMatch As Variant
    Dim i As Long, j As Long
    Dim arrTemp() As Variant
    Dim dblChildQty As Double
    
    For i = 1 To UBound(arrMaterial, 2)
        If arrMaterial(1, i) = strProductCode Then
            If UBound(arrMatch) = -1 Then
                ReDim arrMatch(1 To 3, 0)
            End If
            
            arrMatch(1, UBound(arrMatch, 2) + 1) = arrMaterial(1, i)
            arrMatch(2, UBound(arrMatch, 2) + 1) = arrMaterial(2, i)
            arrMatch(3, UBound(arrMatch, 2)) = dblProductQty * arrMaterial(2, i)
            
            If arrMaterial(1, i + 1) <> "" Then
                dblChildQty = dblProductQty * arrMaterial(2, i)
                GetMaterialRequirement arrMaterial(1, i + 1), dblChildQty
            End If
        End If
    Next i
    
    If UBound(arrMatch) > -1 Then
        arrTemp = arrResult
        ReDim arrResult(1 To UBound(arrTemp, 1), 1 To UBound(arrTemp, 2) + UBound(arrMatch, 2) + 1)
        
        For i = 1 To UBound(arrTemp, 1)
            For j = 1 To UBound(arrTemp, 2)
                arrResult(i, j) = arrTemp(i, j)
            Next j
        Next i
        
        For i = 1 To UBound(arrMatch, 1)
            For j = 1 To UBound(arrMatch, 2)
                arrResult(i, UBound(arrTemp, 2) + j) = arrMatch(i, j)
            Next j
        Next i
    End If
End Sub


3、最后将结果输出到另一个工作表中。代码如下:

Sub OutputResult()
Sub CalculateBOM()
    Dim ws As Worksheet
    Dim lastRow As Long
    Dim dictBOM As Object
    Dim dictLevel As Object
    Dim arrLevel() As String
    Dim i As Long, j As Long
    Dim prodCode As String
    Dim reqQty As Double
    Dim bomQty As Double
    
    '初始化字典对象
    Set dictBOM = CreateObject("Scripting.Dictionary")
    Set dictLevel = CreateObject("Scripting.Dictionary")
    
    '指定工作表
    Set ws = ThisWorkbook.Sheets("Sheet1")
    
    '确定最后一行
    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    
    '遍历每一行数据,建立BOM字典
    For i = 2 To lastRow
        prodCode = ws.Cells(i, 2).Value '成品代码
        If Not dictBOM.exists(prodCode) Then
            dictBOM.Add prodCode, CreateObject("Scripting.Dictionary")
        End If
        dictBOM(prodCode).Add ws.Cells(i, 3).Value, 1 'BOM第一层级
        
        '记录每个BOM层级的列号
        For j = 4 To 9 '最多6层
            If ws.Cells(i, j).Value <> "" Then
                If Not dictLevel.exists(ws.Cells(i, j).Value) Then
                    dictLevel.Add ws.Cells(i, j).Value, j
                End If
            Else
                Exit For '遇到空单元格退出
            End If
        Next j
    Next i
    
    '输入需要计算的成品代码和需求量
    prodCode = InputBox("请输入成品代码", "计算BOM需求")
    reqQty = InputBox("请输入需求量", "计算BOM需求")
    
    '初始化数组,记录当前层级
    ReDim arrLevel(dictLevel.Count - 1)
    For i = 0 To UBound(arrLevel)
        arrLevel(i) = dictLevel.keys()(i)
    Next i
    
    '递归计算BOM需求
    CalculateBOMQty prodCode, reqQty, dictBOM, dictLevel, arrLevel, 0
    
    '输出结果
    For i = 0 To UBound(arrLevel)
        ws.Cells(1, 11 + i).Value = arrLevel(i) & "需求量"
    Next i
    ws.Cells(2, 1).Value = prodCode
    ws.Cells(2, 2).Value = reqQty
    For i = 0 To UBound(arrLevel)
        ws.Cells(2, 11 + i).Value = dictLevel(arrLevel(i))
    Next i
    
End Sub

Sub CalculateBOMQty(prodCode As String, reqQty As Double, dictBOM As Object, dictLevel As Object, arrLevel() As String, level As Long)
    Dim i As Long
    Dim bomCode As String
    
    '递归出口
    If level > UBound(arrLevel) Then Exit Sub
    
    '遍历当前层级的所有物料
    For Each bomCode In dictBOM(prodCode).keys
        '判断是否为当前层级
        If dictLevel(bomCode) = dictLevel(arrLevel(level +


如下:

Sub BOM()
'
' BOM 宏
'
'
    Dim data As Range, currow As Integer, datarows As Integer
    Dim row As Integer, part As Integer


Set data = Range("A1:C10")
datarows = data.Rows.Count
currow = 2
part = 1
For row = 1 To datarows
    If Cells(row, 3) = part Then
        Cells(currow, 4) = Cells(row, 1)
        currow = currow + 1
        part = Cells(row, 2)
    End If
Next row

End Sub

```bash

你可以使用 VBA 根据成品需求计算对应的物料需求。以下是一种可能的实现方法:

首先,将所有的 BOM 数据加载到 VBA 中,可以使用 ADO 连接到 Excel 表格或直接在 VBA 中使用 Excel 对象模型。

接着,编写一个函数,输入成品代码和所需数量,返回物料需求列表。函数实现的大体思路如下:

a. 找到成品代码所在行,并读取该行的 BOM 数据;

b. 对于每个 BOM 条目,递归计算其下级物料需求,并将其与数量相乘累加到结果中;

c. 返回最终的物料需求列表。

在递归计算 BOM 条目下级物料需求时,你需要考虑以下情况:

a. 如果下级物料是半成品,那么需要继续递归计算其下级物料需求;

b. 如果下级物料是材料,那么直接返回所需数量;

c. 如果下级物料是委外加工件,那么需要在结果中增加对应的委外加工费用,可以使用类似于“材料 * 单价”的方式计算。

由于 BOM 层级最多 6 层,你可以考虑使用一个递归函数,每次递归计算一层 BOM 条目下级物料需求。

这是一个大概的实现思路,你可以根据具体情况进行调整。注意在编写代码时,要注意对 BOM 数据进行检查和处理,避免出现错误的计算结果。


Sub CalculateMaterialRequirement()
    '定义变量和常量
    Dim targetProductCode As String
    Dim startRow As Long
    Dim startCol As Long
    Const maxBomLevel As Long = 6
    Dim wb As Workbook
    Dim ws As Worksheet
    Dim currentRow As Long
    Dim currentCol As Long
    Dim currentBomLevel As Long
    Dim currentProductCode As String
    Dim materialRequirement As Double
    
    '设置变量和常量的值
    targetProductCode = "ABC-123"
    startRow = 2
    startCol = 3
    
    '打开BOM表格
    Set wb = Workbooks.Open("C:\BOM.xlsx")
    Set ws = wb.Worksheets("Sheet1")
    
    '循环读取BOM表格数据
    For currentRow = startRow To ws.Cells(ws.Rows.Count, startCol).End(xlUp).Row
        '比较成品代码
        currentProductCode = ws.Cells(currentRow, 2).Value
        If currentProductCode = targetProductCode Then
            '找到需要计算物料需求的行
            currentBomLevel = 1
            materialRequirement = ws.Cells(currentRow, startCol).Value
            For currentCol = startCol + 1 To startCol + maxBomLevel - 1
                If Not IsEmpty(ws.Cells(currentRow, currentCol).Value) Then
                    currentBomLevel = currentBomLevel + 1
                    materialRequirement = materialRequirement + ws.Cells(currentRow, currentCol).Value
                Else
                    Exit For
                End If
            Next currentCol
            
            '输出物料需求结果
            ws.Cells(currentRow, startCol + maxBomLevel).Value = materialRequirement
        End If
    Next currentRow
    
    '关闭BOM表格
    wb.Close SaveChanges:=False
End Sub

img

可以使用递归的方式来计算出物料的需求,递归函数可以从顶层开始,依次递归计算下一级的物料需求,直到最底层的材料。

首先,我们可以定义一个函数来递归计算每个成品的物料需求,函数需要输入一个成品的名称、数量和一个物料需求字典(key为物料名称,value为物料需求量),函数返回更新后的物料需求字典。代码如下:

Function CalculateMaterialDemand(productName As String, qty As Double, materialDemand As Scripting.Dictionary) As Scripting.Dictionary
    Dim productRange As Range
    Dim materialRange As Range
    Dim bomLevel1 As String
    Dim bomLevel2 As String
    Dim bomLevel3 As String
    Dim bomLevel4 As String
    Dim bomLevel5 As String
    Dim bomLevel6 As String
    Dim materialQty As Double
    
    Set productRange = Range("A:A").Find(productName)
    If Not productRange Is Nothing Then
        Set materialRange = productRange.Offset(0, 2)
        bomLevel1 = productRange.Offset(0, 2).Value
        bomLevel2 = productRange.Offset(0, 3).Value
        bomLevel3 = productRange.Offset(0, 4).Value
        bomLevel4 = productRange.Offset(0, 5).Value
        bomLevel5 = productRange.Offset(0, 6).Value
        bomLevel6 = productRange.Offset(0, 7).Value
        
        ' Calculate demand for level 1 BOM
        If Not materialDemand.Exists(bomLevel1) Then
            materialDemand.Add bomLevel1, 0
        End If
        materialQty = qty * productRange.Offset(0, 8).Value * productRange.Offset(0, 9).Value
        materialDemand(bomLevel1) = materialDemand(bomLevel1) + materialQty
        
        ' Calculate demand for level 2 BOM
        If Not materialDemand.Exists(bomLevel2) Then
            materialDemand.Add bomLevel2, 0
        End If
        materialQty = qty * productRange.Offset(0, 10).Value * productRange.Offset(0, 11).Value
        materialDemand(bomLevel2) = materialDemand(bomLevel2) + materialQty
        
        ' Calculate demand for level 3 BOM
        If Not materialDemand.Exists(bomLevel3) Then
            materialDemand.Add bomLevel3, 0
        End If
        materialQty = qty * productRange.Offset(0, 12).Value * productRange.Offset(0, 13).Value
        materialDemand(bomLevel3) = materialDemand(bomLevel3) + materialQty
        
        ' Calculate demand for level 4 BOM
        If Not materialDemand.Exists(bomLevel4) Then
            materialDemand.Add bomLevel4, 0
        End If
        materialQty = qty * productRange.Offset(0, 14).Value * productRange.Offset(0, 15).Value
        materialDemand(bomLevel4) = materialDemand(bomLevel4) + materialQty
        
        ' Calculate demand for level 5 BOM
        If Not materialDemand.Exists(bomLevel5) Then
            materialDemand.Add bomLevel5, 0
        End If
        materialQty = qty * productRange.Offset(0, 



import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

public class BOMCalculator {
    public static void main(String[] args) throws IOException {
        FileInputStream file = new FileInputStream(new File("data.xlsx"));

        // 创建 Workbook 对象
        Workbook workbook = WorkbookFactory.create(file);

        // 获取第一个工作表
        Sheet sheet = workbook.getSheetAt(0);

        // 创建 BOM 项列表
        List<BOMItem> bomItems = new ArrayList<BOMItem>();

        // 读取 Excel 表格数据并将它们转换成 BOM 项
        for (Row row : sheet) {
            String product = row.getCell(1).getStringCellValue();
            String material = row.getCell(2).getStringCellValue();
            double rate = row.getCell(3).getNumericCellValue();
            double usage = row.getCell(4).getNumericCellValue();
            int level = row.getCell(5).getCellType() == Cell.CELL_TYPE_BLANK ? 1 : (int) row.getCell(5).getNumericCellValue();

            BOMItem bomItem = new BOMItem(product, material, rate, usage);

            // 如果 BOM 层级大于 1,则将该 BOM 项添加到上一级 BOM 项的子项列表中
            if (level > 1) {
                BOMItem parentItem = bomItems.get(bomItems.size() - 1);
                for (int i = 2; i < level; i++) {
                    parentItem = parentItem.getSubItems().get(parentItem.getSubItems().size() - 1);
                }
                parentItem.addSubItem(bomItem);
            } else { // 否则将该 BOM 项添加到 BOM 项列表中
                bomItems.add(bomItem);
            }
        }

        // 关闭文件输入流
        file.close();

        // 计算物料需求
        Map<String, Double> quantities = new HashMap<String, Double>();
        calculateMaterials("A001", 100, quantities, bomItems);

        // 输出物料需求
        for (Map.Entry<String, Double> entry : quantities.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }

    public static void calculateMaterials(String product, double quantity, Map<String, Double> quantities, List<BOMItem> bomItems) {
        for (BOMItem bomItem : bomItems) {
            if (!bomItem.getProduct().equals(product)) { // 如果成品不匹配,则继续循环
                continue;
            }

            String material = bomItem.getMaterial(); // 获取物料名称
            double rate = bomItem.getRate(); // 获取良率
            double usage = bomItem.getUsage(); // 获取 BOM 用量

            String subproduct = null;
            List<BOMItem> subItems = bomItem.getSubItems();
            if (subItems.size() > 0) {

实现这个需求可以考虑使用递归算法,具体实现步骤如下:

1.在VBA中读取成品代码和对应的需求量,例如:


Dim demand As Long
Dim productCode As String

demand = Range("E2").Value
productCode = Range("B2").Value

2.根据成品代码,在BOM表格中找到对应的一级物料代码和对应的用量,例如:


Dim row As Long
Dim firstLevelCode As String
Dim firstLevelUsage As Long

row = Application.WorksheetFunction.Match(productCode, Range("B:B"), 0)
firstLevelCode = Range("C" & row).Value
firstLevelUsage = Range("D" & row).Value

3.如果一级物料代码为空,则返回空值,否则继续递归查找下一级物料的需求量,例如:


Dim demandOfFirstLevel As Long

If firstLevelCode = "" Then
    demandOfFirstLevel = 0
Else
    demandOfFirstLevel = CalculateDemandOfMaterial(firstLevelCode, firstLevelUsage * demand)
End If

4.递归查找下一级物料的需求量,每一级物料都需要考虑良率和用量,例如:


Function CalculateDemandOfMaterial(materialCode As String, usage As Long) As Long
    Dim row As Long
    Dim nextLevelCode As String
    Dim nextLevelUsage As Long
    Dim yieldRate As Double
    Dim demand As Long
    
    row = Application.WorksheetFunction.Match(materialCode, Range("B:B"), 0)
    yieldRate = Range("E" & row).Value / 100
    demand = Round(usage / yieldRate, 0)
    
    If yieldRate = 0 Or demand = 0 Then
        CalculateDemandOfMaterial = 0
    Else
        nextLevelCode = Range("C" & row).Value
        nextLevelUsage = Range("D" & row).Value
        
        If nextLevelCode = "" Then
            CalculateDemandOfMaterial = demand
        Else
            CalculateDemandOfMaterial = CalculateDemandOfMaterial(nextLevelCode, nextLevelUsage * demand)
        End If
    End If
End Function

5.最后将递归查找的结果输出到对应的单元格中,例如:


Range("F2").Value = demandOfFirstLevel

这样,就可以根据成品需求量递归地计算出所有下级物料的需求量,并考虑了良率和BOM用量的影响。

您可以使用VBA编写一个递归函数来计算多层BOM物料需求。以下是一个示例代码,您可以根据自己的数据表结构和计算公式进行调整。

Sub CalculateMaterialDemand()
    '定义变量
    Dim ws As Worksheet
    Dim lr As Long '数据表最后一行
    Dim i As Long '行计数器
    Dim j As Long '列计数器
    Dim bomLevel As Long 'BOM层级
    Dim materialQty As Double '物料需求量
    Dim materialID As String '物料编号
    Dim bomQty As Double 'BOM用量
    Dim yield As Double '良率
    Dim demand As Double '物料总需求量
    
    '设置工作表
    Set ws = ThisWorkbook.Sheets("Sheet1")
    
    '获取最后一行
    lr = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    
    '循环处理每一行数据
    For i = 2 To lr '从第2行开始,第1行为表头
        '判断是否为BOM一级
        If ws.Cells(i, 3).Value = "" Then
            '计算成品需求量
            demand = ws.Cells(i, 5).Value
        Else
            '获取物料编号、BOM用量、BOM层级、良率
            materialID = ws.Cells(i, 3).Value
            bomQty = ws.Cells(i, 4).Value
            bomLevel = ws.Cells(i, 3).Column - 3 'BOM层级为列数减去3
            yield = ws.Cells(i, 6).Value
            '计算物料需求量
            materialQty = demand * bomQty * yield
            '输出结果
            ws.Cells(i, 7).Value = materialQty '物料需求量
            ws.Cells(i, 8).Value = bomLevel 'BOM层级
            ws.Cells(i, 9).Value = demand '父级需求量
            '递归计算下一层物料需求量
            CalculateNextLevel ws, materialID, materialQty, bomLevel + 1, demand
        End If
    Next i
End Sub

'递归函数
Sub CalculateNextLevel(ws As Worksheet, materialID As String, materialQty As Double, bomLevel As Long, demand As Double)
    Dim lr As Long '数据表最后一行
    Dim i As Long '行计数器
    Dim j As Long '列计数器
    Dim childMaterialID As String '子物料编号
    Dim childBOMQty As Double '子物料BOM用量
    Dim childYield As Double '子物料良率
    Dim childMaterialQty As Double '子物料需求量
    
    '获取最后一行
    lr = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    
    '循环处理每一行数据
    For i = 2 To lr '从第2行开始,第1行为表头
        '判断是否为当前物料的下一层物料
        If ws.Cells(i, bomLevel + 3).Value = materialID Then
            '获取子物料编号、BOM

如果对您有帮助,请给与采纳,谢谢。

上面的兄弟解释的差不多了,我直接给代码


Dim wb As Workbook
Dim ws As Worksheet

Set wb = Workbooks.Open("C:\path\to\your\file.xlsx")
Set ws = wb.Sheets("Sheet1")
Dim lastRow As Long
Dim i As Long

lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row

For i = 2 To lastRow
  ' 在这里执行操作
Next i
Dim bomLevel As Integer
Dim quantity As Integer
Dim yield As Double
Dim bomUsage As Double
Dim finalRequirement As Double

bomLevel = ws.Cells(i, 1).Value
quantity = ws.Cells(i, 2).Value
yield = ws.Cells(i, 3).Value
bomUsage = ws.Cells(i, 4).Value

' 计算最终物料需求
finalRequirement = quantity * bomUsage / yield

' 将结果写入第5列
ws.Cells(i, 5).Value = finalRequirement

https://www.baidu.com/link?url=aZNIJnwBp7Vcd2lmYu5Mr5iLC-DE1vzYwObajyfk4QgHXJaUQWUMwsNf4ZVTVNbD6EhtCl6WNBdLZ7-5kGWFb_&wd=&eqid=cf070e3600005be70000000263f2d62d

以下答案基于ChatGPT与GISer Liu编写:

您可以使用 VBA 编写一个宏来自动计算对应的物料需求。以下是一个示例:

vbnet

Sub CalculateMaterialRequirement()

    ' 定义变量
    Dim productCode As String
    Dim quantity As Double
    Dim productRow As Integer
    Dim material As String
    Dim materialRow As Integer
    Dim bomLevel As Integer
    
    ' 获取成品代码和数量
    productCode = InputBox("请输入成品代码", "成品需求")
    If productCode = "" Then Exit Sub
    quantity = InputBox("请输入成品数量", "成品需求")
    If quantity = 0 Then Exit Sub
    
    ' 查找成品代码所在行
    productRow = 2
    Do While Cells(productRow, 2).Value <> productCode
        productRow = productRow + 1
        If Cells(productRow, 2).Value = "" Then
            MsgBox "未找到对应的成品代码", vbCritical
            Exit Sub
        End If
    Loop
    
    ' 循环计算每个物料的需求
    For bomLevel = 1 To 6
        materialRow = productRow
        Do While materialRow <= Rows.Count And Cells(materialRow, 2).Value = productCode And Cells(materialRow, 1).Value <> ""
            material = Cells(materialRow, bomLevel + 2).Value
            If material <> "" Then
                quantity = quantity * Cells(materialRow, bomLevel + 8).Value * Cells(materialRow, 9).Value
                materialRow = 1
                Exit Do
            End If
            materialRow = materialRow + 1
        Loop
        If materialRow > Rows.Count Or Cells(materialRow, 1).Value = "" Then Exit For
        Debug.Print "Level " & bomLevel & ": " & material & " x " & quantity
    Next
    
End Sub

这个宏会要求您输入成品代码和数量,然后计算每个物料的需求量,并在 VBA 编辑器中输出结果。您可以根据自己的需求对其进行修改,比如将输出结果写入工作表中。


你可以使用VBA来实现这个功能,首先,你需要编写一个函数,根据bom的层级信息来计算出需要每个物料的需求数量。然后,根据客户的需求数量和良率计算出最终需求数量。最后,用VBA编程的形式将计算的需求结果输出到Excel表格中。