Load Classifications For Any Object Using RFC From Excel

Jimbo's picture

SAP has decided that customer-supplied Classification Types are "out of scope" for S4 in their Migration Cockpit tool. When attempting to load custom Classification Types against Objects in S4, the Migration Cockpit throws an "Enter a correct class type" error and further states that your Class Type "isn't supported". SAP provides a little more information in Knowledge Base Article 3128863.

Migration Object: Object classification - General template
Occurred During: Simulate Migration: Completed with Errors
The provided class type is not supported by data migration object. For supported class type list, please see object document and tool tips.
Technical Information
Error Message
Message Class: CNV_DMC_SN
Message Number: 433

Related articles:

Rather than allowing the project to fail due to a simple existential crisis, this programmer sat down to write a tool in Excel that uses RFCs to call BAPIs in SAP using a little VBA. The code is even more confusing than the acronyms, so feel free to skip directly to the download link below to use this tool.

SAP SecuritySAP has certain functions called BAPIs (Business Application Programming Interface) that can be called from outside of SAP by external systems. In this case, a little Visual Basic code calls the BAPI remotely by use of an RFC (Remote Function Call).

As long as the connection to the SAP system exists with appropriate authorization, the call can be made regardless of the external system or computer language. Because the source data is already in Excel and Excel supports VBA, it made perfect sense to use Excel and VBA.

The tool allows the user to provide a list of servers from which data can be extracted and to which data can be loaded. A dropdown in Cell A1 of the Classifications tab tells Excel with which server to interact.

SAP SecurityThe Extract code teases out a report that includes all of the Classification Allocations and the Characteristics that have values along with those values. The extracted data in this report can be used later to load updated data back into the server or migrate that data to another server; it is already perfectly formatted to be loaded and can be used as a template when receiving data to be loaded.

Once the source data is available in Excel, the Extract code first creates a connection to SAP. This code has been recycled so many times that the variable name is still R3. The credentials are provided by the user when SAP's popup requests them.

    Dim R3 As Object

    Set R3 = CreateObject("SAP.Functions")
    '*************************************
    '  Put in your own credentials here.
    '*************************************
    R3.Connection.System = Range("A1").Value
    R3.Connection.SystemNumber = strSystemNumber
    R3.Connection.client = strClient
    R3.Connection.User = ""  ' Put your User ID here (optional).
    R3.Connection.Password = "" 'Put your password here (optional).
    R3.Connection.Language = "EN" 'Put the desired language here (optional).
    R3.Connection.ApplicationServer = strApplicationServer
       
    If R3.Connection.logon(0, True) <> True Then
       If R3.Connection.logon(0, False) <> True Then
            GetR3 = False
            Exit Function
       End If
    End If

SAP SecurityNext, we loop through the records in the Spreadsheet with data to be extracted and pass that to the BAPIs to tease out the data that are associated with each Classification allocation. It first checks to see what Classes are associated with the Object; if no Classification Type (KLART) value is provided then it checks all Classification Types associated with the Object Table provided in the ServerList tab (see Instructions tab in the spreadsheet).

The VBA code for this tool is particularly complicated; a lot of data is juggled into and out of these BAPIs. For the sake of maintaining this programmer's sanity, the various bits are abstracted into small functions that can be called independently to trigger the RFCs.

Function CallGetClassesBAPI(ByRef R3 As Object, strObject As String, strObjectTable As String, strClassType As String, ByRef strClasses() As String) As Boolean
    '* * * * * * * * * * * * * * * * * * * * * * * * * * * *
    '*                                                     *
    '*  This function only checks to see if the Object has *
    '*  an assignment in the particular Class Type.        *
    '*                                                     *
    '* * * * * * * * * * * * * * * * * * * * * * * * * * * *
    Dim MyFunc, App As Object, Result As Boolean, objALLOCLISTRow As Object, objALLOCVALUERow As Object, hasChars As Boolean
    Dim strClass As String, nCurrentLine As Long
    
    'Let the user know what is happening.
    Application.StatusBar = "Extracting for " & strObject & ", " & strClassType
    
    'Define the parameters to be passed to the BAPI.
    Dim OBJECTKEY_IMP As Object
    Dim OBJECTTABLE_IMP As Object
    Dim CLASSTYPE_IMP As Object
    Dim READ_VALUATIONS As Object
    
    'Define the variables to be populated by the BAPI.
    Dim ALLOCLIST As Object
    Dim BAPIRET2 As Object  'Will hold the messages from the BAPI.
    
    'Tell the connection which BAPI to call.
    Set MyFunc = R3.Add("BAPI_OBJCL_GETCLASSES")
    
    'Prepare the parameters to be passed to the BAPI.
    Set OBJECTKEY_IMP = MyFunc.Exports("OBJECTKEY_IMP")
    Set OBJECTTABLE_IMP = MyFunc.Exports("OBJECTTABLE_IMP")
    Set CLASSTYPE_IMP = MyFunc.Exports("CLASSTYPE_IMP")
    Set READ_VALUATIONS = MyFunc.Exports("READ_VALUATIONS")
    
    'Populate the values to be passed to the BAPI.
    OBJECTKEY_IMP.Value = strObject
    OBJECTTABLE_IMP.Value = strObjectTable
    CLASSTYPE_IMP.Value = strClassType
    READ_VALUATIONS.Value = ""
    
    'Call the function.
    Result = MyFunc.call
    
    If Result Then
    'Prepare the variables to be populated by the BAPI.
        Set ALLOCLIST = MyFunc.Tables("ALLOCLIST")
        Set BAPIRET2 = MyFunc.Tables("RETURN")
        'We loop through the Char data to tease it out.
        For Each objALLOCLISTRow In ALLOCLIST.Rows
            strClasses(0, 0) = 1 + strClasses(0, 0)
            strClasses(strClasses(0, 0), 0) = objALLOCLISTRow("OBJECT")
            strClasses(strClasses(0, 0), 1) = objALLOCLISTRow("OBJTYP")
            strClasses(strClasses(0, 0), 2) = objALLOCLISTRow("CLASSNUM")
            strClasses(strClasses(0, 0), 3) = objALLOCLISTRow("CLASSTYPE")
            CallGetClassesBAPI = True
        Next
        retval = DoEvents
        ALLOCLIST.Rows.RemoveAll
        Set ALLOCLIST = Nothing
        BAPIRET2.Rows.RemoveAll
        Set BAPIRET2 = Nothing
    Else
        'Meh, do nothing.
    End If
    
    
    'Clean up memory.
    Set OBJECTKEY_IMP = Nothing
    Set OBJECTTABLE_IMP = Nothing
    Set CLASSTYPE_IMP = Nothing
    Set READ_VALUATIONS = Nothing
    Set MyFunc = Nothing
    Set objALLOCLISTRow = Nothing
    Set objALLOCVALUERow = Nothing

    R3.Remove ("BAPI_OBJCL_GETCLASSES")
    Application.StatusBar = False
End Function

Function CallClassGetDetailBAPI(ByRef R3 As Object, strObject As String, strObjectTable As String, strClass As String, strClassType As String, strOutput() As String) As Boolean
    '* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    '*                                                           *
    '*  This function teases existing Characteristic data from   *
    '*  from SAP using the BAPI_OBJCL_GETDETAIL function.        *
    '*                                                           *
    '* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    Dim MyFunc, App As Object, Result As Boolean, objALLOCLISTRow As Object, objALLOCVALUERow As Object, hasChars As Boolean
    Application.StatusBar = "Extracting " & strObject & ", " & strClass & " . . ."
    
    'Define the variables to pass parameters to the BAPI.
    Dim OBJECTKEY As Object
    Dim OBJECTTABLE As Object
    Dim CLASSNUM As Object  'The Classification
    Dim CLASSTYPE As Object
    
    'Define the variable receive returned values from BAPI
    Dim ALLOCVALUESCHAR As Object
    Dim ALLOCVALUESCURR As Object
    Dim ALLOCVALUESNUM As Object
    Dim BAPIRET2 As Object  'Will hold the messages from the BAPI.
    
    'Tell the connection which BAPI to call.
    Set MyFunc = R3.Add("BAPI_OBJCL_GETDETAIL")
    
    'Prepare the parameters to be passed to the BAPI.
    Set OBJECTKEY = MyFunc.Exports("OBJECTKEY")
    Set OBJECTTABLE = MyFunc.Exports("OBJECTTABLE")
    Set CLASSNUM = MyFunc.Exports("CLASSNUM")
    Set CLASSTYPE = MyFunc.Exports("CLASSTYPE")
    
    'Populate the values to be passed to the BAPI.
    OBJECTKEY.Value = strObject
    OBJECTTABLE = strObjectTable
    CLASSNUM = strClass
    CLASSTYPE = strClassType
    
    'Call the function.
    Result = MyFunc.call
    
    If Result Then
    'Prepare the variables to be populated by the BAPI.
        Set ALLOCVALUESCHAR = MyFunc.Tables("ALLOCVALUESCHAR")
        Set ALLOCVALUESCURR = MyFunc.Tables("ALLOCVALUESCURR")
        Set ALLOCVALUESNUM = MyFunc.Tables("ALLOCVALUESNUM")
        Set BAPIRET2 = MyFunc.Tables("RETURN")
        'We loop through the Char data to tease it out.
        'strClass = objALLOCLISTRow(4)
        For Each objALLOCVALUERow In ALLOCVALUESCHAR.Rows
            strOutput(0, 0) = 1 + strOutput(0, 0)
            strOutput(strOutput(0, 0), 0) = strObject
            strOutput(strOutput(0, 0), 1) = strObjectTable
            strOutput(strOutput(0, 0), 2) = strClass
            strOutput(strOutput(0, 0), 3) = strClassType
            strOutput(strOutput(0, 0), 4) = objALLOCVALUERow("CHARACT")
            strOutput(strOutput(0, 0), 5) = objALLOCVALUERow("VALUE_NEUTRAL")  'The key value and not description.
            CallClassGetDetailBAPI = True
        Next
        For Each objALLOCVALUERow In ALLOCVALUESCURR.Rows
            strOutput(0, 0) = 1 + strOutput(0, 0)
            strOutput(strOutput(0, 0), 0) = strObject
            strOutput(strOutput(0, 0), 1) = strObjectTable
            strOutput(strOutput(0, 0), 2) = strClass
            strOutput(strOutput(0, 0), 3) = strClassType
            strOutput(strOutput(0, 0), 4) = objALLOCVALUERow("CHARACT")
            strOutput(strOutput(0, 0), 5) = objALLOCVALUERow("VALUE_FROM")
            strOutput(strOutput(0, 0), 6) = objALLOCVALUERow("CURRENCY_FROM")
            CallClassGetDetailBAPI = True
        Next
        For Each objALLOCVALUERow In ALLOCVALUESNUM.Rows
            strOutput(0, 0) = 1 + strOutput(0, 0)
            strOutput(strOutput(0, 0), 0) = strObject
            strOutput(strOutput(0, 0), 1) = strObjectTable
            strOutput(strOutput(0, 0), 2) = strClass
            strOutput(strOutput(0, 0), 3) = strClassType
            strOutput(strOutput(0, 0), 4) = objALLOCVALUERow("CHARACT")
            strOutput(strOutput(0, 0), 5) = objALLOCVALUERow("VALUE_FROM")
            strOutput(strOutput(0, 0), 6) = objALLOCVALUERow("VALUE_TO")
            CallClassGetDetailBAPI = True
        Next
        retval = DoEvents
    End If
    
    'Clean up memory.
    Set OBJECTKEY = Nothing
    Set OBJECTTABLE = Nothing
    Set CLASSTYPE = Nothing
    Set CLASSNUM = Nothing
    ALLOCVALUESCHAR.Rows.RemoveAll
    ALLOCVALUESCURR.Rows.RemoveAll
    ALLOCVALUESNUM.Rows.RemoveAll
    Set ALLOCVALUESCHAR = Nothing
    Set ALLOCVALUESCURR = Nothing
    Set ALLOCVALUESNUM = Nothing
    BAPIRET2.Rows.RemoveAll
    Set BAPIRET2 = Nothing

    R3.Remove ("BAPI_OBJCL_GETDETAIL")
    Application.StatusBar = False

End Function

SAP SecurityThe Load feature is more of the same. It calls the CHECK_OBJECT_IN_CLASS BAPI to see if the Classification is already allocated to the Object and, if so, teases out the existing Characteristic data into memory and then knits in the new data to be loaded; this preserves values when only a subset of Characteristic values is being updated.

When no allocation exists, the tool calls the BAPI_OBJCL_CREATE BAPI. When the object already has an allocation, the code calls BAPI_OBJCL_CHANGE BAPI.

The code snippets that call the functions above are well documented in the remarks. Naturally, this tool comes with no warranty--expressed or implied--so be sure test this extensively in a sandbox before using it on a production system.

Downloads

xlsm_RFC_Classifications.xlsm