Update Master Data with Excel by Calling RFCs

Jimbo's picture

Updating Plant Extensions with RFCsIn rare cases, access to transactions to perform mass loads or to affect changes to data with any significant volume are restricted for users--even for users who are responsible for maintaining Master Data. One option is to use Excel to create an OLE connection to the SAPGUI interface to run scripts, but that option requires scripting to be turned on in the SAP system and each record takes several seconds to call a transaction and run it to completion in foreground.

Another option is to use run BAPIs from Excel using Remote Function Calls (RFCs) to perform the updates. This option is more cumbersome than transactions designed by SAP to perform mass updates and it requires that the user performing the updates have RFC access, but it is faster than using Excel for scripting and more accurate than manually keying data.

One commonly-used function performed via RFC is RFC_READ_TABLE. This function returns data from tables in SAP that can be used to import table data into Access or to populate Excel spreadsheets with table data from SAP.

In the sample code below, the RFC_READ_TABLE function is called to populate spreadsheets with data from the DD03L table and from various check tables. This data is then combined with the source data to populate the parameters that are then passed into the BAPI. In this sample, the BAPI_MATERIAL_SAVEDATA function is used to update Plant extensions along with the associated Valuation data.

Step 1: Validating the source data and preparing load data

This code knits the source data together with the data from DD03L to determine which of the esoteric fields in the structures will be populated with data to load. The combined data is then compared against the data from the MARC table and the T032F table to pick a View in the Material Master Plant extension to update.

If the Material Master does not exist or is not extended to the Plant wherein the update is to be performed then the code will catch that instead of relying on the BAPI to return an error. Additionally, if the Material Master is not extended to a View that has the field to be populated then the code will identify that problem instead of trying to call the BAPI with no View to update.

This code does not validate values to be loaded in fields before calling the BAPI. Invalid values will be mentioned in the log entry after the BAPI runs and fails to update a Plant extension.

Sub PrepareRFC()
    Dim R3 As Object, nCurrent As Long, nCurrentColumn As Long, strTabName As String
    Dim nCurrentField As Long, strCurrentField As String, nCurrentTranslate As Long
    Dim strPSTAT As String, strTempPSTAT As String, nCountMARA As Long, isProblem As Boolean
    Dim strMATNR As String, strWERKS As String, strFieldName As String
    Dim nCurSec As Integer, strRange As String, nCurrentPlant As Long
    Dim wsInput As Worksheet, dStart As Date
    
    dStart = Now 'Report how long this takes.
    
    Set wsInput = CreateTab("MARA")
    Set wsInput = CreateTab("MARC")
    Set wsInput = Nothing
    
    Dim wsHEADDATA As Worksheet, wsPLANTDATA As Worksheet, wsVALUATIONDATA As Worksheet
    Dim wsPLANTDATAX As Worksheet, wsVALUATIONDATAX As Worksheet

    a = ActivatePlantMagicTab

    If Not GetR3(R3) Then
        Exit Sub
    End If
    
    a = SAP_RFC_READ_TABLE(R3, "T130F", "tblT130F", "")
    a = SAP_RFC_READ_TABLE(R3, "T132", "tblT132", "")
    nCurrent = SAP_RFC_READ_TABLE(R3, "DD03L", "tblDD03L", "", "TABNAME in ('BAPI_MARC','BAPI_MBEW','BAPIMATHEAD','MARC','MBEW')")
    Call SortDD03L(nCurrent)
    
    a = CreateLogEntry("Finished pulling check tables.")
    
    For nCurrent = 2 To 1048576
        If Range("B" & nCurrent).Value <> "" Then
            Range("B" & nCurrent & ":C" & nCurrent).NumberFormat = "@"
            Range("B" & nCurrent & ":V" & nCurrent).Interior.Color = xlNone
            Range("B" & nCurrent).Value = Right("0000000000" & Trim(UCase(Range("B" & nCurrent).Value)), 18)
            Range("C" & nCurrent).Value = Trim(UCase(Range("C" & nCurrent).Value))
        End If
    Next

    nCountMARA = PopulateMARA(R3, Sheets("PlantUpdateRFC").Range("B:B"))
    a = CreateLogEntry("Finished pulling MARA.")
    nCountMARC = PopulateMARCbyMATNR(R3, Sheets("PlantUpdateRFC").Range("B:B"))
    a = CreateLogEntry("Finished pulling MARC.")
    Call SortMARC
    a = CreateLogEntry("Finished sorting MARC.")
    
    Set wsHEADDATA = CreateTab("HEADDATA")
    nCurrentColumn = 0
    For nCurrent = 2 To 900
        If Sheets("tblDD03L").Range("A" & nCurrent) = "BAPIMATHEAD" Then
            nCurrentColumn = nCurrentColumn + 1
            wsHEADDATA.Range(FindExcelCell(nCurrentColumn, 1)).Value = Sheets("tblDD03L").Range("B" & nCurrent).Value
            wsHEADDATA.Range(FindExcelCell(nCurrentColumn, 1)).Font.Bold = True
            wsHEADDATA.Range(FindExcelCell(nCurrentColumn, 1)).Interior.Color = RGB(222, 222, 222)
        End If
    Next
    Set wsPLANTDATAX = CreateTab("PLANTDATAX")
    Set wsPLANTDATA = CreateTab("PLANTDATA")
    nCurrentColumn = 0
    For nCurrent = 2 To 900
        If Sheets("tblDD03L").Range("A" & nCurrent) = "BAPI_MARC" Then
            nCurrentColumn = nCurrentColumn + 1
            wsPLANTDATA.Range(FindExcelCell(nCurrentColumn, 1)).Value = Sheets("tblDD03L").Range("B" & nCurrent).Value
            wsPLANTDATA.Range(FindExcelCell(nCurrentColumn, 1)).Font.Bold = True
            wsPLANTDATA.Range(FindExcelCell(nCurrentColumn, 1)).Interior.Color = RGB(222, 222, 222)
            wsPLANTDATAX.Range(FindExcelCell(nCurrentColumn, 1)).Value = Sheets("tblDD03L").Range("B" & nCurrent).Value
            wsPLANTDATAX.Range(FindExcelCell(nCurrentColumn, 1)).Font.Bold = True
            wsPLANTDATAX.Range(FindExcelCell(nCurrentColumn, 1)).Interior.Color = RGB(222, 222, 222)
        End If
    Next
    Set wsVALUATIONDATAX = CreateTab("VALUATIONDATAX")
    Set wsVALUATIONDATA = CreateTab("VALUATIONDATA")
    nCurrentColumn = 0
    For nCurrent = 2 To 900
        If Sheets("tblDD03L").Range("A" & nCurrent) = "BAPI_MBEW" Then
            nCurrentColumn = nCurrentColumn + 1
            wsVALUATIONDATA.Range(FindExcelCell(nCurrentColumn, 1)).Value = Sheets("tblDD03L").Range("B" & nCurrent).Value
            wsVALUATIONDATA.Range(FindExcelCell(nCurrentColumn, 1)).Font.Bold = True
            wsVALUATIONDATA.Range(FindExcelCell(nCurrentColumn, 1)).Interior.Color = RGB(222, 222, 222)
            wsVALUATIONDATAX.Range(FindExcelCell(nCurrentColumn, 1)).Value = Sheets("tblDD03L").Range("B" & nCurrent).Value
            wsVALUATIONDATAX.Range(FindExcelCell(nCurrentColumn, 1)).Font.Bold = True
            wsVALUATIONDATAX.Range(FindExcelCell(nCurrentColumn, 1)).Interior.Color = RGB(222, 222, 222)
        End If
    Next
        
    For nCurrentColumn = 2 To 20
        Range(Chr(64 + nCurrentColumn) & "1").Value = Trim(UCase(Range(Chr(64 + nCurrentColumn) & "1").Value))
    Next
    
    a = ActivatePlantMagicTab
    
    For nCurrent = 2 To 1048576
        If Range("B" & nCurrent).Value <> "" Then
            Range("B" & nCurrent & ":C" & nCurrent).Interior.Color = xlNone
            isProblem = False
            strPSTAT = ""
            Range("A" & nCurrent).Show
            strMATNR = Range("B" & nCurrent).Value
            strWERKS = Range("C" & nCurrent).Value
            
            strRange = GetMARCRange(strMATNR)
            For nCurrentPlant = Val(Left(strRange, 9)) To Val(Right(strRange, 9))
                If GetMARC(nCurrentPlant, "MATNR") = strMATNR Then
                    If GetMARC(nCurrentPlant, "WERKS") = strWERKS Then
                        
                        For nCurrentColumn = 4 To 20
                            strCurrentField = Range(Chr(64 + nCurrentColumn) & "1").Value
                            If strCurrentField <> "" Then
                                strTabName = GetTabName(strCurrentField)
                                strFieldName = GetFieldName(strCurrentField)
                                If strFieldName <> "" Then
                                    strTempPSTAT = GetPSTAT(Right(strTabName, 4) & "-" & strCurrentField, GetMARC(nCurrentPlant, "PSTAT"))
                                    If strTempPSTAT <> "" Then
                                        Range(Chr(64 + nCurrentColumn) & nCurrent).Interior.Color = RGB(0, 222, 0)
                                        strPSTAT = strPSTAT & strTempPSTAT
                                        If strTabName = "BAPI_MARC" Then
                                            a = WriteCell("PLANTDATA", "PLANT", nCurrent, strWERKS)
                                            a = WriteCell("PLANTDATA", strFieldName, nCurrent, Trim(Range(Chr(64 + nCurrentColumn) & nCurrent).Value))
                                            a = WriteCell("PLANTDATAX", "PLANT", nCurrent, strWERKS)
                                            a = WriteCell("PLANTDATAX", strFieldName, nCurrent, "X")
                                        Else
                                            a = WriteCell("VALUATIONDATA", "VAL_AREA", nCurrent, strWERKS)
                                            a = WriteCell("VALUATIONDATA", strFieldName, nCurrent, Trim(Range(Chr(64 + nCurrentColumn) & nCurrent).Value))
                                            a = WriteCell("VALUATIONDATAX", "VAL_AREA", nCurrent, strWERKS)
                                            a = WriteCell("VALUATIONDATAX", strFieldName, nCurrent, "X")
                                        End If
                                    Else
                                        Range(Chr(64 + nCurrentColumn) & nCurrent).Interior.Color = RGB(222, 0, 0)
                                        Range("A" & nCurrent).Value = "No view in Plant extension for field."
                                        isProblem = True
                                    End If
                                Else
                                    Range(Chr(64 + nCurrentColumn) & nCurrent).Interior.Color = RGB(222, 0, 0)
                                    Range("A" & nCurrent).Value = "Field does not exist."
                                    isProblem = True
                                End If
                            End If
                        Next
                        
                    End If
                End If
            Next
            If strPSTAT <> "" And Not isProblem Then
                If WriteCell("HEADDATA", "MATERIAL", nCurrent, Range("B" & nCurrent)) Then
                    If InStr(strPSTAT, "A") Then
                        a = WriteCell("HEADDATA", "WORK_SCHED_VIEW", nCurrent, "X")
                    End If
                    If InStr(strPSTAT, "B") Then
                        a = WriteCell("HEADDATA", "ACCOUNT_VIEW", nCurrent, "X")
                    End If
                    If InStr(strPSTAT, "D") Then
                        a = WriteCell("HEADDATA", "MRP_VIEW", nCurrent, "X")
                    End If
                    If InStr(strPSTAT, "E") Then
                        a = WriteCell("HEADDATA", "PURCHASE_VIEW", nCurrent, "X")
                    End If
                    If InStr(strPSTAT, "G") Then
                        a = WriteCell("HEADDATA", "COST_VIEW", nCurrent, "X")
                    End If
                    If InStr(strPSTAT, "P") Then
                        a = WriteCell("HEADDATA", "FORECAST_VIEW", nCurrent, "X")
                    End If
                    If InStr(strPSTAT, "Q") Then
                        a = WriteCell("HEADDATA", "QUALITY_VIEW", nCurrent, "X")
                    End If
                    If InStr(strPSTAT, "V") Then
                        a = WriteCell("HEADDATA", "ACCOUNT_VIEW", nCurrent, "X")
                    End If
                    Range("B" & nCurrent & ":C" & nCurrent).Interior.Color = RGB(255, 255, 0)
                End If
            End If
        End If
        If nCurSec <> Second(Now()) Then
            RetVal = DoEvents
            If Range("B" & nCurrent).Value = "" Then
                Exit For
            End If
        End If
    Next
    Range("A1").Show
    R3.Connection.logoff
    Set R3 = Nothing
    nCurrent = Hour(Now - dStart) * 3600 + Minute(Now - dStart) * 60 + Second(Now - dStart)
    RetVal = MsgBox("The Check/Prepare phase has completed in " & nCurrent & " seconds.", vbOKOnly, "Finished")
End Sub


Sub SortDD03L(nRows As Long)
    Dim strTabName As String, nCurrent As Long
    
    For nCurrent = 2 To nRows + 1
        If Left(Sheets("tblDD03L").Range("B" & nCurrent).Value, 1) = "." Then
            Sheets("tblDD03L").Range("A" & nCurrent).Value = "zz_Ignore this."
        End If
    Next
    
    strTabName = "tblDD03L"
    ActiveWorkbook.Worksheets(strTabName).Sort.SortFields.Clear
    ActiveWorkbook.Worksheets(strTabName).Sort.SortFields.Add Key:=Range("A2:A" & (1 + nRows)), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
    ActiveWorkbook.Worksheets(strTabName).Sort.SortFields.Add Key:=Range("E2:E" & (1 + nRows)), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
    With ActiveWorkbook.Worksheets(strTabName).Sort
        .SetRange Range("A2:AZ" & (1 + nRows))
        .Header = xlGuess
        .MatchCase = False
        .Orientation = xlTopToBottom
        .SortMethod = xlPinYin
        .Apply
    End With
End Sub
Function WriteCell(strTabName As String, strHeader As String, nCurrent As Long, strValue As String) As Boolean
    Dim nCurrentHeader As Long
    For nCurrentHeader = 1 To 200
        If Sheets(strTabName).Range(FindExcelCell(nCurrentHeader, 1)).Value = strHeader Then
            Sheets(strTabName).Range(FindExcelCell(nCurrentHeader, nCurrent)).NumberFormat = "@"
            Sheets(strTabName).Range(FindExcelCell(nCurrentHeader, nCurrent)).Value = strValue
            WriteCell = True
            Exit Function
        End If
    Next
End Function
Function ReadCell(strTabName As String, strHeader As String, nCurrent As Long) As String
    Dim nCurrentHeader As Long
    For nCurrentHeader = 1 To 200
        If Sheets(strTabName).Range(FindExcelCell(nCurrentHeader, 1)).Value = strHeader Then
            ReadCell = Sheets(strTabName).Range(FindExcelCell(nCurrentHeader, nCurrent)).Value
            Exit Function
        End If
    Next
    ReadCell = ""
End Function
Function GetTabName(strField As String) As String
    Dim nCurrent As Long, nTranslate As Long
    For nCurrent = 2 To 999
        If Sheets("tblDD03L").Range("B" & nCurrent).Value = strField Then
            For nTranslate = 2 To 999
                If Sheets("tblDD03L").Range("A" & nTranslate).Value = "BAPI_" & Sheets("tblDD03L").Range("A" & nCurrent).Value Then
                    If Sheets("tblDD03L").Range("H" & nTranslate).Value = Sheets("tblDD03L").Range("H" & nCurrent).Value Then
                        'Found the correct field!!!  Return this.
                        GetTabName = Sheets("tblDD03L").Range("A" & nTranslate).Value
                        Exit Function
                    End If
                End If
            Next
        End If
    Next
End Function
Function GetFieldName(strField As String) As String
    Dim nCurrent As Long, nTranslate As Long
    For nCurrent = 2 To 999
        If Sheets("tblDD03L").Range("B" & nCurrent).Value = strField Then
            For nTranslate = 2 To 999
                If Sheets("tblDD03L").Range("A" & nTranslate).Value = "BAPI_" & Sheets("tblDD03L").Range("A" & nCurrent).Value Then
                    If Sheets("tblDD03L").Range("H" & nTranslate).Value = Sheets("tblDD03L").Range("H" & nCurrent).Value Then
                        'Found the correct field!!!  Return this.
                        GetFieldName = Sheets("tblDD03L").Range("B" & nTranslate).Value
                        Exit Function
                    End If
                End If
            Next
        End If
    Next
End Function
Function GetPSTAT(strField As String, strPSTAT As String) As String
    Dim nCurrent As Long, nCurrent2 As Long, nCurrent3 As Long
    For nCurrent = 2 To 9999
        If Sheets("tblT130F").Range("B" & nCurrent).Value = strField Then
            For nCurrent2 = 1 To Len(Sheets("tblT130F").Range("E" & nCurrent).Value)
                If InStr(strPSTAT, Mid(Sheets("tblT130F").Range("E" & nCurrent).Value, nCurrent2, 1)) Then
                    GetPSTAT = Mid(Sheets("tblT130F").Range("E" & nCurrent).Value, nCurrent2, 1)
                    Exit Function
                End If
            Next
        End If
    Next
End Function

Function ActivatePlantMagicTab() As Boolean
    'Sort the tabs to keep them neat.
    Dim nCurrent As Long, nCurrent2 As Long
    Dim wsA As Worksheet, wsB As Worksheet
    For nCurrent = 1 To Sheets.Count - 1
        For nCurrent2 = nCurrent + 1 To Sheets.Count
            If Sheets(nCurrent).Name = "PlantUpdateRFC" Then
                'Do nothing...
            ElseIf Sheets(nCurrent2).Name = "PlantUpdateRFC" Then
                'Sheets(nCurrent2).Move (Sheets(ncurrent1))
                Sheets(nCurrent2).Move before:=Sheets(nCurrent)
            Else
                If Len(Sheets(nCurrent2).Name) < Len(Sheets(nCurrent).Name) Then
                    Sheets(nCurrent2).Move before:=Sheets(nCurrent)
                End If
            End If
        Next
    Next
    Sheets("PlantUpdateRFC").Activate
    RetVal = DoEvents
End Function




Naturally, a connection to SAP for the purpose of performing the RFCs is required. That connection is created using the GetR3 function.

Function GetR3(ByRef R3 As Object) As Boolean
    Dim wsTemp As Worksheet
    Set wsTemp = CreateTab("Attention")
    wsTemp.Range("A1").Value = "Please sign into SAP."
    wsTemp.Range("A1").Font.Size = 33
    
    Set R3 = CreateObject("SAP.Functions")
    '*************************************
    '  Put in your own credentials here.
    '*************************************
    R3.Connection.System = "P01"
    R3.Connection.SystemNumber = "00"
    R3.Connection.client = "100"
    R3.Connection.User = ""  ' Put your User ID here.
    R3.Connection.Password = "" 'Put your password here (optional).
    R3.Connection.Language = "EN"
    R3.Connection.ApplicationServer = "your.server.hostname.here"  'or IP address.
       
    If R3.Connection.logon(0, True) <> True Then
       If R3.Connection.logon(0, False) <> True Then
            GetR3 = False
            Exit Function
       End If
    End If
    GetR3 = True
    a = NukeTab("Attention")
End Function



A handful of functions for adding and removing tabs (spreadsheets) to the Excel workbook are required for this code. They are included here along with a function that converts X & Y coordinates to Excel cell addresses.

Function CreateTab(strTabName) As Worksheet
    Dim objCurrentSheet As Worksheet
    For Each objCurrentSheet In Sheets
        If objCurrentSheet.Name = strTabName Then
            Application.DisplayAlerts = False
            objCurrentSheet.Delete
            Application.DisplayAlerts = True
            Exit For
        End If
    Next
    Set CreateTab = Sheets.Add
    CreateTab.Name = strTabName
End Function

Function GetTab(strTabName) As Worksheet
    Dim objCurrentSheet As Worksheet
    For Each objCurrentSheet In Sheets
        If objCurrentSheet.Name = strTabName Then
            Set GetTab = objCurrentSheet
            Exit Function
        End If
    Next
    Set GetTab = Sheets.Add
    GetTab.Name = strTabName
End Function

Function NukeTab(strTabName) As Boolean
    Dim objCurrentSheet As Worksheet
    For Each objCurrentSheet In Sheets
        If objCurrentSheet.Name = strTabName Then
            Application.DisplayAlerts = False
            objCurrentSheet.Delete
            Application.DisplayAlerts = True
            NukeTab = True
            Exit Function
        End If
    Next
    NukeTab = False
End Function


Function FindExcelCell(nX As Long, nY As Long) As String
    Dim nPower1 As Long, nPower2 As Long
    nPower2 = 0
    If nX > 26 Then
        nPower2 = Int((nX - 1) / 26)
    End If
    nPower1 = nX - (26 * nPower2)
    If nPower2 > 0 Then
        FindExcelCell = Chr(64 + nPower2) & Chr(64 + nPower1) & nY
    Else
        FindExcelCell = Chr(64 + nPower1) & nY
    End If
End Function


A small host of code snippets are called to tease out the MARA and MARC data and store them in a meaningful way. The sorting is included to increase the efficiency reading back the data (peeking) the data that has been stored (poked) into Excel using some simple addressing tricks.

Const nSetWidth As Long = 30

Function PopulateMARA(ByRef R3 As Object, objRange As Range) As Long
    Dim nCountMARA As Long, objCell As Object, strMaterial As String, strMaterialFilter As String, nMaterials As Long, nCurrentRow As Long
    Dim nCurSec As Integer, strSkipMaterials As String, wsMARA As Worksheet
    
    Set wsMARA = GetTab("MARA")
    
    'First, get the number of existing MARC rows...
    For nCurrentRow = 2 To 1048567
        If Sheets("MARA").Range("A" & Trim(nCurrentRow)).Value <> "" Then
            nCountMARA = nCurrentRow - 1
            strSkipMaterials = strSkipMaterials & Sheets("MARA").Range("A" & Trim(nCurrentRow)).Value
        Else
            Exit For
        End If
    Next

    For Each objCell In objRange.Cells
        If objCell.Value <> "" Then
            strMaterial = objCell.Value
            If InStr(strMaterialFilter, strMaterial) < 1 And InStr(strSkipMaterials, objCell.Value) < 1 Then
                If Sheets("MARA").Range("A:A").Find(strMaterial, LookIn:=xlValues) Is Nothing Then
                    If strMaterialFilter = "" Then
                        strMaterialFilter = "MATNR EQ '" & strMaterial & "'"
                    Else
                        strMaterialFilter = strMaterialFilter & " OR MATNR EQ " & "'" & strMaterial & "'"
                    End If
                Else
                    strSkipMaterials = strSkipMaterials & objCell.Value
                End If
            End If
            If Len(strMaterialFilter) > 9000 Then
                nMaterials = SAP_RFC_READ_TABLE(R3, "MARA", "tempMARA", "MATNR,MTART,MATKL,PSTAT,MEINS,NTGEW,BRGEW,GEWEI,PRDHA", strMaterialFilter)
                If nMaterials > 0 Then
                    Sheets("MARA").Range("A" & Trim(2 + nCountMARA) & ":H" & Trim(1 + nCountMARA + nMaterials)).NumberFormat = "@"
                    Sheets("MARA").Range("A" & Trim(2 + nCountMARA) & ":H" & Trim(1 + nCountMARA + nMaterials)).Value = Sheets("tempMARA").Range("A2:H" & Trim(1 + nMaterials)).Value
                    nCountMARA = nCountMARA + nMaterials
                End If
                strMaterialFilter = ""
            End If
        End If
        If nCurSec <> Second(Now) Then
            nCurSec = Second(Now)
            If nCurSec / 2 = Int(nCurSec / 2) Then
                Sheets("MARA").Tab.Color = RGB(222, 222, 22)
            Else
                Sheets("MARA").Tab.Color = RGB(111, 111, 11)
            End If
            DoEvents
        End If
    Next
    If strMaterialFilter <> "" Then
        nMaterials = SAP_RFC_READ_TABLE(R3, "MARA", "tempMARA", "MATNR,MTART,MATKL,PSTAT,MEINS,NTGEW,BRGEW,GEWEI,PRDHA", strMaterialFilter)
        If nMaterials > 0 Then
            Sheets("MARA").Range("A" & Trim(2 + nCountMARA) & ":H" & Trim(1 + nCountMARA + nMaterials)).NumberFormat = "@"
            Sheets("MARA").Range("A" & Trim(2 + nCountMARA) & ":H" & Trim(1 + nCountMARA + nMaterials)).Value = Sheets("tempMARA").Range("A2:H" & Trim(1 + nMaterials)).Value
            nCountMARA = nCountMARA + nMaterials
        End If
        strMaterialFilter = ""
    End If
    If nMaterials > 0 Then
        Sheets("MARA").Range("A1:H1").Value = Sheets("tempMARA").Range("A1:H1").Value
        Sheets("MARA").Range("A1:H1").Font.Bold = True
        Sheets("MARA").Range("A1:H1").Interior.Color = RGB(222, 222, 222)
    End If
    nCurrent = NukeTab("tempMARA")
    PopulateMARA = nCountMARA
    Sheets("MARA").Tab.Color = RGB(222, 222, 22)
End Function


Function GetMARA(strMATNR As String, strField As String) As String
    Static strLastMATNR As String, nLastCurrent As Long
    Dim nCurrent As Long, nCurrentField As Long, nStart As Long
    
    If Sheets("MARA").Range("A1").Interior.Color <> RGB(222, 222, 0) Then
        Call SortMARA
        Sheets("MARA").Range("A1").Interior.Color = RGB(222, 222, 0)
    End If
    If strMATNR = strLastMATNR And nLastCurrent <> 0 Then
        nStart = nLastCurrent
    Else
        nStart = 2
        strLastMATNR = strMATNR
        For nCurrent = 2 To 1040000 Step 1000
            If Sheets("MARA").Range("A" & nCurrent).Value > strMATNR Or Sheets("MARA").Range("A" & nCurrent).Value = "" Then
                Exit For
            End If
            If Sheets("MARA").Range("A" & nCurrent).Value < strMATNR Then
                nStart = nCurrent
            End If
        Next
        For nCurrent = nStart To 1040000 Step 100
            If Sheets("MARA").Range("A" & nCurrent).Value > strMATNR Or Sheets("MARA").Range("A" & nCurrent).Value = "" Then
                Exit For
            End If
            If Sheets("MARA").Range("A" & nCurrent).Value < strMATNR Then
                nStart = nCurrent
            End If
        Next
        For nCurrent = nStart To 1040000 Step 10
            If Sheets("MARA").Range("A" & nCurrent).Value > strMATNR Or Sheets("MARA").Range("A" & nCurrent).Value = "" Then
                Exit For
            End If
            If Sheets("MARA").Range("A" & nCurrent).Value < strMATNR Then
                nStart = nCurrent
            End If
        Next
    End If

    For nCurrent = nStart To 1048576
        If Sheets("MARA").Range("A" & nCurrent).Value = strMATNR Then
            For nCurrentField = 1 To 10
                If Sheets("MARA").Range(FindExcelCell(nCurrentField, 1)).Value = strField Then
                    GetMARA = Sheets("MARA").Range(FindExcelCell(nCurrentField, nCurrent)).Value
                    nLastCurrent = nCurrent
                    Exit Function
                End If
            Next
        End If
    Next
End Function


Sub SortMARA()
    Dim arr As Object
    Dim cell As Range, strTemp As Variant, nCurrent As Long, nCurrent2 As Long
    Dim nCountMARA As Long
    
    
    Set arr = CreateObject("System.Collections.ArrayList")
    For nCurrent = 1 To 1048575
        If Sheets("MARA").Range("A" & nCurrent).Value <> "" Then
            strTemp = Sheets("MARA").Range("A" & (1 + nCurrent)).Value & Right("000000000" & nCurrent, 9)
            arr.Add strTemp
            If nCurSec <> Second(Now) Then
                nCurSec = Second(Now)
                RetVal = DoEvents
            End If
        Else
            nCountMARA = nCurrent - 1
            Exit For
        End If
    Next
    arr.Sort
    
    Dim wsTemp As Worksheet
    Set wsTemp = CreateTab("tempMARA")
    Sheets("tempMARA").Range("A2:H" & nCurrent).NumberFormat = "@"
    Sheets("tempMARA").Range("A2:H" & nCurrent).Value = Sheets("MARA").Range("A2:H" & nCurrent).Value
    
    nCurrent = 0
    For Each strTemp In arr
        nCurrent = nCurrent + 1
        nCurrent2 = Val(Mid(strTemp, 19, 9))
        If nCurrent <> nCurrent2 Then
            Sheets("MARA").Range("A" & (nCurrent + 1) & ":H" & (nCurrent + 1)).Value = Sheets("tempMARA").Range("A" & (nCurrent2 + 1) & ":H" & (nCurrent2 + 1)).Value
            Sheets("tempMARA").Range("A" & (nCurrent2 + 1) & ":H" & (nCurrent2 + 1)).Clear
            If nCurSec <> Second(Now) Then
                nCurSec = Second(Now)
                If nCurSec / 2 = Int(nCurSec / 2) Then
                    Sheets("MARA").Tab.Color = RGB(222, 222, 22)
                Else
                    Sheets("MARA").Tab.Color = RGB(111, 111, 11)
                End If
                RetVal = DoEvents
            End If
        End If
    Next
    nCurrent = NukeTab("tempMARA")
    Sheets("MARA").Tab.Color = RGB(222, 222, 22)
End Sub


Function PopulateMARCbyMATNR(ByRef R3 As Object, objRange As Range) As Long
    Dim nCountMARA As Long, objCell As Object, strMaterial As String, strMaterialFilter As String, nMaterials As Long, nCurrentRow As Long
    Dim nCurSec As Integer, strSkipMaterials As String, strSkipPlants As String, nCurrent As Long
    Const strGetFields As String = "MATNR,WERKS,PSTAT,AUSME,DISMM,DISPO,DISLS,BESKZ,SOBSL,LOSGR,SOBSK,LGPRO,DISGR,LGFSB"
    Dim nLGORTs As Long, nCurrentLGORT As Long, strLGORTs As String, strOptions As String
    
    '* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    '*                                                             *
    '*  This function only pulls a list of Plants associated with  *
    '*  the target Material Masters and then uses that data to     *
    '*  call the PopulateMARC function.                            *
    '*                                                             *
    '* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    
            
    For Each objCell In objRange.Cells
        If objCell.Value <> "" Then
            strMaterial = objCell.Value
            If InStr(strMaterialFilter, strMaterial) < 1 And InStr(strSkipMaterials, objCell.Value) < 1 Then
                If strMaterialFilter = "" Then
                    strMaterialFilter = "MATNR EQ '" & strMaterial & "'"
                Else
                    strMaterialFilter = strMaterialFilter & " OR MATNR EQ " & "'" & strMaterial & "'"
                End If
                strSkipMaterials = strSkipMaterials & "," & objCell.Value
            End If
            If Len(strMaterialFilter) > 4000 Then
                a = CreateLogEntry("Pulling MARC entries for " & strMaterialFilter)
                nMaterials = SAP_RFC_READ_TABLE(R3, "MARC", "tempMARC", strGetFields, strMaterialFilter)
                If nMaterials > 0 Then
                    'nLGORTs = SAP_RFC_READ_TABLE(R3, "MARD", "tempMARD", "MATNR,WERKS,LGORT", strMaterialFilter)
                    'a = SortByFirstTwoColumns("tempMARC", nMaterials)
                    'a = SortByFirstTwoColumns("tempMARD", nLGORTs)
                    For nCurrentRow = 2 To nMaterials + 1
                        'strLGORTs = GetLGORTs(Sheets("tempMARC").Range("A" & nCurrentRow).Value, Sheets("tempMARC").Range("B" & nCurrentRow).Value)
                        'Sheets("tempMARC").Range("O" & nCurrentRow).Value = strLGORTs
                        nCountMARC = SetMARC(Sheets("tempMARC").Range("A" & nCurrentRow & ":O" & nCurrentRow))
                        If nCurSec <> Second(Now) Then
                            nCurSec = FlashTab("MARC")
                        End If
                    Next
                End If
                strMaterialFilter = ""
            End If
        End If
        If nCurSec <> Second(Now) Then
            nCurSec = FlashTab("MARC")
        End If
    Next
    If strMaterialFilter <> "" Then
        a = CreateLogEntry("Pulling MARC entries for " & strMaterialFilter)
        nMaterials = SAP_RFC_READ_TABLE(R3, "MARC", "tempMARC", strGetFields, strMaterialFilter)
        If nMaterials > 0 Then
            'nLGORTs = SAP_RFC_READ_TABLE(R3, "MARD", "tempMARD", "MATNR,WERKS,LGORT", strMaterialFilter)
            'a = SortByFirstTwoColumns("tempMARC", nMaterials)
            'a = SortByFirstTwoColumns("tempMARD", nLGORTs)
            For nCurrentRow = 2 To nMaterials + 1
                'strLGORTs = GetLGORTs(Sheets("tempMARC").Range("A" & nCurrentRow).Value, Sheets("tempMARC").Range("B" & nCurrentRow).Value)
                'Sheets("tempMARC").Range("O" & nCurrentRow).Value = strLGORTs
                nCountMARC = SetMARC(Sheets("tempMARC").Range("A" & nCurrentRow & ":O" & nCurrentRow))
                If nCurSec <> Second(Now) Then
                    nCurSec = FlashTab("MARC")
                End If
            Next
        End If
        strMaterialFilter = ""
    End If
    If nMaterials > 0 Then
        Sheets("MARC").Range("A1:O1").Value = Sheets("tempMARC").Range("A1:O1").Value
        'Sheets("MARC").Range("O1").Value = "LGORTS"
        Sheets("MARC").Range("A1:O1").Font.Bold = True
        Sheets("MARC").Range("A1:O1").Interior.Color = RGB(222, 222, 222)
    End If
    nCurrent = NukeTab("tempMARC")
    'nCurrent = NukeTab("tempMARD")
    Sheets("MARA").Tab.Color = RGB(222, 222, 22)
    PopulateMARCbyMATNR = nCountMARA
End Function


Function FlashTab(strTabName As String) As Long
    Dim nCurSec As Long, nCurrentRGB As Long
    nCurSec = Second(Now)
    If nCurSec / 2 = Int(nCurSec / 2) Then
        nCurrentRGB = nCurSec * 3
    Else
        nCurrentRGB = 255 - (nCurSec * 3)
    End If
    Sheets(strTabName).Tab.Color = RGB(nCurrentRGB, 255 - nCurrentRGB, nCurrentRGB) '(256 ^ 3) - Sheets(strTabName).Tab.Color
    RetVal = DoEvents
    FlashTab = Second(Now)
End Function




Function SetMARC(objRange As Range) As Long
    Static nCountMARC As Long, wsMARC As Worksheet, nCurrentSet As Long, nCurrent As Long, nCurrentLine As Long
    Dim nRangeLength As Long, nStart As Long
    'Const nSetWidth As Long = 30
    Set wsMARC = Sheets("MARC")
    If wsMARC.Range("A2").Value = "" Then
        nCountMARC = 0 'Refreshed!
    End If
    If nCountMARC = 0 Or objRange.Cells.Count = 1 Then
        For nCurrentSet = 21 To 0 Step -1
            If wsMARC.Range(FindExcelCell(1 + (nCurrentSet * nSetWidth), 2)).Value <> "" Then
                nCountMARC = 1000000 * nCurrentSet
                If nCurrentSet = 0 Then
                    nStart = 2
                Else
                    nStart = 1
                End If
                For nCurrent = nStart To 1048000
                    If wsMARC.Range(FindExcelCell(1 + (nCurrentSet * nSetWidth), nCurrent)).Value <> "" Then
                        nCountMARC = nCountMARC + 1
                    Else
                        Exit For
                    End If
                    If nCurSec <> Second(Now) Then
                        nCurSec = Second(Now)
                        RetVal = DoEvents
                    End If
                Next
                Exit For
            End If
        Next
    End If
    If objRange.Cells.Count > 1 Then
        nCountMARC = nCountMARC + 1
        nCurrentSet = Int(nCountMARC / 1000000)
        nCurrentLine = nCountMARC - Int(nCurrentSet * 1000000)
        'If nCurrentLine > 1000000 Then
        '    nCurrentSet = nCurrentSet + 1
        '    nCurrentLine = nCountMARC - Int(nCurrentSet * 1000000)
        'End If
        nRangeLength = objRange.Cells.Count
        wsMARC.Range(FindExcelCell(1 + (nCurrentSet * nSetWidth), 1 + nCurrentLine) & ":" & FindExcelCell(nRangeLength + (nCurrentSet * nSetWidth), 1 + nCurrentLine)).NumberFormat = "@"
        wsMARC.Range(FindExcelCell(1 + (nCurrentSet * nSetWidth), 1 + nCurrentLine) & ":" & FindExcelCell(nRangeLength + (nCurrentSet * nSetWidth), 1 + nCurrentLine)).Value = objRange.Value
    End If
    SetMARC = nCountMARC
End Function

Function IsInMARC(strPlant As String) As Boolean
    Dim wsMARC As Worksheet, nCurrentSet As Long, nCurrent As Long, nCurrentLine As Long
    Dim nRangeLength As Long
    Static strIsInMARC As String
    'Const nSetWidth As Long = 30
    
    If InStr(strIsInMARC, strPlant) > 1 Then
        IsInMARC = True
        Exit Function
    End If
    
    Dim nPower1 As Long, nPower2 As Long, strHeaderColumn As String
    Set wsMARC = Sheets("MARC")
    
    For nCurrentSet = 21 To 0 Step -1
        nCurrent = 2 + (nSetWidth * nCurrentSet) 'MARC Column
        nPower2 = 0
        If nCurrent > 26 Then
            nPower2 = Int((nCurrent - 1) / 26)
        End If
        nPower1 = nCurrent - (26 * nPower2)
        If nPower2 > 0 Then
            strHeaderColumn = Chr(64 + nPower2) & Chr(64 + nPower1)
        Else
            strHeaderColumn = Chr(64 + nPower1)
        End If
        If wsMARC.Range(strHeaderColumn & "2").Value <> "" Then
            'There is something in this set...
            If Not Sheets("MARC").Range(strHeaderColumn & ":" & strHeaderColumn).Find(strPlant, LookIn:=xlValues) Is Nothing Then
                IsInMARC = True
                strIsInMARC = strIsInMARC & "," & strPlant
                Exit Function
            End If
        End If
    Next
    IsInMARC = False
End Function

Function GetMARC(nCurrent As Long, strField As String) As String
    Static nCountMARC As Long, wsMARC As Worksheet, nCurrentSet As Long, nCurrentLine As Long
    'Static nLastCurrent As Long, strLastField As String, strLastValue As String
    Dim nCurrentColumn As Long
    Static strColumns(70) As String
    'Const nSetWidth As Long = 30
    
    'If nCurrent = 0 Then
    '    GetMARC = ""
    '    Exit Function
    'End If
    
    If strColumns(1) = "" Then
        For nCurrentColumn = 1 To 70
            strColumns(nCurrentColumn) = Sheets("MARC").Range(FindExcelCell(nCurrentColumn, 1)).Value
        Next
    End If
    
    nCurrentSet = Int(nCurrent / 1000000)
    nCurrentLine = nCurrent - (1000000 * nCurrentSet)
    
    For nCurrentColumn = 1 To 70
        If strColumns(nCurrentColumn) = strField Then
            Exit For
        End If
    Next
    
    GetMARC = Sheets("MARC").Range(FindExcelCell((nCurrentSet * nSetWidth) + nCurrentColumn, nCurrentLine + 1)).Value
    
End Function

Function GetMARCRange(strMATNR As String) As String
    Static nCount As Long, strRanges(999999, 1) As String, nCountMARC As Long
    Static strLastMATNR As String, nLastLast As Long, nStart As Long
    Dim nCurrent As Long, nFirst As Long, nLast As Long, nCurrentMATNR As String
    
    nStart = 1
    
    If Sheets("MARC").Range("A1048570").Value <> "" Then
        'This was just sorted.  Start from scratch...
        Sheets("MARC").Range("A1048570").Value = ""
        nCount = 0
        nCountMARC = 0
        nLastLast = 1
    End If
    
    
    For nCurrent = 1 To nCount
        If strRanges(nCurrent, 1) = strMATNR Then
            GetMARCRange = strRanges(nCurrent, 0)
            Exit Function
        End If
    Next
    
    If nCountMARC = 0 Then
        nCountMARC = SetMARC(Range("ZZ9"))
    End If
    
    'Seriously speed this process up . . .
    If strLastMATNR < strMATNR And nLastLast <> 0 Then
        nStart = nLastLast
    Else
        For nCurrent = 1000 To nCountMARC - 2000 Step 1000
            If GetMARC(nCurrent, "MATNR") < strMATNR Then
                nStart = nCurrent
            Else
                Exit For
            End If
        Next
    End If
    
    nFirst = 0
    nLast = 0
    For nCurrent = nStart To nCountMARC
        If GetMARC(nCurrent, "MATNR") = strMATNR Then
            If nFirst = 0 Then
                nFirst = nCurrent
            End If
            nLast = nCurrent
        Else
            If nLast > 0 Then
                Exit For
            End If
            If GetMARC(nCurrent, "MATNR") > strMATNR Then
                'The Material Master isn't in this list.
                Exit For
            End If
        End If
    Next
    nCount = nCount + 1
    strRanges(nCount, 1) = strMATNR
    strRanges(nCount, 0) = Right("000000000" & nFirst, 9) & Right("000000000" & nLast, 9)
    GetMARCRange = strRanges(nCount, 0)
    strLastMATNR = strMATNR
    nLastLast = nLast
End Function


Function CreateLogEntry(strLogEntry As String) As Boolean
    Sheets("tblT130F").Range("Y1:Z99").Copy Destination:=Sheets("tblT130F").Range("Y2:Z100")
    Sheets("tblT130F").Range("Y1").Value = Now
    Sheets("tblT130F").Range("Z1").Value = strLogEntry
End Function

Sub SortMARC()
    Dim nCurrentColumn As Long, nCurrent As Long, nCurrent2 As Long, nCurSec As Long
    Dim nCountMARC As Long, strTemp As Variant
    Dim nCurrentSet As Long, nCurrentLine As Long
    Dim nCurrentSet2 As Long, nCurrentLine2 As Long
    
    Static strColumns(70) As String
    'Const nSetWidth As Long = 30
        
    'Get the number of Plant extensions in the MARC tab...
    nCountMARC = SetMARC(Range("ZZ9"))
    
    'Set these to be stored as text.
    'Sheets("MARC").Range(FindExcelCell(1, 1048570) & ":" & FindExcelCell(70, 1048570)).NumberFormat = "@"
    Sheets("MARC").Range("A1048570").Value = "Sorted"
        
    Dim arr As Object
    Dim cell As Range
    
    Set arr = CreateObject("System.Collections.ArrayList")
    For nCurrent = 1 To nCountMARC
        nCurrentSet = Int(nCurrent / 1000000)
        nCurrentLine = nCurrent - (1000000 * nCurrentSet)
        strTemp = Sheets("MARC").Range(FindExcelCell((nCurrentSet * nSetWidth) + 1, nCurrentLine + 1)).Value & Right("000000000" & nCurrent, 9)
        arr.Add strTemp
        If nCurSec <> Second(Now) Then
            nCurSec = Second(Now)
            If nCurSec / 2 = Int(nCurSec / 2) Then
                Sheets("MARC").Tab.Color = RGB(222, 22, 222)
            Else
                Sheets("MARC").Tab.Color = RGB(111, 11, 111)
            End If
            RetVal = DoEvents
        End If
    Next
    arr.Sort
    
    Dim wsTemp As Worksheet
    Set wsTemp = CreateTab("tempMARC")
    
    nCurrent = 0
    For Each strTemp In arr
        nCurrent = nCurrent + 1
        nCurrent2 = Val(Mid(strTemp, 19, 9))
        If nCurrent <> nCurrent2 Then
            nCurrentSet = Int(nCurrent / 1000000)
            nCurrentLine = nCurrent - (1000000 * nCurrentSet)
            nCurrentSet2 = Int(nCurrent2 / 1000000)
            nCurrentLine2 = nCurrent2 - (1000000 * nCurrentSet2)
            'Sheets("MARC").Range(FindExcelCell(1, 1048570) & ":" & FindExcelCell(70, 1048570)).Value = Sheets("MARC").Range(FindExcelCell((nCurrentSet * nSetWidth) + 1, nCurrentLine + 1) & ":" & FindExcelCell((nCurrentSet * nSetWidth) + nSetWidth, nCurrentLine + 1)).Value
            Sheets("tempMARC").Range(FindExcelCell((nCurrentSet * nSetWidth) + 1, nCurrentLine + 1) & ":" & FindExcelCell((nCurrentSet * nSetWidth) + nSetWidth, nCurrentLine + 1)).NumberFormat = "@"
            Sheets("tempMARC").Range(FindExcelCell((nCurrentSet * nSetWidth) + 1, nCurrentLine + 1) & ":" & FindExcelCell((nCurrentSet * nSetWidth) + nSetWidth, nCurrentLine + 1)).Value = Sheets("MARC").Range(FindExcelCell((nCurrentSet * nSetWidth) + 1, nCurrentLine + 1) & ":" & FindExcelCell((nCurrentSet * nSetWidth) + nSetWidth, nCurrentLine + 1)).Value
            If nCurrent2 < nCurrent Then
                Sheets("MARC").Range(FindExcelCell((nCurrentSet * nSetWidth) + 1, nCurrentLine + 1) & ":" & FindExcelCell((nCurrentSet * nSetWidth) + nSetWidth, nCurrentLine + 1)).Value = Sheets("tempMARC").Range(FindExcelCell((nCurrentSet2 * nSetWidth) + 1, nCurrentLine2 + 1) & ":" & FindExcelCell((nCurrentSet2 * nSetWidth) + nSetWidth, nCurrentLine2 + 1)).Value
                Sheets("tempMARC").Range(FindExcelCell((nCurrentSet2 * nSetWidth) + 1, nCurrentLine2 + 1) & ":" & FindExcelCell((nCurrentSet2 * nSetWidth) + nSetWidth, nCurrentLine2 + 1)).Clear
            Else
                Sheets("MARC").Range(FindExcelCell((nCurrentSet * nSetWidth) + 1, nCurrentLine + 1) & ":" & FindExcelCell((nCurrentSet * nSetWidth) + nSetWidth, nCurrentLine + 1)).Value = Sheets("MARC").Range(FindExcelCell((nCurrentSet2 * nSetWidth) + 1, nCurrentLine2 + 1) & ":" & FindExcelCell((nCurrentSet2 * nSetWidth) + nSetWidth, nCurrentLine2 + 1)).Value
            End If
            'Sheets("MARC").Range(FindExcelCell((nCurrentSet2 * nSetWidth) + 1, nCurrentLine2 + 1) & ":" & FindExcelCell((nCurrentSet2 * nSetWidth) + nSetWidth, nCurrentLine2 + 1)).Value = Sheets("MARC").Range(FindExcelCell(1, 1048570) & ":" & FindExcelCell(70, 1048570)).Value
        End If
        If nCurSec <> Second(Now) Then
            nCurSec = Second(Now)
            If nCurSec / 2 = Int(nCurSec / 2) Then
                Sheets("MARC").Tab.Color = RGB(222, 22, 222)
            Else
                Sheets("MARC").Tab.Color = RGB(111, 11, 111)
            End If
            Sheets("tempMARC").Range("A1").Value = "Sorting...  " & Int(100 * (nCurrent / nCountMARC)) & "% completed."
            RetVal = DoEvents
        End If
    Next
    
    Set arr = Nothing
    a = NukeTab("tempMARC")
    
End Sub



Step 2: Loading the prepared data

Updating Plant Extensions with RFCsFor this example, these structures are populated with data that will be used to affect changes to existing Material Master Plant extensions. The BAPI will use the data in these structures to update fields in the MARC and MBEW tables associated with the Material Masters.

  • HEADDATA
  • PLANTDATA
  • PLANTDATAX
  • VALUATIONDATA
  • VALUATIONDATAX

Only after analyzing the code and combing through documentation on BAPI_MATERIAL_SAVEDATA do the structures ending with "X" seem intuitive; these tell the BAPI which fields are to be updated allowing for individual fields to be updated without having to populate the entire structure with existing data. For example, in each case that a value is to be changed to a field's value from the PLANTDATA structure, the same field in PLANTDATAX must be ticked with an "X"; those fields that are not ticked will be ignored.

Because all of the heavy lifting is performed by the code above, the code below that actually loads the prepared data is very simple. It populates the structures listed above with the data that has already been validated and prepared, passes them to the BAPI as parameters, performs the RFC and then captures the BAPIRET2 return value which is used to report the status of the load for that record.

Sub LoadData()
    Dim R3 As Object, nCurrent As Long, nCurrentColumn As Long, strTabName As String
    Dim strHeader As String, dStart As Date, nCurSec As Long
    
    dStart = Now 'Report how long this takes.

    a = ActivatePlantMagicTab

    If Not GetR3(R3) Then
        Exit Sub
    End If

    Dim HEADDATA As Object
    Dim PLANTDATA As Object
    Dim PLANTDATAX As Object
    Dim VALUATIONDATA As Object
    Dim VALUATIONDATAX As Object

    Set MyFunc = R3.Add("BAPI_MATERIAL_SAVEDATA")
    
    Set HEADDATA = MyFunc.exports("HEADDATA")
    Set PLANTDATA = MyFunc.exports("PLANTDATA")
    Set PLANTDATAX = MyFunc.exports("PLANTDATAX")
    Set VALUATIONDATA = MyFunc.exports("VALUATIONDATA")
    Set VALUATIONDATAX = MyFunc.exports("VALUATIONDATAX")

    For nCurrent = 2 To 1048576
        If Sheets("PlantUpdateRFC").Range("B" & nCurrent).Value <> "" Then
            If Sheets("PlantUpdateRFC").Range("A" & nCurrent).Value = "" Then 'Not done yet, no error
                If Sheets("HEADDATA").Range("A" & nCurrent).Value <> "" Then 'Data ready to load
                
                    For nCurrentColumn = 2 To 300
                        strHeader = Sheets("tblDD03L").Range("B" & nCurrentColumn).Value
                        If strHeader = "" Then
                            Exit For
                        End If
                        If Sheets("tblDD03L").Range("A" & nCurrentColumn).Value = "BAPIMATHEAD" Then
                            HEADDATA(strHeader) = ReadCell("HEADDATA", strHeader, nCurrent)
                        End If
                        If Sheets("tblDD03L").Range("A" & nCurrentColumn).Value = "BAPI_MARC" Then
                            PLANTDATA(strHeader) = ReadCell("PLANTDATA", strHeader, nCurrent)
                            PLANTDATAX(strHeader) = ReadCell("PLANTDATAX", strHeader, nCurrent)
                        End If
                        If Sheets("tblDD03L").Range("A" & nCurrentColumn).Value = "BAPI_MBEW" Then
                            VALUATIONDATA(strHeader) = ReadCell("VALUATIONDATA", strHeader, nCurrent)
                            VALUATIONDATAX(strHeader) = ReadCell("VALUATIONDATAX", strHeader, nCurrent)
                        End If
                    Next
                    
                    Set BAPIRET2 = MyFunc.Tables("RETURNMESSAGES")
                    BAPIRET2.Rows.RemoveAll
                    
                    Result = MyFunc.Call
                    
                    If Result = True Then
                        Set BAPIRET2 = MyFunc.Tables("RETURNMESSAGES")
                    Else
                        'MsgBox MyFunc.EXCEPTION
                        Sheets("PlantUpdateRFC").Range("A" & nCurrentRow).Value = MyFunc.exception
                    End If
                    
                    For Each objReturn In BAPIRET2.Rows
                        If objReturn("TYPE") = "S" Then
                            Sheets("PlantUpdateRFC").Range("A" & nCurrent).Value = objReturn("MESSAGE")
                            Sheets("PlantUpdateRFC").Range("B" & nCurrent & ":C" & nCurrent).Interior.Color = RGB(0, 222, 0)
                            Range("D" & nCurrent & ":V" & nCurrent).Interior.Color = xlNone
                            Sheets("HEADDATA").Range("A" & nCurrent).Value = "" 'Don't allow this to load again--force re-check.
                        End If
                        If objReturn("TYPE") = "E" Then
                            Sheets("PlantUpdateRFC").Range("A" & nCurrent).Value = objReturn("MESSAGE")
                            Sheets("PlantUpdateRFC").Range("B" & nCurrent & ":C" & nCurrent).Interior.Color = RGB(222, 0, 0)
                            Exit For
                        End If
                    Next
                    BAPIRET2.Rows.RemoveAll
                    
                End If
            End If
            If nCurSec <> Second(Now) Then
                RetVal = DoEvents
                If Second(Now) / 15 = Int(Second(Now) / 15) Then
                    Sheets("PlantUpdateRFC").Range("A" & (10 + nCurrent)).Show
                End If
            End If
        End If
    Next
    Range("A1").Show
    R3.Connection.logoff
    Set R3 = Nothing
    nCurrent = Hour(Now - dStart) * 3600 + Minute(Now - dStart) * 60 + Second(Now - dStart)
    RetVal = MsgBox("The Load phase has completed in " & nCurrent & " seconds.", vbOKOnly, "Finished")
End Sub



The SAP_RFC_READ_TABLE function, along with a more indepth discussion on poking and peeking data into Excel's addressing system, is available on this page. Additionally, it is included in the file linked below which has headers and buttons conveniently included; it looks like this.

The yellow headers can be populated with multiple feildnames from MARC and MBEW and the code populates the appropriate field in the structures passed in as parameters to the BAPI. The purpose of this functionality is to obviate the need to memorize fieldnames from the BAPI_MARC and BAPI_MBEW structures when most Material Master resources are already familiar with the fieldnames from MARC and MBEW.

Things to watch out for

If Excel throws a random error with no description or information other than "409" then it is very likely that the user account being used to connect to the SAP system to perform Remote Function Calls (RFCs) does not have permission to connect to the SAP server to perform RFCs. Reach out to the security team or the BASIS team to see if it is at all possible to get RFC acccess.
Excel 409 Error

Sometimes Excel may throw a "404" error. It isn't clear, but this is likely a lost connection to the server and not a missing web page. Click OK to close the error message and then start again. In the Load step, the software will pick up where it left off.

Download the tool

Please understand that this tool is provided here for free and comes with no warranty, expressed or implied. Support tends to be slow, but can be obtained at this link.

xlsm_MaterialMasterPlantUpdateRFC.xlsm

https://techcommunity.microsoft.com/t5/image/serverpage/image-id/27291i4FF95811E492D1AC/image-size/large?v=v2&px=999|https://cdn.extendoffice.com/images/stories/shot-kutools-excel/reading-view/shot-reading-layout.gif|https://support.content.office.net/en-us/media/8cc0eff7-d899-416d-9aef-ea6c3d71c9e8.gif|https://user-images.githubusercontent.com/4182043/32186288-b62e23a0-bd6f-11e7-83d9-ee83d9671f60.gif|https://cdn.goskills.com/blobs/blogs/203/39.gif|https://static.wixstatic.com/media/9d7f1e_a36ce5f5928142878069d5b79ebae942~mv2.gif|https://i2.wp.com/www.myofficetricks.com/wp-content/uploads/2020/11/myofficetricks.com_2020-11-23_03-53-57.gif?ssl=1|https://s37447.pcdn.co/wp-content/uploads/2016/05/12-gif.gif|https://cdn.vox-cdn.com/uploads/chorus_asset/file/21997566/exceldatatypes.gif|https://miro.medium.com/max/1400/1*t7Gy7CZb7LI-2g8cMfqJBA.gif|https://www.spreadsheetweb.com/wp-content/uploads/2019/04/How-to-filter-by-using-a-formula-in-Excel.gif|https://seotoolsforexcel.com/content/images/2016/11/intro-33.gif|https://www.bleepstatic.com/images/news/u/986406/Microsoft/Excel-JavaScript.gif|https://9.9.9.9|https://www.exceldemy.com/wp-content/uploads/2018/12/4.point-and-click-different-worksheet-keyboard.gif|https://www.vertex42.com/blog/images/excel-training/how-to-group-rows-in-excel-anim.gif|https://i1.wp.com/www.myofficetricks.com/wp-content/uploads/2019/09/SUM4.gif?ssl=1|https://support.content.office.net/en-us/media/f3544583-de81-442d-b387-dc99a76bdaa5.gif|https://www.exceltip.com/wp-content/uploads/2020/01/gif8-1.gif|https://d13ot9o61jdzpp.cloudfront.net/images/hr_dashboard_webinar.gif|https://cdn.pastemagazine.com/www/articles/Autofilter.gif|https://cdn.comsol.com/release/52a/livelink-excel/llexcel_autoupdate.gif|https://blog.hubspot.com/hs-fs/hubfs/excel-simple-math.gif?width=650&name=excel-simple-math.gif|https://d13ot9o61jdzpp.cloudfront.net/images/data_types_geography.gif|https://prodmediacdn.blob.core.windows.net/media/2020/07/New_data_types_and_smart_templates.gif|https://wmfexcel.files.wordpress.com/2019/01/excel-tips-using-structure-reference-in-conditional-formatting3.gif|https://user-images.githubusercontent.com/1297882/35767301-2fd0c490-08ea-11e8-89d7-933238fed3f2.gif|https://www.windowscentral.com/sites/wpcentral.com/files/field/image/2020/03/money_in_excel.gif