【SAP自动化】(1)自动登录

本文介绍了如何使用Python进行SAP自动化操作,特别是创建SAP Session和实现自动登录的功能。文中提供了实用的Python函数,并进行了测试,旨在方便SAP自动化脚本的开发和测试。
该文章已生成可运行项目,

前言

最近,接触的项目需要自动化操作SAP,所以对SAP的自动化脚本进行调查了。整理了一些函数,用于创建SAP Session和自动化登录。

实用函数

项目中使用的是Python语言,如果需要其他语言的版本可参考这里的函数。

创建SAP Session实用函数

import time
from contextlib import contextmanager

import pythoncom
import pywintypes
import win32com.client as client


LANGUAGES_WINDOW_TITLES = {
    "EN": {
        "MULTIPLE_LOGONS": "License Information for Multiple Logons",
        "COPYRIGHT": "Copyright",
        "CHANGE_PASSWORD": "SAP"
    },
    "ZH": {
        "MULTIPLE_LOGONS": "多次登录许可证信息",
        "COPYRIGHT": "版权",
        "CHANGE_PASSWORD": "SAP"
    },
    "JA": {
        "MULTIPLE_LOGONS": "多重ログオンに関するライセンス情報",
        "COPYRIGHT": "著作権",
        "CHANGE_PASSWORD": "SAP"
    }
}


def create_connection(*, description="", mandt="", name="", password="", language="EN"):
    """
    创建一个新的SAP服务器连接,并打开一个客户端窗口。

    :param description: 登录描述字符串
    :param mandt: 集团
    :param name: 用户名
    :param password: 密码
    :param language: 客户端语言
    :return: 返回SAP GuiConnection对象
    """
    pythoncom.CoInitialize()
    try:
        sapgui = client.GetObject("SAPGUI")
    except pywintypes.com_error as _:
        pythoncom.CoUninitialize()
        raise RuntimeError("检测到SAP客户端没有启动, 请启动SAP客户端后重试。")

    try:
        application = sapgui.GetScriptingEngine
        connection = application.OpenConnection(description, True)
    except pywintypes.com_error as _:
        pythoncom.CoUninitialize()
        raise RuntimeError("连接SAP服务器失败, 请确认连接参数和服务器状态正常后重试。")

    try:
        # 使用第一个未登录的Session创建一个登录的Session
        session = connection.Sessions(0)
        active_window = session.ActiveWindow
        active_window.findById("usr/txtRSYST-MANDT").text = mandt
        active_window.findById("usr/txtRSYST-BNAME").text = name
        active_window.findById("usr/pwdRSYST-BCODE").text = password
        active_window.findById("usr/txtRSYST-LANGU").text = language
        active_window.sendVKey(0)

        # 有模态窗口, Session的Children.Count表示是窗口数量
        while session.ActiveWindow.Type == "GuiModalWindow":
            wnd = session.ActiveWindow
            title = wnd.Text  # 临时保存窗口标题

            # 关闭多用登录提示窗口
            if title == LANGUAGES_WINDOW_TITLES[language]["MULTIPLE_LOGONS"]:
                # 多用户登录是的确认选择。
                wnd.findById("usr/radMULTI_LOGON_OPT2").select()
                wnd.findById("tbar[0]/btn[0]").press()
            # 关闭版权提示窗口
            elif title == LANGUAGES_WINDOW_TITLES[language]["COPYRIGHT"]:
                wnd.findById("tbar[0]/btn[0]").press()
            # 修改密码窗口
            elif title == LANGUAGES_WINDOW_TITLES[language]["CHANGE_PASSWORD"]:
                print("自动登录失败,系统密码过期,请手动修改密码后重试。")
                break
            else:
                # 不识别的识别的窗口
                print(f"自动登录失败,遇到不识别的窗口({title}),请联系技术支持人员")
                # 不关闭窗口,保持出错窗口。
                # wnd.Close()
                break
    except Exception as ex:
        raise ex
    finally:
        pythoncom.CoUninitialize()

    return connection


def remove_session(session):
    """
    关闭SAP客户端窗口。

    :param session: SAP Session对象
    """
    if session:
        connection = session.Parent
        connection.CloseSession(session.Id)


def create_session():
    """
    为SAP操作任务创建独立的Session对象。

    使用第一个连接的创建脚本执行的Session,如果Session的数据大于一个时,表示有脚本正在执行。
    如果是用户打开有多个Session或由于执行失败导致多个Session残留时,需手动关闭多余的Session。

    也可以提供接口,关闭多余的Session。

    这里一个Session就是一个SAP客户端窗口。
    :return: 返回SAP GuiSession对象
    """
    try:
        sapgui = client.GetObject("SAPGUI")
    except pywintypes.com_error as _:
        raise RuntimeError("检测到SAP客户端没有启动, 请启动SAP客户端后重试。")
    application = sapgui.GetScriptingEngine

    # 连接数等于0时,表示没有用户登录SAP客户端
    if application.Connections.Count == 0:
        session = None
    else:
        # 仅使用第一个连接的Session
        session_count = application.Connections(0).Sessions.Count
        # Session数量大于一则表明有脚本正在执行(后面在尝试是否可以开更多的脚本)
        if session_count > 1:
            raise ValueError("有脚本正在执行,请稍后重试。如果确认没有脚本在执行,请关闭多余的窗口后重试。")
        else:
            # 创建一个新的Session,用于执行脚本
            application.Connections(0).Sessions(0).CreateSession()
            # 等待新创建的Session可以访问。
            # 设置超时时间
            timeout = 3
            while timeout > 0 and application.Connections(0).Sessions.Count <= 1:
                timeout -= 0.1
                time.sleep(0.1)

            # 判断是否已经超时
            if timeout < 0:
                TimeoutError("创建执行脚本的Session失败")

            session = application.Connections(0).Sessions(1)
    return session


@contextmanager
def create_sap_session():
    """
    创建Sap Session,即打开一个SAP GUI客户端窗口,以备执行后续的SAP操作。

    """
    session = None
    try:
        # 在CoInitialize的范围内使用session,否则将会抛出异常
        pythoncom.CoInitialize()
        session = create_session()
        if session is None:
            raise RuntimeError("没有登录的SAP客户端,请登录后重试")
        session.ActiveWindow.Maximize()
        yield session
    # 捕获异常可以明确判断出错原因
    except Exception as ex:
        print(ex)
        raise ex
    finally:
        remove_session(session)
        pythoncom.CoUninitialize()


@contextmanager
def create_sap_transaction(session, transaction_code):
    """
    开启一个SAP事务。
    """
    try:
        session.StartTransaction(transaction_code)
        yield
    finally:
        session.EndTransaction()


def sap_executor(handler):
    """
    在SAP客户端中,自动执行SAP操作,包括执行多个事务操作。
    """
    def wrapper(*args, **kwargs):
        with create_sap_session() as session:
            return handler(session, *args, **kwargs)
    return wrapper

测试

def do_cji3(layout, *wbs_list):
    @sap_executor
    def executor(session, *args, **kwargs):
        with create_sap_transaction(session, 'CJI3'):
            try:
                session.findById("wnd[1]/usr/ctxtTCNT-PROF_DB").text = "000000000001"
                session.findById("wnd[1]/tbar[0]/btn[0]").press()
            except:
                pass

            # 输入检索条件
            session.findById("wnd[0]/usr/ctxtCN_PSPNR-LOW").text = ""
            session.findById("wnd[0]/usr/ctxtR_BUDAT-LOW").text = ""
            if layout:
                session.findById("wnd[0]/usr/ctxtP_DISVAR").text = layout

            session.findById("wnd[0]/usr/btnBUT1").press()
            session.findById("wnd[1]/usr/txtKAEP_SETT-MAXSEL").text = "999999999"
            session.findById("wnd[1]/tbar[0]/btn[0]").press()

            session.findById("wnd[0]/usr/btn%_CN_PSPNR_%_APP_%-VALU_PUSH").press()
            session.findById("wnd[1]/usr/tabsTAB_STRIP/tabpSIVA").select()
            text = '\r\n'.join(wbs_list)
            clipboard_copy(text) # 自动定义的剪贴板拷贝函数
            session.findById("wnd[1]/tbar[0]/btn[24]").press()
            session.findById("wnd[1]/tbar[0]/btn[8]").press()
            session.findById("wnd[0]/tbar[1]/btn[8]").press()

    return executor(layout, *wbs_list)


def login():
    import login_info
    import sys

    # 自动登录,
    create_connection(**login_info.get_login_info(sys.argv[1]))


def task():
    # 执行SAP操作。
    do_cji3(None, None)

总结

使用上的函数方便测试和其他SAP脚本的作成。

仅供参考,如有帮助不胜荣幸,请关注,点赞。
如需转载请注明出处。

本文章已经生成可运行项目
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值