Quantcast
Channel: Scripting Languages
Viewing all 60 articles
Browse latest View live

How to use actual SAP NetWeaver RFC Library with Python - Invoke Functions

$
0
0

Hello community,

 

yesterday I presented here the first steps how to connect an SAP system via Python 3 without any additional packages. Here now the next steps, how to invoke functions. You will see how easy it is now, after the first step of connection - it is not more than a prolongation.

 

We start with an ordinary ping in two variants. The first variant explains how to invoke a RFC function, in our case RFC_PING. The second variant shows how to use the RfcPing function of the RFC library. In the focus of this example is the invocation of the RFC function call.

To invoke a RFC function you must get at first the description of the function via RfcGetFunctionDesc. The next step is to create the function with RfcCreateFunction and now you can invoke it. Last but not least you must destroy the created function via RfcDestroyFunction. Look at the example below.

 

#-Begin-----------------------------------------------------------------

 

#-Packages--------------------------------------------------------------

from ctypes import *

 

#-Structures------------------------------------------------------------

class RFC_ERROR_INFO(Structure):

  _fields_ = [("code", c_long),

              ("group", c_long),

              ("key", c_wchar * 128),

              ("message", c_wchar * 512),

              ("abapMsgClass", c_wchar * 21),

              ("abapMsgType", c_wchar * 2),

              ("abapMsgNumber", c_wchar * 4),

              ("abapMsgV1", c_wchar * 51),

              ("abapMsgV2", c_wchar * 51),

              ("abapMsgV3", c_wchar * 51),

              ("abapMsgV4", c_wchar * 51)]

 

class RFC_CONNECTION_PARAMETER(Structure):

  _fields_ = [("name", c_wchar_p),

              ("value", c_wchar_p)]

 

#-Constants-------------------------------------------------------------

RFC_OK = 0

 

#-Main------------------------------------------------------------------

ErrInf = RFC_ERROR_INFO; RfcErrInf = ErrInf()

ConnParams = RFC_CONNECTION_PARAMETER * 5; RfcConnParams = ConnParams()

 

SAPNWRFC = "sapnwrfc.dll"

SAP = windll.LoadLibrary(SAPNWRFC)

 

#-Prototypes------------------------------------------------------------

SAP.RfcOpenConnection.argtypes = [POINTER(ConnParams), c_ulong, \

  POINTER(ErrInf)]

SAP.RfcOpenConnection.restype = c_void_p

 

SAP.RfcCloseConnection.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcCloseConnection.restype = c_ulong

 

SAP.RfcGetFunctionDesc.argtypes = [c_void_p, c_wchar_p, POINTER(ErrInf)]

SAP.RfcGetFunctionDesc.restype = c_void_p

 

SAP.RfcCreateFunction.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcCreateFunction.restype = c_void_p

 

SAP.RfcInvoke.argtypes = [c_void_p, c_void_p, POINTER(ErrInf)]

SAP.RfcInvoke.restype = c_ulong

 

SAP.RfcDestroyFunction.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcDestroyFunction.restype = c_ulong

 

SAP.RfcPing.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcPing.restype = c_ulong

 

#-Connection parameters-------------------------------------------------

RfcConnParams[0].name = "ASHOST"; RfcConnParams[0].value = "ABAP"

RfcConnParams[1].name = "SYSNR" ; RfcConnParams[1].value = "00"

RfcConnParams[2].name = "CLIENT"; RfcConnParams[2].value = "001"

RfcConnParams[3].name = "USER"  ; RfcConnParams[3].value = "BCUSER"

RfcConnParams[4].name = "PASSWD"; RfcConnParams[4].value = "minisap"

 

 

hRFC = SAP.RfcOpenConnection(RfcConnParams, 5, RfcErrInf)

if hRFC != None:

 

  #-Variant 1-----------------------------------------------------------

  hFuncDesc = SAP.RfcGetFunctionDesc(hRFC, "RFC_PING", RfcErrInf)

  if hFuncDesc != 0:

    hFunc = SAP.RfcCreateFunction(hFuncDesc, RfcErrInf)

    if hFunc != 0:

      if SAP.RfcInvoke(hRFC, hFunc, RfcErrInf) == RFC_OK:

        print("Ping successful")

      else:

        print("Ping not successful")

      rc = SAP.RfcDestroyFunction(hFunc, RfcErrInf)

 

  #-Variant 2-----------------------------------------------------------

  if SAP.RfcPing(hRFC, RfcErrInf) == RFC_OK:

    print("Ping successful")

  else:

    print("Ping not successful")

 

  rc = SAP.RfcCloseConnection(hRFC, RfcErrInf)

 

else:

  print(RfcErrInf.key)

  print(RfcErrInf.message)

 

del SAP

 

#-End-------------------------------------------------------------------

 

In the next example I demonstrate how to invoke the function RFC_SYSTEM_INFO. It is the same way as I described above, until the invocation of the function. After the invocation we get the information of the function module via RfcGetStructure and RfcGetChars, in our case SYSID, HOST, DBHOST and DBSYS - that's all. Look at the example below:

 

#-Begin-----------------------------------------------------------------


#-Packages--------------------------------------------------------------

from ctypes import *


#-Structures------------------------------------------------------------

class RFC_ERROR_INFO(Structure):

  _fields_ = [("code", c_long),

              ("group", c_long),

              ("key", c_wchar * 128),

              ("message", c_wchar * 512),

              ("abapMsgClass", c_wchar * 21),

              ("abapMsgType", c_wchar * 2),

              ("abapMsgNumber", c_wchar * 4),

              ("abapMsgV1", c_wchar * 51),

              ("abapMsgV2", c_wchar * 51),

              ("abapMsgV3", c_wchar * 51),

              ("abapMsgV4", c_wchar * 51)]

 

class RFC_CONNECTION_PARAMETER(Structure):

  _fields_ = [("name", c_wchar_p),

              ("value", c_wchar_p)]

 

#-Constants-------------------------------------------------------------

RFC_OK = 0

 

#-Main------------------------------------------------------------------

ErrInf = RFC_ERROR_INFO; RfcErrInf = ErrInf()

ConnParams = RFC_CONNECTION_PARAMETER * 5; RfcConnParams = ConnParams()

 

SAPNWRFC = "sapnwrfc.dll"

SAP = windll.LoadLibrary(SAPNWRFC)

 

#-Prototypes------------------------------------------------------------

SAP.RfcOpenConnection.argtypes = [POINTER(ConnParams), c_ulong, \

  POINTER(ErrInf)]

SAP.RfcOpenConnection.restype = c_void_p

 

SAP.RfcCloseConnection.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcCloseConnection.restype = c_ulong

 

SAP.RfcGetFunctionDesc.argtypes = [c_void_p, c_wchar_p, POINTER(ErrInf)]

SAP.RfcGetFunctionDesc.restype = c_void_p

 

SAP.RfcCreateFunction.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcCreateFunction.restype = c_void_p

 

SAP.RfcInvoke.argtypes = [c_void_p, c_void_p, POINTER(ErrInf)]

SAP.RfcInvoke.restype = c_ulong

 

SAP.RfcDestroyFunction.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcDestroyFunction.restype = c_ulong

 

SAP.RfcGetStructure.argtypes = [c_void_p, c_wchar_p, \

  POINTER(c_void_p), POINTER(ErrInf)]

SAP.RfcGetStructure.restype = c_ulong

 

SAP.RfcGetChars.argtypes = [c_void_p, c_wchar_p, c_void_p, c_ulong, \

  POINTER(ErrInf)]

SAP.RfcGetChars.restype = c_ulong

 

#-Connection parameters-------------------------------------------------

RfcConnParams[0].name = "ASHOST"; RfcConnParams[0].value = "ABAP"

RfcConnParams[1].name = "SYSNR" ; RfcConnParams[1].value = "00"

RfcConnParams[2].name = "CLIENT"; RfcConnParams[2].value = "001"

RfcConnParams[3].name = "USER"  ; RfcConnParams[3].value = "BCUSER"

RfcConnParams[4].name = "PASSWD"; RfcConnParams[4].value = "minisap"

 

 

hRFC = SAP.RfcOpenConnection(RfcConnParams, 5, RfcErrInf)

if hRFC != None:


  hFuncDesc = SAP.RfcGetFunctionDesc(hRFC, "RFC_SYSTEM_INFO", RfcErrInf)

  if hFuncDesc != 0:

    hFunc = SAP.RfcCreateFunction(hFuncDesc, RfcErrInf)

    if hFunc != 0:

      if SAP.RfcInvoke(hRFC, hFunc, RfcErrInf) == RFC_OK:

        hStruct = c_void_p(0)

        if SAP.RfcGetStructure(hFunc, "RFCSI_EXPORT", hStruct, \

          RfcErrInf) == RFC_OK:

 

          SAPHost = create_unicode_buffer(8 + 1)

          rc = SAP.RfcGetChars(hStruct, "RFCHOST", SAPHost, 8, \

            RfcErrInf)

          print(SAPHost.value)

 

          SAPSysID = create_unicode_buffer(8 + 1)

          rc = SAP.RfcGetChars(hStruct, "RFCSYSID", SAPSysID, 8, \

            RfcErrInf)

          print(SAPSysID.value)

 

          SAPDBHost = create_unicode_buffer(32 + 1)

          rc = SAP.RfcGetChars(hStruct, "RFCDBHOST", SAPDBHost, 32, \

            RfcErrInf)

          print(SAPDBHost.value)

 

          SAPDBSys = create_unicode_buffer(10 + 1)

          rc = SAP.RfcGetChars(hStruct, "RFCDBSYS", SAPDBSys, 10, \

            RfcErrInf)

          print(SAPDBSys.value)

 

      rc = SAP.RfcDestroyFunction(hFunc, RfcErrInf)

 

  rc = SAP.RfcCloseConnection(hRFC, RfcErrInf)

 

else:

  print(RfcErrInf.key)

  print(RfcErrInf.message)

 

del SAP

 

#-End-------------------------------------------------------------------

 

With the COM Connector (CCo) I published a lot of examples, which can be almost identical implemented in Python. All you have to do is to define the argument and result type of the prototype of the function. As you can see it is easy to use SAP NetWeaver RFC library with Python 3. I hope this examples opens the gate wide for the future development of this kind of requirements.

 

Cheers

Stefan


How to use actual SAP NetWeaver RFC Library with Python - Execute ABAP Report

$
0
0

Hello community,

 

I presented until now in the series "How to use actual SAPNetWeaver RFC Library with Pyhton" :

 

Here now an example how to define and run ABAP code from Python.

All constants, structures and prototypes are in the quasi-include file sapnwrfc.py - you find it below.

The ABAP report itself is an array of strings. We use the function module (FM) RFC_ABAP_INSTALL_AND_RUN to implement and execute the ABAP program. We set the report line by line in the PROGRAM table of the FM, invoke the function and get a result back in the WRITES table. We read each line of ZEILE and add it to a Result string.

 

# -*- coding: iso-8859-15 -*-
#-Begin-----------------------------------------------------------------

 

 

#-Include---------------------------------------------------------------
FileName = "sapnwrfc.py"
exec(compile(open(FileName).read(), FileName, "exec"))

 

#-Main------------------------------------------------------------------

 

#-Connection parameters-------------------------------------------------
RfcConnParams[0].name = "ASHOST"; RfcConnParams[0].value = "NSP"
RfcConnParams[1].name = "SYSNR" ; RfcConnParams[1].value = "00"
RfcConnParams[2].name = "CLIENT"; RfcConnParams[2].value = "001"
RfcConnParams[3].name = "USER"  ; RfcConnParams[3].value = "BCUSER"
RfcConnParams[4].name = "PASSWD"; RfcConnParams[4].value = "minisap"

 

#-ABAPReport------------------------------------------------------------
#-
#- Code your ABAP report here. The length of each line must be equal or
#- less than 72 characters.
#-
#-----------------------------------------------------------------------
ABAP=[]
ABAP.append("Report zTest Line-Size 256.")
ABAP.append("Write: 'Hello World from'.")
ABAP.append("Write: sy-sysid.")

 


hRFC = SAP.RfcOpenConnection(RfcConnParams, 5, RfcErrInf)
if hRFC != None:

 

  charBuffer = create_unicode_buffer(256 + 1)
  Result = ""

 

  hFuncDesc = SAP.RfcGetFunctionDesc(hRFC, "RFC_ABAP_INSTALL_AND_RUN", \
    RfcErrInf)

  if hFuncDesc != 0:
    hFunc = SAP.RfcCreateFunction(hFuncDesc, RfcErrInf)
    if hFunc != 0:

      hTable = c_void_p(0)

 

      #-Writes the report into the PROGRAM table------------------------
      if SAP.RfcGetTable(hFunc, "PROGRAM", hTable, RfcErrInf) == RFC_OK:
        for i in range(0, len(ABAP)):
          hRow = SAP.RfcAppendNewRow(hTable, RfcErrInf)
          rc = SAP.RfcSetChars(hRow, "LINE", ABAP[i], len(ABAP[i]), \
            RfcErrInf)

 

        if SAP.RfcInvoke(hRFC, hFunc, RfcErrInf) == RFC_OK:
          #-Gets the result from the WRITES table-----------------------
          if SAP.RfcGetTable(hFunc, "WRITES", hTable, RfcErrInf) == RFC_OK:
            RowCount = c_ulong(0)
            rc = SAP.RfcGetRowCount(hTable, RowCount, RfcErrInf)
            rc = SAP.RfcMoveToFirstRow(hTable, RfcErrInf)
            for i in range(0, RowCount.value):
              hRow = SAP.RfcGetCurrentRow(hTable, RfcErrInf)
              rc = SAP.RfcGetChars(hRow, "ZEILE", charBuffer, 256, \
                RfcErrInf)
              Result = Result + charBuffer.value
              if i < RowCount.value:
                rc = SAP.RfcMoveToNextRow(hTable, RfcErrInf)

 

            #-Shows the result------------------------------------------
            print(Result)

 

      rc = SAP.RfcDestroyFunction(hFunc, RfcErrInf)

 

  rc = SAP.RfcCloseConnection(hRFC, RfcErrInf)

 

else:
  print(RfcErrInf.key)
  print(RfcErrInf.message)

 

del SAP

 

#-End-------------------------------------------------------------------

 

As you can see, it is no problem to define and execute an ABAP program inside Python.

 

Enjoy it.

 

Cheers

Stefan

 

 

P.S. Here the file sapnwrfc.py - at the end of the post.

How to use actual SAP NetWeaver RFC Library with Python - Read Table

$
0
0

Hello community,

 

I presented until now in the series "How to use actual SAPNetWeaver RFC Library with Pyhton" :

 

Here now an example how to read a table with the function module (FM) RFC_READ_TABLE. You need the file sapnwrfc.py from here - look at the end of the posting.

After the connection we get the function description of the FM, in our case RFC_READ_TABLE. We set the arguments QUERY_TABLE, in our case USR01, and the DELIMITER. We invoke the FM and print the result line by line. The result is in the DATA table, in the field WA.

 

# -*- coding: iso-8859-15 -*-

#-Begin-----------------------------------------------------------------

 

#-Include---------------------------------------------------------------

FileName = "sapnwrfc.py"

exec(compile(open(FileName).read(), FileName, "exec"))

 

#-Main------------------------------------------------------------------

 

#-Connection parameters-------------------------------------------------

RfcConnParams[0].name = "ASHOST"; RfcConnParams[0].value = "ABAP"

RfcConnParams[1].name = "SYSNR" ; RfcConnParams[1].value = "00"

RfcConnParams[2].name = "CLIENT"; RfcConnParams[2].value = "001"

RfcConnParams[3].name = "USER"  ; RfcConnParams[3].value = "BCUSER"

RfcConnParams[4].name = "PASSWD"; RfcConnParams[4].value = "minisap"

 

TableName = "USR01"

 

hRFC = SAP.RfcOpenConnection(RfcConnParams, 5, RfcErrInf)

if hRFC != None:

 

  charBuffer = create_unicode_buffer(512 + 1)

 

  hFuncDesc = SAP.RfcGetFunctionDesc(hRFC, "RFC_READ_TABLE", RfcErrInf)

  if hFuncDesc != 0:

    hFunc = SAP.RfcCreateFunction(hFuncDesc, RfcErrInf)

    if hFunc != 0:

 

      rc = SAP.RfcSetChars(hFunc, "QUERY_TABLE", TableName, \

        len(TableName), RfcErrInf)

      rc = SAP.RfcSetChars(hFunc, "DELIMITER", "~", 1, RfcErrInf)

 

      if SAP.RfcInvoke(hRFC, hFunc, RfcErrInf) == RFC_OK:

 

        hTable = c_void_p(0)

        if SAP.RfcGetTable(hFunc, "DATA", hTable, RfcErrInf) == RFC_OK:

 

          RowCount = c_ulong(0)

          rc = SAP.RfcGetRowCount(hTable, RowCount, RfcErrInf)

          rc = SAP.RfcMoveToFirstRow(hTable, RfcErrInf)

          for i in range(0, RowCount.value):

            hRow = SAP.RfcGetCurrentRow(hTable, RfcErrInf)

            rc = SAP.RfcGetChars(hRow, "WA", charBuffer, 512, RfcErrInf)

            print(charBuffer.value)

            if i < RowCount.value:

              rc = SAP.RfcMoveToNextRow(hTable, RfcErrInf)

 

      rc = SAP.RfcDestroyFunction(hFunc, RfcErrInf)

 

  rc = SAP.RfcCloseConnection(hRFC, RfcErrInf)

 

else:

  print(RfcErrInf.key)

  print(RfcErrInf.message)

 

del SAP

 

#-End-------------------------------------------------------------------

 

Cheers

Stefan

How to use actual SAP NetWeaver RFC Library with Python - Python Server Application

$
0
0

Hello community,

 

I presented until now in the series "How to use actual SAPNetWeaver RFC Library with Pyhton" :

 

Here now an example how to use Python as server application from ABAP.

 

We start with the transaction code SM59 to configure the RFC destination of the Python server application.

001.JPG

Here the Python source:

 

# -*- coding: iso-8859-15 -*-

#-Begin-----------------------------------------------------------------

 

#-Packages--------------------------------------------------------------

from threading import Thread

import time

 

#-Include---------------------------------------------------------------

FileName = "sapnwrfc.py"

exec(compile(open(FileName).read(), FileName, "exec"))

 

#-Asynchronous function-------------------------------------------------

def SetQuit():

  global Quit

  windll.user32.MessageBoxW(None, "Stop Python server", "", 0)

  Quit = True

 

#-ABAP CallBack---------------------------------------------------------

def ABAPCall(rfcHandle, funcHandle, RfcErrInf):

  windll.user32.MessageBoxW(None, "ABAP calling", "", 0)

  return RFC_OK

 

#-Main------------------------------------------------------------------

Quit = False

t = Thread(target=SetQuit); t.start()

 

#-Connection parameters-------------------------------------------------

RfcSConParams[0].name = "PROGRAM_ID"; RfcSConParams[0].value = "PYTHONSERVER"

RfcSConParams[1].name = "GWHOST"    ; RfcSConParams[1].value = "ABAP"

RfcSConParams[2].name = "GWSERV"    ; RfcSConParams[2].value = "sapgw00"

 

ABAPFunc = WINFUNCTYPE(c_ulong, c_void_p, c_void_p, POINTER(ErrInf))

hDesc = SAP.RfcCreateFunctionDesc("ABAPCall", RfcErrInf)

if hDesc != None:

  if SAP.RfcInstallServerFunction("NSP", hDesc, ABAPFunc(ABAPCall), \

    RfcErrInf) == RFC_OK:

 

    hCon = SAP.RfcRegisterServer(RfcSConParams, 3, RfcErrInf)

    if hCon != None:

      rc = RFC_OK

      while (rc == RFC_OK, rc == RFC_RETRY):

        rc = SAP.RfcListenAndDispatch(hCon, 1, RfcErrInf)

        time.sleep(0.250)

        if Quit == True:

          break

    else:

      print(RfcErrInf.key); print(RfcErrInf.message)

 

  else:

    print(RfcErrInf.key); print(RfcErrInf.message)

 

  rc = SAP.RfcDestroyFunctionDesc(hDesc, RfcErrInf)

 

else:

  print(RfcErrInf.key); print(RfcErrInf.message)

 

del SAP

 

#-End-------------------------------------------------------------------

 

We define at first an asynchonous thread, to set a global variable to break the execution of the server, when we no longer need it. Then we define the callback function, which can be called from ABAP - in our case only a simple message box. We create an empty function description with RfcCreateFunctionDesc and install, with this description, the callback function on the SAP system with RfcInstallServerFunction. Now we register the server connection at an SAP gateway via RFCRegisterServer and listen for incoming RFC calls with RfcListenAndDispatch. Very simple, isn't it?

 

Now we can use the Python function ABAPCall from ABAP like this:

 

"-Begin-----------------------------------------------------------------

  Program ZTEST.


    Call Function 'ABAPCall' Destination 'PYTHONSERVER'.

 

"-End-------------------------------------------------------------------

 

And this is the result:

002.JPG

As you can see, it is very easy to use Python as SAP server application.

 

Cheers

Stefan

 

P.S. Here  the updated include file sapnwrfc.py:

 

#-Begin-----------------------------------------------------------------

 

#-Packages--------------------------------------------------------------

from ctypes import *

import platform, os

 

#-Structures------------------------------------------------------------

class RFC_ERROR_INFO(Structure):

    _fields_ = [("code", c_long),

                ("group", c_long),

                ("key", c_wchar * 128),

                ("message", c_wchar * 512),

                ("abapMsgClass", c_wchar * 21),

                ("abapMsgType", c_wchar * 2),

                ("abapMsgNumber", c_wchar * 4),

                ("abapMsgV1", c_wchar * 51),

                ("abapMsgV2", c_wchar * 51),

                ("abapMsgV3", c_wchar * 51),

                ("abapMsgV4", c_wchar * 51)]

 

class RFC_CONNECTION_PARAMETER(Structure):

    _fields_ = [("name", c_wchar_p),

                ("value", c_wchar_p)]

 

 

#-Constants-------------------------------------------------------------

 

#-RFC_RC - RFC return codes---------------------------------------------

RFC_OK = 0

RFC_COMMUNICATION_FAILURE = 1

RFC_LOGON_FAILURE = 2

RFC_ABAP_RUNTIME_FAILURE = 3

RFC_ABAP_MESSAGE = 4

RFC_ABAP_EXCEPTION = 5

RFC_CLOSED = 6

RFC_CANCELED = 7

RFC_TIMEOUT = 8

RFC_MEMORY_INSUFFICIENT = 9

RFC_VERSION_MISMATCH = 10

RFC_INVALID_PROTOCOL = 11

RFC_SERIALIZATION_FAILURE = 12

RFC_INVALID_HANDLE = 13

RFC_RETRY = 14

RFC_EXTERNAL_FAILURE = 15

RFC_EXECUTED = 16

RFC_NOT_FOUND = 17

RFC_NOT_SUPPORTED = 18

RFC_ILLEGAL_STATE = 19

RFC_INVALID_PARAMETER = 20

RFC_CODEPAGE_CONVERSION_FAILURE = 21

RFC_CONVERSION_FAILURE = 22

RFC_BUFFER_TOO_SMALL = 23

RFC_TABLE_MOVE_BOF = 24

RFC_TABLE_MOVE_EOF = 25

RFC_START_SAPGUI_FAILURE = 26

RFC_ABAP_CLASS_EXCEPTION = 27

RFC_UNKNOWN_ERROR = 28

RFC_AUTHORIZATION_FAILURE = 29

 

#-RFCTYPE - RFC data types----------------------------------------------

RFCTYPE_CHAR = 0

RFCTYPE_DATE = 1

RFCTYPE_BCD = 2

RFCTYPE_TIME = 3

RFCTYPE_BYTE = 4

RFCTYPE_TABLE = 5

RFCTYPE_NUM = 6

RFCTYPE_FLOAT = 7

RFCTYPE_INT = 8

RFCTYPE_INT2 = 9

RFCTYPE_INT1 = 10

RFCTYPE_NULL = 14

RFCTYPE_ABAPOBJECT = 16

RFCTYPE_STRUCTURE = 17

RFCTYPE_DECF16 = 23

RFCTYPE_DECF34 = 24

RFCTYPE_XMLDATA = 28

RFCTYPE_STRING = 29

RFCTYPE_XSTRING = 30

RFCTYPE_BOX = 31

RFCTYPE_GENERIC_BOX = 32

 

#-RFC_UNIT_STATE - Processing status of a background unit---------------

RFC_UNIT_NOT_FOUND = 0

RFC_UNIT_IN_PROCESS = 1

RFC_UNIT_COMMITTED = 2

RFC_UNIT_ROLLED_BACK = 3

RFC_UNIT_CONFIRMED = 4

 

#-RFC_CALL_TYPE - Type of an incoming function call---------------------

RFC_SYNCHRONOUS = 0

RFC_TRANSACTIONAL = 1

RFC_QUEUED = 2

RFC_BACKGROUND_UNIT = 3

 

#-RFC_DIRECTION - Direction of a function module parameter--------------

RFC_IMPORT = 1

RFC_EXPORT = 2

RFC_CHANGING = RFC_IMPORT + RFC_EXPORT

RFC_TABLES = 4 + RFC_CHANGING

 

#-RFC_CLASS_ATTRIBUTE_TYPE - Type of an ABAP object attribute-----------

RFC_CLASS_ATTRIBUTE_INSTANCE = 0

RFC_CLASS_ATTRIBUTE_CLASS = 1

RFC_CLASS_ATTRIBUTE_CONSTANT = 2

 

#-RFC_METADATA_OBJ_TYPE - Ingroup repository----------------------------

RFC_METADATA_FUNCTION = 0

RFC_METADATA_TYPE = 1

RFC_METADATA_CLASS = 2

 

 

#-Variables-------------------------------------------------------------

ErrInf = RFC_ERROR_INFO; RfcErrInf = ErrInf()

ConnParams = RFC_CONNECTION_PARAMETER * 5; RfcConnParams = ConnParams()

SConParams = RFC_CONNECTION_PARAMETER * 3; RfcSConParams = SConParams()

 

 

#-Library---------------------------------------------------------------

if str(platform.architecture()[0]) == "32bit":

  os.environ['PATH'] += ";C:\\SAPRFCSDK\\32bit"

  SAPNWRFC = "C:\\SAPRFCSDK\\32bit\\sapnwrfc.dll"

elif str(platform.architecture()[0]) == "64bit":

  os.environ['PATH'] += ";C:\\SAPRFCSDK\\64bit"

  SAPNWRFC = "C:\\SAPRFCSDK\\64bit\\sapnwrfc.dll"

SAP = windll.LoadLibrary(SAPNWRFC)

 

 

#-Prototypes------------------------------------------------------------

SAP.RfcAppendNewRow.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcAppendNewRow.restype = c_void_p

 

SAP.RfcCloseConnection.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcCloseConnection.restype = c_ulong

 

SAP.RfcCreateFunction.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcCreateFunction.restype = c_void_p

 

SAP.RfcCreateFunctionDesc.argtypes = [c_wchar_p, POINTER(ErrInf)]

SAP.RfcCreateFunctionDesc.restype = c_void_p

 

SAP.RfcDestroyFunction.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcDestroyFunction.restype = c_ulong

 

SAP.RfcDestroyFunctionDesc.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcDestroyFunctionDesc.restype = c_ulong

 

SAP.RfcGetChars.argtypes = [c_void_p, c_wchar_p, c_void_p, c_ulong, \

  POINTER(ErrInf)]

SAP.RfcGetChars.restype = c_ulong

 

SAP.RfcGetCurrentRow.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcGetCurrentRow.restype = c_void_p

 

SAP.RfcGetFunctionDesc.argtypes = [c_void_p, c_wchar_p, POINTER(ErrInf)]

SAP.RfcGetFunctionDesc.restype = c_void_p

 

SAP.RfcGetRowCount.argtypes = [c_void_p, POINTER(c_ulong), \

  POINTER(ErrInf)]

SAP.RfcGetRowCount.restype = c_ulong

 

SAP.RfcGetStructure.argtypes = [c_void_p, c_wchar_p, \

  POINTER(c_void_p), POINTER(ErrInf)]

SAP.RfcGetStructure.restype = c_ulong

 

SAP.RfcGetTable.argtypes = [c_void_p, c_wchar_p, POINTER(c_void_p), \

  POINTER(ErrInf)]

SAP.RfcGetTable.restype = c_ulong

 

SAP.RfcGetVersion.argtypes = [POINTER(c_ulong), POINTER(c_ulong), \

  POINTER(c_ulong)]

SAP.RfcGetVersion.restype = c_wchar_p

 

SAP.RfcInstallServerFunction.argtypes = [c_wchar_p, c_void_p, \

  c_void_p, POINTER(ErrInf)]

SAP.RfcInstallServerFunction.restype = c_ulong

 

SAP.RfcInvoke.argtypes = [c_void_p, c_void_p, POINTER(ErrInf)]

SAP.RfcInvoke.restype = c_ulong

 

SAP.RfcListenAndDispatch.argtypes = [c_void_p, c_ulong, POINTER(ErrInf)]

SAP.RfcListenAndDispatch.restype = c_ulong

 

SAP.RfcMoveToFirstRow.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcMoveToFirstRow.restype = c_ulong

 

SAP.RfcMoveToNextRow.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcMoveToNextRow.restype = c_ulong

 

SAP.RfcOpenConnection.argtypes = [POINTER(ConnParams), c_ulong, \

  POINTER(ErrInf)]

SAP.RfcOpenConnection.restype = c_void_p

 

SAP.RfcPing.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcPing.restype = c_ulong

 

SAP.RfcRegisterServer.argtypes = [POINTER(SConParams), c_ulong, \

  POINTER(ErrInf)]

SAP.RfcRegisterServer.restype = c_void_p

 

SAP.RfcSetChars.argtypes = [c_void_p, c_wchar_p, c_wchar_p, c_ulong, \

  POINTER(ErrInf)]

SAP.RfcSetChars.restype = c_ulong

 

#-End-------------------------------------------------------------------

Python for Basis (Part 2): Scheduling SUIM Jobs using Python

$
0
0

In this post I will document how to schedule batch jobs using python and how to handle the output. You may want to check out my first blog post as an introduction on how to access SAP through Python: Python for Basis (Part 1) .

 

Description of the Scenario

In the following section we want to perform a security assessment by scheduling report RSUSR002 to find out the following:

 

  • Which users have SAP_ALL?
  • Which users are able to execute any function module thanks to S_DEVELOP?

 

That means, we need to accomplish the following tasks:

 

  • Logon to the SAP client we want to evaluate
  • Create a variant for report RSUSR002
  • Schedule report RSUSR002 in the background with immediate start
  • Wait for completion of the job
  • Download the generated spool
  • Extract the users from the downloaded spool
  • Generate an Excel file with the extracted information

 

Extending the PyRFC Connection Class

The easiest way to accomplish our task is to create a subclass of the PyRFC Connection class and add our own functions to it.

 

import pyrfc

class sap_abapclient(pyrfc.Connection):

    def __init__(self, *args, **kwargs ):

        pyrfc.Connection.__init__(self, *args, **kwargs )

        self.condetails=self.get_connection_attributes()

 

Instead of using the standard pyrfc.Connection class to connect to an SAP system we will use our own class. The following sections will document the different functions required to complete the task.

 

Check whether RSUSR002 variant already exists

Before we schedule a RSUSR002 job we need to create or modify a corresponding variant. Since we need to use different function modules for maintenance and creation, we need to check first, whether a variant exists. The following function will take care of this:

 

    def check_if_variant_exists(self, program, variant):

        result = self.call('RFC_READ_TABLE',

                         QUERY_TABLE='VARID', \

                         DELIMITER='|', \

                         FIELDS=[{'FIELDNAME':'VARIANT'}], \

                         OPTIONS = [{'TEXT':"REPORT EQ '" + program.upper()+"'"}])

 

        tabledump=result['DATA']

        variant_exists=False

        for record in tabledump:

            if record['WA']==variant.upper():

                variant_exists=True

        return(variant_exists)

 

The function performs the following:

 

  • Execute function module RFC_READ_TABLE to download the existing variant names of report RSUSR002
  • Initialize the boolean variable variant_exists
  • loop over all records and find out if the variant name we picked already exists.
  • If the variant exists, variant_exists will be set to True
  • At the end the function returns the content of the variant_exists.

 

Create/Maintain RSUSR002 variant

To create a variant we use RS_CREATE_VARIANT_RFC, to modify an existing variant we use RS_CHANGE_CREATED_VARIANT_RFC. The corresponding python function starts with an underscore ('_') which by convention determines that a function should not be called directly. It would be a private method but python doesn't prevent calling these functions directly.

 

If you look at the definition of the function modules then you see several different import parameters. We need the following:

 

  • VARI_DESC: Defines the variant name and will contain the change date and timestamp
  • VARI_CONTENTS: contains the actual variant values
  • VARI_TEXT: Contains the description in the variant attributes

 

VARI_CONTENTS is defined as a table with the following fields that are part of the variant values table when looking at the variant through transaction SE38:

  • SELNAME: Selection Screen
  • KIND: Type
  • OPTION: Option
  • LOW: frm
  • HIGH: to
  • SIGN: I/E

 

The easiest way to get an idea of the variants is to define it in the report itself and then look at the actual values using the variant editor in transaction SE38. Like with all tables, the variant has to be defined as a list of dictionaries. This is the python function to create a create or maintain a variant for RSUSR002:

 

    def _create_suim_variant(self, variant):

        """

        Creates a temporary variant to schedule the report

        :param variant: list of dictionaries that comprise the variant content.

 

        """

        import datetime

        report = u'RSUSR002'

        variantname = u'TEMPVARI'

        vari_text = list()

        vari_list = list()

        vari_contents = list()

        vscreens = list()

 

        temp = dict(MANDT = self.condetails['client'],

                     LANGU=u'EN',

                     REPORT=report,

                     VARIANT=variantname,

                     VTEXT=u'temporary')

 

        vari_text.append(temp)

 

        vari_desc = dict(REPORT = report,

                         VARIANT=  variantname,

                         AEDAT = datetime.date(2013,12,12),

                         AETIME = datetime.time(00,00))

 

        vari_contents.extend(variant)

 

        if self.check_if_variant_exists(report, variantname)==True:

            test = self.call('RS_CHANGE_CREATED_VARIANT_RFC', CURR_REPORT= report,

                                                              CURR_VARIANT= variantname,

                                                              VARI_DESC = vari_desc,

                                                              VARI_CONTENTS = vari_contents,

                                                              VARI_TEXT = vari_text)

        else:

            test = self.call('RS_CREATE_VARIANT_RFC', CURR_REPORT= report,

                                                      CURR_VARIANT= variantname,

                                                      VARI_DESC = vari_desc,

                                                      VARI_CONTENTS = vari_contents,

                                                      VARI_TEXT = vari_text)

 

Schedule report RSUSR002 as Batch Job

To schedule the report in background we will to use the XBP interface (Background Processing, Job Scheduling (BC-XBP). we are going to run the following function modules:

 

  • BAPI_XMI_LOGON: Before we schedule a job we need to register with the instance
  • BAPI_XBP_JOB_OPEN: Initiate the creation of a new job definition
  • BAPI_XBP_JOB_ADD_ABAP_STEP: Define the job step to execute
  • BAPI_XBP_JOB_CLOSE: complete the job creation
  • BAPI_XBP_JOB_START_IMMEDIATELY: Change the job definition for immediate start.
  • BAPI_XMI_LOGOFF: log off from the instance

 

Here is the code:

 

    def schedule_job_immediately(self, jobname, program, variant='none', wait='yes'):

        import datetime

 

        if variant<>'none':

            if self.check_if_variant_exists(program, variant)==False:

                print('Variant Does not Exist')

                exit(3)

 

        result = self.call ('BAPI_XMI_LOGON', EXTCOMPANY='LARS',

                                              EXTPRODUCT='assessment',

                                              INTERFACE='XBP',

                                              VERSION='2.0')

 

        result = self.call('BAPI_XBP_JOB_OPEN', JOBNAME=jobname,

                                                EXTERNAL_USER_NAME='AUDIT')


        jobcount=result['JOBCOUNT']

 

        if variant<>'none':

            result = self.call('BAPI_XBP_JOB_ADD_ABAP_STEP', JOBNAME=jobname,

                                                             JOBCOUNT=jobcount,

                                                             EXTERNAL_USER_NAME='AUDIT',

                                                             ABAP_PROGRAM_NAME=program,

                                                             ABAP_VARIANT_NAME=variant)

        else:

            result = self.call('BAPI_XBP_JOB_ADD_ABAP_STEP', JOBNAME=jobname,

                                                             JOBCOUNT=jobcount,

                                                             EXTERNAL_USER_NAME='AUDIT',

                                                             ABAP_PROGRAM_NAME=program)

 

        result = self.call('BAPI_XBP_JOB_CLOSE', JOBNAME=jobname,

                                                 JOBCOUNT=jobcount,

                                                 EXTERNAL_USER_NAME='AUDIT')

 

        result = self.call('BAPI_XBP_JOB_START_IMMEDIATELY', JOBNAME=jobname,

                                                             JOBCOUNT=jobcount,

                                                             EXTERNAL_USER_NAME='AUDIT')

 

        if wait=='yes':

            jobstatus=self._wait_until_job_completed(jobname,jobcount)

 

        result=dict(JOBCOUNT  = jobcount,

                    JOBSTATUS = jobstatus)

        return(result)

 

 

Wait until Job Completes

The job was started immediately, but before we can download the spool, we need to make sure that the job is really complete. We will use function module SUBST_CHECK_BATCHJOB for this:

 

    def _wait_until_job_completed(self, jobname, jobcount):

        """

        Checks whether a job is still running and waits until it completes.

        :param jobname:

        :param jobcount:

        """

        jobstatus = 'X'

        while jobstatus not in ['F','A']:

            status=self.call('SUBST_CHECK_BATCHJOB',JOBNAME=jobname, JOBCOUNT=jobcount)

            jobstatus=status['JOBSTATUS']

            print ('job running')

            time.sleep(3)

        return(jobstatus)

 

Determine the Spool-ID

The batch job will generate a spool request that is identified by the spool ID. To determine the spool ID we need to check table TBTCP:

 

 

    def determine_spool_id(self, jobname, jobcount):

        """

        find the spool ID based on the job name and job count.

        :param jobname: name of the SM37 job

        :param jobcount: Job Count

        :return:

        """


        where_clause = "JOBNAME EQ '" +jobname+"' AND JOBCOUNT EQ '" + jobcount + "' AND STEPCOUNT EQ '1'"

 

        result = self.call('RFC_READ_TABLE', \

                           QUERY_TABLE='TBTCP', \

                           DELIMITER='|', \

                           FIELDS=[{'FIELDNAME':'LISTIDENT'}], \

                           OPTIONS = [{'TEXT':where_clause}])

 

        tabledump=result['DATA']

        spoolid = tabledump[0]['WA']

        return spoolid

 

Download Spool

After finding out the spool ID we can download the spool into a variable for further analysis:

 

    def download_spool(self, spoolid):

        """

        Download Spool File

        :param spoolid: spool ID of the job

        """

        result = self.call ('BAPI_XMI_LOGON', EXTCOMPANY='linkies',

                                              EXTPRODUCT='assessment',

                                              INTERFACE='XBP',

                                              VERSION='2.0')

        result = self.call('BAPI_XBP_JOB_READ_SINGLE_SPOOL',SPOOL_REQUEST=spoolid,

                                                            EXTERNAL_USER_NAME='AUDIT'

                                                            )

        return(result['SPOOL_LIST_PLAIN'])

 

Extract Users From Spool

The spool file will be a large text stored in a variable. We need to parse the spool file to extract the user information:

 

    def _extract_users_from_spool(self, spoolfile):

        """

        Extracts the users from the spool file generated by SUIM

        :param spoolfile:

        :return: returns a list of dictionaries with the user records.

        """

        import re

        users = list()

        issues = list()

        userrecord = dict()

        for line in spoolfile:

            flatline = line['LINE']

            if flatline<>'':

                if flatline[0]=='|' and flatline[0:7] == '|  User':  

                    linearray=self.locations_of_substring(flatline,'|')

                if flatline[0]=='|' and flatline[0:9] <>'|  User  ' \

                                    and flatline[0:9] <>'|--------' \

                                    and flatline[0:9] <>'|  User n':

                    userrecord=dict(username     = flatline[1:linearray[1]].strip(),

                                    completename = flatline[linearray[1]+1:linearray[2]].strip(),

                                    usergroup    = flatline[linearray[2]+1:linearray[3]].strip(),

                                    accountnumber= flatline[linearray[3]+1:linearray[4]].strip(),

                                    locked       = flatline[linearray[4]+1:linearray[5]].strip(),

                                    reason       = flatline[linearray[5]+1:linearray[6]].strip(),

                                    validfrom    = flatline[linearray[6]+1:linearray[7]].strip(),

                                    validto      = flatline[linearray[7]+1:linearray[8]].strip(),

                                    usertype     = flatline[linearray[8]+1:linearray[9]].strip(),

                                    refuser      = flatline[linearray[9]+1:linearray[10]].strip())

                    if len(linearray)==12:

                        # there is a new policy column with 7.31. That needs to be included

                        userrecord['policy'] = flatline[linearray[10]+1:linearray[11]].strip()

                    if userrecord['reason'] <>'':

                        userrecord['locked']='X'

                    users.append(userrecord)

        return(users)

 

The function uses another function to determine the number of '|' in the first line that contains the headers. We need to add that function to our class, too:

 

    def locations_of_substring(self, string, substring):

        """Return a list of locations of a substring.

        :param substring:

        """

        substring_length = len(substring)

        def recurse(locations_found, start):

            location = string.find(substring, start)

            if location != -1:

                return recurse(locations_found + [location], location+substring_length)

            else:

                return locations_found

        return recurse([], 0)

 

Generate the Excel file with the users

To generate the excel file I will use a different python module that needs to be installed called xlsxwriter (XlsxWriter 0.5.7 : Python Package Index).

 

    def generate_xlsx_file(self, data, filename):

        """

        Generate a XLSX file using returned data

        :param data: data

        aram filename: xlsx filename

        """

 

        import xlsxwriter

        workbook = xlsxwriter.Workbook(filename)

        worksheet = workbook.add_worksheet()

        worksheet.freeze_panes(1,0)

 

        xls_row=0

        xls_column=0

 

        bold = workbook.add_format({'bold': True})

 

        for record in data:

            if len(record.keys())==10:

                if xls_row==0:

                    for i in range(0,9):

                        worksheet.write(xls_row,i,self.columns_10[i],bold)

                        worksheet.set_column(0,0,20)

                        worksheet.set_column(1,1,50)

                    xls_row=1

                else:

                    for i in range(0,9):

                        if i == 0: worksheet.write(xls_row,i,  data['username'])

                        elif i == 1: worksheet.write(xls_row,i,data['completename'])

                        elif i == 2: worksheet.write(xls_row,i,data['usergroup'])

                        elif i == 3: worksheet.write(xls_row,i,data['accountnumber'])

                        elif i == 4: worksheet.write(xls_row,i,data['locked'])

                        elif i == 5: worksheet.write(xls_row,i,data['reason'])

                        elif i == 6: worksheet.write(xls_row,i,data['validfrom'])

                        elif i == 7: worksheet.write(xls_row,i,data['validto'])

                        elif i == 8: worksheet.write(xls_row,i,data['usertype'])

                        elif i == 9: worksheet.write(xls_row,i,data['refuser'])

                    xls_row=xls_row+1

 

            elif len(record.keys())==11:

                if xls_row==0:

                    for i in range(0,10):

                        worksheet.write(0,i,self.columns_11[i],bold)

                        worksheet.set_column(0,0,20)

                        worksheet.set_column(1,1,50)

                    xls_row=1

                else:

                    for i in range(0,10):

                        if   i == 0: worksheet.write(xls_row, i,record['username'])

                        elif i == 1: worksheet.write(xls_row, i,record['completename'])

                        elif i == 2: worksheet.write(xls_row, i,record['usergroup'])

                        elif i == 3: worksheet.write(xls_row, i,record['accountnumber'])

                        elif i == 4: worksheet.write(xls_row, i,record['locked'])

                        elif i == 5: worksheet.write(xls_row, i,record['reason'])

                        elif i == 6: worksheet.write(xls_row, i,record['validfrom'])

                        elif i == 7: worksheet.write(xls_row, i,record['validto'])

                        elif i == 8: worksheet.write(xls_row, i,record['usertype'])

                        elif i == 9: worksheet.write(xls_row, i,record['refuser'])

                        elif i == 10: worksheet.write(xls_row,i,record['policy'])

                    xls_row=xls_row+1

        workbook.close()

 

Combining all functions

The last function will perform all of the previous functions to make the scheduling easier.

 

    def run_suim_job(self,variant, filename):

        """

        run a SUIM report using a single variant

        :param variant: dictionary that contains the variant

        :return:

        """

        self._create_suim_variant(variant)

        result=self.schedule_job_immediately('RSUSR002','RSUSR002','TEMPVARI')

        spoolid=self.determine_spool_id('RSUSR002',result['JOBCOUNT'])

        spoolfile = self.download_spool(int(spoolid))

        users = self._extract_users_from_spool(spoolfile)

        self.generate_xlsx_file(self, users, filename)

 

Putting it all together

 

The entire class would look like this:

 

 

import pyrfc, xlsxwriter

class sap_abapclient(pyrfc.Connection):

    def __init__(self, *args, **kwargs ):

        pyrfc.Connection.__init__(self, *args, **kwargs )

        self.condetails=self.get_connection_attributes()



    def run_suim_job(self,variant, filename):

         ...


    def def generate_xlsx_file(self, data, filename):

         ...


    def locations_of_substring(self, string, substring):

         ...

 

    def _extract_users_from_spool(self, spoolfile):

         ...


    def download_spool(self, spoolid):

         ...


    def schedule_job_immediately(self, jobname, program, variant='none', wait='yes'):

          ...

 

    def check_if_variant_exists(self, program, variant):

          ...


    def _create_suim_variant(self, variant):

         ...


    def _wait_until_job_completed(self, jobname, jobcount):

         ...


    def determine_spool_id(self, jobname, jobcount):

         ...


    def download_spool(self, spoolid):

         ...


Executing the Script

To test the script we need to add some lines to the end of the file to make it actually do something. Otherwise the file would only be a text file that contains a definition of a python class but nothing else. The following construct will execute the script.

 

  • the IF condition will execute the corresponding code underneath
  • several variables will be initialized
  • a connection to the SAP system is initiated using our own subclass that inherited everything from the pyrfc.Connection class.
  • our own functions to schedule RSUSR002 will be executed.

 

If everything works well, we will have two Excel files in our current work directory. The example does not contain any error handling though.

 

if __name__ == '__main__':

     sap_all_variant=[]

        # the next lines contain the data for the variant as stored in parameters.

        tempdict=dict(SELNAME = "PROF1", KIND = "S", OPTION = "EQ", LOW = "SAP_ALL", HIGH = "", SIGN = "I")

        self.variantdict.append(tempdict)


    s_develop_variant=[]

        tempdict=dict(SELNAME = "OBJ1",   KIND = "P",  OPTION = "", LOW = "S_DEVELOP",  HIGH = "", SIGN = "")

        self.variantdict.append(tempdict)

        tempdict=dict(SELNAME = "VAL111", KIND = "P",  OPTION = "", LOW = 'PROG',    HIGH = "", SIGN = "")

        self.variantdict.append(tempdict)

        tempdict=dict(SELNAME = "VAL141", KIND = "P",  OPTION = "", LOW = '03',      HIGH = "", SIGN = "")

        self.variantdict.append(tempdict)


    logon_details = dict( user = <username>,

                          passwd = <secret>,

                         ashost = <hostname>,

                          client = <client>,

                          sysnr = <systemnumber>,

                         sid = <sid> )


    connection=sap_abapclient(**params)

    connection.run_suim_job(sap_all_variant,'sap_all_users.xlsx')

    connection.run_suim_job(s_develop_variant,'s_develop_users.xlsx')


 

This script can be easily extended. You don't need to write things into a excel spreadsheet. You can store things in a database, refresh the data regularly and send an email with changes for example... or generate a nice HTML page using a template engine like jinja2 (Welcome | Jinja2 (The Python Template Engine)), Or you can use the example and write a script that schedules all kinds of basis jobs consistently across an SAP landscape....

SAP GUI script automation my experience

$
0
0

Activity

Automation SAP mass activities

Technology used

SAP GUI scripting

Method of working

Record your activity in SAP, add custom variables, Loop it to do it repeatedly by reading

Values from excel sheet

Comparative benefits

There is no other tool in SAP which can automate activities partially. Portability is best in SAPGUI scripting . SAP GUI Partially automate a sequence of repeated activity. For example role creation , how to automate in such activities where consultant need to interfere in between and provide some inputs in system ? What if such automating scripts can be stored and executed from your desktop? This makes your automation one time activity for your entire lifetime. You can apply such scripts in any SAP system , you switch client, you switch systems, you switch network , you switch SAP products- doesn't matter.

Successful activities

Mass user creation fully automated, Table updates fully automated, Role building partially automated

Successful project

An implementation was done with just one Security consultant.

Required non SAP Skill

To execute scripts just SAP knowledge is enough. To create script a little bit of VBScript understanding and aptitude for programming required. Excel knowledge with Vlookup is added advatage.

 

How To Use tRFC/qRFC Calls With VBScript Via CCo

$
0
0

Hello community,

 

I introduced COM Connector (CCo) here. Here now an example how to use transactional RFC calls with VBScript via CCo. The queued RFC calls are commented. You can find more information about the different RFC variants here.

 

'-Begin-----------------------------------------------------------------

 

  '-Directives----------------------------------------------------------

    Option Explicit

 

  '-Constants-----------------------------------------------------------

    Const RFC_OK = 0

 

  '-Sub Main------------------------------------------------------------

    Sub Main()

 

      '-Variables-------------------------------------------------------

        Dim SAP, hRFC, rc, TID, hTrans, hFuncDesc, hFunc

 

      Set SAP = CreateObject("COMNWRFC")

      If Not IsObject(SAP) Then

        Exit Sub

      End If

 

      hRFC = SAP.RfcOpenConnection("ASHOST=ABAP, SYSNR=00, " & _

        "CLIENT=001, USER=BCUSER")

      If hRFC = 0 Then

        Set SAP = Nothing

        Exit Sub

      End If

 

      hFuncDesc = SAP.RfcGetFunctionDesc(hRFC, "RFC_PING")

      If hFuncDesc = 0 Then

        rc = SAP.RfcCloseConnection(hRFC)

        Set SAP = Nothing

        Exit Sub

      End If

 

      hFunc = SAP.RfcCreateFunction(hFuncDesc)

      If hFunc = 0 Then

        rc = SAP.RfcCloseConnection(hRFC)

        Set SAP = Nothing

        Exit Sub

      End If

 

      If SAP.RfcGetTransactionID(hRFC, TID) <> RFC_OK Then

        rc = SAP.RfcCloseConnection(hRFC)

        Set SAP = Nothing

        Exit Sub

      End If

 

      'qRFC

      'hTrans = SAP.RfcCreateTransaction(hRFC, TID, "StefansQueue")

 

      hTrans = SAP.RfcCreateTransaction(hRFC, TID, "")

      If hTrans = 0 Then

        rc = SAP.RfcCloseConnection(hRFC)

        Set SAP = Nothing

        Exit Sub

      End If

 

      If SAP.RfcInvokeInTransaction(hTrans, hFunc) = RFC_OK Then

 

        If SAP.RfcSubmitTransaction(hTrans) = RFC_OK Then

 

          'qRFC

          'MsgBox "Look in TAC SMQ2 (Inbound Queue)"

 

          MsgBox "Look in table ARFCRSTATE for TID " & TID

 

          rc = SAP.RfcConfirmTransaction(hTrans)

 

        End If

 

      End If

 

      rc = SAP.RfcDestroyTransaction(hTrans)

      rc = SAP.RfcDestroyFunction(hFunc)

      rc = SAP.RfcCloseConnection(hRFC)

      Set SAP = Nothing

 

    End Sub

 

  '-Main----------------------------------------------------------------

    Main()

 

'-End-------------------------------------------------------------------

 

Enjoy it.

 

Cheers

Stefan

How To Use bgRFC Calls With VBScript Via CCo

$
0
0

Hello community,

 

I introduced COM Connector (CCo) here. Here now an example how to use background RFC calls with VBScript via CCo. The queued RFC calls are commented. You can find more information about the different RFC variants here. I presented the possibility to use transactional (tRFC) and queued (qRFC) RFC calls here.

 

'-Begin-----------------------------------------------------------------

'-

'- Look also in TAC SBGRFCCONF and SBGRFCMON

'- Hint: You need SAP 7.0 EHP 1 for SAP NetWeaver 7.0 Support Package 4

'-       and higher

'-

'-----------------------------------------------------------------------

 

  '-Directives----------------------------------------------------------

    Option Explicit

 

  '-Constants-----------------------------------------------------------

    Const RFC_OK = 0

 

  '-Sub Main------------------------------------------------------------

    Sub Main()

 

      '-Variables-------------------------------------------------------

        Dim SAP, CCo, hRFC, rc, UID, hUnit, hFuncDesc, hFunc

        Dim unitAttrID, unitAttr, unitIdentID, unitIdent

 

      Set SAP = CreateObject("COMNWRFC")

      If Not IsObject(SAP) Then

        Exit Sub

      End If

 

      Set CCo = CreateObject("COMNWRFCHELP")

      If Not IsObject(CCo) Then

        Exit Sub

      End If

 

      hRFC = SAP.RfcOpenConnection("ASHOST=ABAP, SYSNR=00, " & _

        "CLIENT=001, USER=BCUSER")

      If hRFC = 0 Then

        Set SAP = Nothing

        Set CCo = Nothing

        Exit Sub

      End If

 

      hFuncDesc = SAP.RfcGetFunctionDesc(hRFC, "RFC_PING")

      If hFuncDesc = 0 Then

        rc = SAP.RfcCloseConnection(hRFC)

        Set SAP = Nothing

        Set CCo = Nothing

        Exit Sub

      End If

 

      hFunc = SAP.RfcCreateFunction(hFuncDesc)

      If hFunc = 0 Then

        rc = SAP.RfcCloseConnection(hRFC)

        Set SAP = Nothing

        Set CCo = Nothing

        Exit Sub

      End If

 

      If SAP.RfcGetUnitID(hRFC, UID) <> RFC_OK Then

        rc = SAP.RfcCloseConnection(hRFC)

        Set SAP = Nothing

        Set CCo = Nothing

        Exit Sub

      End If

 

      '-Create structures RFC_UNIT_ATTRIBUTES and RFC_UNIT_IDENTIFIER---

        If CCo.AllocateMemory(273, unitAttrID, unitAttr) = vbFalse Or _

          CCo.AllocateMemory(70, unitIdentID, unitIdent) = vbFalse Then

          rc = SAP.RfcCloseConnection(hRFC)

          Set SAP = Nothing

          Set CCo = Nothing

          Exit Sub

        End If

 

      '-qRFC------------------------------------------------------------

'       hUnit = SAP.RfcCreateUnit(hRFC, UID, "STEFANSQUEUE", unitAttr, _

'         unitIdent)

 

      '-tRFC------------------------------------------------------------

        hUnit = SAP.RfcCreateUnit(hRFC, UID, "", unitAttr, unitIdent)

        If hUnit = 0 Then

          rc = SAP.RfcCloseConnection(hRFC)

          Set SAP = Nothing

          rc = CCo.FreeMemory(unitIdentID)

          rc = CCo.FreeMemory(unitAttrID)

          Set CCo = Nothing

          Exit Sub

        End If

 

      If SAP.RfcInvokeInUnit(hUnit, hFunc) = RFC_OK Then

 

        If SAP.RfcSubmitUnit(hUnit) = RFC_OK Then

 

          MsgBox "Look in table BGRFC_SRV_STATE for UID " & UID

 

          SAP.ErrorMsgTarget = 2

          If SAP.RfcConfirmUnit(hRFC, unitIdent) = RFC_OK Then

            MsgBox "Look in table BGRFC_SRV_CFM for UID " & UID

          Else

            MsgBox SAP.ErrorMessage

          End If

 

        End If

 

      End If

 

      rc = SAP.RfcDestroyUnit(hUnit)

      rc = SAP.RfcDestroyFunction(hFunc)

      rc = SAP.RfcCloseConnection(hRFC)

      Set SAP = Nothing

      rc = CCo.FreeMemory(unitIdentID)

      rc = CCo.FreeMemory(unitAttrID)

      Set CCo = Nothing

 

    End Sub

 

  '-Main----------------------------------------------------------------

    Main()

 

'-End-------------------------------------------------------------------

 

Background RFCs are the recommended kind of RFCs.You should never use a synchronous RFC to write data into an SAP system, only to read data. To write data use tRFC or qRFC, or better the bgRFC. With this examples here and here you have now the easy possibility to use any kind of RFC with your favorite scripting language.

 

Enjoy it.

 

Cheers

Stefan


UI5 / HTML5 UI Generators

$
0
0

Reading the Raymond Ni blog on generating UI forms from JSON script, I combined this "cool hack with potential" with Python scripting, to read ABAP metadata from the backend system and generate UI5 or HTML5 form, like this one here, automatically.

 

This can save the prototyping time because the developer does not have to start from scratch but from automatically generated form HTML fragment.

 

The source code can be downloaded from bsrdjan/formgen · GitHub

 

Form Metadata

 

Let take one complex form as example and assume that selection screen of Timesheets approval transaction CAPS, for some reason shall be implemented in UI5 or HTML5:

CAPS.png

Using F1 HELP in ABAP Workbench you may find out technical types of input field of this selection screen and copy your findings in one JSON or YAML file, like below. For the purpose of this blog I used YAML, just to put few comments in, but you can remove them and save-as JSON.

 

form.yaml

 

{

    #

    # CAPS Selection

    #

    "form": [

        { "FIELD": ["PERNR", "PERNR"], "SHLP": "PREM" }, # Personnel Number

        { "FIELD": ["PERNR", "STAT2"]},   # Employment status

        { "FIELD": ["PERNR", "PERSG"]},   # Employment group

        { "FIELD": ["PERNR", "PERSK"]},   # Employee subgroup

        { "FIELD": ["PERNR", "VDSK1"]},   # Organization ke

        { "FIELD": ["PERNR", "ABKRS"]},   # Payroll area

        { "FIELD": ["PERNR", "KOKRS"]},   # Controlling area

        { "FIELD": ["PERNR", "KOSTL"]},   # Cost center

        { "FIELD": ["PERNR", "ORGEH"]},   # Organizational unit

        { "FIELD": ["PERNR", "MSTBR"]},   # Supervisor area

        { "FIELD": ["PERNR", "SBMOD"]},   # Administrator group

        { "FIELD": ["PERNR", "SACHZ"]},   # Time recording administrator

        { "FIELD": ["PERNR", "SASBZ"]}    # Administrator group: time

    ]

}


To process this form you can write a simple nodejs or Python script, like this one.

 

UI5 Form

 

ui5form.py

 

 

#!/usr/bin/python

 

# connect to abap

 

from pyrfc import *

 

def get_connection (conn):

   """ get Connection"""

   print 'connecting %s ...' % conn['ashost']

   return Connection(**conn)

 

I64 = {

  'user': 'demo',

  'passwd': 'welcome',

  'ashost': '10.11.12.12',

  'sysnr': '00',

  'client': '800',

  'saprouter': ''

}

 

c = get_connection(I64)

 

# read form fields

 

import yaml

from pprint import pprint

with open('form.yaml', 'r') as data_file:

    form_fields = yaml.load(data_file)["form"]

 

# make ui5 form

 

# header

 

print '''

var oLayout1 = new sap.ui.layout.form.GridLayout();

var oLayout2 = new sap.ui.layout.form.ResponsiveLayout();

var oLayout3 = new sap.ui.layout.form.ResponsiveGridLayout();

 

var oForm1 = new sap.ui.layout.form.Form("F1",{

    title: new sap.ui.core.Title({text: "Time Approval", tooltip: "Time Approval tooltip"}),

    layout: oLayout1,

    formContainers: [

        new sap.ui.layout.form.FormContainer("F1C1",{

            title: "Time Approval",

            formElements: [

'''

 

for _line in form_fields:

    field = _line['FIELD']

    r = c.call('DDIF_FIELDINFO_GET',

                TABNAME=field[0], FIELDNAME=field[1],

                ALL_TYPES='X',    GROUP_NAMES='X' )

    dfies=r['DFIES_TAB'][0]

 

 

    print '''

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"%s",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "%s", maxLength: "%d",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),'''  % (dfies['SCRTEXT_L'], '', int(dfies['LENG']))

 

 

    print

    print

 

 

# footer

 

print '''

        ]

    });

oForm1.placeAt("sample1");

'''

 

Both Python and nodejs can directly call ABAP RFCs (see PyRFC and nodeRFC) and you can use the language you like more.

 

After running this script and redirecting the output to ui5form.html, the following fragment is generated

 

ui5form.html

 

connecting 10.117.24.158 ...

 

var oLayout1 = new sap.ui.layout.form.GridLayout();

var oLayout2 = new sap.ui.layout.form.ResponsiveLayout();

var oLayout3 = new sap.ui.layout.form.ResponsiveGridLayout();

 

 

var oForm1 = new sap.ui.layout.form.Form("F1",{

    title: new sap.ui.core.Title({text: "Time Approval", tooltip: "Time Approval tooltip"}),

    layout: oLayout1,

    formContainers: [

        new sap.ui.layout.form.FormContainer("F1C1",{

            title: "Time Approval",

            formElements: [

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Personnel Number",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "8",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Employment status",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "1",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Employee group",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "1",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Employee subgroup",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "2",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Organizational key",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "14",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Payroll area",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "2",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Controlling Area",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "4",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Cost Center",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "10",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Organizational unit",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "8",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Supervisor area",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "8",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Administrator group",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "4",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Time administrator",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "3",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Admin.modif, time admin.",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "7",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        ]

    });

oForm1.placeAt("sample1");

 

 

The similarity with UI5 form example is obvious, this template was used in this script example. From ABAP metadata I extracted the label text, input field type and max length, but perhaps more is possible. Some manual work still needed but not like starting from the blank sheet of paper.

 

HTML5 Form

 

With slightly modified logic, the HTML5 form can be generated. The section "make ui5 form" shall be replaced with the code below.

 

html5form.py

 

 

# make html5 form

 

# header

 

print '<form name="Time Approval" novalidate role="form">'

 

for _line in form_fields:

    field = _line['FIELD']

    r = c.call('DDIF_FIELDINFO_GET',

                    TABNAME=field[0], FIELDNAME=field[1],

                    ALL_TYPES='X',    GROUP_NAMES='X' )

    dfies=r['DFIES_TAB'][0]

    fname = 'name_' + field[1]

    if dfies['DATATYPE'] == 'NUMC':

        ftype = 'number'

    else:

        ftype = 'text'

    print '''

    <div class="form-group">

      <label for="%s">%s</label>

      <input type="%s" name="%s" class="form-control" required maxLength=%d />

    </div>''' % (fname, dfies['SCRTEXT_L'], ftype, fname, int(dfies['LENG']))

    print

 

# footer

 

print '</form>'

 

 

html5form.html

 

<form name="Time Approval" novalidate role="form">

 

 

    <div class="form-group">

      <label for="name_PERNR">Personnel Number</label>

      <input type="number" name="name_PERNR" class="form-control" required maxLength=8 />

    </div>

 

 

    <div class="form-group">

      <label for="name_STAT2">Employment status</label>

      <input type="text" name="name_STAT2" class="form-control" required maxLength=1 />

    </div>

 

 

    <div class="form-group">

      <label for="name_PERSG">Employee group</label>

      <input type="text" name="name_PERSG" class="form-control" required maxLength=1 />

    </div>

 

 

    <div class="form-group">

      <label for="name_PERSK">Employee subgroup</label>

      <input type="text" name="name_PERSK" class="form-control" required maxLength=2 />

    </div>

 

 

    <div class="form-group">

      <label for="name_VDSK1">Organizational key</label>

      <input type="text" name="name_VDSK1" class="form-control" required maxLength=14 />

    </div>

 

 

    <div class="form-group">

      <label for="name_ABKRS">Payroll area</label>

      <input type="text" name="name_ABKRS" class="form-control" required maxLength=2 />

    </div>

 

 

    <div class="form-group">

      <label for="name_KOKRS">Controlling Area</label>

      <input type="text" name="name_KOKRS" class="form-control" required maxLength=4 />

    </div>

 

 

    <div class="form-group">

      <label for="name_KOSTL">Cost Center</label>

      <input type="text" name="name_KOSTL" class="form-control" required maxLength=10 />

    </div>

 

 

    <div class="form-group">

      <label for="name_ORGEH">Organizational unit</label>

      <input type="number" name="name_ORGEH" class="form-control" required maxLength=8 />

    </div>

 

 

    <div class="form-group">

      <label for="name_MSTBR">Supervisor area</label>

      <input type="text" name="name_MSTBR" class="form-control" required maxLength=8 />

    </div>

 

 

    <div class="form-group">

      <label for="name_SBMOD">Administrator group</label>

      <input type="text" name="name_SBMOD" class="form-control" required maxLength=4 />

    </div>

 

 

    <div class="form-group">

      <label for="name_SACHZ">Time administrator</label>

      <input type="text" name="name_SACHZ" class="form-control" required maxLength=3 />

    </div>

 

 

    <div class="form-group">

      <label for="name_SASBZ">Admin.modif, time admin.</label>

      <input type="text" name="name_SASBZ" class="form-control" required maxLength=7 />

    </div>

 

</form>

 

And here the generated form, incorporated into HTML document:

 

html5.png

 

Hope this adds potential to the hack and helps saving cloud/web prototyping time, also find out if more info can be extracted from ABAP metadata and mapped to HTML5 and UI5 input attributes

VBScript is Not Longer Supported in IE11, Run VBScript from JavaScript

$
0
0

Hello community,

 

it seems that the time for VBScript is running out, you can find here an information in the Internet Explorer Dev Center about the deprecation of VBScript in the context of IE 11 in edge mode,

 

However, that is no reason to hang the ears 

 

Here a function in JavaScript to call a VBScript from IE 11 in edge mode:

 

function callVBS(scriptName) {


  //-Variables--------------------------------------------------------

    var wsh, pathName;

 

 

  if ("ActiveXObject" in window) {

    wsh = new ActiveXObject("WScript.Shell");

    if (typeof(wsh) == 'object') {

      pathName = location.pathname.substr(0, location.pathname.lastIndexOf("/"))+"/";

      pathName = pathName.slice(1);

      wsh.run("wscript.exe \"" + pathName + scriptName + ".vbs\"", 1, true);

      wsh = null;

    }

  }

  else {

    alert("Your Browser doesn't support ActiveXObject");

  }

 

}

 

In this case the VBScript file must be in the same directory as the JavaScript file.

 

On this way we do have still the possibility to use our good old VBScript a little bit longer.

And so it is possible to use VBScript in the context of UI5 development with IE 11.

 

Enjoy it.

 

Cheers

Stefan

How to use PowerShell Script Language via dotNET Connector (NCo) in SAP Context

$
0
0

Hello community,

 

PowerShell is in standard available on any Windows 7 and higher and it bases on dotNET. You can find a lot of information here. PowerShell offers a good integrated scripting environment (ISE), so it is very easy to code and test scripts.

 

SAP offers with the dotNET Connector (NCo) an easy way to use the SAP backend in this environment. You can find NCo here. The NCo is primarly used with C# language and you can find a lot of examples.

 

I combine the possibilities of PowerShell and NCo, to show how easy and fast you can create a communication between your presentation server and the application server. The first example shows only the version number of the NCo:

 

#-Begin-----------------------------------------------------------------

 

  #-Function Get-NCoVersion---------------------------------------------

    Function Get-NCoVersion {

 

      #-Loads NCo libraries---------------------------------------------

        $rc = [Reflection.Assembly]::LoadFile("C:\NCo\sapnco.dll")

        $rc = [Reflection.Assembly]::LoadFile("C:\NCo\sapnco_utils.dll")

 

 

      #-Gets the version of NCo-----------------------------------------

        $Version =

          [SAP.Middleware.Connector.SAPConnectorInfo]::get_Version()

        $PatchLevel =

          [SAP.Middleware.Connector.SAPConnectorInfo]::get_KernelPatchLevel()

        $SAPRelease =

          [SAP.Middleware.Connector.SAPConnectorInfo]::get_SAPRelease()

 

      #-Shows the result------------------------------------------------

        Write-Host "`r`nNCo verion:" $Version

        Write-Host "Patch Level:" $PatchLevel

        Write-Host "SAP Release:" $SAPRelease

 

    }

 

  #-Main----------------------------------------------------------------

    Get-NCoVersion

 

#-End-------------------------------------------------------------------

 

001.JPG

The second example shows how to call a function module, with import and export parameters:

 

#-Begin-----------------------------------------------------------------

 

  #-Function Invoke-SAPFunctionModule-----------------------------------

    Function Invoke-SAPFunctionModule {

 

      #-Loads NCo libraries---------------------------------------------

        $rc = [Reflection.Assembly]::LoadFile("C:\NCo\sapnco.dll")

        $rc = [Reflection.Assembly]::LoadFile("C:\NCo\sapnco_utils.dll")

 

 

      #-Sets connection parameters--------------------------------------

        $cfgParams = New-Object SAP.Middleware.Connector.RfcConfigParameters

        $cfgParams.Add("NAME", "TEST")

        $cfgParams.Add("ASHOST", "ABAP")

        $cfgParams.Add("SYSNR", "00")

        $cfgParams.Add("CLIENT", "001")

        $cfgParams.Add("USER", "BCUSER")

        $cfgParams.Add("PASSWD", "minisap")

 

      #-Destination-----------------------------------------------------

        $destination =

          [SAP.Middleware.Connector.RfcDestinationManager]::GetDestination($cfgParams)

 

      #-Metadata--------------------------------------------------------

      [SAP.Middleware.Connector.IRfcFunction]$rfcFunction =

        $destination.Repository.CreateFunction("STFC_CONNECTION")

 

      #-Sets import parameter-------------------------------------------

        $rfcFunction.SetValue("REQUTEXT", "Hello World from PowerShell")

 

      #-Calls function module-------------------------------------------

        $rfcFunction.Invoke($destination)

 

      #-Shows export parameters-----------------------------------------

        Write-Host $rfcFunction.GetValue("ECHOTEXT")

        Write-Host $rfcFunction.GetValue("RESPTEXT")

 

    }

 

  #-Main----------------------------------------------------------------

    Invoke-SAPFunctionModule

      

#-End-------------------------------------------------------------------

 

002.JPG

The PowerShell ISE offers a lot of additional things, e.g. like code completion.

 

003.jpg

 

With PowerShell and NCo you can easily create client functions. All you have to do is to copy the necessary NCo libraries on your presentation server. PowerShell is on Windows available and so you can start up quickly.

 

Enjoy it.

 

Cheers

Stefan

PowerShell Functions: Load NCo in Relation to Shell and Get Destination with Password Requester

$
0
0

Hello community,

 

I presented here the possibility to use PowerShell via dotNET Connector (NCo) with SAP.

 

Here now two functions to simplify the using of PowerShell with NCo.

 

The first function shows how to load NCo in relation to the bus width of the PowerShell. The function gets the directory of the script, set it as current directory, expects the NCo libraries in subdirectories x64 for the 64-bit version and x86 for the 32-bit version and loads it.

 

  #-Function Load-NCo---------------------------------------------------

  #-

  #- The loading of the NCo libraries is in dependence to the shell.

  #- The 64-bit version loads the libraries from the path x64, and the

  #- 32-bit version from the path x86.

  #-

  #---------------------------------------------------------------------

    Function Load-NCo {

 

      $CurrentDir = Get-Location

      [System.IO.Directory]::SetCurrentDirectory($CurrentDir.Path)

 

      $Size = [System.IntPtr]::Size

      if ($Size -eq 4) {

        $Path = $CurrentDir.Path + "\x86\"

      }

      elseif ($Size -eq 8) {

        $Path = $CurrentDir.Path + "\x64\"

      }

 

      $rc = [Reflection.Assembly]::LoadFile($Path + "sapnco.dll")

      $rc = [Reflection.Assembly]::LoadFile($Path + "sapnco_utils.dll")

 

    }

 

The second function shows how to get a destination with a password requester.

 

  #-Function Get-Destination--------------------------------------------

    Function Get-Destination {


      #-Verbindungsparamter---------------------------------------------

        $cfgParams = New-Object SAP.Middleware.Connector.RfcConfigParameters

        $cfgParams.Add("NAME", "TEST")

        $cfgParams.Add("ASHOST", "ABAP")

        $cfgParams.Add("SYSNR", "00")

        $cfgParams.Add("CLIENT", "001")

        $cfgParams.Add("USER", "BCUSER")


        $SecPasswd = Read-Host -Prompt "Passwort" -AsSecureString

        $ptrPasswd = [Runtime.InteropServices.Marshal]::SecureStringToBStr($SecPasswd)

        $Passwd = [Runtime.InteropServices.Marshal]::PtrToStringBStr($ptrPasswd)

        $cfgParams.Add("PASSWD", $Passwd)


      return [SAP.Middleware.Connector.RfcDestinationManager]::GetDestination($cfgParams)


    }

001.JPG

These two functions changes the examples a little bit.

 

#-Begin-----------------------------------------------------------------

 

  #-Function Load-NCo---------------------------------------------------

    Function Load-NCo {

 

      $CurrentDir = Get-Location

      [System.IO.Directory]::SetCurrentDirectory($CurrentDir.Path)

 

      $Size = [System.IntPtr]::Size

      if ($Size -eq 4) {

        $Path = $CurrentDir.Path + "\x86\"

      }

      elseif ($Size -eq 8) {

        $Path = $CurrentDir.Path + "\x64\"

      }

 

      $rc = [Reflection.Assembly]::LoadFile($Path + "sapnco.dll")

      $rc = [Reflection.Assembly]::LoadFile($Path + "sapnco_utils.dll")

 

    }

 

  #-Function Get-Destination--------------------------------------------

    Function Get-Destination {

 

      #-Connectionparamter----------------------------------------------

        $cfgParams = New-Object SAP.Middleware.Connector.RfcConfigParameters

        $cfgParams.Add("NAME", "TEST")

        $cfgParams.Add("ASHOST", "ABAP")

        $cfgParams.Add("SYSNR", "00")

        $cfgParams.Add("CLIENT", "001")

        $cfgParams.Add("USER", "BCUSER")

        $cfgParams.Add("PASSWD", "minisap")

 

      return [SAP.Middleware.Connector.RfcDestinationManager]::GetDestination($cfgParams)

 

    }

 

  #-Function Invoke-SAPFunctionModule-----------------------------------

    Function Invoke-SAPFunctionModule {

 

      $destination = Get-Destination

 

      #-Metadata--------------------------------------------------------

        [SAP.Middleware.Connector.IRfcFunction]$rfcFunction =

          $destination.Repository.CreateFunction("STFC_CONNECTION")

 

      #-Set importparameter---------------------------------------------

        $rfcFunction.SetValue("REQUTEXT", "Hello World from PowerShell")

 

      #-Call function module--------------------------------------------

        $rfcFunction.Invoke($destination)

 

      #-Show exportparameter--------------------------------------------

        Write-Host $rfcFunction.GetValue("ECHOTEXT")

        Write-Host $rfcFunction.GetValue("RESPTEXT")

 

    }

 

  #-Main----------------------------------------------------------------

    Load-NCo

    Invoke-SAPFunctionModule

 

#-End-------------------------------------------------------------------

 

Enjoy it.

 

Cheers

Stefan

How to Call BAPI Function Modules with PowerShell via NCo

$
0
0

Hello community,

 

I presented here the possibility to use PowerShell via dotNET Connector (NCo) with SAP. Also I presented here two auxiliary functions for an easier using of PowerShell via dotNET Connector (NCo) with SAP.

 

Here now a short description with two examples how to call BAPI function modules (FM) with PowerShell and NCo. I use the FM BAPI_USER_CREATE1 and BAPI_USER_DELETE. In comparisaon to a normal function call we use also the FM BAPI_TRANSACTION_COMMIT to do an external commit. Also we use an additional context to start a stateful call sequence, to bind the destination’s connection pool with the current thread of execution, with BeginContext and EndContext.

 

Here the first example how to create the user MYUSER:

 

#-Begin-----------------------------------------------------------------

 

  #-Function Load-NCo---------------------------------------------------

    ...

 

  #-Function Get-Destination--------------------------------------------

    ...

 

  #-Function Invoke-SAPFunctionModule-----------------------------------

    Function Invoke-SAPFunctionModule {

 

      $destination = Get-Destination

 

      #-Metadata--------------------------------------------------------

        try {

          [SAP.Middleware.Connector.IRfcFunction]$bapiCreateUser =

            $destination.Repository.CreateFunction("BAPI_USER_CREATE1")

          [SAP.Middleware.Connector.IRfcFunction]$bapiTransactionCommit =

            $destination.Repository.CreateFunction("BAPI_TRANSACTION_COMMIT")

        }

        catch [SAP.Middleware.Connector.RfcBaseException] {

          Write-Host "Metadata error"

          break

        }

 

      #-Set importparameter---------------------------------------------

        $bapiCreateUser.SetValue("USERNAME", "MYUSER")

        [SAP.Middleware.Connector.IRfcStructure]$imPassword =

          $bapiCreateUser.GetStructure("PASSWORD")

        $imPassword.SetValue("BAPIPWD", "initial")

        [SAP.Middleware.Connector.IRfcStructure]$imAddress =

          $bapiCreateUser.GetStructure("ADDRESS")

        $imAddress.SetValue("FIRSTNAME", "My")

        $imAddress.SetValue("LASTNAME", "User")

        $imAddress.SetValue("FULLNAME", "MyUser")

 

      #-Open context----------------------------------------------------

        $rc =

          [SAP.Middleware.Connector.RfcSessionManager]::BeginContext($destination)

 

      #-Call function modules-------------------------------------------

        try {

          #-Create user-------------------------------------------------

            $bapiCreateUser.Invoke($destination)

          [SAP.Middleware.Connector.IRfcTable]$return =

            $bapiCreateUser.GetTable("RETURN")

          foreach ($line in $return) {

            Write-Host $line.GetValue("TYPE") "-" $line.GetValue("MESSAGE")

          }

          #-Commit------------------------------------------------------

            $bapiTransactionCommit.Invoke($destination)

        }

        finally {

          #-Close context-----------------------------------------------

            $rc =

              [SAP.Middleware.Connector.RfcSessionManager]::EndContext($destination)

        }

 

    }

 

  #-Main----------------------------------------------------------------

    Load-NCo

    Invoke-SAPFunctionModule

 

#-End-------------------------------------------------------------------

 

Here now the second example how to delete the user MYUSER:

 

#-Begin-----------------------------------------------------------------

 

  #-Function Load-NCo---------------------------------------------------

    ...

 

  #-Function Get-Destination--------------------------------------------

    ....

 

  #-Function Invoke-SAPFunctionModule-----------------------------------

    Function Invoke-SAPFunctionModule {

 

      $destination = Get-Destination

 

      #-Metadata--------------------------------------------------------

        try {

          [SAP.Middleware.Connector.IRfcFunction]$bapiDeleteUser =

            $destination.Repository.CreateFunction("BAPI_USER_DELETE")

          [SAP.Middleware.Connector.IRfcFunction]$bapiTransactionCommit =

            $destination.Repository.CreateFunction("BAPI_TRANSACTION_COMMIT")

        }

        catch [SAP.Middleware.Connector.RfcBaseException] {

          Write-Host "Metadaten-Fehler"

          break

        }

 

      #-Set importparameter---------------------------------------------

        $bapiDeleteUser.SetValue("USERNAME", "MYUSER")

 

      #-Open context----------------------------------------------------

        $rc =

          [SAP.Middleware.Connector.RfcSessionManager]::BeginContext($destination)

 

 

      #-Call function modules-------------------------------------------

        try {

          #-Delete user-------------------------------------------------

            $bapiDeleteUser.Invoke($destination)

          [SAP.Middleware.Connector.IRfcTable]$return =

            $bapiDeleteUser.GetTable("RETURN")

          foreach ($line in $return) {

            Write-Host $line.GetValue("TYPE") "-" $line.GetValue("MESSAGE")

          }

          #-Commit------------------------------------------------------

            $bapiTransactionCommit.Invoke($destination)

        }

        finally {

          #-Close context-----------------------------------------------

            $rc =

              [SAP.Middleware.Connector.RfcSessionManager]::EndContext($destination)

        }

 

    }

 

  #-Main----------------------------------------------------------------

    Load-NCo

    Invoke-SAPFunctionModule

 

#-End-------------------------------------------------------------------

 

This examples shows also how to use structures and tables in the import and export parameters.

 

PowerShell offers a lot of possibilities and among the shadow of the circumstance of the end of support of VBScript in a shorter distant future - first signs look here - seems it better to look for an alternative. In my case offers PowerShell all the things to replace VBScript.

 

Enjoy it.

 

Cheers

Stefan

Copy ABAP User using BAPIs

$
0
0

A handy script for copying ABAP user to one or more new users. It works just like SU01 User Copy function, only programatically, by calling BAPIs of SU_USER ABAP Function Group, via Remote Function Calls (RFCs). No new ABAP code or dialog access needed in backend system, only the source ABAP User and the list of new usernames, to be created.

 

Using the transaction SU01 in this example, the source user UNAMEFROM is selected and copied to new user UNAMETO1:

Copy.png

The end result is a new created ABAP User, with Address, Logon Data, Defaults, Parameters, Roles, Profiles and Groups.

UnameTo.png

We achieve the same, using BAPIs from SU_USER ABAP Function Group and Python script:

SU_USER.png

Using the script, you can do these repetitive SU01 task automatically, from your notebook. The Python RFC Connector shall be installed on a notebook and the data to be copied you can customise for your project needs.

Log.png

Here the code (clone from Github). Error checks and optional parameters removed, for the sake of readability.


# -*- coding: utf-8 -*-

 

from pyrfc import *

import datetime

 

# BAPI calls log

def print_log(bapi_return):

    if len(bapi_return) > 0:

        for line in bapi_return:

            print '%s: %s' % (line['TYPE'], line['MESSAGE'])

 

# Connect to ABAP system

SAPROUTER = '/H/123.12.123.12/E/yt6ntx/H/123.14.131.111/H/'

 

EC4 = {

    'user'      : 'abapuser',

    'passwd'    : 'abappass',

    'ashost'    : '10.11.12.13',

    'saprouter' : SAPROUTER,

    'sysnr'     : '00',

    'client'    : '300',

    'trace'     : '3',

    'lang'      : 'EN' }

 

c = Connection(**EC4)

 

# The sourse user, to be copied

uname_from = 'UNAMEFROM'

# Defaults if source user validity not maintained (undefined)

valid_from = datetime.date(2015,1,19)

valid_to   = datetime.date(2015,12,31)

# New users' password. For automatic generation check CREATE BAPI

initpwd = 'InitPa$$21'

 

# Users to be created

users= ['UNAMETO1', 'UNAMETO2']

 

# Get source user details

r = c.call('BAPI_USER_GET_DETAIL', USERNAME = uname_from, CACHE_RESULTS  = ' ')

 

# Set new users' defaults

if r['LOGONDATA']['GLTGV'] is None:

    r['LOGONDATA']['GLTGV'] = valid_from

if r['LOGONDATA']['GLTGB'] is None:

    r['LOGONDATA']['GLTGB'] = valid_to

password = {'BAPIPWD' : initpwd}

 

# Create new users

for uname_to in users:

    print uname_to

    r['ADDRESS']['LASTNAME'] = uname_to

    r['ADDRESS']['FULLNAME'] = uname_to

 

    x = c.call('BAPI_USER_CREATE1',

        USERNAME    = uname_to,

        LOGONDATA   = r['LOGONDATA'],

        PASSWORD    = password,

        DEFAULTS    = r['DEFAULTS'],

        ADDRESS     = r['ADDRESS'],

        COMPANY     = r['COMPANY'],

        REF_USER    = r['REF_USER'],

        PARAMETER   = r['PARAMETER'],

        GROUPS      = r['GROUPS']

    )

 

    print_log(x['RETURN'])

 

    x = c.call('BAPI_USER_PROFILES_ASSIGN',

        USERNAME  = uname_to,

        PROFILES  = r['PROFILES']

    )

 

    print_log(x['RETURN'])

 

    x = c.call('BAPI_USER_ACTGROUPS_ASSIGN',

        USERNAME       = uname_to,

        ACTIVITYGROUPS = r['ACTIVITYGROUPS']

    )

 

    print_log(x['RETURN'])

 

# Finished

print ("%s copied to %d new users\nBye!") % (uname_from, len(users))

 

 

You can do the same from Java, .NET, nodejs, GO, Ruby, using respective RFC connectors (here PyRFC used) and of course in ABAP (clone from Github):


REPORT zsbucopy1.

 

DATA:

  uname_from        LIKE bapibname-bapibname VALUE 'UNAMEFROM',

 

  ls_logondata      TYPE bapilogond,

  ls_defaults      TYPE bapidefaul,

  ls_address        TYPE bapiaddr3,

  ls_company        TYPE  bapiuscomp,

 

  lt_parameter      TYPE STANDARD TABLE OF bapiparam,

  lt_profiles      TYPE STANDARD TABLE OF bapiprof,

  lt_activitygroups TYPE STANDARD TABLE OF bapiagr,

  lt_return        TYPE STANDARD TABLE OF bapiret2,

  lt_parameter1    TYPE STANDARD TABLE OF bapiparam1,

  lt_groups        TYPE STANDARD TABLE OF bapigroups,

 

  uname_to          LIKE bapibname-bapibname VALUE 'UNAMETO',

  is_password      TYPE bapipwd.

 

is_password-bapipwd = 'Init2014'.

 

CALL FUNCTION 'BAPI_USER_GET_DETAIL'

  EXPORTING

    username      = uname_from

    cache_results  = ' '

  IMPORTING

    logondata      = ls_logondata

    defaults      = ls_defaults

    address        = ls_address

    company        = ls_company

  TABLES

    parameter      = lt_parameter

    profiles      = lt_profiles

    activitygroups = lt_activitygroups

    return        = lt_return

    parameter1    = lt_parameter1

    groups        = lt_groups.

 

MOVE uname_to TO: ls_address-lastname, ls_address-fullname.

 

CALL FUNCTION 'BAPI_USER_CREATE1'

  EXPORTING

    username  = uname_to

    logondata  = ls_logondata

    password  = is_password

    defaults  = ls_defaults

    address    = ls_address

    company    = ls_company

    ref_user  = uname_from

  TABLES

    parameter  = lt_parameter

    return    = lt_return

    groups    = lt_groups

    parameter1 = lt_parameter1.

 

CALL FUNCTION 'BAPI_USER_PROFILES_ASSIGN'

  EXPORTING

    username = uname_to

  TABLES

    profiles = lt_profiles

    return  = lt_return.

 

CALL FUNCTION 'BAPI_USER_ACTGROUPS_ASSIGN'

  EXPORTING

    username      = uname_to

  TABLES

    activitygroups = lt_activitygroups

    return        = lt_return.


Hope you enjoy creating new ABAP users programatically.

How to make file-dialogues scriptable (vba)

$
0
0

As Holger Kohn stated here, file dialogues are GUI-modal and recordable in SAP-Basis Release 740 only. Meaning, if you're up to date, you won't have troubles to control Save-As or Open-dialogues within your scripts. But also for those of us still on earlier releases, there's no need to despair!

 

This is how you get it done:

 

The key to accessing external file dialogues while executing your SAPGui Script, is Windows API functions. Basically, it's about finding the right window and bringing it to the foreground, so you can manipulate it via sendkey commands. It's first of all these two functions which you have to make use of:

Public Declare Function FindWindow Lib "user32" _

          (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

 

Private Declare Function SetForegroundWindow Lib "user32" _

         (ByVal hwnd As Long) As Long

 

If you can't find out the ipClassName (e.g. using tools like Spy++) and the FindWindow function won't find and return the window handle only by it's name, don't worry. There is a ready-to-use solution that's more complex but also more reliable: Access/VBA Tutorials - Bring an external application window to the foreground. (You can simply copy and paste it ...)

 

But be careful: If you refer to Gui-object methods (e.g. .pressToolbarButton "XYZ") to open the file-dialogue, your mission is already bound to fail: That's because your client has to wait for the server to answer until it can proceed. Meaning, the code that follows (bringing the save-dialogue to the foreground, sendkeys etc...) will have to wait in the queue.

 

That's why I am using sendkeys not only to fill-in and confirm the file-dialogue, but already to open it. This is how I got it solved, hope it will help some of you, too....

 

' ----------------------- Import Document -------------------------

  

Session.findById("wnd[0]/shellcont[0]/shell/shellcont[1]/shell").doubleClickItem "Key11", "Column1"

 

Call PressButton("Dokument importieren")         ' Finding the button turned out tricky for me, so I put it into a separate procedure, for better readability...
Application.Wait (Now + TimeSerial(0, 0, 3))      ' This pause makes sure the window is already open when you look for it....

 

bln = FnSetForegroundWindow("Öffnen")

  

SendKeys ThisWorkbook.Path & "\testmail.msg", True
SendKeys "{ENTER}", True

  

Application.Wait (Now + TimeSerial(0, 0, 1))

  

Session.findById("wnd[1]/usr/cmb/WSV/CA_DOC_IMPORT-LOB").Key = "ACC"
Session.findById("wnd[1]/usr/ctxt/WSV/CA_DOC_IMPORT-DOCCLASS").Text = "YOTH"
Session.findById("wnd[1]/usr/cmb/WSV/CA_DOC_IMPORT-VISIBILITY_LEVEL").Key = "D"

  


Review: Use Limits of SAP ActiveX Libraries in .NET Environment PowerShell

$
0
0

Hello community,

 

in a normal case you can use the COM libraries, like the SAP ActiveX, very easily in PowerShell scripting language.

 

$SAPFunc = New-Object -ComObject "SAP.Functions"

 

This should be sufficient to use the SAP ActiveX libraries. But in this case you don't have access to the members of the library.

001.JPG

At this point I use the way I described here, I use the helper functions. In this case you can use the ActiveX libraries, but you are missing the advantages of the Integrated Script Environment (ISE) of PowerShell, e.g. code completion.

 

To compensate this disadvantage I create with the type library importer (tlbimp) of the Microsoft .NET SDK an interoperability library.

 

[Reflection.Assembly]::LoadFile("C:\Dummy\Interop.wdtfuncs.dll")

$SAPFunc = New-Object Interop.wdtfuncs.SAPFunctionsClass

 

Now it works all as expected.

002.JPG

So far so good, but the next problem is in front of the door.

 

In a normal case you use the Connection property in the next step.

 

$Connection = $SAPFunc.Connection

 

It works, you get the object, but not all members.

003.JPG

Now I do the same steps with wdtlog.ocx and want to cast the object variable on different ways into the connection class type, but without success.

 

[Interop.wdtlog.ConnectionClass]$Connection = $SAPFunc.Connection

$Connection = [Interop.wdtlog.ConnectionClass]$SAPFunc.Connection

$Connection -As [Interop.wdtlog.ConnectionClass]

$Connection -Is [Interop.wdtlog.ConnectionClass]

 

As far as I can see it is not possible to cast a System.__ComObject to another in PowerShell.

 

Maybe someone has a solution for this problem.

 

Another insight: It is not in any case possible to use reference variables with COM libraries. If you work with an interoperability library it is as well not possible.

 

From these points it seems reasonable to use NCo, which I described here and here.

 

 

2015/09.20 - Addendum: The casting method that I described here doesn't work also. It crashes PowerShell with the errors System.AccessViolationException and System.RuntimeType.ForwardCallToInvokeMember at the point where the first property is assigned..

 

#-Begin-----------------------------------------------------------------

 

  #-Sub Main------------------------------------------------------------

    Function Main() {

 

      $Path = $PSScriptRoot

      [Reflection.Assembly]::LoadFile($Path + "\wdtlog.dll")

      [Reflection.Assembly]::LoadFile($Path + "\wdtfuncs.dll")

      [Reflection.Assembly]::LoadFile($Path + "\wdtaocx.dll")

 

      $SAPFunc = New-Object wdtfuncs.SAPFunctionsClass

      If ($SAPFunc -eq $Null) {

        Break

      }

 

      $SAPFunc.AboutBox()

 

      $Connection = $SAPFunc.Connection

      If ($Connection -eq $Null) {

        Break

      }

 

      [wdtlog.Connection]$Connection =

        [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Connection,

        [wdtlog.ConnectionClass])

 

      $Connection.Client = "001"

      $Connection.User = "BCUSER"

      $Connection.Password = "minisap"

      $Connection.Language = "EN"

      $Connection.System = "NSP"

      $Connection.HostName = "ABAP702"

      $Connection.SystemNumber = 0

    

      $SAPConnection = $Connection.Logon(0, -1)

      If ($SAPConnection -ne 0) {

        Write-Host "Check connection in SAP system with transactin SMGW"

 

      }

 

    }

 

  #-Main----------------------------------------------------------------

    Main

 

#-End-------------------------------------------------------------------

 

Cheers

Stefan

How To Use NCo With PowerShell: Client Apps (PowerShell uses .NET Connector)

$
0
0

Hello community,

 

at the beginning of this year I presented here, here and here different possibilities to use SAPs .NET Connector (NCo) in the context of the scripting language PowerShell. Now with new experience and knowledge I can say that the use of this combination is very profitably. You can try different programming situations fast, without a special IDE. Also you can use it to automate a lot of things, e.g. to download and compare data (customizing on different systems). The PowerShell ISE (Integrated Script Environment) with the integrated debugger offers a good range of functions, e.g. like syntax highlighting and code completion. All in all I can recommend PowerShell unlimited. As a further outlook for the future, a combination of SAP GUI Scripting, NCo and PowerShell seems very interesting. We will see...

 

Here now two more examples. The first shows how to ping an SAP system via RFC_PING and the build-in ping from NCo. The second shows how to get system via the function module RFC_SYSTEM_INFO. The code is commented and I think easy to understand. It shows, in conjunction with the links above, all variants of parameters.

 

 

1st example - Ping

 

#-Begin-----------------------------------------------------------------


  <#

    .Synopsis

      Calls PING of an SAP system.

    .Description

      Calls the function module RFC_PING and the build-in function

      Ping of the .NET connector.

  #>

 

  #-Sub Load-NCo--------------------------------------------------------

    Function Load-NCo {

 

      $ScriptDir = $PSScriptRoot

 

      $Size = [System.IntPtr]::Size

      If ($Size -eq 4) {

        $Path = $ScriptDir + "\x86\"

      }

      ElseIf ($Size -eq 8) {

        $Path = $ScriptDir + "\x64\"

      }

 

      [Reflection.Assembly]::LoadFile($Path + "sapnco.dll") > $Null

      [Reflection.Assembly]::LoadFile($Path + "sapnco_utils.dll") > $Null

 

    }

 

  #-Function Get-Destination--------------------------------------------

    Function Get-Destination {

 

      #-Connection parameters-------------------------------------------

        $cfgParams = New-Object SAP.Middleware.Connector.RfcConfigParameters

        $cfgParams.Add("NAME", "TEST")

        $cfgParams.Add("ASHOST", "ABAP")

        $cfgParams.Add("SYSNR", "00")

        $cfgParams.Add("CLIENT", "001")

        $cfgParams.Add("USER", "BCUSER")

 

        $SecPasswd = Read-Host -Prompt "Passwort" -AsSecureString

        $ptrPasswd = [Runtime.InteropServices.Marshal]::SecureStringToBStr($SecPasswd)

        $Passwd = [Runtime.InteropServices.Marshal]::PtrToStringBStr($ptrPasswd)

        $cfgParams.Add("PASSWD", $Passwd)

 

      Return [SAP.Middleware.Connector.RfcDestinationManager]::GetDestination($cfgParams)

 

    }

 

  #-Sub Execute-Ping----------------------------------------------------

    Function Execute-Ping () {

 

      $destination = Get-Destination

 

      #-Metadata--------------------------------------------------------

        [SAP.Middleware.Connector.IRfcFunction]$rfcFunction =

          $destination.Repository.CreateFunction("RFC_PING")

 

      #-Variant 1: Call function module---------------------------------

        Try {

          $rfcFunction.Invoke($destination)

          Write-Host "Ping successful"

        }

        Catch {

          Write-Host "Exception" $_.Exception.Message "occured"

        }

 

      #-Variant 2: Call build-in function-------------------------------

        Try {

          $destination.Ping()

          Write-Host "Ping successful"

        }

        Catch {

          Write-Host "Exception" $_.Exception.Message "occured"

        }

 

    }

 

  #-Sub Main------------------------------------------------------------

    Function Main () {

      If ($PSVersionTable.PSVersion.Major -ge 3) {

        Load-NCo

        Execute-Ping

      }

    }

 

  #-Main----------------------------------------------------------------

    Main

 

#-End-------------------------------------------------------------------

 

 

2nd example - RFC_SYSTEM_INFO

 

#-Begin-----------------------------------------------------------------

 

  <#

    .Synopsis

      Calls RFC_SYSTEM_INFO of an SAP system.

    .Description

      Calls the function module RFC_SYSTEM_INFO and writes the result

      on the screen.

  #>

 

  #-Sub Load-NCo--------------------------------------------------------

    Function Load-NCo {

 

      $ScriptDir = $PSScriptRoot

 

      $Size = [System.IntPtr]::Size

      If ($Size -eq 4) {

        $Path = $ScriptDir + "\x86\"

      }

      ElseIf ($Size -eq 8) {

        $Path = $ScriptDir + "\x64\"

      }

 

      [Reflection.Assembly]::LoadFile($Path + "sapnco.dll") > $Null

      [Reflection.Assembly]::LoadFile($Path + "sapnco_utils.dll") > $Null

 

    }

 

  #-Function Get-Destination--------------------------------------------

    Function Get-Destination {

 

      #-Connection parameters-------------------------------------------

        $cfgParams = New-Object SAP.Middleware.Connector.RfcConfigParameters

        $cfgParams.Add("NAME", "TEST")

        $cfgParams.Add("ASHOST", "ABAP")

        $cfgParams.Add("SYSNR", "00")

        $cfgParams.Add("CLIENT", "001")

        $cfgParams.Add("USER", "BCUSER")

 

        $SecPasswd = Read-Host -Prompt "Passwort" -AsSecureString

        $ptrPasswd = [Runtime.InteropServices.Marshal]::SecureStringToBStr($SecPasswd)

        $Passwd = [Runtime.InteropServices.Marshal]::PtrToStringBStr($ptrPasswd)

        $cfgParams.Add("PASSWD", $Passwd)

 

      Return [SAP.Middleware.Connector.RfcDestinationManager]::GetDestination($cfgParams)

 

    }

 

  #-Sub Get-SystemInfo--------------------------------------------------

    Function Get-SystemInfo () {

 

      $destination = Get-Destination

 

      #-Metadata--------------------------------------------------------

        [SAP.Middleware.Connector.IRfcFunction]$rfcFunction =

          $destination.Repository.CreateFunction("RFC_SYSTEM_INFO")

 

      #-Call function module--------------------------------------------

        Try {

          $rfcFunction.Invoke($destination)

 

          [SAP.Middleware.Connector.IRfcStructure]$Export =

            $rfcFunction.GetStructure("RFCSI_EXPORT")

 

          #-Get information---------------------------------------------

            Write-Host $Export.GetValue("RFCHOST")

            Write-Host $Export.GetValue("RFCSYSID")

            Write-Host $Export.GetValue("RFCDBHOST")

            Write-Host $Export.GetValue("RFCDBSYS")

 

        }

        Catch {

          Write-Host "Exception" $_.Exception.Message "occured"

        }

 

    }

 

  #-Sub Main------------------------------------------------------------

    Function Main () {

      If ($PSVersionTable.PSVersion.Major -ge 3) {

        Load-NCo

        Get-SystemInfo

      }

    }

 

  #-Main----------------------------------------------------------------

    Main

 

#-End-------------------------------------------------------------------

 

If you like you can find the same functionalityhere, with SAP NetWeaver RFC SDK and FreeBASIC, to compare it.

 

Enjoy it.

 

Cheers

Stefan

From PowerShell Zero To SAP GUI Scripting Hero

$
0
0

Hello community,

 

I wrote here about the possibility to use SAP GUI Scripting inside PowerShell. The basis of this approach is Visual Basic Script (VBS) resp. the Microsoft Script Control. On this way it is very easy to use the existing scripts furthermore. Here I have compared the use of .NET invoke methods with the use of MSScriptControl. This was a step forward to .NET, but the disadvantage is that you can't use the advantages of the Integrated Script Environment (ISE) like code completion.

 

To walk around this shortcoming I searched for a method to cast the ___COMObject types to correct data types in a namespace of a .NET assembly. I found the method CreateWrapperOfType. All I have to do is to build with the type library importer (TLBIMP) a .NET assembly from the SAPFEWSE.OCX library - thats all. If you don't habe the possibility to build it at your own, look in your NWBC or SapDtsCOMFramework directory, here you find the assembly Interop.SAPFEWSELib.dll, it is also possible to use this assembly.

 

Hint: The assembly of the NWBC use the namespace Interop.SAPFEWSELib, the assembly of the Dynamic Test Script framework use only SAPFEWSELib. I find that very awkward. If you use the assembly of the NWBC you must modfy the types below and add Interop. in front of the type names.

 

A code line with a ___COMObject casting to another type of a SAP GUI Scripting object looks like this:

[SAPFEWSELib.GuiApplication]$Application =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Application,

    [SAPFEWSELib.GuiApplicationClass])

Here I cast the variable $Application from the type System.___COMObject to GuiApplication - our base friend of the SAP GUI Scripting object hierarchy. I think it is easy to understand what happens.

 

Here now the complete code:

 

#-Begin-----------------------------------------------------------------

 

  [Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")

 

  [Reflection.Assembly]::LoadFile("SAPFEWSELib.dll")

 

  $SapGuiAuto = [Microsoft.VisualBasic.Interaction]::GetObject("SAPGUI")

  If ($SapGuiAuto -eq $Null) {

    Break

  }

 

  $Application = $SapGuiAuto.GetType().InvokeMember("GetScriptingEngine",

        [System.Reflection.Bindingflags]::InvokeMethod,

        $null, $SapGuiAuto, $null, $null, $null, $null)

  [SAPFEWSELib.GuiApplication]$Application =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Application,

    [SAPFEWSELib.GuiApplicationClass])

  If ($Application -eq $Null) {

    Break

  }

 

  $Connection = $Application.Children.Item(0)

  [SAPFEWSELib.GuiConnectionClass]$Connection =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Connection,

    [SAPFEWSELib.GuiConnectionClass])

  If ($Connection -eq $Null) {

    Break

  }

 

  $Session = $Connection.Children.Item(0)

  [SAPFEWSELib.GuiSession]$Session =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Session,

    [SAPFEWSELib.GuiSessionClass])

  If ($Session -eq $Null) {

    Break

  }

 

  $Mandt = $Session.FindById("wnd[0]/usr/txtRSYST-MANDT")

  [SAPFEWSELib.GuiTextField]$Mandt =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Mandt,

    [SAPFEWSELib.GuiTextFieldClass])

  $BName = $Session.FindById("wnd[0]/usr/txtRSYST-BNAME")

  [SAPFEWSELib.GuiTextField]$BName =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($BName,

    [SAPFEWSELib.GuiTextFieldClass])

  $Langu = $Session.FindById("wnd[0]/usr/txtRSYST-LANGU")

  [SAPFEWSELib.GuiTextField]$Langu =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Langu,

    [SAPFEWSELib.GuiTextFieldClass])

  $BCode = $Session.FindById("wnd[0]/usr/pwdRSYST-BCODE")

  [SAPFEWSELib.GuiPasswordField]$BCode =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($BCode,

    [SAPFEWSELib.GuiPasswordFieldClass])

 

  $Mandt.Text = "001"

  $BName.Text = "BCUSER"

  $Langu.Text = "EN"

  $Bcode.Text = "minisap"

 

  $MainWin = $Session.FindById("wnd[0]")

  [SAPFEWSELib.GuiMainWindow]$MainWin =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($MainWin,

    [SAPFEWSELib.GuiMainWindowClass])

  $MainWin.SendVKey(0)

 

#-End-------------------------------------------------------------------

 

This example is equivalent to the examples in the links above. Sure it is more complex as VBScript, but it offers all adavantages of the integration in the .NET environment.

 

001.jpg

 

This possibility opens now the gate wide to use SAP GUI Scripting seamlessly in PowerShell and in any other language ot the .NET environment.

 

Hint after: As far as I know uses the NetWeaver Business Client (NWBC), version 4 and 5, as well as the Dynamic Test Scripts (DTS) framework the same method. SAP dts is a test framework which enables automation of testing SAP applications. Look at US patent US 20120023485 A1 for more information about DTS.

 

Enjoy it.

 

Cheers

Stefan

Use Named References of SAP ActiveX Libraries in VBA for Easy Programming

$
0
0

Hello community,

 

many Microsoft Office programmers uses the SAP ActiveX libraries of the SAP GUI for Windows installation. The reason for this descision is the availability of the libraries. To use the libraries it is necessary to reference it.

 

Menu Tools > References

101.jpg

Browse > Add Reference

102.jpg

It is necessary to add wdtfuncs.ocx, wdttaocx.ocx, wdtlog.ocx and optional wdobapi.ocx. You can find the files in the directory of the SAP GUI, C:\Program Files (x86)\SAP\FrontEnd\sapgui.

 

103.JPG

Hint: The wdtlog.ocx is in the directory C:\Program Files (x86)\Common Files\SAP Shared.

 

Hint: You can also use the unicode versions of the libraries, the filenames ends with an u.

 

104.jpg

So far the prelude. You can find a very good equivalent description here.

 

But many Office programmers declare their object variables in VBA only as Object. If they choose this method is it at first not necessary to reference the libraries and on second they lose the very good code completion functionality.


'-Begin-----------------------------------------------------------------

  Sub Test()

 

    '-Variables---------------------------------------------------------

      Dim oFunc As Object

      Dim oConn As Object

      Dim SAPConn As Integer

    

    Set oFunc = CreateObject("SAP.Functions.Unicode")

    If Not IsObject(oFunc) Then

       MsgBox "CreateObject(SAP.Functions.Unicode) failed", vbOKOnly, _

         "Error"

       Exit Sub

    End If

 

    Set oConn = oFunc.Connection()

    If Not IsObject(oConn) Then

      MsgBox "SAPFunc.Connection failed", vbOKOnly, "Error"

      Exit Sub

    End If

 

    SAPConn = oConn.Logon(0, vbFalse)

    If SAPConn <> 0 Then

 

      oConn.Logoff

    Else

      MsgBox "Connection.Logon failed", vbOKOnly, "Error"

    End If

 

  End Sub

'-End-------------------------------------------------------------------


If you want to benefit from the advantages, is it necessary to change the source like this:


'-Begin-----------------------------------------------------------------

  Sub Test()

 

    '-Variables---------------------------------------------------------

      Dim oFunc As SAPFunctionsOCX.SAPFunctions

      Dim oConn As SAPLogonCtrl.Connection

      Dim SAPConn As Integer

    

    Set oFunc = CreateObject("SAP.Functions.Unicode")

    If Not IsObject(oFunc) Then

       MsgBox "CreateObject(SAP.Functions.Unicode) failed", vbOKOnly, _

         "Error"

       Exit Sub

    End If

 

    Set oConn = oFunc.Connection()

    If Not IsObject(oConn) Then

      MsgBox "SAPFunc.Connection failed", vbOKOnly, "Error"

      Exit Sub

    End If

 

    SAPConn = oConn.Logon(0, vbFalse)

    If SAPConn <> 0 Then

 

      oConn.Logoff

    Else

      MsgBox "Connection.Logon failed", vbOKOnly, "Error"

    End If

 

  End Sub

'-End-------------------------------------------------------------------

 

As you can see is the source equivalent but the declarations of the variables oFunc and oConn are now not longer Objects, they are now definitive types from the library. And now the VBA IDE offers the code completion.

 

105.jpg

If you reference the SAP ActiveX libraries you should also declare your variables with the correct types, otherwise it makes no sense.

 

Hint: You can find information how to use SAP ActiveX libraries without SAP GUI for Windows here.

 

Cheers

Stefan

How to Use PowerShell for Automated Login to SAP

$
0
0

Hello community,

 

here is an interesting discussion how to automated logon to an SAP system via VBScript. But VBScript offers not really a function to detect the moment if the session is available. Here now an implementation how to do same with PowerShell. PowerShell offers with its interface to the Windows API an easy way to detect the availability of the new session.

 

Here now the code how to detect the availability of the session:

 

#-Begin-----------------------------------------------------------------

 

$Sig = @'

[DllImport("user32.dll", CharSet = CharSet.Auto)]

public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

'@

 

  #-Add FindWindow function---------------------------------------------

    $Win32 = Add-Type -Namespace Win32 -Name Funcs -MemberDefinition $Sig -PassThru

 

  #-Set the path to the SAP GUI directory-------------------------------

    $SAPGUIPath = "C:\Program Files (x86)\SAP\FrontEnd\SAPgui\"

 

  #-Set the SAP system ID or the IP address-----------------------------

    $SID = "NSP"

 

  #-Set the instance number of the SAP system---------------------------

    $InstanceNo = "00"

 

  #-Starts the SAP GUI--------------------------------------------------

    $SAPGUI = $SAPGUIPath + "sapgui.exe"

    & $SAPGUI $SID $InstanceNo

 

  While ($Win32::FindWindow("SAP_FRONTEND_SESSION", "SAP") -eq 0) {

    Start-Sleep -Milliseconds 250

  }

 

  Write-Host "Here now your script..."

 

#-End-------------------------------------------------------------------

 

Here now the code how to logon automatically:

 

#-Begin-----------------------------------------------------------------

 

  #-Includes------------------------------------------------------------

    ."COM.ps1"

 

  #-Signatures----------------------------------------------------------

    $Sig = @'

[DllImport("user32.dll", CharSet = CharSet.Auto)]

public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

'@

 

  #-Add FindWindow function---------------------------------------------

    $Win32 = Add-Type -Namespace Win32 -Name Funcs -MemberDefinition $Sig -PassThru

 

  #-Set the path to the SAP GUI directory-------------------------------

    $SAPGUIPath = "C:\Program Files (x86)\SAP\FrontEnd\SAPgui\"

 

  #-Set the SAP system ID-----------------------------------------------

    $SID = "localhost"

 

  #-Set the instance number of the SAP system---------------------------

    $InstanceNo = "00"

 

  #-Start the SAP GUI---------------------------------------------------

    $SAPGUI = $SAPGUIPath + "sapgui.exe"

    & $SAPGUI $SID $InstanceNo

 

  #-Wait until the session is available---------------------------------

    While ($Win32::FindWindow("SAP_FRONTEND_SESSION", "SAP") -eq 0) {

      Start-Sleep -Milliseconds 250

    }

 

  #-Logon to SAP GUI session--------------------------------------------

    $SapGuiAuto = Get-Object("SAPGUI")

    $application = Invoke-Method $SapGuiAuto "GetScriptingEngine"

    $connection = Get-Property $application "Children" @(0)

    $session = Get-Property $connection "Children" @(0)

 

    $ID = Invoke-Method $session "findById" @("wnd[0]/usr/txtRSYST-MANDT")

    Set-Property $ID "Text" @("001")

    $ID = Invoke-Method $session "findById" @("wnd[0]/usr/txtRSYST-BNAME")

    Set-Property $ID "Text" @("BCUSER")

    $ID = Invoke-Method $session "findById" @("wnd[0]/usr/pwdRSYST-BCODE")

    Set-Property $ID "Text" @("minisap")

    $ID = Invoke-Method $session "findById" @("wnd[0]/usr/txtRSYST-LANGU")

    Set-Property $ID "Text" @("EN")

 

    $ID = Invoke-Method $session "findById" @("wnd[0]")

    Invoke-Method $ID "sendVKey" @(0)

 

#-End-------------------------------------------------------------------

 

You can find the COM include file here.

 

Enjoy it.

 

Cheers

Stefan

Viewing all 60 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>