Zautomatyzuj SAP [3/4] – SAP na sterydach: automatyzacja wielu okien i logowanie z Pythonem

🛠️ Tutorial

Twój skrypt też potrzebuje porannej kawy?

Mam nadzieję, że poprzedni artykuł z tego cyklu (jeśli jeszcze nie widziałeś to zapraszam do tego wpisu) pomógł Ci stworzyć narzędzie, które podczas przygotowywania porannej kawy robi część powtarzalnych zadań w SAP-ie za Ciebie. 

Mogło się tak zdarzyć — szczególnie jeśli Twój skrypt operuje na wielu oknach SAP-a, a Twój ekspres do kawy jest blisko — że kiedy wróciłeś do komputera z filiżanką aromatycznego napoju i pełen entuzjazmu do pracy, zauważyłeś, że nie wszystko jest jeszcze gotowe — wykonywanie skryptu trwa dalej w najlepsze. 

I chociaż — trzeba to uczciwie przyznać — patrzenie na SAP-a pracującego za nas automatycznie jest bardzo przyjemne, to przecież nie o to nam chodziło! 

No właśnie. Pewnie zorientowałeś się wtedy, że kiedy Ty otwierasz rano kilka transakcji w wielu oknach SAP-a, nie czekasz, aż wariant poprzedniej transakcji się załaduje, zanim otworzysz kolejną. Robisz to równolegle. Może zacząłeś zadawać sobie pytanie – „Czy automatyzacja wielu okien SAP jest możliwa?”

A co, gdybym Ci powiedział, że możesz kontrolować kilka okien SAP-a niezależnie od siebie za pomocą skryptu Python? Przyznasz, że brzmi to naprawdę ciekawie. 

W tym wpisie pokażę Ci sposób, w jaki możesz to osiągnąć. Ponadto zmodyfikujemy nasz skrypt start_dnia.py tak, żeby dodatkowo uruchamiał SAP-a i logował się na Twoje konto użytkownika — dzięki temu nie będziesz musiał tego robić sam przed włączeniem skryptu. 

Zanim jednak przejdziemy do sedna, potrzebujemy małego wprowadzenia do niezbędnych zagadnień technicznych.


Napisz raz, używaj wiele razy – poznaj funkcje w Pythonie

W tym miejscu chciałbym pokazać Ci jeden z najważniejszych elementów każdego języka programowania — funkcje.

Już we wcześniejszym wpisie zwracałem uwagę na to, że pewne fragmenty kodu wyglądają niemal identycznie i tylko delikatnie się różnią. Poradziliśmy sobie z tym konsolidując nasz kod do jednej pętli for, która powiela jego działanie. Jest to też idealna sytuacja, żeby wykorzystać funkcję. 

W największym skrócie: funkcja to przepis na zadanie. Piszesz go raz, a potem możesz uruchamiać w dowolnym momencie i z różnymi wartościami parametrów, które są przekazywane do funkcji jako jej argumenty.

W naszym przypadku zadaniem jest uruchomienie transakcji oraz wczytanie wariantu — jeśli został podany. Nasze parametry to z kolei numer sesji na której pracujemy, nazwa transakcji oraz nazwa wariantu. Ten fragment kodu możemy zatem “ubrać” w funkcję i wywoływać go za każdym razem, kiedy nasza pętla for “przygotuje” dla nas nowy zestaw argumentów.

Składnia funkcji w Pythonie wygląda tak:

def nazwa_funkcji(parametr1, parametr2):
   # ciało funkcji – czyli instrukcje, które mają się wykonać
   print(f"Witaj, {parametr1}! Twoje hasło to: {parametr2}")

Zwróć uwagę na wcięcia (ang. indentation) – w Pythonie wcięcia nie są „opcjonalne” jak w wielu innych językach programowania. To właśnie dzięki nim Python rozpoznaje, które linie kodu należą do danej funkcji, pętli czy warunku. Standardem są 4 spacje wcięcia i warto tego pilnować, bo brak lub nadmiar spacji potrafi wywołać błędy, które trudno wychwycić na pierwszy rzut oka.

Aby wywołać taką funkcję, po prostu używasz jej nazwy i przekazujesz odpowiednie argumenty:

nazwa_funkcji("Kamil", "Python!")

W rezultacie zobaczysz na ekranie:

“Witaj, Kamil! Twoje hasło to: Python!”

Funkcja print() to taka funkcja “wbudowana” do Pythona (ang. built-in), która umożliwia wypisywanie tekstu w konsoli.

Sprawdź działanie funkcji w praktyce

Jeśli do tej pory uruchamiałeś skrypty klikając dwukrotnie na plik .py, możesz nie zobaczyć, co dokładnie się dzieje – okno zniknie, zanim zdążysz przeczytać wynik.

W tym przypadku najlepiej uruchomić skrypt z poziomu konsoli systemowej.

Jak to zrobić krok po kroku?

  1. Otwórz Wiersz polecenia (cmd) lub PowerShell w systemie Windows (korzystając z narzędzia Wyszukaj).
  2. Przejdź do folderu, w którym zapisałeś swój plik .py (np. moj_skrypt.py). Użyj polecenia:
cd ŚCIEŻKA_DO_FOLDERU

na przykład:

cd C:\Users\Kamil\Desktop\python_test
  1. Uruchom swój skrypt wpisując:
python moj_skrypt.py
  1. Zobaczysz wynik działania funkcji w konsoli, np.:

“Witaj, Kamil! Twoje hasło to: Python!”

Pobaw się teraz argumentami funkcji — spróbuj zmienić imię, hasło albo dodaj zupełnie nowe wartości i zobacz, jak Twój skrypt reaguje na każdą z nich w czasie rzeczywistym.

Dzięki konsoli masz pełną kontrolę nad tym, co się dzieje w Twoim programie. To też pierwszy krok do bardziej świadomej pracy z kodem.

TIP: Co jeśli nadal chcesz uruchamiać skrypt przez podwójne kliknięcie ikonki skryptu?

Jeśli nie chcesz jeszcze pracować z konsolą i wolisz uruchamiać skrypt dwukrotnym kliknięciem pliku .py, dodaj na sam koniec swojego pliku następującą linię:

input("Naciśnij Enter, aby zamknąć...")

Dzięki temu okno konsoli nie zamknie się od razu po wykonaniu skryptu — pojawi się komunikat, który „zatrzyma” działanie programu, aż naciśniesz Enter. To pozwala spokojnie przeczytać wynik działania Twojej funkcji.

Funkcja w akcji – porządkujemy kod naszego narzędzia

Wiesz już jak działa funkcja, teraz pora wykorzystać ją w praktyce. W kodzie naszego narzędzia start_dnia.py funkcja, którą możemy wykorzystać może wyglądać tak:

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


def otworz_transakcje_i_wczytaj_wariant(numer_sesji, nazwa_transakcji, nazwa_wariantu):
   session = connection.Children(numer_sesji)
   session.findById("wnd[0]").maximize()
   session.findById("wnd[0]/tbar[0]/okcd").text = nazwa_transakcji
   session.findById("wnd[0]").sendVKey(0)

   if nazwa_wariantu:
       # 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 = nazwa_wariantu  # 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)

W tym przypadku nazwa funkcji to otworz_transakcje_i_wczytaj_wariant, parametrami tej funkcji z kolei są: numer_sesji, nazwa_transakcji oraz nazwa_wariantu. Aby ją wywołać, i z niej skorzystać, potrzebujemy jedynie “dostarczyć” jej odpowiednie wartości parametrów, przekazując stosowne argumenty. Możemy to zrobić w sposób następujący:

numer_okna = 0

for slownik in zadania_do_uruchomienia:
   # w ten sposób pobieramy wartości przypisane do kluczy: 'transakcja' oraz 'wariant'
   # a następnie przypisujemy je do zmiennych wariant oraz transakcja, które będą argumentami do naszej funkcji
   wariant = slownik['wariant']
   transakcja = slownik['transakcja']

   # Wywołujemy funkcję, która realizuje operacje, które wcześniej mieliśmy w pętli
   otworz_transakcje_i_wczytaj_wariant(numer_sesji=numer_okna, nazwa_transakcji=transakcja, nazwa_wariantu=wariant)

   if numer_okna < 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_okna += 1  # zwiększamy numer sesji o 1 po każdej iteracji pętli

Jak zapewne zauważyłeś, zdefiniowaną wcześniej funkcję wywołujemy w ramach pętli for. Wcześniej “przygotowujemy” sobie niezbędne argumenty: wariant oraz transakcja — pobierając ich wartości ze słownika oraz numer_okna — zmienna, którą po prostu zwiększamy o 1 podczas każdej iteracji.

Tak zdefiniowaną funkcję, możesz wykorzystać dowolną ilość razy — co więcej, możesz ją wykorzystać w różnych innych programach, które wymagają otwarcia jakiejś transakcji. Nie musisz pisać / nagrywać na nowo tego samego kodu. Przyznasz, że to naprawdę zachęca, żeby od razu spróbować tego podejścia w swoich własnych mini-automatyzacjach! Aby Ci to ułatwić, w kolejnym krótkim podrozdziale zaprezentuję przykład teoretyczny, który obrazuje “moc” takiego podejścia w programowaniu.

Inny przykład wykorzystania funkcji

Wyobraź sobie taki hipotetyczny przykład: chcesz zautomatyzować pobieranie danych do dwóch raportów: “sprzedaż” oraz “produkcja”. Może się okazać, że w obu tych przypadkach możesz pozyskać niezbędne dane poprzez wywołanie dwóch funkcji po kolei: otworz_transakcje_i_wczytaj_wariant (dokładnie takiej, jakiej używaliśmy w narzędziu start dnia) oraz eksport_danych_do_excela (funkcji, która po prostu eksportuje dane do arkusza kalkulacyjnego). Konstrukcja takiego programu mogłaby wyglądać mniej więcej tak (nazwy funkcji skróciłem dla przejrzystości przykładu):

def otw_trans_i_wczyt_war(numer_sesji, nazwa_transakcji, nazwa_wariantu):
    print("Otwieranie transakcji i ładowanie wariantu...")


def eksp_danych_do_excl(sciezka_do_pliku, nazwa_pliku):
   print("Eksportowanie danych do Excela..")


def pobierz_dane_produkcji(numer_sesji, nazwa_transakcji, nazwa_wariantu, sciezka_do_pliku, nazwa_pliku):
   # funkcja, która pobiera dane dotyczące produkcji
   otw_trans_i_wczyt_war(numer_sesji=numer_sesji, nazwa_transakcji=nazwa_transakcji, nazwa_wariantu=nazwa_wariantu)
   eksp_danych_do_excl(sciezka_do_pliku=sciezka_do_pliku, nazwa_pliku=nazwa_pliku)


def pobierz_dane_sprzedazy(numer_sesji, nazwa_transakcji, nazwa_wariantu, sciezka_do_pliku, nazwa_pliku):
   # funkcja, która pobiera dane dotyczące sprzedaży
   otw_trans_i_wczyt_war(numer_sesji=numer_sesji, nazwa_transakcji=nazwa_transakcji, nazwa_wariantu=nazwa_wariantu)
   eksp_danych_do_excl(sciezka_do_pliku=sciezka_do_pliku, nazwa_pliku=nazwa_pliku)


# Przygotowanie argumentów dla obydwu raportów
tr_sp = "COHV"
tr_pr = "COHV"
war_sp = "wariant_sprzedaż"
war_prod = "wariant_produkcja"
num_okna = 0
sciezka_do_pliku = "C:/Dysk/Raporty/"
nazwa_sp = "dane_sprzdaz.xlsx"
nazwa_prod = "dane_produkcja.xlsx"


# Uruchomienie całego programu
pobierz_dane_produkcji(numer_sesji=num_okna, nazwa_transakcji=tr_pr, nazwa_wariantu=war_prod,
                      sciezka_do_pliku=sciezka_do_pliku, nazwa_pliku=nazwa_prod)
pobierz_dane_sprzedazy(numer_sesji=num_okna, nazwa_transakcji=tr_sp, nazwa_wariantu=war_sp,
                      sciezka_do_pliku=sciezka_do_pliku, nazwa_pliku=nazwa_sp)

Dzięki wydzieleniu tych operacji do osobnych funkcji możesz teraz łatwo je testować, modyfikować i wykorzystywać w wielu miejscach bez powielania kodu. Pomaga to też izolować problemy, jeśli coś nie działa — wiesz, w której funkcji szukać przyczyny.

Przekazując parametry jako argumenty, możesz łatwo generować różne raporty tylko zmieniając dane wejściowe — bez modyfikowania logiki programu. W ten sposób możesz wygenerować dane zarówno do jednego, dwóch, jak również pięciu — i więcej! — kolejnych raportów. Gorąco Cię zachęcam do przetestowania takiego podejścia w przykładach, które już teraz z pewnością przychodzą Ci na myśl. W programowaniu “learning by doing” to najszybsza (i najciekawsza!) droga do sukcesu.


Automatyzacja wielu okien SAP — wprowadzenie do multiprocessing

Jeśli koncepcja funkcji jest dla Ciebie jasna, jesteś gotowy, żeby pójść dalej. Wróćmy zatem do obietnicy, jaką złożyłem na początku tego artykułu — otwieranie kilku okien SAP-a równolegle.

Wykonywanie kodu: linia po linii

Na początek małe wprowadzenie. Co do zasady program komputerowy wykonywany jest linia po linii. To oznacza, że nie jest on w stanie przejść do kolejnej linii kodu, zanim ta poprzednia nie zostanie ukończona. Posłużę się przykładem z naszego skryptu, aby jak najlepiej to zobrazować. Na koniec naszej funkcji wciskamy przycisk, który inicjuje wczytywanie wariantu:

session.findById("wnd[0]").sendVKey(8)

Od tego momentu obiekt session jest zajęty wykonywaniem tego zadania, a nasz program czeka na jego ukończenie — dopiero wtedy przechodzi dalej. Może się to wydawać nieintuicyjne, ponieważ my — użytkownicy SAP-a — moglibyśmy bez problemu wcisnąć przycisk w SAP GUI, a następnie przejść np. do kolejnego okna i tam pracować dalej.

Chociaż teraz takie zachowanie programu może wydać Ci się uciążliwe to — wierz mi — z punktu widzenia tworzenia takich automatyzacji jest ono bardzo dobre. Przy prostych operacjach program komputerowy działa ultra szybko — są to dosłownie ułamki milisekund. Gdyby w przypadku SAP GUI Scripting kod wykonywany był tak bardzo szybko, po chwili mielibyśmy błąd. Dlaczego? To proste — program próbowałby pracować na widoku, który jeszcze nie został załadowany. Zauważ, że mamy podobną sytuację w naszym kodzie:

if numer_okna < 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_okna += 1  # zwiększamy numer sesji o 1 po każdej iteracji pętli

Po utworzeniu nowego okna GUI, robimy 1 sekundową “pauzę” w wykonywaniu programu za pomocą time.sleep(1) — właśnie po to, żeby SAP miał czas na otworzenie tego nowego okna. Jeśli spróbujesz uruchomić program bez tej pauzy, najprawdopodobniej dostaniesz błąd — skrypt będzie próbował pracować na oknie, którego interfejs graficzny nie został jeszcze załadowany. 

Bardziej wnikliwi czytelnicy mogą się zastanawiać, dlaczego w tym przypadku jest inaczej. Odpowiedź jest dość prosta: linia kodu session.createSession() wywołuje tylko nowe okno w SAP, które jest już nowym obiektem. Obiekt session, na którym my pracujemy nie jest więc tym procesem zajęty — program przechodzi dalej. 

Tworzone okno nie jest od razu gotowe do pracy, ale główna sesja (obiekt session) nie musi na nie czekać, więc program biegnie dalej. To tak, jakbyś otwierał nowe okno SAP ręcznie — zanim się ono załaduje, nadal możesz klikać w starym.

Może trochę uprościłem, ale ogólnie zrozumienie tego będzie bardzo istotne dla każdego, kto chce pracować z SAP GUI Scripting. W kolejnym kroku zobaczysz, jak możemy otwierać te okna naprawdę jednocześnie — bez czekania, aż poprzednie zakończy swoje działanie. 

Multiprocessing w praktyce SAP GUI

Do tej pory nasze narzędzie otwierało kolejne transakcje po kolei, w jednej pętli. To podejście działa — ale im więcej zadań do uruchomienia, tym dłużej musimy czekać, aż program wykona wszystkie kroki.

A co jeśli dałoby się uruchomić te transakcje równolegle? To właśnie umożliwia nam multiprocessing – czyli uruchamianie kilku zadań jednocześnie, w osobnych procesach. To trochę tak, jakbyś miał pięciu pracowników i każdemu zlecił jedno zadanie, zamiast dawać wszystko jednej osobie do zrobienia po kolei. Kod naszego skryptu z wykorzystaniem multiprocessing będzie wyglądał tak (dodałem komentarze objaśniające nowe elementy składni):

import time
import multiprocessing  # importujemy niezbędną bibliotekę
import win32com.client


def otworz_transakcje_i_wczytaj_wariant(numer_sesji, nazwa_transakcji, nazwa_wariantu):
    # Inicjalizacja COM w nowym procesie
    SapGuiAuto = win32com.client.GetObject("SAPGUI")
    application = SapGuiAuto.GetScriptingEngine
    connection = application.Children(0)
    session = connection.Children(numer_sesji)

    session.findById("wnd[0]").maximize()
    session.findById("wnd[0]/tbar[0]/okcd").text = nazwa_transakcji
    session.findById("wnd[0]").sendVKey(0)

    if nazwa_wariantu:
        session.findById("wnd[0]").sendVKey(17)  # CTRL + F5
        session.findById("wnd[1]/usr/txtV-LOW").text = nazwa_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 __name__ == "__main__":
    # Inicjalizacja COM w procesie głównym
    SapGuiAuto = win32com.client.GetObject("SAPGUI")
    application = SapGuiAuto.GetScriptingEngine
    connection = application.Children(0)
    session = connection.Children(0)

    # === TWOJA KONFIGURACJA ===
    zadania_do_uruchomienia = [
        {'transakcja': 'COHV', 'wariant': 'PLAN_LU_001'},
        {'transakcja': 'MD04', 'wariant': None},
        {'transakcja': 'COHV', 'wariant': 'PLAN_LU_002'},
    ]

    numer_okna = 0
    procesy = []  # Tworzymy pustą listę, w której będziemy przechowywać nasze procesy

    for slownik in zadania_do_uruchomienia:
        wariant = slownik['wariant']
        transakcja = slownik['transakcja']

        # Tworzymy nowy proces, który uruchomi daną transakcję w osobnym oknie SAP
        proces = multiprocessing.Process(
            target=otworz_transakcje_i_wczytaj_wariant,
            args=(numer_okna, transakcja, wariant)
        )
        procesy.append(proces)  # Dodajemy proces do listy, by później móc na niego zaczekać
        proces.start()  # Uruchamiamy proces (czyli otwieranie i konfigurację okna)

        if numer_okna < len(zadania_do_uruchomienia) - 1:
            session.createSession()
            time.sleep(1)
            numer_okna += 1

    # ⏳ Główny program czeka, aż wszystkie okna SAP zakończą swoje zadania
    for proces in procesy:
        proces.join()

Najważniejsze różnice w wersji z multiprocessing:

  • importujemy dodatkowo moduł multiprocessing, który pozwala na uruchamianie wielu procesów równolegle,
  • musimy użyć konstrukcji if __name__ == "__main__":, bo na Windowsie każdy nowy proces uruchamia cały plik od nowa — bez tego program wpadnie w błędne koło lub rzuci wyjątek,
  • zamiast wywoływać funkcję bezpośrednio, tworzymy dla niej osobny proces i dopiero ten proces ją uruchamia — dzięki temu kilka sesji SAP może działać jednocześnie.

Efekt? W praktyce umożliwia to działanie wielu okien SAP, co znacząco przyspiesza wykonywanie skryptu. Postaram się to udowodnić w kolejnym podrozdziale.


Test i porównanie

W celu sprawdzenia czy równoległa praca na kilku oknach może przynieść nam jakieś korzyści wykorzystam moduł time, aby zmierzyć czas wykonywania dwóch wersji skryptu:

  • tej z wykorzystaniem funkcji (podejście sekwencyjne - krok po kroku, z poprzedniego artykułu) oraz 
  • najnowszej wersji programu (podejście z wykorzystaniem multiprocessing — kod z poprzedniego podrozdziału).

Test przeprowadzę dla wczytania dokładnie tego samego wariantu 6 razy — wykorzystam wszystkie możliwe okna SAP-a.

# === KONFIGURACJA TESTOWA ===
zadania_do_uruchomienia = [
   {'transakcja': 'COHV', 'wariant': 'PLAN_LU_ZAR'},
   {'transakcja': 'COHV', 'wariant': 'PLAN_LU_ZAR'},
   {'transakcja': 'COHV', 'wariant': 'PLAN_LU_ZAR'},
   {'transakcja': 'COHV', 'wariant': 'PLAN_LU_ZAR'},
   {'transakcja': 'COHV', 'wariant': 'PLAN_LU_ZAR'},
   {'transakcja': 'COHV', 'wariant': 'PLAN_LU_ZAR'},
]

Na początek sprawdźmy jak to wygląda dla podejścia sekwencyjnego. Poniżej prezentuję zrzut ekranu z krótkim logiem wykonywania programu testowego, który pokazuje po jakim czasie jego trwania rozpoczął się proces uruchamiania transakcji w konkretnym oknie. Na końcu widać całkowity czas potrzebny do wykonania programu — czyli uruchomienia wszystkich 6 okien SAP-a i wczytania tam wariantów.

SAP Automation comparison sequence - tothepypoint.pl - Automatyzacja procesów
Rys. 1. Przebieg programu pracującego sekwencyjnie.

Jak widzisz, całkowity czas to około 45 sekund. Pierwsze okno rozpoczyna swoją pracę już na samym początku (0.02s), a kolejne uruchamiane są sekwencyjnie co około 8 sekund.

A jak to wygląda dla podejścia z równoległą pracą na kilku oknach? Poniższy zrzut ekranu wszystko wyjaśnia.

SAP Automation comparison concurrent - tothepypoint.pl - Automatyzacja procesów
Rys. 2. Przebieg programu pracującego równolegle.

Czas wykonywania programu został skrócony o jakieś 60% — do około 19 sekund. Z zarejestrowanych informacji można dowiedzieć się jednak czegoś jeszcze — nie wszystkie okna były uruchamiane jednocześnie. Zauważ, że praca w pierwszych trzech oknach programu została zainicjowana w pierwszych 3 sekundach działania skryptu. Kolejne 3 okna uruchomiły się natomiast dopiero po upływie kolejnych około 7-8 s.

Wniosek? Nasza automatyzacja operowała tak naprawdę na trzech oknach jednocześnie, a nie na wszystkich 6-ciu. Nie potrafię wyjaśnić dlaczego tak się dzieje. Zakładam, że to jakieś ograniczenie po stronie SAP GUI, ale mogę się mylić. Osobiście nie próbowałem szukać rozwiązań, które pozwoliłby obejść to ograniczenie. W praktyce możliwość interakcji z trzema oknami SAP-a w sposób automatyczny jest w pełni wystarczająca i otwiera ogromne możliwości automatyzowania procesów.


Automatyczny start SAP i logowanie

Zanim przejdziemy do krótkiego podsumowania, obiecany na początku artykułu bonus — sposób na automatyczne uruchamianie oraz logowanie się do systemu SAP. Zaznaczam, że metoda, którą pokażę działa najlepiej dla użytkowników mających dostęp do tzw. logowania SSO (ang. Single Sign-On). Jest to mechanizm, dzięki któremu użytkownik może logować się do SAP-a bez wpisywania loginu i hasła, bo uwierzytelnienie odbywa się automatycznie na podstawie danych z systemu Windows. 

Jak poznać, czy masz SSO? Gdy otwierasz SAP i nie musisz wpisywać loginu i hasła - to znak, że działa SSO. 

A co jeśli nie masz SSO?

Technicznie możliwe jest również zautomatyzowanie logowania bez SSO — skrypt może wpisywać login i hasło do pól logowania w SAP GUI. Jednak takie rozwiązanie wymaga przechowywania danych logowania na dysku komputera (w kodzie lub w pliku konfiguracyjnym), co wiąże się z ryzykiem ich przechwycenia.

Dla początkujących w SAP GUI Scripting (i w środowisku firmowym) zdecydowanie polecam najpierw sprawdzić możliwość włączenia SSO, bo jest to znacznie bezpieczniejsze i eliminuje problem zarządzania hasłami w skryptach.

Jeśli obecnie logując się do systemu SAP wpisujesz dane logowania, sugeruję skontaktować się z administratorem SAP — treść gotowego maila znajdziesz poniżej:

Temat: Prośba o informację nt. możliwości logowania do SAP z wykorzystaniem SSO

Dzień dobry,

Chciałbym zapytać, czy w naszej organizacji istnieje możliwość korzystania z logowania do SAP za pomocą mechanizmu SSO (Single Sign-On), bez konieczności każdorazowego podawania loginu i hasła.

Jeśli taka opcja jest dostępna, chciałbym prosić o jej aktywację dla mojego użytkownika.

Z góry dziękuję za informację i pozdrawiam,

[Twoje imię i nazwisko]
[Twój login SAP lub identyfikator użytkownika]

Jeśli masz już możliwość logowania SSO to wszystkim, czego będziesz jeszcze potrzebować jest skrypt, który za chwilę udostępnię oraz pełna nazwa Twojego systemu SAP. Skąd ją wziąć? Zaraz po uruchomieniu SAP-a wybierasz tę nazwę z listy, która się wyświetla. Wygląda ona mniej więcej tak:

SAP LOGON 800 systems list - tothepypoint.pl - Automatyzacja procesów
Rys. 3. Okno wyboru systemu SAP podczas logowania.

Potrzebujesz pełnej nazwy z kolumny “Nazwa” aby podać ją do programu, który może wyglądać tak (dodałem komentarze objaśniające):

import os
import time
import subprocess
import win32com.client


# Podajemy lokalizację, w której zainstalowany jest system SAP
# Wykorzystujemy ją w funkcji `otworz_sap`
# Dopasuj ścieżkę do Twojej instalacji (np. dla wersji 64 bit-owej SAP-a będzie inna)
SAP_PATH = r"C:\Program Files (x86)\SAP\FrontEnd\SAPgui\saplogon.exe"


class NazwySystemowSAP:
    """
    Klasa przechowująca nazwy systemów SAP, z którymi możemy się połączyć.
    Dzięki temu możesz łatwo przełączać się np. pomiędzy systemem produkcyjnym i testowym
    Możesz zdefiniować tyle nazw, ile potrzebujesz
    """
    SYSTEM_Prod = "Nazwa_systemu_prod"
    SYSTEM_Test = "Nazwa_systemu_test"


def otworz_sap():
    """
    Uruchamia program SAP Logon, jeśli znajduje się w domyślnej lokalizacji.
    """
    sciezka_do_sap_gui = SAP_PATH

    if os.path.exists(sciezka_do_sap_gui):
        # Uruchomienie SAP GUI
        subprocess.Popen(sciezka_do_sap_gui)
    else:
        print(f"Błąd: Nie znaleziono SAP GUI pod ścieżką {sciezka_do_sap_gui}")

    # Odczekanie chwili, aby GUI mogło się poprawnie załadować
    time.sleep(2)


def zaloguj_do_sap(system_sap):
    """
    Łączy się z wybranym systemem SAP za pomocą SAP GUI Scripting.

    Parametry:
    system_sap (str): Nazwa systemu SAP (zgodna z SAP Logon")

    Zwraca:
    connection: Obiekt połączenia z systemem SAP
    """
    # Inicjalizacja silnika SAP GUI Scripting
    sap_gui_auto = win32com.client.GetObject("SAPGUI")
    aplikacja = sap_gui_auto.GetScriptingEngine

    # Otwarcie połączenia z systemem
    # w dotychczasowej pracy ze skryptami spotkałeś się z tym 
    # obiektem pod angielską nazwą "connection"
    polaczenie = aplikacja.OpenConnection(system_sap, True)

    return polaczenie


# -----------------------------
# Przykład użycia programu
# -----------------------------

if __name__ == "__main__":
    # Krok 1: Uruchom SAP GUI
    otworz_sap()

    # Krok 2: Zaloguj się do wybranego systemu (tu: system produkcyjny)
    connection = zaloguj_do_sap(NazwySystemowSAP.SYSTEM_Prod)

    # Dalej możesz wykonać inne operacje na obiekcie `connection`, 
    # np. otworzyć transakcję

Możesz teraz wykorzystać ten kod w narzędziu start_dnia.py. Dzięki temu od tej pory będziesz mógł klikać tylko w ikonkę skryptu, a SAP uruchomi się automatycznie przed otwieraniem wszystkich Twoich transakcji! Jeśli z jakiegoś powodu preferujesz gotowca — bez obaw, na końcu tego artykułu udostępnię cały kod do pobrania.


Podsumowanie

Mam nadzieję, że wiedza, którą zdobyłeś w tym artykule pozwoliła Ci usprawnić Twój skrypt start_dnia.py na tyle, żeby nie frustrować się na opieszale działającą automatyzację, a zamiast tego niezwłocznie po przyniesieniu sobie porannej kawy, rozpocząć pracę.

Zdaję sobie sprawę, że treść tego wpisu daleko wykraczała poza podstawy. Zależało mi jednak na tym, aby czytelnik, który przeczyta wszystkie części tego cyklu, miał pełne wyobrażenie o możliwościach, jakie może przynieść SAP GUI Scripting. Przetwarzanie równoległe może być nieocenione przy większych projektach — to dosłownie tak, jakbyś miał kilku pracowników zamiast jednego! Starałem się więc, aby przy wprowadzeniu minimalnej ilości zagadnień technicznych, poszerzyć Twoje perspektywy oraz otworzyć drogę do zgłębiania tej tematyki i budowania bardziej zaawansowanych automatyzacji SAP-a. 

Możliwe, że czytając ten artykuł, momentami czułeś, że niektóre fragmenty są dla Ciebie nie do końca jasne. Poniżej wklejam listę kilku tematów, które mogły sprawić Ci trudność, wraz z gotowymi promptami do na przykład Chata GPT (lub innego LLM - ang. Large Language Model - jak np.: Calude, Gemini itd.) z prośbą o wyjaśnienie:

  1. Wyjaśnij mi w jakim celu stosujemy warunek if __name__ == "__main__":  w skrypcie Python.
  2. Pokaż na prostym przykładzie jak zmierzyć czas wykonywania skryptu Python wykorzystując bibliotekę time.
  3. Jakie zastosowanie ma słowo kluczowe return funkcji w Pythonie?

Możesz też przejść całą ścieżkę nauki z wpisu nauka Pythona z AI na moim blogu.

Życzę Ci owocnej dalszej nauki i sukcesów w budowaniu własnych automatyzacji!

Już wkrótce na blogu pojawi się ostatni artykuł z cyklu — pokażę w nim, jak zintegrować skrypty SAP z Excelem. A jeśli chcesz otrzymać gotową wersję programu, nad którym pracowaliśmy w ostatnich trzech częściach, wypełnij poniższy formularz.

Pobierz gotowy program

Zapisz się, a otrzymasz gotową wersję programu, który tworzyliśmy w cyklu „Zautomatyzuj SAP”.
Po potwierdzeniu adresu e-mail od razu dostaniesz link do pobrania.

We use Brevo as our marketing platform. By submitting this form you agree that the personal data you provided will be transferred to Brevo for processing in accordance with Brevo's Privacy Policy.

Napisz komentarz

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

Scroll to Top