---
# System prepended metadata

title: Python + SAPGUI Script = 自動化重覆性事務性作業

---

# Python + SAPGUI Script = 自動化重覆性事務性作業
###### `python` `SAP` `SAPGUI` `GUI Script`
[reference](https://k-weiming.github.io/2021-08-23-sap-connection-python/)

今天因為其他部門的資訊作業上的問題導致我這邊必需協助刪除庫存批屬性，雖然才八十幾筆，但也是讓我感到憤怒，憤怒可以提升技術力，所以我決定找方法自動化處理。

兩年前的今天，k-weiming寫下這個，真的是緣份，我的環境是python 3.11，需要的工具就是pywin32：
```shell=
pip install pywin32
```

sap的設置也要確認，不過這取決於公司的管理策略，如果剛好沒被管控的話那就可以使用：
![](https://hackmd.io/_uploads/BJ3NTHQ6n.png)
1. Enable scripting：啟用scripting
2. Notify when a script attaches to SAP GUI：對接上的時候是不是會跳出訊息，另一個也是一樣的設置


準備工作完成，再來就是要先取得執行的GUI的session：
```python=
import win32com.client

def get_client():
    #: 從com服務器中取得SAPGUI的物件
    sap_gui_auto = win32com.client.GetObject('SAPGUI')

    if not type(sap_gui_auto) == win32com.client.CDispatch:
        print("SAPGUI is not running")
        return

    #: 取得SAPGUI的腳本引擎，所以接上的時候GUI會出現警示訊息
    #: 這可以從gui設罝上取消
    application = sap_gui_auto.GetScriptingEngine
    if not type(application) == win32com.client.CDispatch:
        print("SAPGUI is not running")
        return

    #: 這邊取得的是實際啟動的SAPGUI的物件
    #: 以實際登入的使用者為條件
    for conn in range(application.Children.Count):
        connection = application.Children(conn)

        #: 這邊取得的是登入的帳號所開啟的視窗(session)
        for sess in range(connection.Children.Count):
            session = connection.Children(sess)
            if session.Info.Transaction == "SESSION_MANAGER":
                return session
```

接下來的動作我們可以先利用錄製的方式來取得腳本，畢竟欄位框那麼多，即使看著官方的文件也很難一個一個找出來，最好的方法就是讓系統幫我們找出來。

開啟錄製程式：
![](https://hackmd.io/_uploads/H1RJ0HXp2.png)

錄製程式開啟之後如下：
![](https://hackmd.io/_uploads/HyFz0SXp3.png)

中間那個圓點就是腳本錄製，腳本的保存地方則是根據你的『Save To』而定。

錄製完成之後在指定資料夾就會有`vbs`與`txt`檔，打開`txt`檔，裡面會有你的腳本資訊：
![](https://hackmd.io/_uploads/BJEhRHmph.png)

紅框處的部份就是操作過程的相關物件的定位資訊以及操作的動作，像是按下去或是選擇某個分頁之類的。

以我自己的作業目標為例，我是想要自動化刪除很多庫存批屬性資料，所以就可以這麼做：
```python=
if __name__ == '__main__':
    #: reference
    #: https://k-weiming.github.io/2021-08-23-sap-connection-python/
    session = get_client()
    #: 到SAP的交易代碼輸入欄位輸入MSC2N
    session.findById("wnd[0]/tbar[0]/okcd").text = "/nMSC2N"
    #: 按下Enter
    session.findById("wnd[0]").sendVKey(0)
    #: 輸入物料編號
    session.findById("wnd[0]/usr/subSUBSCR_BATCH_MASTER:SAPLCHRG:1111/subSUBSCR_HEADER:SAPLCHRG:1500/ctxtDFBATCH-MATNR").text = "CA09B2002-1Z01"
    #: 輸入工廠
    session.findById("wnd[0]/usr/subSUBSCR_BATCH_MASTER:SAPLCHRG:1111/subSUBSCR_HEADER:SAPLCHRG:1500/ctxtDFBATCH-WERKS").text = "A101"
    #: 輸入批號
    session.findById("wnd[0]/usr/subSUBSCR_BATCH_MASTER:SAPLCHRG:1111/subSUBSCR_HEADER:SAPLCHRG:1500/ctxtDFBATCH-CHARG").text = "A120069722"
    #: 按下Enter
    session.findById("wnd[0]").sendVKey(0)
    #: 選擇批屬性分頁
    session.findById("wnd[0]/usr/subSUBSCR_BATCH_MASTER:SAPLCHRG:1111/subSUBSCR_TABSTRIP:SAPLCHRG:2000/tabsTS_BODY/tabpCLAS").select()
    #: 按下刪除
    session.findById("wnd[0]/usr/subSUBSCR_BATCH_MASTER:SAPLCHRG:1111/subSUBSCR_TABSTRIP:SAPLCHRG:2000/tabsTS_BODY/tabpCLAS/ssubSUBSCR_BODY:SAPLCHRG:2300/btnCLASS_DEL").press()
```

我的範例中少了按下保存的動作，因為只是測試，所以就沒有錄保存的動作。

這接口很方便，因為我不需要去寫abap來做錄製，不過這種作法應該是比較適用於重覆性的事務性作業，又或者剛好沒有BAPI、RFC可以使用，那就可以用這種方式來搜集資料。