Search code examples
pythonsap-gui

How to Log into SAPGUI using Python 3?


I'm trying to login to SAPGUI with Python in order to create a script to run a transaction automatically and I'm running into an issue. Whenever I try to login I get a <class 'pywintypes.com_error'> error. I'm not sure why I'm getting this error. This is my first time trying to navigate into SAP GUI so I'm not sure if I am missing an important line to establish a successful logon or not.

We've utilized VBA previously but my coworkers have expressed interest into possibly moving our automation scripts from Excel to Python and since I'm the most experience with Python I've been tasked in getting it working.

# Importing the Libraries
import win32com.client
import sys
import subprocess
import time


# This function will Login to SAP from the SAP Logon window

def saplogin():

    try:

        path = r"C:\Program Files (x86)\SAP\FrontEnd\SAPgui\saplogon.exe"
        subprocess.Popen(path)
        time.sleep(10)


        SapGuiAuto = win32com.client.GetObject("SAPGUI")
        if not type(SapGuiAuto) == win32com.client.CDispatch:
            return

        application = SapGuiAuto.GetScriptingEngine
        if not type(application) == win32com.client.CDispatch:
            SapGuiAuto = None
            return

        connection = application.Children(0)
        if not type(connection) == win32com.client.CDispatch:
            application = None
            SapGuiAuto = None
            return

        session = connection.Children(1)
        if not type(session) == win32com.client.CDispatch:
            connection = None
            application = None
            SapGuiAuto = None
            return

        session.findById("wnd[0]/usr/txtRSYST-BNAME").text = "USER"
        session.findById("wnd[0]/usr/pwdRSYST-BCODE").text = "PASS"
        session.findById("wnd[0]").sendVKey(0)

    except:
        print(sys.exc_info()[0])

    finally:
        session = None
        connection = None
        application = None
        SapGuiAuto = None


saplogin()

Solution

  • Probably I'll answer more than one question. I assume that people automate SAP for long repeatable operations. Anyway the information below will be useful. Tested on SAP Client 7.20 and 7.50, Python 3.8.3.

    First, you may use a config.py:

    import os  # for path join
    
    SAP_SID = 'SID'
    SAP_MANDANT = '1000'
    SAP_USER = 'login'
    SAP_PASS = 'secret'
    SAP_EXE = "C:\Program Files (x86)\SAP\FrontEnd\SAPgui\sapshcut.exe"
    
    # minimize SAP window, enable it after debugging is completed; unfortunately small windows still appear with this setting
    ICONIFY = False
    
    # screenshots for debug and visual logging
    SCR_ERR = os.path.join('path', 'err_screen.png')
    

    Second, the main script, which performs a connection, then opens a transations:

    import config
    import pyautogui as pya  # for screenshots
    import time  # for count seconds for approx. measuring
    import subprocess
    import win32com.client
    from win32gui import GetWindowText, GetForegroundWindow
    
    
    def saplogin():
        try:
            # 1.1. Connect to SAP
            subprocess.check_call([config.SAP_EXE,
                                   '-user=%s' % config.SAP_USER,
                                   '-pw=%s' % config.SAP_PASS,
                                   '-system=%s' % config.SAP_SID,
                                   '-client=%s' % config.SAP_MANDANT])
    
            time.sleep(10)  # need to ensure SAP created a session
    
            # Get session
            sap_gui_auto = win32com.client.GetObject('SAPGUI').GetScriptingEngine
            session = sap_gui_auto.findById("con[0]/ses[0]")  # 1 - to run the second session comment this line and uncomment the next, with con[1] and so on; probably it may work with a param stored in config
            # session = sap_gui_auto.findById("con[1]/ses[0]")  # 2
            # session = sap_gui_auto.findById("con[2]/ses[0]")  # 3
            # session = sap_gui_auto.findById("con[3]/ses[0]")  # 4
    
            # 1.2. Check if dialog window appears
            if session.children.count > 1:
                try:
                    # 1.2.1 License window (this code was written for different PCs with the same ses[0]; 
                    # to run this code on the same machine it shoild be modified by adding a check for ses[1] and so on)
                    # Open new session if the title equals to <your title>
                    if (GetWindowText(GetForegroundWindow())) == "<Информация по лицензии при многократной регистрации>":
                        print("Title: %s" % session.children(1).text)  # you may use this instead of GetWindowText in the line above
                        
                        # Choose an option to create additional session
                        try:
                            session.findById("wnd[1]/usr/radMULTI_LOGON_OPT2").select()
                            session.findById("wnd[1]/usr/radMULTI_LOGON_OPT2").setFocus()
                            session.findById("wnd[0]").sendVKey(0)
    
                        except Exception as ex:
                            print("Cannot perform an operation (%s)" % ex)
                            pya.screenshot(config.SCR_ERR)
                            exit(1)
    
                    time.sleep(1)  # for case if new dialog window appears
    
                    try:
                        # Now we are about to be logged. Often at this moment the system message is shown 
                        if session.children(1).text == "<Системные сообщения>":  # replace to your text
                            print("Title: %s" % session.children(1).text)
                            # in my case this window always has two lines of text; you may safely delete these two lines of code
                            print("%s | %s" % (session.findById("wnd[1]/usr/lbl[4,1]").text,
                                                           session.findById("wnd[1]/usr/lbl[17,1]").text))
                            print("%s | %s" % (session.findById("wnd[1]/usr/lbl[4,3]").text,
                                                           session.findById("wnd[1]/usr/lbl[17,3]").text))
                            
                            print("The window is closed '%s', please wait..." % session.children(1).text)
                            session.findById("wnd[1]").sendVKey(0)
                        else:
                            # Exit if the window has unknown title
                            print("Title: %s. Exit." % session.children(1).text)
                            pya.screenshot(config.SCR_ERR)
                            exit(1)
    
                    except Exception as ex:
                        # Exit for unknown window
                        print("Unknown window: %s. Exit (%s)" % (session.children(1).text, ex))
                        pya.screenshot(config.SCR_ERR)
                        exit(1)
    
                except Exception as ex:
                    # Finally, exit for unknown reason
                    print("Unknown error: %s. Exit (%s)" % (session.children(1).text, ex))
                    pya.screenshot(config.SCR_ERR)
                    exit(1)
    
            print("Logged to SAP.")
            return session
    
        except Exception as ex:
            print("Error. Cannot create session. Exit (%s)" % ex)
            pya.screenshot(config.SCR_ERR)
            exit(1)
    
    
    def export_npp(session, params):
        # 2.1. Start counter (for those who cares about statistics and measuring)
        start = time.time()
    
        if config.ICONIFY:
            # minimize main window
            session.findById("wnd[0]").iconify()
        else:
            # or resize it
            session.findById("wnd[0]").resizeWorkingPane(84, 40, 0)
    
        # 2.2. Open transation
        session.findById("wnd[0]/tbar[0]/okcd").text = 'transaction'
        session.findById("wnd[0]").sendVKey(0)
    
        """
        startx = time.time()
        'doing some repeatable stuff'
        finish = time.time()
        total_time = finish - startx
        print("Time %02d seconds added to dict" % total_time)
        """
        total_finish = time.time()
        total_time = total_finish - start
        print("Total: %02d seconds" % total_time)
    
    
    # Begin, start session
    ses = saplogin()
    
    # Use params of session to use it in transaction 'export_npp' and so on
    npp = export_npp(ses, params)