Zautomatyzuj SAP [2/4] – wykorzystaj przewagę Pythona

🛠️ Tutorial

Idziemy po więcej! – SAP GUI Scripting z Pythonem

W pierwszym artykule z tego “mini-cyklu” (jeśli temat jest dla Ciebie nowy, zacznij od tego wpisu) pokazałem podstawy SAP GUI scripting. Jeśli ten temat Cię zainteresował i poświęciłeś trochę czasu na “zabawę” i testowanie tej funkcjonalności systemu SAP, jest spora szansa, że doszedłeś do dwóch następujących wniosków:

  1. Narzędzie to może być prawdziwym “game changerem” w codziennej pracy z SAP-em.
  2. Samo nagrywanie i odtwarzanie skryptów ma znaczne ograniczenia i często jest niewystarczające do zautomatyzowania nieco bardziej złożonej operacji. 

Mogłeś więc czuć pewien niedosyt — trochę tak, jakby czegoś brakowało do pełnego wykorzystania potencjału tego rozwiązania i osiągnięcia pełnej automatyzacji. Tym “czymś” mogła być np. potrzeba powtórzenia jakiejś operacji wielokrotnie z wykorzystaniem różnych danych wejściowych dla każdej z nich. W ujęciu programistycznym to po prostu zastosowanie pętli, gdzie każda iteracja wykonuje te same kroki, ale z nowym zestawem danych.

W tym wpisie pokażę Ci od czego zacząć, aby, wykorzystując podstawowe umiejętności programowania, efektywnie tworzyć programy automatyzujące SAP-a. Wyjaśnię też dlaczego Python nadaje się do tego najlepiej.

Ten artykuł będzie szczególnie wartościowy dla osób, które miały choćby minimalną styczność z jakimkolwiek językiem programowania. Jeśli jednak nie zaliczasz się do tej grupy — nie zrażaj się! Skoro tu trafiłeś, to znaczy, że temat automatyzacji SAP-a jest dla Ciebie interesujący. Artykuł ten dostarczy Ci solidnych podstaw, pokaże praktyczne możliwości i — być może — zainspiruje Cię do dalszej nauki (np. wykorzystując mini kurs nauka Pythona z AI z tego bloga). Co więcej, udostępnię gotowe narzędzie, które możesz od razu wykorzystać w codziennej pracy z SAP-em.


Dlaczego Python?

Ustaliliśmy już, że skrypty SAP domyślnie nagrywane są w języku VBS. Dla osoby, która ma pewne doświadczenie z tworzeniem makr VBA w Excelu, rozbudowanie nagranego skryptu o pętle, instrukcje warunkowe itd. nie będzie większym problemem. Jeśli jednak nie masz doświadczenia w programowaniu, albo znasz jedynie podstawy VBA i myślisz poważnie o budowaniu automatyzacji do SAP-a — i nie tylko — zachęcam Cię, abyś od początku postawił na Pythona.

Największe przewagi, jakie daje Python zarówno w automatyzowaniu systemu SAP, jak również budowaniu innych automatyzacji dla procesów biznesowych są następujące:

  • Czytelność: kod jest bardzo zrozumiały, prawie jak język angielski. Docenią to zarówno osoby, które dopiero zaczynają programować, jak i te, które znają VBA — dla nich podstawy będą po prostu oczywiste.
  • Przetwarzanie danych: Python posiada potężny ekosystem bibliotek do przetwarzania oraz wizualizowania danych (np. Pandas). 
  • Możliwość integracji z Excelem: pobranie lub zapisanie tam jakichś danych (biblioteka openpyxl). Jeśli Twoim celem jest na przykład automatyczne tworzenie dużej liczby zleceń produkcyjnych na podstawie danych z Excela, biblioteki Pandas i openpyxl znakomicie to ułatwią.
  • Bogaty ekosystem bibliotek: w tym przede wszystkim pywin32 – umożliwia interakcję z aplikacjami Windows (np. SAP GUI), smtplib – pozwala obsługiwać e-maile, sqlalchemy – interakcje z bazami danych, selenium – praca z przeglądarką stron internetowych i możliwość integracji z aplikacjami webowymi.

Lista zalet i możliwości Pythona jest znacznie dłuższa, ale na potrzeby tego wpisu skupmy się na tych kilku najważniejszych. Podsumowując ten krótki rozdział, warto podkreślić, że Python może “rosnąć razem z Tobą”. Możesz zacząć od prostych automatyzacji jednego zadania, a w miarę potrzeb — rozbudowywać skrypty o kolejne elementy, tworzyć całe przepływy pracy, a nawet własne dashboardy. W przeciwieństwie do bardziej ograniczonych narzędzi, takich jak VBA, Python daje przestrzeń do rozwoju i budowania coraz bardziej zaawansowanych rozwiązań. 

Jeśli jesteś jeszcze na początku drogi automatyzowania procesów, możesz nie dostrzegać wszystkich atutów różnych technologii — to zupełnie normalne. Decyzję o wyborze środowiska, w którym będziesz pracował, warto jednak podjąć świadomie. Pamiętając o tym, że wraz z rozwojem, nasze oczekiwania zmieniają się. Chociaż to nic odkrywczego — prawda życiowa, jakich wiele, dobrze pamiętać o tym, że ma ona zastosowanie również w świecie programowania i automatyzacji oraz wyborze ścieżki, jaką w tym świecie podążymy.


Przejmij kontrolę nad systemem SAP z wykorzystaniem Pythona — część praktyczna

Zanim przekształcimy skrypt VBS na Pythona i zaczniemy w pełni cieszyć się z możliwości, jakie ten język daje, musimy przygotować nasz warsztat. Ten rozdział jest dla Ciebie, jeśli nie masz jeszcze zainstalowanego Pythona i biblioteki pywin32 na swoim komputerze.

Krok 0: Instalacja Pythona (5 minut)

Na początek pobierz najnowszą dostępną wersję ze strony python.org/downloads. Obecnie jest to wersja 3.13.x — i to właśnie ona pojawi się jako pierwsza. Możesz śmiało z niej skorzystać, zwłaszcza jeśli dopiero zaczynasz. Sam wciąż korzystam z wersji 3.10, ale wszystko, co pokazuję w tym artykule, będzie działać również w nowszych wersjach. Następnie uruchom instalator oraz przejdź przez proces instalacji, który przebiega w sposób standardowy.

Uwaga: Podczas instalacji zaznacz opcję “Add Python to PATH”.

Będziesz również potrzebował “mostu” między Pythonem, a SAP-em. W tym celu zainstaluj bibliotekę pywin32. Aby to zrobić otwórz Wiersz Polecenia – cmd (używając po prostu wyszukiwarki systemu Windows). Następnie wpisz i zatwierdź jedną komendę: pip install pywin32.

Python jest zainstalowany i gotowy do działania. Masz już wszystko aby przejść dalej. Do edycji naszego skryptu w zupełności wystarczy Notepad++, a nawet systemowy Notatnik. Gdy Twoje projekty staną się bardziej złożone, warto zainteresować się profesjonalnymi edytorami kodu i przygotować środowisko tzw. IDE (ang. Integrated Development Environment). Pozwoli Ci to pracować jeszcze efektywniej.

Krok 1: Tłumaczymy nasz skrypt — od VBS do Pythona

Zacznijmy od bardzo prostego przykładu skryptu, który omawialiśmy w poprzednim wpisie z tego cyklu (jeśli nie pamiętasz, o jaki skrypt chodzi, wróć do poprzedniego wpisu). Skrypt ten otwiera transakcję MD04, wpisuje numer artykułu oraz zakład, a następnie potwierdza i przechodzi dalej “wirtualnym” klawiszem Enter. Dla przypomnienia wklejam kod z pliku “.vbs”:

If Not IsObject(application) Then
   Set SapGuiAuto  = GetObject("SAPGUI")
   Set application = SapGuiAuto.GetScriptingEngine
End If
If Not IsObject(connection) Then
   Set connection = application.Children(0)
End If
If Not IsObject(session) Then
   Set session    = connection.Children(0)
End If
If IsObject(WScript) Then
   WScript.ConnectObject session,     "on"
   WScript.ConnectObject application, "on"
End If

'---------------------------------------------------------------
' 🔽 Od tego momentu zaczyna się faktyczna interakcja z SAP GUI:

session.findById("wnd[0]").maximize
session.findById("wnd[0]/tbar[0]/okcd").text = "/nmd04"
session.findById("wnd[0]").sendVKey 0
session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-MATNR").text = "991111"
session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-WERKS").text = "2101"
session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-WERKS").setFocus
session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-WERKS").caretPosition = 4
session.findById("wnd[0]").sendVKey 0

Ten sam skrypt, przetłumaczony na język Python, będzie wyglądał bardzo podobnie:

import win32com.client
import sys

try:
    # Pobranie uruchomionej aplikacji SAP GUI
    SapGuiAuto = win32com.client.GetObject("SAPGUI")
    if not isinstance(SapGuiAuto, win32com.client.CDispatch):
        # Ta linia jest dla bezpieczeństwa, zazwyczaj nie jest konieczna
        sys.exit("Nie można było uzyskać dostępu do obiektu SAPGUI.")

    application = SapGuiAuto.GetScriptingEngine
    if not isinstance(application, win32com.client.CDispatch):
        sys.exit("Nie można było uzyskać dostępu do silnika skryptowego.")

    # Połączenie z pierwszą otwartą sesją
    connection = application.Children(0)
    if not isinstance(connection, win32com.client.CDispatch):
        sys.exit("Nie znaleziono aktywnego połączenia.")

    session = connection.Children(0)
    if not isinstance(session, win32com.client.CDispatch):
        sys.exit("Nie znaleziono aktywnej sesji.")

    # ---------------------------------------------------------------
    #  🔽 Od tego momentu zaczyna się faktyczna interakcja z SAP GUI:

    session.findById("wnd[0]").maximize()
    session.findById("wnd[0]/tbar[0]/okcd").text = "/nmd04"
    session.findById("wnd[0]").sendVKey(0)
    session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-MATNR").text = "991111"
    session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-WERKS").text = "2101"
    session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-WERKS").setFocus()
    session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-WERKS").caretPosition = 4
    session.findById("wnd[0]").sendVKey(0)

except Exception as e:
    print(f"Wystąpił błąd: {e}")
    print("Upewnij się, że SAP Logon jest uruchomiony i jesteś zalogowany do systemu.")

finally:
    # Opcjonalne: zwolnienie obiektów COM
    session = None
    connection = None
    application = None
    SapGuiAuto = None

Przykład ten zawiera obsługę wyjątków (ang. error handling) — tzn. kod zawiera “mechanizmy”, które dbają o to, żeby program nie wysypał się w sposób niekontrolowany, jeśli np.: nie znalazł połączenia z SAP-em. Na początkowym etapie może to być nieco przytłaczające. Wyczyśćmy więc kod VBS do absolutnego minimum na potrzeby dydaktyczne, aby jak najlepiej zaakcentować różnice pomiędzy VBS oraz Pythonem. W takiej wersji skryptu jego pierwsza część (odpowiadająca za połączenie z SAP-em) ograniczy się do 4 linijek (zbędne linie zostały wykomentowane — znak '):

'If Not IsObject(application) Then
   Set SapGuiAuto  = GetObject("SAPGUI")
   Set application = SapGuiAuto.GetScriptingEngine
'End If
'If Not IsObject(connection) Then
   Set connection = application.Children(0)
'End If
'If Not IsObject(session) Then
   Set session    = connection.Children(0)
'End If
'If IsObject(WScript) Then
'   WScript.ConnectObject session,     "on"
'   WScript.ConnectObject application, "on"
'End If

'---------------------------------------------------------------
' 🔽 Od tego momentu zaczyna się faktyczna interakcja z SAP GUI:

session.findById("wnd[0]").maximize
session.findById("wnd[0]/tbar[0]/okcd").text = "/nmd04"
session.findById("wnd[0]").sendVKey 0
session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-MATNR").text = "991111"
session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-WERKS").text = "2101"
session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-WERKS").setFocus
session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-WERKS").caretPosition = 4
session.findById("wnd[0]").sendVKey 0

Taka uproszczona wersja w Pythonie będzie wyglądała w sposób następujący (o wiele łatwiejsza do przyswojenia, prawda?):

import win32com.client

# Połączenie z uruchomioną aplikacją i sesją SAP
SapGuiAuto = win32com.client.GetObject("SAPGUI")
application = SapGuiAuto.GetScriptingEngine
connection = application.Children(0)
session = connection.Children(0)

# ---------------------------------------------------------------
#  🔽 Od tego momentu zaczyna się faktyczna interakcja z SAP GUI:
#     (Składnia jest niemal identyczna jak w VBS)

session.findById("wnd[0]").maximize()
session.findById("wnd[0]/tbar[0]/okcd").text = "/nmd04"
session.findById("wnd[0]").sendVKey(0)
session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-MATNR").text = "991111"
session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-WERKS").text = "2101"
session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-WERKS").setFocus()
session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-WERKS").caretPosition = 4
session.findById("wnd[0]").sendVKey(0)

Możesz teraz zmienić wartości odpowiadające za numer materiału oraz zakład i od razu zapisać go do osobnego pliku z rozszerzeniem “.py” (rozszerzenie plików Python), a następnie uruchomić podwójnym kliknięciem myszy. Jeśli jesteś zalogowany do systemu SAP, wskazany przez Ciebie artykuł powinien zostać wyświetlony w transakcji MD04.

Uwaga: Przed uruchomieniem skryptu upewnij się, że nie masz otwartych jakichś niezapisanych danych w SAP-ie.

Różnice w składni – wywoływanie metod

Najważniejsza różnica w składni dotyczy wywoływania metod:

  • W VBS można pominąć nawiasy przy wywoływaniu funkcji/metod bez argumentów: session.findById("wnd[0]").maximize
  • W Pythonie nawiasy () są obowiązkowe zawsze: session.findById("wnd[0]").maximize()

Dodatkowo Python — w przeciwieństwie do VBS, który ma to wbudowane — domyślnie nie komunikuje się z aplikacjami Windows. Potrzebuje do tego dodatkowej biblioteki pywin32 — zainstalowaliśmy ją wcześniej. Tak więc w kodzie Python najpierw importujemy tę bibliotekę:

import win32com.client

a potem korzystamy z niej, aby utworzyć obiekt SapGuiAuto (pomijając słowo Set):

SapGuiAuto = win32com.client.GetObject("SAPGUI")

Poza tymi punktami, sama logika interakcji z SAP GUI (czyli komendy session.findById(...)) pozostaje praktycznie identyczna. W praktyce praca z nagranym kodem VBS, przed zaimplementowaniem go do programu Python, będzie w większości przypadków polegała właśnie na dodaniu nawiasów przy wywoływaniu metod.

Niewielki wysiłek, jaki trzeba włożyć, aby przekonwertować kod z VBS na Python jest więc w pełni uzasadniony biorąc pod uwagę, jakie możliwości daje nam ten język programowania przy dalszym rozbudowywaniu projektów. W dzisiejszych czasach jest to jeszcze prostsze, ponieważ dowolny model językowy np. Chat GPT lub Gemini zrobi tę konwersję za Ciebie w dosłownie kilka sekund. Zachęcam do eksperymentów, jednak gorąco polecam abyś wcześniej sam zrozumiał istotę problemu, a dopiero później wspierał się świetnymi możliwościami, jakie daje AI (ang. Artificial Intelligence).

W tym miejscu nie pozostaje mi nic innego jak tylko szczerze Ci pogratulować! Jeśli dotarłeś do tego momentu, potrafisz już samodzielnie tworzyć skrypty Python automatyzujące pracę z SAP-em. To ogromny krok do przodu!

Krok 2: Budujemy narzędzie “Start Dnia”

Osoby pracujące na co dzień z SAP-em często rozpoczynają dzień od przygotowania swojego środowiska pracy: uruchomienia kilku okienek SAP-a, załadowania odpowiednich transakcji i wczytania zdefiniowanych wariantów. Spróbujmy zautomatyzować tę prostą, ale żmudną czynność.

Zacznijmy od nagrania prostego skryptu sekwencji kilku operacji wykonanych w następującym porządku:

  1. Uruchomienie transakcji COHV (“/ncohv”).
  2. Uruchomienie okna dialogowego wyboru wariantu (CTRL + F5).
  3. Wprowadzenie nazwy wariantu oraz wyczyszczenie pola z nazwą użytkownika.
  4. Potwierdzenie wyboru i załadowanie wariantu.
  5. Kliknięcie ikonki “Nowe okno GUI” w celu otwarcia kolejnego okna SAP.
  6. Powtórzenie kroków 1-4 w nowo otwartym oknie dla innego wariantu (z tą samą, lub inną transakcją).

Próba nagrania takiej sekwencji operacji da nam następujący skrypt (pokazuję tylko część odpowiedzialną za interakcję z GUI, już w wersji kodu Python):

session.findById("wnd[0]").maximize()
session.findById("wnd[0]/tbar[0]/okcd").text = "/ncohv"
session.findById("wnd[0]").sendVKey(0)
session.findById("wnd[0]").sendVKey(17)  # CTRL + F5
session.findById("wnd[1]/usr/txtV-LOW").text = "PLAN_LU_001"
session.findById("wnd[1]/usr/txtENAME-LOW").text = ""  # Wyczyszczenie pola z nazwą użytkownika
session.findById("wnd[1]").sendVKey(0)
session.findById("wnd[1]").sendVKey(8)
session.findById("wnd[0]").sendVKey(8)

session.createSession()  # "Nowe okno GUI"

Jak zapewne zauważyłeś, kod kończy się w momencie uruchomienia kolejnego okna. Tak działa to narzędzie. Czy to oznacza, że programy pozwalają zautomatyzować tylko jedno okno SAP? Absolutnie nie! Za chwilę pokażę Ci jak ten problem rozwiązać.

Zacznijmy od wyjaśnienia: w naszym kodzie to obiekt session decyduje o tym, na którym oknie SAP wykonujemy operacje. A więc nawet gdybyś spróbował teraz skopiować wszystkie te linie kodu, które odpowiadają za uruchomienie transakcji, wczytanie wariantu itd. i wkleić je po linii, w której uruchamiana jest kolejna sesja (nowe okno GUI) – nic by to nie dało. Operacje, owszem, zostałyby wykonane ale na naszym pierwszym oknie SAP – zdecydowanie nie tak, jak byśmy sobie tego życzyli.

Cofnijmy się zatem do pierwszej części naszego kodu i sprawdźmy, skąd bierze się obiekt session:

session = connection.Children(0)

OK. Wygląda na to, że obiekt session jest pierwszym elementem kolekcji Children obiektu connection. Python stosuje indeksowanie od zera (ang. zero-based indexing), co oznacza, że pierwszy element listy ma indeks 0. Na początku naszej pracy, kiedy mamy otwarte tylko jedno okno, kolekcja ta ma tylko jeden element. W momencie uruchomienia nowego okna kolekcja ta powiększa się – są w niej już dwa elementy. Możemy więc “pobrać” z niej drugie okno aby na nim pracować:

session2 = connection.Children(1)

Od tej chwili w naszym programie mamy dwa obiekty: session oraz session2. W dowolnym momencie możemy się odwołać do każdego z nich, realizując automatycznie operacje na pierwszym lub drugim oknie SAP, w zależności od potrzeb. Nagrany wcześniej skrypt możemy poprawić teraz samodzielnie, powinien on wyglądać mniej więcej tak:

# ---------------------------------------------------------------
#  🔽 Od tego momentu zaczyna się faktyczna interakcja z SAP GUI:

session.findById("wnd[0]").maximize()
session.findById("wnd[0]/tbar[0]/okcd").text = "/ncohv"
session.findById("wnd[0]").sendVKey(0)
session.findById("wnd[0]").sendVKey(17)  # CTRL + F5
session.findById("wnd[1]/usr/txtV-LOW").text = "PLAN_LU_001"
session.findById("wnd[1]/usr/txtENAME-LOW").text = ""  # Wyczyszczenie pola z nazwą użytkownika
session.findById("wnd[1]").sendVKey(0)
session.findById("wnd[1]").sendVKey(8)
session.findById("wnd[0]").sendVKey(8)

session.createSession()  # "Nowe okno GUI"
time.sleep(1)  # Tutaj dodajemy 1s pauzę w programie, aby nowe okno "zdążyło się uruchomić"

session2 = connection.Children(1)

session2.findById("wnd[0]/tbar[0]/okcd").text = "/ncohv"
session2.findById("wnd[0]").sendVKey(0)
session2.findById("wnd[0]").sendVKey(17)  # CTRL + F5
session2.findById("wnd[1]/usr/txtV-LOW").text = "PLAN_LU_002"
session2.findById("wnd[1]/usr/txtENAME-LOW").text = ""  # Wyczyszczenie pola z nazwą użytkownika
session2.findById("wnd[1]").sendVKey(0)
session2.findById("wnd[1]").sendVKey(8)
session2.findById("wnd[0]").sendVKey(8)

Na samym początku skryptu, musimy jeszcze zaimportować moduł time, niezbędny do krótkiej pauzy, jakiej potrzebujemy w tym programie zaraz po wywołaniu nowego okna GUI:

import time

Włączenie tego programu uruchomi — jedno po drugim — dwa okna SAP z transakcją COHV, a w każdym z nich wczyta inny wariant. Ty, naturalnie, możesz wybrać dowolne inne transakcje, z których korzystasz na co dzień. Jeśli w którymś z okien SAP chcesz tylko załadować transakcję np. MD04 bez wczytywania wariantu — to po prostu w tej konkretnej sekcji Twojego kodu pomiń fragment, który za to odpowiada.

Chciałbym zaznaczyć, że kod, który zaprezentowałem powyżej nie jest jeszcze napisany “zgodnie ze sztuką”. Jest jednak w pełni funkcjonalny i adekwatny do obecnego etapu nauki. O tym jak go udoskonalić, porozmawiamy w następnym podrozdziale.

Jestem przekonany, że dzięki umiejętnościom, które zdobyłeś dotychczas, bez trudu poradzisz sobie ze stworzeniem własnego skryptu, który jednym kliknięciem otworzy dla Ciebie dowolną liczbę okien SAP. Wystarczy, że zalogujesz się do systemu i uruchomisz swój program, a kiedy… wrócisz ze świeżo zaparzoną, pachnącą kawą wszystko będzie gotowe do pracy. 

Powodzenia!

Krok 3: Od działania do elegancji — jak ulepszyć nasz kod

Załóżmy, że kod Twojego narzędzia start_dnia.py otwiera trzy okna SAP i uruchamia w nich następujące transakcje:

  • Okno nr 1: Transakcja COHV wraz z wczytaniem wariantu.
  • Okno nr 2: Transakcja MD04
  • Okno nr 3: Transakcja COHV wraz z wczytaniem wariantu

Kod Twojego skryptu będzie wtedy wyglądał mniej więcej tak:

# ---------------------------------------------------------------
#  🔽 Od tego momentu zaczyna się faktyczna interakcja z SAP GUI:

# ---------------------------------------------------------------
# Okno nr 1: Transakcja COHV
session.findById("wnd[0]").maximize()
session.findById("wnd[0]/tbar[0]/okcd").text = "/ncohv"
session.findById("wnd[0]").sendVKey(0)
session.findById("wnd[0]").sendVKey(17)  # CTRL + F5
session.findById("wnd[1]/usr/txtV-LOW").text = "PLAN_LU_ZRI"
session.findById("wnd[1]/usr/txtENAME-LOW").text = ""  # Wyczyszczenie pola z nazwą użytkownika
session.findById("wnd[1]").sendVKey(0)
session.findById("wnd[1]").sendVKey(8)
session.findById("wnd[0]").sendVKey(8)

session.createSession()  # "Nowe okno GUI"
time.sleep(1)  # Tutaj dodajemy 1s pauzę w programie, aby nowe okno "zdążyło się uruchomić"

# ---------------------------------------------------------------
# Okno nr 2: Transakcja MD04 (pomijamy fragment kodu związany z wczytywaniem wariantu)
session2 = connection.Children(1)

session2.findById("wnd[0]/tbar[0]/okcd").text = "/nmd04"
session2.findById("wnd[0]").sendVKey(0)

session2.createSession()  # "Nowe okno GUI"
time.sleep(1)  # Tutaj dodajemy 1s pauzę w programie, aby nowe okno "zdążyło się uruchomić"

# ---------------------------------------------------------------
# Okno nr 3: Transakcja COHV (nie uruchamiamy kolejnego okna GUI)
session3 = connection.Children(2)

session3.findById("wnd[0]/tbar[0]/okcd").text = "/ncohv"
session3.findById("wnd[0]").sendVKey(0)
session3.findById("wnd[0]").sendVKey(17)  # CTRL + F5
session3.findById("wnd[1]/usr/txtV-LOW").text = "PLAN_LU_ZAR"
session3.findById("wnd[1]/usr/txtENAME-LOW").text = ""  # Wyczyszczenie pola z nazwą użytkownika
session3.findById("wnd[1]").sendVKey(0)
session3.findById("wnd[1]").sendVKey(8)
session3.findById("wnd[0]").sendVKey(8)

Z łatwością można zauważyć, że pewne fragmenty kodu powtarzają się wielokrotnie. Na przykład linia odpowiedzialna za wybór transakcji:

session.findById("wnd[0]/tbar[0]/okcd").text = "/ncohv"

pojawia się w każdej z trzech sekcji w niemal identycznej postaci — różni się jedynie nazwą transakcji oraz obiektem session, na którym wywoływana jest metoda .findById().

Spróbujmy więc podejść do tego sprytniej i zapisać ten fragment bardziej elegancko. W tym celu sięgniemy po dwa absolutne fundamenty programowania: pętlę for i instrukcję warunkową if.

Pętla for pozwala nam powtórzyć tę samą czynność wiele razy — ale bez kopiowania kodu. Dzięki niej możemy „przejść” po liście transakcji lub sesji i wykonać określoną operację dla każdego elementu z osobna.

Instrukcja if umożliwia wykonanie pewnego fragmentu kodu tylko wtedy, gdy spełniony jest określony warunek. To taki programistyczny „jeśli” — dzięki któremu możemy dodać do naszego skryptu prostą logikę, np. sprawdzić, czy wariant ma zostać wczytany dla danej transakcji, zanim spróbujemy to zrobić.

Posłużymy się również dwoma “pojemnikami” na dane, które są absolutną podstawą w języku Python — mam na myśli listę (list()) oraz słownik (dict()).

Lista to uporządkowany zbiór elementów, który możemy przeszukiwać, modyfikować lub po prostu przechodzić po nim krok po kroku – idealny, gdy chcemy coś wykonać dla każdego elementu z kolei. Można ją sobie wyobrazić jako rząd pudełek ustawionych obok siebie, ponumerowanych od zera — a w każdym z nich trzymamy dane, które chcemy wykorzystać.

Słownik z kolei działa bardziej jak klasyczna książka telefoniczna — zamiast numerków, mamy klucze, a do każdego z nich przypisana jest jakaś wartość. To bardzo wygodne, gdy chcemy szybko odwołać się do konkretnej informacji, np. nazwy transakcji przypisanej do konkretnej sesji.

Oba te typy danych świetnie ze sobą współpracują — a w kontekście naszego przykładu pozwolą nam nie tylko uprościć kod, ale także uczynić go bardziej elastycznym i przejrzystym.

Ostatnim nowym pojęciem, jakie chciałbym wprowadzić na potrzeby tego programu, jest funkcja len(). To bardzo proste, ale przydatne narzędzie – zwraca liczbę elementów w danym obiekcie, np. w liście.

Zacznijmy od utworzenia listy zadania_do_uruchomienia, w której zapiszemy naszą konfigurację — umieścimy w niej dane dotyczące transakcji oraz wariantów, które chcemy uruchomić. Pojedynczym elementem listy będzie słownik, który będzie zawierał dwa klucze: 'transakcja' oraz 'wariant' Lista będzie miała tyle elementów, ile transakcji chcemy uruchomić. Dla analizowanego przez nas przykładu konfiguracja będzie wyglądać w sposób następujący:

# ---------------------------------------------------------------
#  🔽 Od tego momentu zaczyna się faktyczna interakcja z SAP GUI:

# === TWOJA KONFIGURACJA ===
# Wpisz tutaj transakcje i warianty, które chcesz uruchomić
zadania_do_uruchomienia = [
   {'transakcja': 'COHV', 'wariant': 'PLAN_LU_ZRI'},  # Słownik dla okna nr 1
   {'transakcja': 'MD04', 'wariant': None},  # Słownik dla okna nr 2
   {'transakcja': 'COHV', 'wariant': 'PLAN_LU_ZAR'},  # Słownik dla okna nr 3
   # Możesz dodać więcej!
]

Jak widzisz, słownik przechowujący dane dla drugiego okna ma wartość None przypisaną do klucza 'wariant' Oznacza to, że w tym przypadku nie wczytujemy żadnego wariantu, a więc program ma pominąć fragment kodu, który jest związany z tą operacją. Mając w ten sposób przygotowane dane, nie pozostaje nam nic innego, jak tylko napisać pętlę for, która przejdzie przez wszystkie elementy listy zadania_do_uruchomienia oraz wykona zdefiniowane w niej operacje wykorzystując dane ze słownika. Za pomocą instrukcji if upewnimy się, że program nie będzie próbował wczytywać wariantu, jeśli nie został on podany. Bardziej elegancka wersja naszego skryptu będzie wyglądać tak:

# ---------------------------------------------------------------
#  🔽 Od tego momentu zaczyna się faktyczna interakcja z SAP GUI:

# === TWOJA KONFIGURACJA ===
# Wpisz tutaj transakcje i warianty, które chcesz uruchomić
zadania_do_uruchomienia = [
   {'transakcja': 'COHV', 'wariant': 'PLAN_LU_ZRI'},  # Słownik dla okna nr 1
   {'transakcja': 'MD04', 'wariant': None},  # Słownik dla okna nr 2
   {'transakcja': 'COHV', 'wariant': 'PLAN_LU_ZAR'},  # Słownik dla okna nr 3
   # Możesz dodać więcej!
]

numer_sesji = 0

for slownik in zadania_do_uruchomienia:
   session = connection.Children(numer_sesji)
   session.findById("wnd[0]").maximize()
   session.findById("wnd[0]/tbar[0]/okcd").text = slownik['transakcja']  # w ten sposób pobieramy wartość przypisaną do klucza 'transakcja'
   session.findById("wnd[0]").sendVKey(0)
   if slownik['wariant']:
       # Program zrealizuje poniższe linie tylko, jeśli użytkownik podał jakiś wariant
       session.findById("wnd[0]").sendVKey(17)  # CTRL + F5
       session.findById("wnd[1]/usr/txtV-LOW").text = slownik['wariant']  # podajemy nazwę wariantu
       session.findById("wnd[1]/usr/txtENAME-LOW").text = ""
       session.findById("wnd[1]").sendVKey(0)
       session.findById("wnd[1]").sendVKey(8)
       session.findById("wnd[0]").sendVKey(8)

   if numer_sesji < len(zadania_do_uruchomienia) - 1:
       # Po ostatnim oknie nie tworzymy nowej sesji
       session.createSession()  # "Nowe okno GUI"
       time.sleep(1)  # Tutaj dodajemy 1s pauzę w programie, aby nowe okno "zdążyło się uruchomić"
       numer_sesji += 1  # zwiększamy numer sesji o 1 po każdej iteracji pętli

I mamy to! Tak napisany program, to gotowe narzędzie, które może być wykorzystane przez każdego. Wystarczy tylko odpowiednio skonfigurować skrypt edytując listę zadania_do_uruchomienia. Program będzie działał niezależnie od tego, czy chcesz uruchomić jedno, dwa, czy wszystkie możliwe sześć okien w systemie SAP. Naturalnie, skrypt ten można udoskonalić jeszcze bardziej, oto kilka pomysłów:

  • Zweryfikować, czy liczba okien nie przekracza 6 (limit SAP GUI).
  • Dodanie walidacji, czy lista zadania_do_uruchomienia nie jest pusta.
  • Obsługa błędów, jeśli SAP GUI nie jest uruchomione.
  • itp.

Podsumowanie i co dalej?

To tyle na dziś!

W tym artykule pokazałem Ci, jak — wykorzystując język Python — stworzyć własne narzędzie do automatycznego uruchamiania transakcji potrzebnych na start dnia pracy. 

Drobne udoskonalenia, które zasugerowałem, nie są konieczne — obecna forma skryptu powinna świetnie sprawdzić się jako praktyczne narzędzie do otwierania SAP-a. Co więcej, dzięki elastycznej konstrukcji kodu, możesz bez problemu wdrożyć podobne rozwiązanie u swoich koleżanek i kolegów z zespołu — wystarczy dostosować konfigurację do potrzeb konkretnego użytkownika. 

To dopiero początek możliwości, jakie daje automatyzacja SAP-a — ale już teraz masz w rękach coś, co realnie może oszczędzić czas każdego dnia.

Jeśli w trakcie pracy nad swoim skryptem czujesz, że pętle for, instrukcje warunkowe if czy struktury danych, takie jak listy i słowniki, nie są jeszcze dla Ciebie w pełni oczywiste — to zupełnie normalne. Warto poświęcić chwilę, aby te fundamenty zrozumieć naprawdę dobrze. Możesz sięgnąć po darmowe materiały dostępne w Internecie, obejrzeć kilka krótkich filmów na YouTube albo… po prostu zapytać ChatGPT lub Gemini. Te narzędzia świetnie sprawdzają się jako nauczyciel “na żądanie”. Możesz też przejść cały mini kurs nauka Pythona z AI z tego bloga.

Zachęcam Cię również do samodzielnego rozwijania tego programu, zwłaszcza jeśli Twoja poranna rutyna w SAP-ie to coś więcej, niż tylko otwarcie transakcji, w których później pracujesz. Być może każdego ranka pobierasz dane z SAP-a i zapisujesz je do Excela na potrzeby jakiegoś raportu? To doskonały punkt wyjścia do dalszej automatyzacji. 

A może w ciągu dnia korzystasz z różnych zestawów transakcji — w zależności od pory czy zadania? Wtedy możesz przygotować kilka wersji skryptu, np. “start_dnia_rano”, “raporty_po_poludniu” — i uruchamiać je w zależności od potrzeby.

Czas zaoszczędzony dzięki takim prostym automatyzacjom możesz poświęcić na rzeczy naprawdę ważne — na przykład na własny rozwój i dalsze zgłębianie tematu automatyzowania SAP-a. Już teraz zapraszam Cię więc na kolejny artykuł z tej serii, w którym pokażę Ci m.in. jak pracować na kilku oknach SAP-a jednocześnie.

Powodzenia!

Napisz komentarz

Twój adres email nie zostanie opublikowany. Wymagane pola są oznaczone *

Scroll to Top