Zautomatyzuj SAP [4/4] – Połącz VBA z Pythonem

🛠️ Tutorial

Jeśli pracujesz z SAP-em, to niemal na sto procent korzystasz też z Excela — ten pierwszy służy często jako źródło danych, a ten drugi jako narzędzie do ich analizy. Nie spotkałem jeszcze firmy, w której byłoby inaczej. Takie są realia — przekonuję się o tym na konferencjach i czytając wpisy praktyków SAP-a.

Jest całkiem prawdopodobne, że jeśli trafiłeś tutaj to masz już trochę automatyzacji, które wspierają Cię w Twoich zadaniach w Excelu. Na przykład makra VBA, które przetwarzają dane pobrane wcześniej z SAP-a. Teraz, widząc możliwości Pythona, możesz się zastanawiać: “Czy wdrażanie kolejnego języka programowania jest w ogóle zasadne?” albo “Czy to oznacza, że będę musiał teraz przepisać wszystkie makra VBA — nad którymi tyle pracowałem — na Pythona, żeby to wszystko było spójne?”. Jeśli zadajesz sobie takie pytania — to bardzo dobrze! W istocie, to bardzo ważne kwestie do przemyślenia.

Sam stałem przed takim dylematem szczególnie, że — jak wspomniałem we wprowadzeniu do pierwszego wpisu — takich automatyzacji w Excelu z wykorzystaniem VBA mieliśmy naprawdę dużo. Niektóre z nich to bardzo rozbudowane programy — górne kilkaset lub więcej linii kodu. Z jednej strony idealna sytuacja to taka, w której cały “ekosystem” naszych automatyzacji zbudowany jest z wykorzystaniem minimalnej liczby technologii. Z drugiej jednak strony przepisywanie dobrze działających programów tylko po to, żeby mieć wszystko w jednym języku, może mijać się z celem — i generować dodatkowe koszty.

Oczywiście, każda sytuacja jest inna i może wymagać indywidualnego podejścia i analizy. Ja w większości przypadków postawiłem na wykorzystanie dotychczasowych automatyzacji (np. makro VBA przetwarzające dane) oraz połączenie ich z programami dającymi dodatkowe funkcjonalności (np. skrypt Python umożliwiający interakcję z SAP-em). W tym wpisie pokażę Ci, jak z poziomu kodu VBA uruchamiać skrypty Pythona automatyzujące SAP-a. Zapraszam do lektury!


Integracja VBA z Pythonem i SAP – kiedy to ma sens?

W skrócie: warto, gdy masz już działające programy VBA, do których chcesz dodać nowe funkcjonalności, w których Python sprawdzi się lepiej (np. SAP GUI Scripting lub inne automatyzacje). Takie połączenie obu tych światów — Pythona i VBA — może dać sporo korzyści, przy zachowaniu stabilności całego rozwiązania. 

Domyślnie VBA tylko uruchomi skrypt Pythona i “pójdzie dalej” — do następnej linii kodu. Interpreter kodu VBA nie będzie czekał, aż Python zrealizuje swoją pracę — to dwa niezależne procesy, napisane w różnych językach. Oczywiście da się to obejść, jeśli jest taka potrzeba. W tym artykule pokazuję oba te podejścia, zaczynając od najprostszego i przechodząc stopniowo do tych bardziej złożonych — dających największą kontrolę nad wykonywanym skryptem.

Czy warto tworzyć nową automatyzację od podstaw, zakładając współpracę VBA i Pythona? Moim zdaniem — nie. Nie ma sensu komplikować projektu.  Owszem, zaletą VBA jest to, że dobrze “widzi” Excela. Prawda jest jednak taka, że Python wyśmienicie radzi sobie z danymi, a do tego daje duże możliwości obsługiwania plików Excel z pomocą bibliotek openpyxl oraz pandas. Co więcej, Python w połączeniu z tymi bibliotekami potrafi znacznie szybciej przetwarzać duże zestawy danych niż VBA.

Po VBA sięgam tylko wtedy, gdy jest to uzasadnione. Przykład: budowa dashboardu w Excelu, który integruje dane z wielu różnych źródeł. Wtedy wszystko, co dzieje się “wewnątrz Excela”, może być zautomatyzowane za pomocą VBA (np. przenoszenie danych z tabeli SAP Analytics Cloud). Z kolei pobieranie i przetwarzanie danych z innych zewnętrznych plików (np. plików Excel eksportowanych automatycznie przez SAP-a) może być realizowane przez skrypt Python uruchamiany z poziomu VBA.

Dla mnie, kiedy chcę zbudować coś od podstaw, pierwszym wyborem zdecydowanie jest Python. Jego zalety omawiałem szerzej w pierwszym artykule tej serii — jeśli jeszcze go nie widziałeś, zachęcam do lektury. Jeżeli zacząłeś uczyć się Pythona (np. poprzez naukę Pythona z AI) i chcesz zacząć automatyzować SAP-a z pomocą SAP GUI Scripting — Tobie również rekomenduję pracę z Pythonem w całym tym projekcie. 

No dobrze, wiesz już, kiedy możesz wykorzystać możliwość integracji VBA z Pythonem. Przejdźmy zatem do aspektów nieco bardziej technicznych. Myślę, że zdecydowanie lżejszych niż w ostatnim wpisie.


Twoje pierwsze makro uruchamiające skrypt Python

Na potrzeby tego przykładu przygotowałem bardzo prosty skrypt integracja_excel_md04.py. Jego jedynym zadaniem jest uruchomienie jednej transakcji i — jeśli został podany — wczytanie wariantu. Skrypt wygląda tak (zauważ, że wykorzystałem funkcje, które opracowaliśmy wcześniej — w poprzednich artykułach):

import time
import win32com.client
import subprocess
import os


class NazwySystemowSAP:
   SYSTEM_Prod = "P [ERP PRD]"
   SYSTEM_Test = "K [ERP QAS]"


def otworz_sap():...

def zaloguj_do_sap(system_sap):...

def otworz_transakcje_i_wczytaj_wariant(numer_sesji, nazwa_transakcji, nazwa_wariantu):...


if __name__ == "__main__":
   # Tutaj otwieramy SAP-a i logujemy się do systemu
   otworz_sap()
   zaloguj_do_sap(NazwySystemowSAP.SYSTEM_Prod)

   # Inicjalizacja COM w procesie głównym
   SapGuiAuto = win32com.client.GetObject("SAPGUI")
   application = SapGuiAuto.GetScriptingEngine
   connection = application.Children(0)
   session = connection.Children(0)

   numer_okna = 0

   otworz_transakcje_i_wczytaj_wariant(numer_sesji=0, nazwa_transakcji="MD04", nazwa_wariantu=None)

   input("Wciśnij enter aby kontynuować...")

Na końcu dodałem linijkę z funkcją input() — to bardzo przydatne w fazie testów. Bez niej, gdyby coś poszło nie tak, okno konsoli z Pythonem zamknęłoby się od razu, przez co nie miałbyś nawet szansy odczytać błędu. Funkcja input() sprawia, że program “czeka” na naciśnięcie Enter przed zamknięciem.

Jak uruchomić taki skrypt za pomocą VBA? Wystarczy ścieżka do Twojego pliku .py oraz poniższe proste makro:

Sub UruchomSkryptPython()
   Dim scriptPath As String
   Dim command As String

   ' Pełna ścieżka do skryptu Pythona, który chcemy uruchomić           
   scriptPath= "C:\99_Moje_projekty\97_BLOG\01_Zautomatyzuj_SAP\integracja_excel_md04.py"

   ' Stworzenie komendy - jak w konsoli CMD
   ' Podwójne cudzysłowy w VBA ("") wstawiają jeden prawdziwy cudzysłów w finalnym ciągu znaków.
   command = """" & "python" & """ """ & scriptPath & """"

   ' Wywołanie komendy za pomocą wbudowanej funkcji Shell
   Shell command, vbNormalFocus
  
End Sub

Pamiętasz jak w poprzednim artykule uruchomiliśmy skrypt Pythona przez konsolę systemu Windows? Zauważ, że powyższe makro działa niemal identycznie — tutaj również wywołujemy komendę skonstruowaną ze słowa kluczowego “python” oraz ścieżki do naszego skryptu.

Korzystamy do tego z wbudowanej w VBA funkcji Shell, która pozwala uruchamiać z poziomu Excela (lub innej aplikacji Office) dowolne programy czy polecenia systemowe Windows — dokładnie tak, jakbyś wpisał je ręcznie w Wierszu Polecenia (CMD). Argument vbNormalFocus sprawia, że okno konsoli (jeśli się pojawi) będzie widoczne w normalnym rozmiarze — możesz wtedy zobaczyć komunikaty wypisywane przez print() w skrypcie.

Podwójne cudzysłowy, które zastosowałem nie są przypadkowe — dzięki nim system Windows poprawnie odczyta pełne ścieżki do plików, nawet jeśli zawierają spacje.

Aby to działało:

  • Musisz mieć zainstalowanego Pythona.
  • Komenda python musi działać w konsoli (czyli Python musi być dodany do zmiennej środowiskowej PATH). Wspominałem o tym w pierwszym wpisie w sekcji o przygotowaniu środowiska.

Jeśli Twój Python nie jest dodany do zmiennej środowiskowej PATH, poniższy kod w VBA pozwoli Ci wskazać pełną ścieżkę do interpretera i uruchomić dowolny skrypt Pythona z poziomu Excela.

Sub UruchomSkryptPython()
    Dim pythonExe As String
    Dim scriptPath As String
    Dim command As String
    
    ' Pełna ścieżka do interpretera Pythona (podmień na własną)
    ' Użyj tej metody, jeśli Python NIE jest dodany do zmiennej środowiskowej PATH
    pythonExe = "C:\Users\nazwa_użytkownika\AppData\Local\Programs\Python\Python310\python.exe"
    
    ' Pełna ścieżka do skryptu Pythona, który chcemy uruchomić  
    scriptPath = "C:\99_Moje_projekty\97_BLOG\01_Zautomatyzuj_SAP\integracja_excel_md04.py"
    
    ' Stworzenie komendy - jak w konsoli CMD
    command = """" & pythonExe & """ """ & scriptPath & """"
    
    ' Wywołanie komendy za pomocą wbudowanej funkcji Shell
    Shell command, vbNormalFocus
End Sub

Teraz możesz podpiąć takie makro pod przycisk w Excelu i jednym kliknięciem uruchamiać dowolny skrypt Pythona, który będzie wykonywał operacje w SAP-ie.

W następnym rozdziale pokażę Ci, jak uzyskać jeszcze większą kontrolę nad uruchamianym skryptem, przekazując do niego dane bezpośrednio z poziomu VBA.


Steruj Pythonem z poziomu Excela — przekazywanie argumentów z VBA

Makro uruchamiające skrypt Pythona to świetny pierwszy krok — ale co jeśli chcesz, by to makro nie tylko uruchamiało, ale również przekazywało dane, które mają zostać wykorzystane w działaniu skryptu? Wyobraź sobie, że użytkownik wpisuje coś w arkusz Excela — nazwę transakcji, wariant, czy zakres dat — a Python automatycznie wykorzystuje te dane w pracy z SAP-em.

Brzmi jak magia? Spokojnie — to tylko argumenty wiersza poleceń, a za chwilę pokażę Ci, jak z nich skorzystać.

Praca z jednym argumentem – nazwa transakcji

Na początek spróbuj uruchomić następujący skrypt napisany w Pythonie za pomocą konsoli systemu Windows (tak, jak robiliśmy to dotychczas):

import sys

if __name__ == "__main__":

   print(sys.argv)
   input("Wciśnij enter aby kontynuować...")

Jak widzisz, program ten drukuje do konsoli parametr sys.argv. Wynik, jaki powinieneś otrzymać to lista Pythona z jednym elementem — nazwą Twojego skryptu. U mnie wygląda to tak:

Komenda...
C:\99_Moje_projekty\97_BLOG\01_Zautomatyzuj_SAP>python integracja_excel_md04.py

Wynik działania skryptu...
['integracja_excel_md04.py']
Wciśnij enter aby kontynuować...

Teraz spróbuj do polecenia przesyłanego przez wiersz poleceń dodać argument — niech to będzie nazwa transakcji — “MD04”. Dodaj ten tekst po spacji – za nazwą pliku:

python integracja_excel_md04.py MD04

Wynik, jaki otrzymasz w konsoli będzie następujący:

Komenda...
C:\99_Moje_projekty\97_BLOG\01_Zautomatyzuj_SAP>python integracja_excel_md04.py

Wynik działania skryptu...
['integracja_excel_md04.py', 'MD04']
Wciśnij enter aby kontynuować...

Argument, który podałeś znalazł się w liście sys.argv. Dodając więcej argumentów (oddzielonych spacją), miałbyś je wszystkie dostępne w Pythonie jako kolejne elementy tej listy. 

To samo w VBA

Sprawdźmy czy to samo podejście zadziała w przypadku funkcji Shell w VBA. Tym razem nasze makro VBA — analogicznie do tego, co zrobiliśmy w konsoli — będzie przekazywać również argument:

Sub UruchomSkryptPython2(arg)
   Dim scriptPath As String
   Dim command As String
           
   scriptPath = "C:\99_Moje_projekty\97_BLOG\01_Zautomatyzuj_SAP\integracja_excel_md04.py"
  
   ' Stworzenie komendy - jak w konsoli CMD
   command = """" & "python" & """ """ & scriptPath & """ """ & arg & """"

   ' Wywołanie komendy za pomocą wbudowanej funkcji Shell
   Shell command, vbNormalFocus
  
End Sub


Sub WywolajSkrypt()
   Dim arg As String
  
   arg = "MD04 COHV"
  
   Call UruchomSkryptPython2(arg)
  
End Sub

Uruchomienie makra WywolajSkrypt() daje następujący rezultat:

Wynik działania skryptu...
['C:\\99_Moje_projekty\\97_BLOG\\01_Zautomatyzuj_SAP\\integracja_excel_md04.py', 'MD04 COHV']
Wciśnij enter aby kontynuować...

Tym razem zamiast nazwy pliku mamy pełną jego ścieżkę. Poza tym szczegółem, cała reszta pozostaje bez zmian. Zwróć uwagę, że w kodzie VBA ponownie użyłem tzw. zagnieżdżonych cudzysłowów (ang. escaped quotes) — wspomniałem je wcześniej pod nazwą podwójne cudzysłowy. Dzięki temu argument "MD04 COHV" jest traktowany jako jeden, a nie dwa osobne argumenty — jest jednym elementem listy wyświetlonej w konsoli.

Jeśli zrezygnujesz z zagnieżdżonych cudzysłowów i utworzysz komendę w VBA w taki sposób:

command = "python" & " " & scriptPath & " " & arg

Argument ze spacją zostanie potraktowany jako dwa różne argumenty, a więc wydrukowana do konsoli wartość parametru sys.argv będzie listą z trzema elementami i będzie wyglądać tak:

Wynik działania skryptu...
['C:\\99_Moje_projekty\\97_BLOG\\01_Zautomatyzuj_SAP\\integracja_excel_md04.py', 'MD04', 'COHV']
Wciśnij enter aby kontynuować...

Taką samą sytuację mielibyśmy ze ścieżką do pliku, która zawiera spacje. Zostałaby ona podzielona na dwie (lub więcej) części, a program w ogóle by się nie uruchomił. Teraz już wiesz, dlaczego stosowanie zagnieżdżonych cudzysłowów jest takie istotne.

Praca z wieloma argumentami

No dobrze ale co z pozostałymi parametrami funkcji otworz_transakcje_i_wczytaj_wariant — jest ich tam przecież więcej. Naturalnie, możemy sobie z tym poradzić. Poniżej przedstawię jeden ze sposobów. Załóżmy, że chcemy przekazać wartości wszystkich trzech parametrów jako argumenty naszej funkcji i mamy je zapisane w Excelu w komórkach A1:C1:

A1 → nazwa transakcji
B1 → nazwa wariantu
C1 → numer okna

Makro możemy więc zmodyfikować tak, aby do skryptu Python przekazywało jeden argument w postaci tekstu, gdzie wartości poszczególnych parametrów oddzielone są przecinkami, a więc:

“nazwa_transakcji,nazwa_wariantu,numer_okna”

Makro VBA WywolajSkrypt() będzie wyglądało wtedy tak:

Sub WywolajSkrypt()
   Dim arg As String
   Dim transakcja As String
  
   argumenty = Sheets("List1").Range("A1").Value & "," & Sheets("List1").Range("B1").Value & "," & Sheets("List1").Range("C1").Value
   arg = argumenty
  
   Call UruchomSkryptPython2(arg)
  
End Sub

W tym przykładzie argumenty to zwykły ciąg znaków, który zostanie przekazany do Pythona.

Teraz czas na modyfikację kodu w Pythonie. Musimy zadbać o to, żeby z tekstu "transakcja,wariant,numer_okna" wydobyć każdą wartość osobno. Najprościej zrobić to metodą .split(','), która zamieni taki ciąg na listę.

Kod Python:

argumenty_tekst = str(sys.argv[1])  # "transakcja,wariant,numer_okna"
argumenty_lista = argumenty_tekst.split(',')  # ['transakcja', 'wariant', 'numer_okna']

# Pobieramy poszczególne wartości
transakcja = argumenty_lista[0]
wariant = argumenty_lista[1]
numer_okna = int(argumenty_lista[2])  # konwersja tekstu na integer

otworz_transakcje_i_wczytaj_wariant(
    numer_sesji=numer_okna,
    nazwa_transakcji=transakcja,
    nazwa_wariantu=wariant
)

Tip: Jeżeli w którymś z argumentów (np. w nazwie wariantu) znajduje się przecinek, rozdzielanie .split(',') może się posypać.

W takim przypadku lepiej użyć innego separatora, który na pewno nie wystąpi w danych — np. średnika ; albo potrójnego znaku |||.

Przykład w VBA:

argumenty = Sheets("List1").Range("A1").Value & "|||" & _
            Sheets("List1").Range("B1").Value & "|||" & _
            Sheets("List1").Range("C1").Value

A w Pythonie:

argumenty_lista = argumenty_tekst.split('|||')

W ten sposób unikniesz pomyłek przy przekazywaniu danych między VBA i Pythonem.

Oczywiście moglibyśmy teraz pójść krok dalej i dostosować program tak, żeby otwierał dowolną liczbę transakcji w SAP-ie — analogicznie do tego, co robiliśmy wcześniej. Zatrzymamy się jednak w tym miejscu. 

Celem tego wpisu jest pokazanie Ci sposobu integracji kodu VBA z Pythonem.

Wykorzystanie tej wiedzy w praktyce zależy już od Ciebie i Twoich potrzeb.


Zaawansowana integracja VBA z Pythonem — dwustronna komunikacja

Do tej pory pokazywałem Ci, jak uruchomić skrypt Pythona z poziomu VBA. Standardowe wywołanie przez funkcję Shell odbywa się jednak w tle — VBA natychmiast przechodzi do kolejnych instrukcji, nie czekając na zakończenie działania skryptu.

Co w sytuacji, gdy chcesz, aby makro VBA poczekało na zakończenie pracy Pythona? A może zależy Ci na sprawdzeniu, czy program zakończył się powodzeniem albo jakie komunikaty zwrócił? To szczególnie ważne, jeśli chcesz odebrać wyniki wygenerowane w Pythonie albo masz proces, w którym kolejność kroków musi być ściśle zachowana. W dalszej części pokażę Ci, jak rozbudować integrację VBA–Python tak, aby dała Ci pełniejszą kontrolę niż samo uruchomienie skryptu.

Jak wstrzymać kod VBA, aby poczekał na zakończenie skryptu Python

Jeśli chcesz, aby VBA poczekało na zakończenie działania Pythona, trzeba lekko zmodyfikować sposób uruchamiania skryptu.Poniżej znajdziesz przykład z wykorzystaniem obiektu WScript.Shell, który uruchamia skrypt Pythona i jednocześnie wstrzymuje wykonywanie makra VBA do momentu jego zakończenia.

Sub UruchomSkryptPythonZCzekaniem()
    Dim objShell As Object
    Dim command As String
    
    ' Ścieżka do skryptu Python
    Dim scriptPath As String
    scriptPath = "C:\99_Moje_projekty\97_BLOG\01_Zautomatyzuj_SAP\integracja_excel_md04.py"
    
    ' Pobranie argumentów z arkusza Excel
    arg = Sheets("List1").Range("A1").Value & "," & Sheets("List1").Range("B1").Value & "," & Sheets("List1").Range("C1").Value
    
    ' Budowanie komendy do uruchomienia Pythona
    command = """" & "python" & """ """ & scriptPath & """ """ & arg & """"
    
    ' Tworzymy obiekt WScript.Shell
    Set objShell = CreateObject("WScript.Shell")
    
    ' Uruchamiamy skrypt i czekamy, aż zakończy swoje działanie
    objShell.Run command, 1, True
    
    ' Kontynuujemy dalsze operacje w VBA po zakończeniu działania Pythona
    MsgBox "Skrypt Python zakończył działanie!", vbInformation
End Sub

Co się tu dzieje?

  • objShell.Run — To polecenie uruchamia skrypt Python. Parametr 1 oznacza, że okno konsoli będzie widoczne, a parametr True sprawia, że VBA czeka, aż skrypt Python zakończy działanie, zanim przejdzie do kolejnych operacji.
  • MsgBox — Ta linia wyświetla komunikat o zakończeniu działania. Możesz go zastąpić dowolnym kodem: np. odczytem danych, aktualizacją Excela czy innymi operacjami, które mają się wykonać po Pythonie..

Co ważne: ta metoda pozwala na “odczytanie” statusu wykonania skryptu. W Pythonie można zakończyć skrypt, zwracając tzw. kod wyjścia (ang. Exit Code). Instrukcja sys.exit(0) przekaże kod oznaczający sukces, natomiast sys.exit(1) — błąd. VBA może przechwycić ten kod i odpowiednio zareagować.

W VBA kod wyjścia z uruchomionego skryptu Pythona zwracany jest przez metodę .Run wykonaną na obiekcie WScript.Shell. Aby go przechwycić do programu wystarczy odpowiednio zmodyfikować linię wywołującą skrypt:

exitCode = WshShell.Run(command, 1, True)

Dzięki temu możesz dostosować dalszą część programu do wyniku działania Pythona — na przykład wyświetlić użytkownikowi stosowny komunikat w zależności od tego, czy skrypt zakończył się sukcesem, czy błędem. Poniższy fragment kodu VBA pozwala odpowiednio “zarządzić” informacją o statusie wykonania skryptu Python:

' Sprawdzenie przechwyconego kodu 
If exitCode = 0 Then 
    MsgBox "SUKCES!" & vbCrLf & "Skrypt Pythona zakończył się pomyślnie (Exit Code = 0)." 
Else 
    MsgBox "BŁĄD!" & vbCrLf & "Skrypt Pythona zgłosił błąd (Exit Code = " & exitCode & ")." 
End If

Zapewnienie odpowiedniego kodu wyjścia w skrypcie Pythona można zrealizować za pomocą instrukcji try oraz except. To standardowy sposób w Pythonie na obsługę wyjątków. Jeśli w trakcie działania programu wystąpi błąd, Python „wejdzie” w blok except i użyje instrukcji sys.exit(1), aby wskazać, że coś poszło nie tak. Dzięki temu VBA będzie wiedziało, że operacja zakończyła się niepowodzeniem. Przykład poniżej (zwróć uwagę na komentarze objaśniające działanie składni):

try:
   # Program zawsze "próbuje" uruchomić tę część kodu
   otworz_transakcje_i_wczytaj_wariant(numer_sesji=numer_okna, nazwa_transakcji=transakcja, nazwa_wariantu=wariant)
   sys.exit(0)  # pomyślne zakończenie programu (opcjonalnie)
except Exception as e:
   # Ta część kodu zostanie wykonana tylko, jeśli wystąpi wyjątek
   sys.exit(1)  # kończymy program z kodem zamknięcia 1 - co oznacza błąd

Jeśli tak zmodyfikowanym programem spróbujesz uruchomić np. nieistniejącą transakcję, skrypt Python zakończy swoje działanie przekazując do makra VBA, które go wywołało, kod wyjścia równy 1. Dzięki temu VBA wyświetli odpowiedni komunikat informujący o błędzie.

Dwustronna wymiana informacji pomiędzy VBA i Pythonem

Jeśli chcesz uzyskać więcej informacji z Pythona niż tylko wynik zakończenia skryptu, możesz użyć metody .Exec. Pozwala ona stworzyć “kanał komunikacji” ze skryptem. Umożliwia to odczytanie wszystkiego, co skrypt Pythona wypisuje na standardowe wyjście (czyli tego, co normalnie pojawia się w konsoli po użyciu funkcji print()).

Aby pokazać to na przykładzie zmodyfikujemy lekko naszą funkcję otworz_transakcje_i_wczytaj_wariant() w Pythonie — dodamy do niej dwie instrukcje print(), które wypiszą, jaka transakcja została uruchomiona oraz jaki wariant został wczytany:

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)
   print(f"Transakcja {nazwa_transakcji} została uruchomiona.")

   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)
       print(f"Wariant {nazwa_wariantu} został wczytany.")

Teraz potrzebujemy makra VBA, które uruchomi skrypt Pythona za pomocą metody .Exec oraz “przechwyci” wszystkie komunikaty, wypisane przez ten skrypt. Poniżej znajdziesz przykład takiego makra VBA:

Sub UruchomPythonaPrzezExec()
   Dim objShell As Object
   Dim objExec As Object
   Dim command As String
   Dim pythonOutput As String
   Dim exitCode As Integer
   Dim scriptPath As String
  
   ' Ścieżka do skryptu Python
   scriptPath = "C:\99_Moje_projekty\97_BLOG\01_Zautomatyzuj_SAP\integracja_excel_md04.py"
  
   arg = Sheets("List1").Range("A1").Value & "," & Sheets("List1").Range("B1").Value & "," & Sheets("List1").Range("C1").Value
  
   ' Budowanie komendy do uruchomienia Pythona
   command = """" & "python" & """ """ & scriptPath & """ """ & arg & """"
  
   ' Utworzenie obiektu WScript.Shell
   Set objShell = CreateObject("WScript.Shell")
  
   ' Uruchomienie skryptu za pomocą metody .Exec
   Set objExec = objShell.Exec(command)
  
   ' Czekanie na zakończenie procesu
   '    Pętla sprawdza status procesu. Dopóki jest on uruchomiony (status = 0),
   '    VBA czeka.
   Do While objExec.Status = 0
       DoEvents ' Pozwala Excelowi "oddychać" w trakcie czekania
   Loop

   ' Odczytanie całego wyjścia (wszystkiego, co Python "wydrukował")
   pythonOutput = objExec.StdOut.ReadAll

   ' Wyświetlenie przechwyconych komunikatów w oknie dialogowym
   MsgBox "Odebrano następujące komunikaty z Pythona:" & vbCrLf & vbCrLf & pythonOutput
 
End Sub

Tak zbudowany program VBA powinien wyświetlić wszystkie komunikaty ze skryptu Python dokładnie tak, jak pokazałem na poniższym obrazku (rys. 1).

- tothepypoint.pl - Automatyzacja procesów
Rys. 1. Komunikat z VBA, który wyświetla informacje ze skryptu Pythona.

Zwróć uwagę, że w kodzie VBA zastosowałem mechanizm, który pozwala pracować w Excelu podczas czekania na wykonanie skryptu Pythona — po prostu Excel nie zawiesza się w tym czasie. W niektórych przypadkach może być to bardzo praktyczne i efektywne.

Wymiana danych przez pliki: Najbardziej niezawodne podejście

Poruszając temat integracji makr VBA ze skryptami Pythona oraz wymiany danych pomiędzy nimi, nie sposób pominąć tematu plików. Dla większych ilości danych lub w skomplikowanych procesach, komunikacja przez pliki jest najlepszą i najbezpieczniejszą metodą. 

Przykładowy workflow, w którym pliki wykorzystane są jako “nośniki” danych, mógłby wyglądać następująco:

  1. VBA przygotowuje dane w pliku input.csv.
  2. VBA uruchamia skrypt Pythona (metodą .Run z czekaniem!), przekazując ścieżkę do pliku jako argument.
  3. Python odczytuje input.csv, wykonuje operacje w SAP, a wyniki zapisuje do pliku output.csv.
  4. Po pomyślnym zakończeniu skryptu Pythona, VBA importuje dane z output.csv do arkusza.

Zaletami takiego podejścia są:

  • niezawodność,
  • łatwość debugowania (pliki pośrednie można sprawdzić),
  • możliwość pracy z dużą ilością danych.

W praktyce, w większych automatyzacjach, gdzie z reguły mamy do czynienia z dużą ilością danych, pliki będą najlepszym wyborem. W tym artykule zakończę temat na powyższym opisie przepływu pracy. Myślę jednak, że to zagadnienie mogłoby stanowić ciekawy materiał na osobny wpis — a może nawet krótką serię — poświęconą zarządzaniu danymi w automatyzacji procesów biznesowych. Daj znać w komentarzu, czy taki artykuł byłby dla Ciebie interesujący!

Kiedy wybrać .Run, .Exec, a kiedy pliki?

Wybór odpowiedniej metody komunikacji między VBA a Pythonem zależy od tego, jakiej informacji zwrotnej potrzebujesz i jak dużo danych chcesz przekazać.

Kiedy wybrać .Run?

Metody .Run używamy wtedy, gdy VBA musi wiedzieć tylko jedną rzecz: CZY skrypt zakończył się sukcesem, czy porażką.

To działa jak sygnalizacja świetlna. Otrzymujesz prosty sygnał:

  • Zielone światło (Exit Code = 0): Sukces. Makro VBA może kontynuować.
  • Czerwone światło (Exit Code > 0): Błąd. Makro VBA powinno zatrzymać proces.

Użyj .Run, gdy:

  • Chcesz sprawdzić, czy logowanie do SAP-a się powiodło.
  • Uruchamiasz proces, który ma wygenerować plik i chcesz mieć pewność, że został on utworzony bez błędów.
  • Wykonujesz zadania w tle i interesuje Cię tylko potwierdzenie ich ukończenia.

Podsumowując: Wybierz .Run, jeśli Twoje pytanie do Pythona brzmi: „Czy udało Ci się?”.

Kiedy wybrać .Exec?

Metody .Exec używamy, gdy potrzebujemy od Pythona czegoś więcej niż tylko „tak” lub „nie”. Chcemy otrzymać konkretną informację tekstową – wynik, status lub logi z jego działania.

To działa jak rozmowa z kurierem. Nie tylko chcesz wiedzieć, czy dotarł, ale chcesz też odebrać od niego paczkę z wiadomością. Ta „wiadomość” to wszystko, co skrypt Pythona wypisuje za pomocą funkcji print().

Użyj .Exec, gdy:

  • Chcesz uzyskać z SAP numer nowo utworzonego dokumentu (np. zamówienia).
  • Chcesz przechwycić dynamiczną nazwę pliku, który wygenerował Python.
  • Chcesz wyświetlać w Excelu szczegółowe logi postępu operacji.

Podsumowując: Wybierz .Exec, jeśli Twoje pytanie do Pythona brzmi: „Co jest wynikiem twojej pracy?”.

A co z wymianą danych przez pliki?

To najbardziej niezawodna i skalowalna metoda, szczególnie gdy w grę wchodzą duże lub złożone zbiory danych. Zamiast bezpośredniej komunikacji, skrypty wymieniają się informacjami za pośrednictwem plików.

Działa to trochę jak współdzielona skrytka pocztowa lub segregator z dokumentami. VBA zostawia w umówionym miejscu plik z danymi wejściowymi, Python go odbiera, przetwarza, po czym zostawia w skrytce plik z wynikami, który z kolei odbiera VBA.

Użyj wymiany przez pliki, gdy:

  • Chcesz przetworzyć w Pythonie duży raport z SAP-a (setki lub tysiące wierszy).
  • Musisz masowo utworzyć w SAP-ie dokumenty na podstawie danych z tabeli w Excelu.
  • Przekazywane dane mają złożoną, tabelaryczną strukturę (np. pliki CSV, Excel, JSON).
  • Chcesz, aby oba skrypty (VBA i Python) były od siebie niezależne, co ułatwia testowanie i debugowanie.

Podsumowując: Wybierz tę metodę, gdy musisz przekazać strukturę danych (tabelę, listę), a nie tylko prosty status czy komunikat.

Łącz różne rozwiązania

Poniżej znajdziesz krótką tabelę (Tab. 1.) z podsumowaniem trzech metod integracji VBA z Pythonem opisanych w tym rozdziale.

Cecha.Run.ExecWymiana przez pliki
Główny celKontrola poprawności wykonaniaPrzechwytywanie wyników tekstowychNiezawodna wymiana dużych lub złożonych danych
Zwracana informacjaKod wyjścia (liczba, np. 0 lub 1)Standardowe wyjście (ciąg znaków)Pliki (np. CSV, Excel, JSON)
AnalogiaSygnalizacja świetlnaRozmowa z kurieremWspółdzielona skrytka pocztowa
Najlepsze do…Obsługi błędów, walidacji procesówPrzekazywania wyników, logowaniaPrzetwarzania raportów, masowego tworzenia danych
Tab. 1. Porównanie metod wymiany informacji pomiędzy VBA i Pythonem.

Warto podkreślić, że przedstawione metody nie wykluczają się wzajemnie — wprost przeciwnie, w profesjonalnych rozwiązaniach najczęściej się je łączy. Wymiana danych przez pliki jest strategią przekazywania danych, ale sam skrypt Pythona wciąż musi zostać jakoś uruchomiony i skontrolowany.

Dlatego złotym standardem jest połączenie wymiany plików np. z metodą .Run. Taki przepływ pracy wygląda następująco:

  1. VBA zapisuje dane wejściowe do pliku.
  2. VBA za pomocą .Run uruchamia skrypt Pythona, czekając na jego zakończenie i przechwytując kod wyjścia (Exit Code).
  3. Python przetwarza plik i zwraca 0 w razie sukcesu lub 1 w razie błędu.
  4. VBA, na podstawie kodu wyjścia, wie, czy może bezpiecznie odczytać plik z wynikami, czy też powinno zgłosić błąd.

Dzięki temu zyskujesz to, co najlepsze z obu światów: niezawodność i skalowalność transferu danych przez pliki oraz prostą i skuteczną kontrolę nad poprawnością wykonania całego procesu.


Podsumowanie

Excel jest nieodłącznym kompanem praktycznie każdego, kto pracuje z SAP-em. To w nim często dokonujemy analiz, tworzymy wykresy czy dashboardy. Myślę więc, że jest spora szansa, iż po zapoznaniu się z tym artykułem w Twojej głowie pojawiły się różne — ciekawe — pomysły na usprawnienie swojej pracy. Życzę Ci powodzenia w ich wdrażaniu!

Zawartość całej tej serii dała Ci przede wszystkim wyobrażenie, co jest, a co nie jest możliwe dzięki wykorzystaniu SAP GUI Scripting. Nie wątpię, że dla niektórych sama możliwość automatyzowania SAP-a w ten sposób jest czymś zupełnie nowym. 

Dalsza ścieżka Twojej nauki automatyzacji SAP-a zależy głównie od tego, gdzie chcesz dojść oraz tego, gdzie jesteś teraz. Jeśli chcesz budować bardziej złożone automatyzacje, które na przykład: pobierają dane z jednej transakcji, przetwarzają je (np. filtrując, oczyszczając — ang. data wrangling), a następnie przekazują do kolejnej transakcji SAP-a — zdecydowanie polecam Ci Pythona. Jeśli znasz już podstawy tego języka — to świetnie, masz wszystko, żeby zacząć. Jeśli nie — nic straconego! Zacznij uczyć się podstaw małymi krokami, ale konsekwentnie.

Osoby znające np. VBA będą miały łatwiej wejść w „świat Pythona”. Ale także ci, którzy nie mają żadnego doświadczenia w programowaniu, przy odrobinie chęci, wysiłku i systematycznej pracy z pewnością sobie poradzą — Python jest językiem relatywnie prostym, z czytelną składnią. Sama możliwość nagrywania skryptów w SAP-ie, które można później odpowiednio przerobić, sprawia, że jak na tacy otrzymujemy nazwy najważniejszych funkcji, metod i parametrów, z których powinniśmy skorzystać.

Na koniec — czy warto zgłębiać tematykę SAP GUI Scripting oraz implementować tego typu rozwiązania w procesy biznesowe? Myślę, że na to pytanie każdy musi odpowiedzieć sobie sam. Wierzę, że ta krótka seria ułatwiła podjęcie tej decyzji. Ja osobiście uważam, że gra jest warta świeczki. Dobry skrypt może wspierać Cię zarówno w prostych operacjach, jak pobieranie danych z SAP-a i przygotowywanie ich do analizy, jak i w tworzeniu dokumentów w SAP-ie na podstawie danych z Excela — np. zakładanie zleceń produkcyjnych (CO01) lub zamówień (ME21N). Możliwości jest wiele, a wszystko zależy od obszaru biznesowego, w którym działasz.

Potencjał tej funkcjonalności SAP-a nie kończy się jednak na prostych — choć bardzo pomocnych — automatyzacjach. Można w ten sposób zautomatyzować całe procesy biznesowe i sprowadzić je, dosłownie, do jednego kliknięcia myszy (tego, które uruchamia skrypt). Przykładem takiego wdrożenia jest program do planowania produkcji opisany w sekcji portfolio (SAP Integration Tool). Powstał on poprzez rozbudowę narzędzia istniejącego już w Excelu i VBA. Do pierwotnej „warstwy analitycznej” dodano możliwość interakcji z SAP-em za pomocą skryptów w Pythonie. Program korzysta z wielu transakcji, tworzy kilkadziesiąt dokumentów w SAP-ie (zamówienia i zlecenia produkcyjne), a w niektórych dodatkowo edytuje dane — wszystko po naciśnięciu jednego przycisku VBA.

Co więcej, nie musisz biernie patrzeć, jak skrypt wykonuje operacje w SAP-ie — możecie pracować równolegle na osobnych oknach. Jeśli przeanalizujesz sposób, w jaki skrypty łączą się z sesjami SAP-a, zobaczysz, że nic nie stoi na przeszkodzie, by napisać program dodający nowe sesje do tych, z których sam korzystasz. W efekcie Ty i skrypt pracujecie niezależnie.

Czy uważasz, że SAP GUI Scripting może pomóc Ci w codziennej pracy? Jakie procesy zautomatyzowałbyś w pierwszej kolejności? Koniecznie podziel się swoimi przemyśleniami — jestem bardzo ciekaw Twojej opinii. A może chcesz więcej artykułów o automatyzowaniu SAP-a z wykorzystaniem Pythona? Daj znać w komentarzu! Ta „mini seria” jedynie musnęła temat, a z pewnością znajdzie się jeszcze wiele wątków, które warto omówić.

Jeśli nie wiesz, od czego zacząć, zastanów się nad jedną, najbardziej monotonną czynnością, którą wykonujesz w SAP-ie każdego dnia. Może to być pobranie raportu, sprawdzenie statusu zamówień czy zmiana danych w zleceniu. Opisz ją w komentarzu — jestem pewien, że wspólnie znajdziemy sposób, by ją uprościć!

Mam nadzieję, że te cztery wpisy przyniosły Ci wartość i że wiedza w nich zawarta pomoże Ci „okiełznać” sprawy pilne, tak abyś mógł poświęcić więcej czasu rzeczom ważnym. Już teraz zapraszam do kolejnego wpisu na blogu.

Powodzenia!

Napisz komentarz

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

Scroll to Top