4 SAP R/3 Remote Function Call
Transcrição
4 SAP R/3 Remote Function Call
Kap04.fm Seite 49 Dienstag, 22. April 2003 2:31 14 4 SAP R/3 Remote Function Call Es gibt verschiedene Begriffsvarianten für RPC, so heißt es bei SAP Remote Function Call (RFC) und bei Java Remote Method Invokation (RMI). Es ist sicher nicht vermessen, zu sagen, dass der andauernde Erfolg von SAP R/3 zu einem großen Teil dessen RFC-Funktionalität zu verdanken ist. RFC hat SAP R/3 zu einem offenen System gemacht und das ist es, was die IT-Welt benötigt. Keine Software ist so integriert und perfekt, dass sie alle Fälle und Sonderfälle eines Geschäftszweiges abdecken kann. Die Möglichkeit, mit einem Programm all das zu erzielen, was man auch durch eine Benutzereingabe erreichen kann, ist eine Grundforderung an ein modernes Software-Paket. RFC ist Grundlage für den Erfolg von R/3 RFC bildet auch die Grundlage der R/3-Technologien ALE/IDoc und BAPI. In beiden Fällen kommuniziert SAP R/3 mit einem externen Programm, im Falle von ALE/IDocs durch asynchronen Austausch von Nachrichten, im Falle von BAPIs durch direkten Aufruf eines Funktionsbausteins. RFC ist Grundlage für IDoc/ALE und BAPI 4.1 Was ist RFC? Die SAP-Technologie RFC ist eine auf dem IBM-Protokoll CPI-C aufbauende Schnittstellentechnologie, die es externen Programmen oder anderen R/3-Instanzen erlaubt, auf eine bestimmte R/3-Instanz zuzugreifen. RFC baut auf CPI-C auf Das RFC-Protokoll ist ein bidirektionales, synchrones Kommunikationsprotokoll zum Austausch von Nachrichten über ein Netzwerk. RFC sendet dazu Datagramme von einem Client zu einer Server-Applikation über ein UDP/IP-Netzwerk. Grundsätzlich tut RFC nichts anderes als HTTP, nur die benutzte Codierung und Konvention unterscheiden sich. RFC ist ein sicheres, bidirektionales Protokoll Genaue Kenntnis der Details des RFC-Protokolls sind für einen Entwickler normalerweise nicht notwendig. Um den Zugriff auf RFC zu erleichtern, stellt SAP eigene RFC-Bibliotheken für die verschiedenen Frameworks zur Verfügung. Derzeit gibt es RFC-Libraries für alle Plattformen, auf denen SAP R/3 läuft, namentlich Windows, UNIX, Linux, AS/400 und z/OS. RFC erlaubt es einem Programm, 왘 R/3-Funktionen in einem anderen R/3-System oder einer weiteren R/3-Instanz aufzurufen SAP R/3 Remote Function Call 49 Kap04.fm Seite 50 Dienstag, 22. April 2003 2:31 14 왘 R/3-Funktionen von Nicht-SAP-Programmen aufzurufen, zum Beispiel Java, C++ oder Visual Basic 왘 externe Programme außerhalb eines SAP-Systems von SAP R/3 aus auf- zurufen Die folgenden Abschnitte zeigen konkrete Beispiele zum Aufruf von Programmen über RFC. Den Zugriff von externen Computern zeigen wir unter Verwendung von DCOM für alle Windows-Betriebssysteme und von Java aus. Grundsätzlich gibt es auch Zugriffsmöglichkeiten von allen Betriebssystemen aus, die auch eine R/3-Instanz unterstützen, zum Beispiel einer AS/400 oder von IBM/390. Allerdings werden diese Zugriffsvariationen so selten in der Praxis verwendet, dass auch die Unterstützung dafür leidet. Für all diese Betriebssysteme ist es empfehlenswerter, die Kommunikation über einen HTTP-Proxy-Server durchzuführen. & " & " ' !"#$% $& Abbildung 4.1 Remote Program Calls als Kernfunktionalität einer Client-ServerLandschaft 50 SAP R/3 Remote Function Call Kap04.fm Seite 51 Dienstag, 22. April 2003 2:31 14 4.2 R/3-RFC von einer anderen R/3-Instanz Eine R/3-Verbindung von einer anderen SAP R/3-Instanz aus erfolgt immer über die Angabe einer Destination. Eine Destination wird in Transaktion SM59 gepflegt und kann dann von allen ABAPs dieser Instanz benutzt werden. Im Beispiel in Listing 4.1 definieren wir eine RFC-Verbindung zu einem R/3-Computer, in diesem Fall ist es der SAPOSS-Server. RFCs via R/3Destination werden in SM59 definiert Für alle nachfolgenden Aufrufe in das Remote-System brauchen wir uns fortan nicht mehr um die Logon-Daten zu kümmern, da grundsätzlich auf die Angabe in SM59 zurückgegriffen wird. Ein RFC-Aufruf von ABAP in das Remote-System fügt dann dem CALL FUNCTION einfach noch den Parameter DESTINATION hinzu (siehe Listing 4.1). Wenn Sie keine Destination definiert haben, aber dennoch einen RFC testen wollen, können Sie die vordefinierte Destination NONE verwenden, die immer auf das eigene System verweist und implizit vorhanden ist und demnach nicht erst mit SM59 angelegt werden muss. Funktionsaufrufe erhalten den Parameter DESTINATION Listing 4.1 Aufruf einer RFC-Function von ABAP via Destination NONE (= eigenes System) DATA: from_curr_range TYPE STANDARD TABLE OF bapi1093_3. DATA: to_currncy_range TYPE STANDARD TABLE OF bapi1093_4. DATA: exch_rate_list TYPE STANDARD TABLE OF bapi1093_0 WITH HEADER LINE. DATA: return TYPE STANDARD TABLE OF bapiret1 WITH HEADER LINE. CALL FUNCTION 'BAPI_EXCHRATE_GETCURRENTRATES' DESTINATION ‘NONE’ EXPORTING date = sy-datum TABLES from_curr_range = from_curr_range to_currncy_range = to_currncy_range exch_rate_list = exch_rate_list return = return. LOOP AT exch_rate_list. WRITE: / exch_rate_list-from_curr. WRITE: exch_rate_list-to_currncy. WRITE: exch_rate_list-exch_rate. ENDLOOP. R/3-RFC von einer anderen R/3-Instanz 51 Kap04.fm Seite 52 Dienstag, 22. April 2003 2:31 14 4.2.1 Interne Destination NONE RFC-Destination NONE R/3 hat bereits einige RFC-Destinationen fest vorgegeben, unter anderem die Destination NONE. Gibt man NONE als Ziel des Aufrufs an, wird ein RFC in das rufende, also das eigene System ausgeführt. Hierzu sind natürlich keine Logon-Daten erforderlich. NONE eignet sich zum Testen von RFC-Aufrufen, wenn das entfernte System nicht zur Verfügung steht. Als besonderen Nebeneffekt gestattet es aber auch ein Entkoppeln des Aufrufs von der laufenden Transaktion. Jeder RFC öffnet eine eigene LUW Ein RFC startet grundsätzlich einen eigenen Programmkontext (Logical Unit of Work, LUW). Sie können somit aus einer laufenden Transaktion heraus mehrere RFC-Bausteine mit Destination NONE aufrufen. Zum Abschluss können Sie dann mit den BAPI-Bausteinen BAPI_COMMIT_ WORK beziehungsweise BAPI_ROLLBACK_WORK die Remote-Aufrufe beenden, ohne dass sich der COMMIT auf die rufende Transaktion auswirkt. Abbildung 4.2 Definition einer RFC-Destination mit SM59 4.3 Zugriff auf R/3 erfolgt via DCOM 52 Windows-zu-R/3-Connectivity mit DCOM Der Zugriff von Windows-Systemen aus nach R/3 via RFC bedient sich einer Reihe von DLLs, die mit dem SAP GUI oder dem RFC-SoftwareDevelopment-Kit (RFCSDK) ausgeliefert werden. Diese DLLs basieren auf SAP R/3 Remote Function Call Kap04.fm Seite 53 Dienstag, 22. April 2003 2:31 14 DCOM, dem Distributed Common Object Protocol von Microsoft, mit dem alle Kommunikationen zwischen Applikationen innerhalb einer Windows-Umgebung durchgeführt werden. Für den Zugriff auf SAP R/3 müssen beim Aufbau der Connection immer die richtigen Anmeldedaten (englisch Logon-Credentials) angegeben werden. Dies erledigen Sie normalerweise im SAP-Logon-Panel, das die Daten wiederum in der Konfigurationsdatei SAPLOGON.INI abspeichert. Abbildung 4.3 Angabe der Credentials in SM59 Befindet sich eine solche SAPLOGON.INI (normalerweise im WindowsSystem-Ordner, zum Beispiel C:\WINNT) auf Ihrem Rechner, können auch die auf der librfc32.dll basierenden Libraries wie wdtlog.ocx oder der JavaConnector darauf zurückgreifen; es müssen nur noch UserID und Password explizit angegeben werden. Wollen Sie nicht auf die SAPLOGON.INI zurückgreifen, müssen Sie alle Logon-Credentials selbst vollständig angeben. Abbildung 1.5 zeigt, wie die Credentials aus dem SAP Logon entnommen werden können. Windows-zu-R/3-Connectivity mit DCOM 53 Kap04.fm Seite 54 Dienstag, 22. April 2003 2:31 14 Abbildung 4.4 Mapping der Logon-Credentials mit SAPLOGON.INI 4.3.1 Zugriff von Windows erfolgt über ActiveX R/3-Logon mit VBA Das Logon von einer Windows-Applikation – also auch von VBA aus – erfolgt grundsätzlich über die SAP.LogonCtrl (in wdtlog.ocx) oder auf unterster Ebene durch die zentrale RFC-Library librfc32.dll. Wir beschränken uns hier immer auf das SAP.LogonCtrl. Alle folgenden Beispiele für Windows wurden entweder mit Visual Basic for Applications (VBA) oder Visual Basic Script (VBS) erstellt. Aufruf eines BAPI von VBA Das Beispiel, das jetzt folgt, zeigt zunächst ein ganz einfaches VB-Programm, das einen Logon zu R/3 durchführt, und zwar ohne Verwendung von Klassen. Das Programm beschafft sich ein Connection-Objekt zu R/3, setzt die Parameter und führt den Logon durch. Anschließend ruft es noch den Funktionsbaustein BAPI_EXCHRATE_GETCURRENTRATES mit dem aktuellen Datum auf. Listing 4.2 Logon zu R/3 und Aufruf des Funktionsbausteins BAPI_EXCHRATE_GETCURRENTRATES ' Example calling BAPI BAPI_EXCHRATE_GETCURRENTRATES Option Explicit Public Functions As SAPFunctionsOCX.SAPFunctions 54 SAP R/3 Remote Function Call Kap04.fm Seite 55 Dienstag, 22. April 2003 2:31 14 Private LogonControl As SAPLogonCtrl.SAPLogonControl Private R3Connection As SAPLogonCtrl.Connection Dim Func As SAPFunctionsOCX.Function Public iDATE As SAPFunctionsOCX.Parameter Public tEXCH_RATE_LIST As SAPTableFactoryCtrl.Table Private Sub Main() Dim ix As Integer Dim retcd As Boolean Dim SilentLogon As Boolean Set LogonControl = CreateObject("SAP.LogonControl.1") Set Functions = CreateObject("SAP.Functions") Set R3Connection = LogonControl.NewConnection R3Connection.Client = "000" R3Connection.ApplicationServer = "192.168.69.111" R3Connection.Language = "EN" R3Connection.User = "DEVELOPER" R3Connection.Password = "19920607" R3Connection.System = "WAS" R3Connection.SystemID = "$WebAS" R3Connection.SystemNumber = "18" R3Connection.UseSAPLogonIni = False SilentLogon = True retcd = R3Connection.Logon(0, SilentLogon) If retcd <> True Then MsgBox "Logon failed": Exit Sub Functions.Connection = R3Connection Set Func = Functions.Add("BAPI_EXCHRATE_GETCURRENTRATES") Set iDATE = Func.Exports("DATE") Set tEXCH_RATE_LIST = Func.Tables("EXCH_RATE_LIST") iDATE.Value = "20030101" Func.Call For ix = 1 To tEXCH_RATE_LIST.RowCount Debug.Print tEXCH_RATE_LIST.Cell(ix, 2), Debug.Print tEXCH_RATE_LIST(ix, 3), 'Different ways to access matrix Debug.Print tEXCH_RATE_LIST(ix, "EXCH_RATE") Windows-zu-R/3-Connectivity mit DCOM 55 Kap04.fm Seite 56 Dienstag, 22. April 2003 2:31 14 Next R3Connection.logoff End Sub 4.3.2 Anatomie der RFC- und BAPI-Aufrufe von Windows RFC-Aufruf mit RFC_READ_TABLE In diesem Abschnitt gehen wir noch einmal ins Detail und sezieren die RFC-Aufrufe von einer Visual-Basic-Applikation aus. Dazu nehmen wir dieses Mal den RFC-Baustein RFC_READ_TABLE, mit dem man den Inhalt fast jeder beliebigen R/3-Tabelle via RFC lesen kann. Das verwendete Beispiel wird RFC_READ_TABLE von VBA aufrufen und den Inhalt der SAPMandantentabelle T000 lesen. Wenn Sie dieses Beispiel verstanden haben, dürfte es Ihnen keine Schwierigkeit mehr bereiten, eine Anwendung mit Zugriff auf einen beliebigen anderen RFC-Baustein zu entwickeln. Daten einer beliebigen SAP R/3Tabelle mit RFC_ READ_TABLE lesen Listing 4.3 Lesen einer beliebigen R/3-Tabelle via RFC 56 Sub R3RFC_READ_TABLE(pQueryTab) '-----------------------------------------------------' Add the R/3 RFC function RFC_READ_TABLE to the collection '-----------------------------------------------------Set RFC_READ_TABLE = funcControl.Add("RFC_READ_TABLE") '-----------------------------------------------------' Create objects for each parameter '-----------------------------------------------------Set eQUERY_TAB = RFC_READ_TABLE.Exports("QUERY_TABLE") Set TOPTIONS = RFC_READ_TABLE.Tables("OPTIONS") ' Set TDATA = RFC_READ_TABLE.Tables("DATA") ' Set TFIELDS = RFC_READ_TABLE.Tables("FIELDS") ' eQUERY_TAB.Value = pQueryTab ' pQueryTab is the R/3 name of the table TOPTIONS.AppendRow ' new item line TOPTIONS(1, "TEXT") = "MANDT EQ '000'" If RFC_READ_TABLE.Call = True Then If TDATA.RowCount > 0 Then MsgBox "Call to RFC_READ_TABLE successful! Data found" MsgBox TDATA(1, "WA") Else MsgBox "Call to RFC_READ_TABLE successful! No SAP R/3 Remote Function Call Kap04.fm Seite 57 Dienstag, 22. April 2003 2:31 14 data found" End If Else MsgBox "Call to RFC_READ_TABLE failed!" End If End Sub SAP stellt uns eine Anzahl von ActiveX-Controls und DLLs zur Verfügung, die als intelligenter Proxy für die gesamte Kommunikation mit R/3 dienen. Ein ActiveX-Control ist ein DCOM-Objekt und deshalb auch kompatibel mit Visual Basic oder anderen DCOM-Anwendungen. Somit erscheint SAP R/3 aus der Sicht eines ActiveX-Entwicklers wie jedes andere DCOM-Objekt, wird also genauso behandelt wie etwa ein ADOoder ein DAO-Objekt. Mit anderen Worten ist aus der Sicht eines Entwicklers die SAP R/3-Instanz nichts weiter als ein Datenbankserver und die Funktionsbausteine entsprechen aus dieser Sicht in etwa den Stored Procedures eines Datenbanksystems. DCOM ist das Zugriffsprotokoll von Windows für verteilte Objekte, im Grunde das Gleiche wie RFC, aber für Windows. DCOM ist das RPC-Protokoll von Windows Sollte es dennoch Probleme geben, die ActiveX-Controls von SAP aufzurufen, liegt das Problem mit sehr großer Sicherheit bei der Installation der Controls oder an einer mangelhaften Windows-Installation. Hauptgrund für Fehler: korrupte Installation der DLLs Über SAP ActiveX-Controls brauchen Sie außer den Logon Credentials und dem Namen des aufzurufenden Funktionsbausteins nichts weiter zu wissen. Die Controls bauen eine Session mit R/3 auf, und sobald Sie den Funktionsbaustein dem lokalen Repository des ActiveX-Controls hinzufügen, können Sie alle Eigenschaften des Bausteins einschließlich der Namen und Typen der Parameter abfragen. Die folgenden kommentierten Code-Zeilen geben Ihnen Schritt für Schritt einen Überblick über die einzelnen Methoden der RFC-Controls am Beispiel eines einfachen Visual-Basic-Programms. Das Beispiel verzichtet auf die Typisierung der Variablen und ist somit auch unter Visual Basic Script ausführbar. Dazu muss der Code nur in einer ASCII-Datei mit der Dateierweiterung .vbs abgespeichert sein. Ein Doppelklick auf die Datei vom Windows-Explorer aus führt das Script dann aus. Windows-zu-R/3-Connectivity mit DCOM Visual-Basic-Code zum Lesen von Tabelle T000 via RFC 57 Kap04.fm Seite 58 Dienstag, 22. April 2003 2:31 14 Declarations Zunächst deklarieren wir eine neue R/3-Logon-OCX-Komponente: Dim LogonControl As SAPLogonCtrl.SAPLogonControl Deklarieren eines ConnectionObjekts Das Connection-Objekt ist das Gateway zwischen lokaler Instanz und R/3. Über die Connection wird zu Beginn eine Session durch Übermittlung der Anmeldedaten hergestellt. Grundsätzlich kann die Verbindung von beliebig vielen Routinen gleichzeitig verwendet werden (Connection-Pooling): Dim conn As SAPLogonCtrl.Connection Deklarieren eines Pointers auf ein R/3 RFC-Repository-Objekt Diesem Pointer wird später eine Referenz auf die Verwaltungsinformationen der gewünschten Funktionsbausteine zugewiesen. Die Eigenschaften der Funktion werden demnach dynamisch bestimmt: Dim funcControl As SAPFunctionsOCX.SAPFunctions Dies ist der Pointer zum aktuellen R/3 RFC-Funktionsbaustein: Dim RFC_READ_TABLE As SAPFunctionsOCX.Function Als nächstes folgt die Deklaration eines Pointers für jeden Parameter: Dim Dim Dim Dim eQUERY_TAB As SAPFunctionsOCX.Parameter TOPTIONS As SAPFunctionsOCX.Table TDATA As SAPFunctionsOCX.Table TFIELDS As SAPFunctionsOCX.Table Logon-Routine Das Logon zu R/3 fassen wir in einer eigenen Unterroutine zusammen. Darin legen wir eine neue Connection zu R/3 an, die die Verbindungsdaten über die ganze Programmlaufzeit hinweg hält und die Anmeldung an R/3 zur Applikation hin abschottet. Sub R3Logon() Erzeugen einer neuen Connection Eine neue Connection muss mit der Methode NewConnection angelegt werden. Das Anlegen eines Connection-Objekts mit CreateObject funktioniert ausdrücklich nicht, weil es durch die fehlende Vererbung in Visual Basic nicht möglich ist, das Objekt mit CreateObject sauber zu initialisieren. Set conn = LogonControl.NewConnection 58 SAP R/3 Remote Function Call Kap04.fm Seite 59 Dienstag, 22. April 2003 2:31 14 Lediglich die Angabe von Applikationsserver und Systemnummer ist zwingend, alle anderen Daten werden gegebenenfalls in einem Popup abgefragt. Selbstverständlich ist dies in einer automatisierten Umgebung nicht wünschenswert, weshalb Sie schließlich dann doch alle Angaben machen oder in der SAPLOGON.INI auf dem Server hinterlegen müssen. Angabe der Logon-Credentials conn.ApplicationServer = "R3Linux"' IP or DNS-Name of the R/3 application server conn.System = "00" ' System ID of the instance, usually 00 conn.Client = "100" ' opt. Client number to logon to conn.Language = "EN" ' opt. Your login language conn.User = "" ' opt. Your user id conn.Password = "" ' opt. Your password Dann folgt der Aufruf der Logon-Methode: retcd = conn.Logon(0, False) Und schließlich die Prüfung, ob das Login erfolgreich war: If retcd MsgBox MsgBox Stop else MsgBox End If End Sub <> True Then " Cannot log on! " retcd " Logon OK." Aufruf eines RFC-Funktionsbausteins Der RFC-Funktionsbaustein wird über die Connection aufgerufen, die wir im Logon-Schritt erzeugt haben. Das Coding lässt die lokalen Pointer auf die Parameter des Funktionsbausteins verweisen. Das ist zwar nicht wirklich notwendig, macht aber das Programm lesbarer, als wenn immer der ganze Parameterkontext angegeben werden muss. Hat ein RFC-Funktionsbaustein Tabellenparameter, erscheinen die in Visual Basic wie ADORecordsets und werden auch wie solche behandelt. Die Typisierung der Recordsets erfolgt automatisch beim Hinzufügen des Funktionsbausteins zum lokalen RFC-Repository: Windows-zu-R/3-Connectivity mit DCOM RFC-Aufruf benutzt die zuvor eröffnete Connection 59 Kap04.fm Seite 60 Dienstag, 22. April 2003 2:31 14 Sub R3RFC_READ_TABLE(pQueryTab) Erzeugen einer neuen Collection für den Funktionsbaustein Alle zu verwendenden Funktionsbausteine müssen einer lokalen Collection hinzugefügt werden, die als Cache für die Parameterinformation dient: Set RFC_READ_TABLE = funcControl.Add("RFC_READ_TABLE") Als Nächstes wird ein Pointer auf die Import- und Exportparameter gesetzt: Set eQUERY_TAB = RFC_READ_TABLE.Exports("QUERY_TABLE") Set TOPTIONS = RFC_READ_TABLE.Tables("OPTIONS") ' Set TDATA = RFC_READ_TABLE.Tables("DATA") ' Set TFIELDS = RFC_READ_TABLE.Tables("FIELDS") ' Import-, Export- und Tables-Parameter werden durch einen Visual-BasicPointer referenziert. Die function collection, die wir oben mit funcControl.Add erzeugt haben, stellt uns dynamisch einen Proxy für die Methoden und Pointer auf alle Parameter des Funktionsbausteins zur Verfügung. Bevor wir den Funktionsbaustein aufrufen, müssen wir den Parametern die gewünschten Werte zuweisen: Lesen und Schreiben der Parameterwerte eQUERY_TAB.Value = pQueryTab ' pQueryTab is the R/3 name of the table TOPTIONS.AppendRow ' new item line TOPTIONS(1,"TEXT") = "MANDT EQ '000'" Sobald den Parametern gültige Werte zugewiesen wurden, kann der Funktionsbaustein aufgerufen werden. Der Aufruf retourniert TRUE oder FALSE, je nachdem, ob der Aufruf erfolgreich war oder nicht: If RFC_READ_TABLE.Call = True Then Wenn der RFC-Aufruf erfolgreich war, können die Werte weiter verarbeitet werden. Im Beispiel hier zeigen wir jeweils die erste Zeile der Tabelle in einer VB Messagebox an. Ausgabe des Ergebnisses 60 If TDATA.RowCount > 0 Then MsgBox "Call to RFC_READ_TABLE successful! Data found" MsgBox TDATA(1, "WA") SAP R/3 Remote Function Call Kap04.fm Seite 61 Dienstag, 22. April 2003 2:31 14 Else MsgBox "Call to RFC_READ_TABLE successful! No data found" End If Else MsgBox "Call to RFC_READ_TABLE failed!" End If End Sub Hauptprogramm Damit hätten wir den spannenden Teil des Codings auch schon hinter uns. Im Folgenden packen wir dem Ganzen noch eine Main-Routine hinzu: Sub Main() Main() procedure Nun erzeugen wir noch eine Instanz der SAP.LogonControl-Klasse (Version 1) Set LogonControl = CreateObject("SAP.LogonControl.1") Dieses Statement hat eine neue Instanz des Logon-Objekts erzeugt. Die Nummer 1 am Ende des Klassennamens SAP.LogonControl.1 ist die Versionsnummer der Klasse. Normalerweise gibt man die Version einer Klasse nur an, wenn man wirklich explizit die Verwendung einer bestimmten Version erzwingen will. Im Falle des SAP.LogonControl gibt es nur die eine Version. Nun erzeugen wir eine Instanz der SAP.Functions collection: Set funcControl = CreateObject("SAP.Functions") Als Nächstes erfolgt der Aufruf der Logon-Routine: call R3Logon Und dann die Zuweisung des Connection-Objekts: funcControl.Connection = conn Dann wird der RFC-Call ausgeführt und es folgt der Logoff: call R3RFC_READ_TABLE("T000") conn.Logoff MsgBox " Logged off from R/3! " End Sub Windows-zu-R/3-Connectivity mit DCOM 61 Kap04.fm Seite 62 Dienstag, 22. April 2003 2:31 14 Start des Programms Call Main() Abhängig davon, welche Visual-Basic-Variante Sie verwenden, ist der Aufruf leicht unterschiedlich. Im Falle von VBS müssen Sie dem ScriptFile noch das Statement Call Main() hinzufügen, um das Hauptprogramm explizit aufzurufen. ASP Call Main() in einer ASP-Seite Falls Sie das VBS-Script in eine ASP-Seite einbinden wollen, können Sie das Coding in der VBS-Datei lassen und mit dem #include-Befehl von ASP in die HTML-Seite einbinden: <HTML> <HEAD> <#include RfcReadTable.vbs > </HEAD> <BODY><%>Call Main()<%></BODY> Funktionsaufruf im Überblick Das nachstehende Beispiel zeigt noch einmal den Aufruf eines Funktionsbaussteins im Überblick. Diesmal wurde ein anderer Funktionsbaustein (RFC_GET_TABLE_ENTRIES) ausgewählt, der ebenfalls den Inhalt einer beliebigen SAP R/3-Tabelle ausliest, jedoch etwas andere Parameter als RFC_READ_TABLE hat. Listing 4.4 Vollständiges Coding zum Aufruf der RFC-Funktion RFC_READ_TABLE Dim LogonControl 'As SAPLogonCtrl.SAPLogonControl Dim conn 'As SAPLogonCtrl.Connection Dim funcControl 'As SAPFunctionsOCX.SAPFunctions Dim TableFactoryCtrl 'As SAPTableFactoryCtrl.SAPTableFactory '-----------------------------------------------------' Pointer to functions '-----------------------------------------------------Dim RFC_READ_TABLE '-----------------------------------------------------' Pointers to function parameters '-----------------------------------------------------Dim eQUERY_TAB Dim TOPTIONS Dim TDATA Dim TFIELDS 62 SAP R/3 Remote Function Call Kap04.fm Seite 63 Dienstag, 22. April 2003 2:31 14 '****************************************************** ' Main Program '****************************************************** Call Main '****************************************************** ' Subroutines '****************************************************** Sub Main() Set LogonControl = CreateObject("SAP.LogonControl.1") Set funcControl = CreateObject("SAP.Functions") Set TableFactoryCtrl = CreateObject("SAP.TableFactory.1") Call R3Logon funcControl.Connection = conn Call R3RFC_READ_TABLE("T000") conn.Logoff MsgBox " Logged off from R/3! " End Sub Sub R3Logon() Set conn = LogonControl.NewConnection conn.ApplicationServer = "r3dev" ' IP or DNS-Name of the R/3 application server conn.System = "00" ' System ID of the instance, usually 00 conn.Client = "100" ' opt. Client number to logon to conn.Language = "EN" ' opt. Your login language conn.User = "" ' opt. Your user id conn.Password = "" ' opt. Your password retcd = conn.Logon(0, False) If retcd <> True Then MsgBox " Cannot log on! " MsgBox retcd Stop Else MsgBox " Logon OK." End If End Sub Windows-zu-R/3-Connectivity mit DCOM 63 Kap04.fm Seite 64 Dienstag, 22. April 2003 2:31 14 Sub R3RFC_READ_TABLE(pQueryTab) '-----------------------------------------------------' Add the R/3 RFC function RFC_READ_TABLE to the collection '-----------------------------------------------------Set RFC_READ_TABLE = funcControl.Add("RFC_READ_TABLE") '-----------------------------------------------------' Create objects for each parameter '-----------------------------------------------------Set eQUERY_TAB = RFC_READ_TABLE.Exports("QUERY_TABLE") Set TOPTIONS = RFC_READ_TABLE.Tables("OPTIONS") ' Set TDATA = RFC_READ_TABLE.Tables("DATA") ' Set TFIELDS = RFC_READ_TABLE.Tables("FIELDS") ' eQUERY_TAB.Value = pQueryTab ' pQueryTab is the R/3 name of the table TOPTIONS.AppendRow ' new item line TOPTIONS(1, "TEXT") = "MANDT EQ '000'" If RFC_READ_TABLE.Call = True Then If TDATA.RowCount > 0 Then MsgBox "Call to RFC_READ_TABLE successful! Data found" MsgBox TDATA(1, "WA") Else MsgBox "Call to RFC_READ_TABLE successful! No data found" End If Else MsgBox "Call to RFC_READ_TABLE failed!" End If End Sub Das nachstehende Programmbeispiel zeigt noch einmal den Aufruf eines RFC-Bausteins an einem Stück. Um ein Vergleichsbeispiel zu haben, verwendet es diesmal den Aufruf des Bausteins RFC_GET_TABLE_ENTRIES. Listing 4.5 Einfacher Aufruf von RFC_GET_TABLE_ENTRIES via RFC und VB ' Example calling BAPI RFC_GET_TABLE_ENTRIES Option Explicit Public Functions As SAPFunctionsOCX.SAPFunctions Private LogonControl As SAPLogonCtrl.SAPLogonControl Private R3Connection As SAPLogonCtrl.Connection 64 SAP R/3 Remote Function Call Kap04.fm Seite 65 Dienstag, 22. April 2003 2:31 14 Dim Func As SAPFunctionsOCX.Function Public iTABLE_NAME As SAPFunctionsOCX.Parameter Public eNUMBER_OF_ENTRIES As SAPFunctionsOCX.Parameter Public tENTRIES As SAPTableFactoryCtrl.Table Private Sub Main() Dim ix As Integer Dim retcd As Boolean Dim SilentLogon As Boolean Set LogonControl = CreateObject("SAP.LogonControl.1") Set Functions = CreateObject("SAP.Functions") Set TableFactory = CreateObject("SAP.TableFactory.1") Set R3Connection = LogonControl.NewConnection R3Connection.Client = "000" R3Connection.ApplicationServer = "192.168.69.111" R3Connection.Language = "EN" R3Connection.User = "DEVELOPER" R3Connection.Password = "19920607" R3Connection.System = "WAS" R3Connection.SystemID = "$WebAS" R3Connection.SystemNumber = "18" R3Connection.UseSAPLogonIni = False SilentLogon = True retcd = R3Connection.Logon(0, SilentLogon) If retcd <> True Then MsgBox "Logon failed": Exit Sub Functions.Connection = R3Connection Set Func = Functions.Add("RFC_GET_TABLE_ENTRIES") Set iTABLE_NAME = Func.Exports("TABLE_NAME") Set eNUMBER_OF_ENTRIES = Func.Imports("NUMBER_OF_ENTRIES") Set tENTRIES = Func.Tables("ENTRIES") iTABLE_NAME.Value = "TCURR" Func.Call Debug.Print eNUMBER_OF_ENTRIES For ix = 1 To tENTRIES.RowCount Windows-zu-R/3-Connectivity mit DCOM 65 Kap04.fm Seite 66 Dienstag, 22. April 2003 2:31 14 Debug.Print tENTRIES(ix, 1) Next R3Connection.logoff End Sub 4.4 Hilfsklassen für wiederkehrende Arbeiten Helper-Klassen für den Zugriff auf R/3 via RFC Um Programme klarer zu gestalten und von immer wiederkehrenden Routinearbeiten zu säubern, macht es Sinn, sich eine Reihe von Hilfsklassen zu erstellen. Im Folgenden sind ein paar Ideen aufgezeigt, wie solche Helper-Klassen aussehen können. Diese sind weniger dazu gedacht, direkt und unverändert übernommen zu werden, vielmehr sollen sie Anregungen geben, was man alles machen kann oder bedenken sollte. 4.4.1 Class R3LogonObj Logon von VB durch die Klasse R3LogonObj In unseren Beispielen führen wir das Logon zu SAP R/3 von Visual Basic durchweg mit einer selbst geschriebenen Proxy-Klasse R3LogonObj durch. Diese Klasse führt den Logon zu SAP R/3 zentral durch, so dass wir uns nur an dieser Stelle um Logon-Daten wie Name des Applikationsservers, UserID, Passwort usw. kümmern müssen. Referenz auf ein Objekt SAPFunctionsOCX.Functions Die Klasse selbst exportiert dann im Wesentlichen das Objekt Functions as SAPFunctionsOCX.Functions, das das zentrale Gateway zur RFCFunktionsbibliothek von SAP R/3 darstellt. Listing 4.6 Class R3LogonObj Public Functions As SAPFunctionsOCX.SAPFunctions Public TableFactory As SAPTableFactoryCtrl.SAPTableFactory Public SilentLogon As Boolean '-----------------------------------------------------Private LogonControl As SAPLogonCtrl.SAPLogonControl Private myCredentials As New R3Credentials Private retcd Private err As New ErrObject Public Property Get R3Connection() As SAPLogonCtrl.connection Set R3Connection = Me.Functions.connection End Property 66 SAP R/3 Remote Function Call Kap04.fm Seite 67 Dienstag, 22. April 2003 2:31 14 Private Property Set R3Connection(conn As SAPLogonCtrl.connection) Set Functions.connection = conn End Property Public Sub R3Logon() retcd = R3Connection.Logon(0, SilentLogon) If retcd <> True Then Exit Sub Else ' MsgBox " Logon OK." End If End Sub Public Sub R3logoff() R3Connection.logoff End Sub Private Sub Class_Initialize() Set LogonControl = CreateObject("SAP.LogonControl.1") Set Functions = CreateObject("SAP.Functions") Set TableFactory = CreateObject("SAP.TableFactory.1") Set R3Connection = LogonControl.NewConnection myCredentials.Read R3Connection Me.SilentLogon = False End Sub Private Sub Class_Terminate() Me.R3logoff End Sub 4.4.2 Class R3Credentials Die eigentlichen Logon-Credentials sind noch einmal in einer eigenen Klasse, den R3Credentials, realisiert. Das erlaubt uns, einen geheimen Mechanismus zu implementieren, um die Logon-Daten zu verstecken. Diese Klasse kann dann in den Verantwortungsbereich der Systemadministration gegeben werden, die ohnehin die Logon-Daten verwaltet, ohne dass sie dem Anwendungsentwickler bekannt sind, denn der weist die Daten durch die Methode READ automatisch dem Connection-Objekt zu, ohne sie im Detail zu kennen. Helper-Klassen für den Zugriff auf R/3 via RFC Logon-Credentials stecken in R3Credentials 67 Kap04.fm Seite 68 Dienstag, 22. April 2003 2:31 14 Listing 4.7 Class R3Credentials Sub Read(ByRef oR3 As SAPLogonCtrl.Connection) Set LogonDataWS = Excel.Worksheets("LogonData") Set LogonData = LogonDataWS.Columns(2) R3Connection.Client = "000" R3Connection.ApplicationServer = "192.168.69.111" R3Connection.Language = "EN" R3Connection.User = "DEVELOPER" R3Connection.Password = "06071992" R3Connection.System = "WAS" R3Connection.SystemID = "$WebAS" R3Connection.SystemNumber = "18" R3Connection.UseSAPLogonIni = False Set LogonData = Nothing Set LogonDataWS = Nothing End Sub Sub Class_Initialize() End Sub Sub Class_Terminate() End Sub 4.4.3 Alternative R3Credentials-Class Zugriff über Excel-Sheet Für den Zugriff von Visual Basic benutzen wir in unserer Entwicklungsumgebung ein Excel-Sheet, das wir einfach mit den Zugriffsdaten mehrerer SAP R/3-Systeme füllen. So können wir unsere Anwendung ohne viel Aufwand auf mehreren SAP R/3-Systemen testen. Im folgenden Beispiel sehen Sie ein VBA-Programm, das die Daten aus dem Excel-Sheet herausliest und den Parametern unserer Logon-Klasse zuweist. Listing 4.8 Class R3Credentials mit Daten aus Excel Private LogonDataWS As Excel.Worksheet Private LogonData As Excel.Range Sub Read(ByRef oR3 As SAPLogonCtrl.Connection) Set LogonDataWS = Excel.Worksheets("LogonData") Set LogonData = LogonDataWS.Columns(2) oR3.ApplicationServer = LogonData.Cells(2) ' IP or DNS-Name of app server oR3.System = LogonData.Cells(3) ' System 68 SAP R/3 Remote Function Call Kap04.fm Seite 69 Dienstag, 22. April 2003 2:31 14 ID of the instance oR3.SystemNumber = LogonData.Cells(4) of Database instance oR3.Client = LogonData.Cells(5) Client number to logon to oR3.Language = LogonData.Cells(6) Your login language oR3.User = LogonData.Cells(7) Your user id oR3.Password = LogonData.Cells(8) Your password oR3.SystemID = LogonData.Cells(9) ID of the instance oR3.UseSAPLogonIni = True Set LogonData = Nothing Set LogonDataWS = Nothing End Sub Sub Class_Initialize() End Sub Sub Class_Terminate() End Sub ' Number ' opt. ' opt. ' opt. ' opt. ' System Die Logon-Daten zu R/3 entsprechen denen aus der saplogon.ini. Name Wert Application server 192.168.69.111 System ID WAS System Number 18 Client 000 Language EN User DEVELOPER Password ******** System Web AS Silent Login FALSCH Use SAPLOGON.INI FALSCH Gateway Tabelle 4.1 Beispiel für Logon-Daten (für die Web AS-Testversion) Helper-Klassen für den Zugriff auf R/3 via RFC 69 Kap04.fm Seite 70 Dienstag, 22. April 2003 2:31 14 4.5 R/3 Java Connectivity 4.5.1 SAP R/3 Java Connector JCo Der Zugriff von Java auf SAP R/3 erfolgt über den Java Connector (JCo). Je nach Plattform gibt es dafür eine kompatible Implementierung, so dass die Aufrufe von Java auf SAP R/3 unabhängig von der Umgebung bleiben. In einer Windows-Umgebung ist der Java Connector eine Java-Klassenbibliothek, die über das JNI (Java Native Interface) die Funktionen der RFCLibrary librfc32.dll aufruft. JCo ist die einzige empfohlene RFC-Klasse Der Java Connector ist die einzige unterstützte RFC-Schnittstelle von Java nach SAP R/3. Alle früheren Java-Tools für RFC werden von SAP nicht mehr unterstützt. Der Java Connector wurde ursprünglich von Thomas Schüssler, http://arasoft.de, entwickelt und kann von Kunden und registrierten SAPNet-Usern über http://service.sap.com heruntergeladen werden. JCo ist derzeit für alle NT-Varianten verfügbar sowie für UNIX, AIX und Linux. Die früher noch unterstützte Bibliothek jRFC wird von SAP nicht mehr ausgeliefert und es wird empfohlen, Anwendungen auf JCo umzustellen. 4.5.2 JCo unterstützt XML-Konvertierung SAP R/3-Logon mit Java Im nachstehenden Listing erfolgt der Aufruf auf das BAPI BAPI_ EXCHRATE_GETCURRENTRATES von Java. Eine Besonderheit des JavaConnectors ist es, dass die Funktionsbausteinparameter bereits eine intrinsische Konvertierung nach XML unterstützen und wir deshalb durch Aufruf der Methode toXML() das Ergebnis sofort in XML ausgeben können: System.out.println(myFunc.tEXCH_RATE_LIST.toXML()); Listing 4.9 Einfacher Aufruf von BAPI_EXCHRATE_GETCURRENTRATES via RFC und Java package R3; import com.sap.mw.JCo.*; public class R3Test extends Object { public static BAPI_EXCHRATE_GETCURRENTRATES myFunc; public static void main (String args[]) { R3RFCfunctions funcs = new R3RFCfunctions(); System.out.println("** SYS: Repository created."); 70 SAP R/3 Remote Function Call Kap04.fm Seite 71 Dienstag, 22. April 2003 2:31 14 funcs.Logon(); myFunc = new BAPI_EXCHRATE_GETCURRENTRATES (funcs, "BAPI_EXCHRATE_GETCURRENTRATES"); myFunc.iDATE.setValue("20030101"); myFunc.Execute(); System.out.println(myFunc.tEXCH_RATE_LIST. toXML()); } } package R3; import com.sap.mw.JCo.*; public class BAPI_EXCHRATE_GETCURRENTRATES // extends R3RFCfunctions { public JCO.Function func; private R3RFCfunctions functions; public JCO.Field iDATE; public JCO.Table tEXCH_RATE_LIST; public BAPI_EXCHRATE_GETCURRENTRATES( R3RFCfunctions funcRepository, String funcname) { functions = funcRepository; func = functions.add(funcname.toUpperCase()); //---Set vectors to the appropriate parameters iDATE = func.getImportParameterList() .getField("DATE"); tEXCH_RATE_LIST = func.getTableParameterList() .getTable("EXCH_RATE_LIST"); try functions.add(funcname. toUpperCase()); } catch (Exception ex) { } } public void Execute() {functions.R3session.Connection.execute(func);} } package R3; import com.sap.mw.JCo.*; public class R3RFCfunctions extends R3RFCrepository R/3 Java Connectivity 71 Kap04.fm Seite 72 Dienstag, 22. April 2003 2:31 14 { public R3RFCfunctions() {} public JCO.Function add(String name) { try { return super.R3session.Functions.getFunctionTemplate (name.toUpperCase()).getFunction();} catch (Exception ex) {} return null; } public class R3RFCrepository { public R3LogonObj R3session; public R3RFCrepository() { R3session = new R3LogonObj(); } public R3RFCrepository(R3LogonObj ActiveSession) { R3session = ActiveSession; } public void Logon() { R3session.Connection.connect();} public void Logoff() { R3session.Connection.disconnect();} } package R3; import com.sap.mw.JCo.*; public class R3LogonObj { public JCO.Repository Functions; public boolean SilentLogon; public JCO.Client Connection; public R3LogonObj() { try {R3.R3Credentials myCred = new R3.R3Credentials(); Connection = myCred.Connect(); } catch (Exception ex) { ex.printStackTrace();System.exit(1);} try {Functions = new JCO.Repository("logosworld", Connection);} catch (Exception ex) { ex.printStackTrace();System.exit(1);} } } 72 SAP R/3 Remote Function Call Kap04.fm Seite 73 Dienstag, 22. April 2003 2:31 14 4.6 Das SAP RFC-Gateway Mit Hilfe des SAP RFC-Gateways können Sie einem externen Programm, dem RFC-Listener, erlauben, sich selbst als Proxy-Server bei SAP R/3 anzubieten. Dadurch müssen in SAP R/3 selbst keine Verbindungsdaten zum Listener gepflegt werden. SAP R/3 spricht den Listener über das Gateway als eine RFC-Destination mit dem Namen an, unter dem der Listener sich zuvor bei SAP R/3 registriert hat. 4.6.1 RFC-Listener Registrierung eines RFC-Listeners beim SAP-Gateway Folgendes ist ein Beispiel einer Registrierung am Gateway: Srfcserv –aMYHOST.srfcserv –gSAPR3.logosworld.com – xSAPGW00 –t Das gezeigte Beispiel registriert sich selbst über Gateway SAPGW00 bei der SAP R/3-Instanz mit dem Namen SAPR3.logosworld.com. Der Name des SAP-Gateways ist von der Form SAPGWxx, wobei xx die System-Nummer der zugehörigen SAP R/3-Instanz ist. SAPGW00 gehört also zur Datenbank-Instanz »00«. Das Gateway des Demo-Web AS ist SAPGW18, da dieses sich auf Datenbankinstanz 18 installiert. Anstatt die Parameter in der Kommandozeile anzugeben, können diese auch in der Datei saprfc.ini hinterlegt werden. saprfc.ini muss im selben Verzeichnis wie der zu registrierende RFC-Server liegen. Parameter können auch in saprfc.ini hinterlegt werden Listing 4.10 Beispiel einer SAPRFC.INI /*===================================================*/ /* Type R: Register a RFC server program at a SAP gateway */ /* or connect to an already registered RFC server program */ /*===================================================*/ DEST=RFCEXT_LISTENER TYPE=R PROGID=SAPR3.srfcserv GWHOST=SAPr3.logosworld.com GWSERV=sapgw00 RFC_TRACE=0 Das SAP RFC-Gateway 73 Kap04.fm Seite 74 Dienstag, 22. April 2003 2:31 14 SMGW zeigt registrierte Gateway-Dienste Eine Übersicht der aktuell angemeldeten Dienste am SAP-Gateway liefert die Transaktion SMGW unter dem Menüpunkt Logged Systems. Listing 4.11 Optionen der Registrierung eines RFC-Servers an einem SAP-Gateway C:\Programme\sapgui46b\SAPGUI\RFCSDK\bin>srfcserv Syntax for start and run in register mode: srfcserv [options] with options = -D<destination with type 'R' in saprfc.ini> = -t RFC-Trace on or options = -a<program ID> e.g. <own host name>.srfcserv = -g<SAP gateway host name> e.g. hs0311 = -x<SAP gateway service> e.g. sapgw53 = -t RFC-Trace on = -L<SNC library, optional> = -S<SNC myname, optional> = -Q<SNC quality of protection, optional> Option L, S and Q can be set if working with SNC (Secure Network Communication). 4.6.2 Anlegen einer RFC-Destination für einen registrierten Listener Um auf einen SAP-Gateway-Service zugreifen zu können, müssen Sie in der rufenden Instanz das Gateway als Destination in Transaktion SM59 bekannt machen. An Stelle der festen IP-Adresse des zu rufenden Servers geben Sie in diesem Fall den Namen an, unter dem sich das Gateway bei seiner Registrierung bei SAP bekannt macht. 4.6.3 Eigene RFC-Listener erstellen Kompletter Code eines GatewayServers im RFC-SDK 74 Das SAP RFCSDK listet den kompletten Sourcecode der in C geschriebenen RFC-Listener rfcexec und srfcserv. Die ausführliche Dokumentation zur Entwicklung eines Listeners findet sich in der Hilfe zum RFCSDK in saprfc.hlp. SAP R/3 Remote Function Call Kap04.fm Seite 75 Dienstag, 22. April 2003 2:31 14 Listing 4.12 Abbildung 1.10Extrakte aus der Implementierung des rfcexec-Servers static RFC_RC DLL_CALL_BACK_FUNCTION _loadds remote_ pipe( RFC_HANDLE handle ); static RFC_RC DLL_CALL_BACK_FUNCTION _loadds remote_ file( RFC_HANDLE handle ); static RFC_RC DLL_CALL_BACK_FUNCTION _loadds remote_ exec( RFC_HANDLE handle ); static RFC_RC DLL_CALL_BACK_FUNCTION _loadds mail ( RFC_HANDLE handle ); /* main function for an RFC server program */ /*ARGSUSED*/ main( int argc, char ** argv ){ /* initialized data */ static RFC_ENV env; RFC_HANDLE handle; RFC_RC rc; if (argc == 1) { help(); return 0; } /* install error handler */ env.errorhandler = myErrorhandler; RfcEnvironment( &env ); /* accept connection * (command line argv must be passed to RfcAccept) */ handle = RfcAccept( argv ); /* static function to install offered function modules */ rc = install(handle); if( rc != RFC_OK ) { /* if error occured, close connection with error message and exit */ RfcAbort( handle, "Initialization error" ); exit(1); } static RFC_RC DLL_CALL_BACK_FUNCTION _loadds remote_ pipe( RFC_HANDLE handle ) { char command[256]; RFC_PARAMETER parameter[4]; RFC_TABLE table[2]; RFC_RC rc; Das SAP RFC-Gateway 75 Kap04.fm Seite 76 Dienstag, 22. April 2003 2:31 14 RFC_CHAR read_flag = 0; int mode; memset( command, 0, sizeof( command ) ); parameter[0].name = "COMMAND"; parameter[0].nlen = 7; parameter[0].addr = (void *) command; parameter[0].leng = sizeof(command); parameter[0].type = RFCTYPE_CHAR; parameter[1].name = "READ"; parameter[1].nlen = 4; parameter[1].addr = (void *) &read_flag; parameter[1].leng = sizeof(read_flag); parameter[1].type = RFCTYPE_CHAR; parameter[2].name = NULL; table[0].name = "PIPEDATA"; table[0].nlen = 8; table[0].type = RFCTYPE_CHAR; table[0].leng = table_size; table[0].itmode = RFC_ITMODE_BYREFERENCE; table[1].name = NULL; rc = RfcGetData( handle, parameter, table ); if( rc != RFC_OK ) return rc; #ifdef SAPonWINDOWS RfcAbort(handle, "Function RFC_REMOTE_PIPE is not supported on Windows"); exit(1); #endif if( read_flag != 'X' ) mode = RUN_WAIT; else mode = RUN_READ; rc = run( handle, command, sizeof(command), table[0].ithandle, mode, (RFC_INT *)0 ); if( rc != RFC_OK ) return rc; parameter[0].name = NULL; rc = RfcSendData( handle, parameter, table ); return rc; } /* remote_pipe */ 76 SAP R/3 Remote Function Call Kap04.fm Seite 77 Dienstag, 22. April 2003 2:31 14 4.7 Troubleshooting RFC Es existiert ein Standard-ABAP in SAP R/3, SRFCTEST, in dem die wichtigsten RFC-Möglichkeiten sehr gut demonstriert werden und auch gleich getestet werden können. SRFCTEST zum Testen von RFC Wenn ein Zugriff via SAP R/3 permanent versagt, testen Sie die gewünschte Funktionalität immer zunächst lokal im Zielsystem. Zum Beispiel setzen die Aufrufe über LOCAL_EXEC voraus, dass rfcexec.exe sich im Zugriffspfad des SAP GUI auf der Workstation befindet. In diesem Fall sollten Sie auf der Arbeitsstation ein DOS-Fenster öffnen und versuchen, rfcexec von einem beliebigen Verzeichnis auszuführen. Klappt dies nicht, müssen Sie rfcexec.exe in eines der Verzeichnisse kopieren, die mit dem PATH-Kommando angezeigt werden, oder Sie müssen den Suchpfad entsprechend erweitern. Lokaler Test Abbildung 4.5 Aufruf von rfcexec.exe über eine DOS-Box Klappt das lokale Ausführen der Programme, haben Sie vermutlich ein Problem mit der RFC-Verbindung an sich. Denkbar sind folgende häufige Störgründe: 왘 Bei Aufrufen über Frontend ist die Wahrscheinlichkeit groß, dass das SAP GUI unvollständig oder fehlerhaft installiert wurde. Hier empfiehlt sich immer eine Neuinstallation des SAP GUI, um zu sehen, ob das vielleicht schon den Fehler behebt. 왘 Prüfen Sie auch die TCP/IP-Verbindungsstrecke vom SAP-Applikations- server zur RFC-Destination. Das können Sie tun, indem Sie auf dem Applikationsserver TELNET aufrufen, sich als SAP-Administrator anmelden (normalerweise USER=SAPsid, wobei sid die System-ID ist) und von dort einen PING auf die IP-Adresse der RFC-Destination durchführen. Auch das TRACEROUTE-Utility zum Anzeigen aller Verbindungsknoten vom Aufrufer bis zum Ziel kann weitere Erkenntnisse geben (Name in Windows: tracert.exe). Troubleshooting RFC 77 Kap04.fm Seite 78 Dienstag, 22. April 2003 2:31 14 Abbildung 4.6 Erfolgreicher und nicht erfolgreicher PING eines Remote-Computers Abbildung 4.7 TRACERT zum Finden eines IP-Hosts 4.8 Aufruf von Remote Programs aus R/3 Via RFC 4.8.1 RFC via Remote Shell Um Programme auf einem entfernten Rechner auszuführen, muss dieser zunächst dafür eingerichtet werden, Remote Program Calls zu akzeptieren. Dazu muss ein TCP/IP-Port auf dem Zielrechner so konfiguriert sein, dass er ankommende Nachrichten als Programmaufruf akzeptiert. UNIX unterstützt standardmäßig RPC-Aufrufe 78 Um von einem UNIX-Rechner ein Programm auf einem anderen UNIXRechner aufzurufen, können Sie das Programm RSH (manchmal auch RSHELL) benutzen. RSH nimmt als Parameter die IP-Adresse des Zielrechners und einen String, der eine gültige Kommandozeile auf dem Zielrechner darstellen muss. Beachten Sie, dass RSH auf dem R/3-Applikationsserver ausgeführt wird, gegebenenfalls müssen Sie den Namen des Zielrechners explizit spezifizieren. SAP R/3 Remote Function Call Kap04.fm Seite 79 Dienstag, 22. April 2003 2:31 14 Falls der Zielrechner ein Windows-Rechner ist, müssen Sie auf diesem ein Utility installieren, das die RSH-Aufrufe entgegennimmt. Ein bewährtes Tool ist der ATAMAN-Manager von ATAMAN.COM. UNIX nach Windows Falls R/3 und die zu rufende Applikation beide auf einem Windows NT/ 2000-System laufen, können Sie für den Aufruf auch DCOM verwenden. Windows nach Windows Falls Sie wenig Erfahrung mit solchen Remote-Aufrufen haben, sollten Sie die Aufrufe immer durch das SAP-Utility rfcexec durchführen. Dies ist normalerweise als RFC-Destination SERVER_EXEC vordefiniert. Zuvor testen Sie dann den Aufruf des Programms auf dem Remote-System von der Kommandozeile des SAP-Applikationsservers aus. Denn die AufrufKette ist immer die folgende: Rfcexec für einfache Anwendungen 1. R/3 bestimmt über die in Transaktion SM59 gegebene Definition, welches Programm aufgerufen werden soll, im Fall der Destination SERVER_EXEC findet R/3 das Programm rfcexec. 2. Der R/3-Kernel führt jetzt das Programm rfcexec zusammen mit eventuellen Parametern auf dem Rechner aus, dessen IP-Adresse in SM59 angegeben wurde. 3. rfcexec ruft eine Command-Shell auf und übergibt dieser die Parameter zur Ausführung. 4. rfcexec fängt die Ergebnisse ab und gibt sie an das rufende R/3-System zurück. 4.8.2 RFC über Webserver Im Zuge der Standardisierung von RPC-Aufrufen hat es sich heute bewährt, Daten und Programmaufrufe ausschließlich über HTTP auszutauschen. Dazu wird auf allen potenziellen Zielrechnern für unsere Remote-Aufrufe ein HTTP-Server installiert, sofern diese nicht ohnehin schon eingerichtet sind, wie es bei fast allen modernen Windows NT/ 2000-Systemen und den meisten Linux-Systemen der Fall ist. Sicherheit durch Gateway-Proxies Alle Remote-Aufrufe sind fortan ausschließlich HTTP-GET- oder HTTPPOST-Requests, die vom HTTP-Server interpretiert werden. Die gewünschte Aktion wird dann durch die aufgerufene ASP, JSP oder CGIPage durchgeführt. Damit reduziert sich die Installation der RFCs auf SAPSeite auf die Konfiguration der HTTP-Server. Aufruf von Remote Programs aus R/3 Via RFC 79 Kap04.fm Seite 80 Dienstag, 22. April 2003 2:31 14 4.8.3 Aufruf eines Programms auf der Workstation mit RFC R/3 verwendet die klassische Client-Server-Technik, um Programme auf einem Remote-Rechner aufzurufen, wobei die Applikation auf dem Remote-Rechner den Server darstellt und R/3 in die Client-Rolle schlüpft. Rfcexec.exe dient als RFC-Server für R/3 Dabei muss das zu rufende Programm den Konventionen des RFC-Protokolls entsprechen. Um Ihnen zu ersparen, jedes Mal dieses Protokoll in Ihre Anwendung einzubinden, existiert ein Standard-RFC-Server rfcexec, der als Proxy zwischen R/3 und einem Betriebssystemkommando dient. rfcexec stellt feste Funktionsaufrufe bereit Das rfcexec-Objekt stellt die in der folgenden Tabelle aufgelisteten Methoden zur Verfügung. Diese erlauben im Wesentlichen, ein auszuführendes Betriebssystemkommando zu übergeben und je nach Methode wahlweise den Ergebnisdatenstrom abzufangen und im rufenden Programm zur Verfügung zu stellen. Methode Beschreibung RFC_REMOTE_PIPE FUNCTION 'RFC_REMOTE_EXEC' IMPORTING command EXCEPTIONS system_failure communication_failure Programm mit rfcexec ausführen. RFC_REMOTE_PIPE FUNCTION 'RFC_REMOTE_PIPE' IMPORTING command TABLES pipedata(80) EXCEPTIONS system_failure communication_failure Ein Programm ausführen, das Eingabedaten durch die Inputpipe übernimmt und die Ergebnisse der Resultpipe zurückgibt. Die Resultpipe ist gewöhnlich der Textdatenstrom, der von einer UNIX- oder DOS-Box zurückgegeben wird. Wenn Sie z.B. dir > mydata.txt ausführen, wird das Ergebnis in die Datei mydata.txt umgeleitet. RFC_REMOTE_ PIPE fängt diesen Datenstrom ab und tritt somit an die Stelle des Ausgabefiles. file: = Name der Remote-Datei pipedata: = Daten, die in die Inputpipe geschrieben werden Tabelle 4.2 Vordefinierte Methoden des rfcexec.exe-Programms 80 SAP R/3 Remote Function Call Kap04.fm Seite 81 Dienstag, 22. April 2003 2:31 14 Methode Beschreibung RFC_REMOTE_FILE FUNCTION 'RFC_REMOTE_FILE' IMPORTING file(256) Write(1) TABLES filedata(80) EXCEPTIONS system_failure Lesen und Schreiben einer Datei via RFC. file: = Name der Remote-Datei write: = falls write=’X’, wird filedata in die Datei geschrieben, ansonsten wird die Datei gelesen und deren Inhalt in filedata retourniert. filedata: = Inhalt der Datei communication_failure Tabelle 4.2 Vordefinierte Methoden des rfcexec.exe-Programms Für ABAP sieht der Aufruf von rfcexece wie jeder andere RFC-Funktionsbaustein aus. Dem ABAP-Programm können Sie also nicht ansehen, dass der gerufene Funktionsbaustein kein R/3-Funktionsbaustein ist. Die Destination des Aufrufs wird mit dem Parameter Destination angegeben und muss zuvor mit Transaktion SM59 definiert worden sein. Syntaktisch sind interne und externe RFC gleich In SAP R/3 ist standardmäßig eine RFC-Destination LOCAL_EXEC vordefiniert, die das Programm rfcexec auf der Workstation des aktuellen Users aufruft. SAP R/3 kommuniziert dazu über das SAP GUI, das dann über OLE die gewünschte Aktion ausführt. rfcexec ist ein RFC-Serverprogram, das ein als Parameter mitgegebenes Programm auf der Kommandozeile von Windows ausführt und den zurückerhaltenen Datenstrom an SAP R/3 zurückmeldet. rfcexec.exe über Destination LOCAL_EXEC aufrufen Listing 4.13 Ausführen eines Programms auf einer Workstation von R/3 aus mit RFC_ REMOTE_EXEC DATA: command(256) DEFAULT 'echo Hello World. >> rfctest.dat' DATA: rfc_mess(128). CALL FUNCTION 'RFC_REMOTE_EXEC' DESTINATION rfcdest EXPORTING command = command EXCEPTIONS system_failure = 1 MESSAGE rfc_mess communication_failure = 2 MESSAGE rfc_mess. Aufruf von Remote Programs aus R/3 Via RFC 81 Kap04.fm Seite 82 Dienstag, 22. April 2003 2:31 14 Listing 4.14 Ausführen eines Programms auf einer Workstation von R/3 aus mit RFC_ REMOTE_PIPE DATA: command(256) DEFAULT 'echo test.dat' DATA: rfc_mess(128). DATA: pipedata(80) occurs 0 with CALL FUNCTION 'RFC_REMOTE_PIPE' DESTINATION rfcdest EXPORTING command = command read = 'X' TABLES pipedata = pipedata EXCEPTIONS system_failure = 1 communication_failure = 2 Hello World. >> rfc- header line. MESSAGE rfc_mess MESSAGE rfc_mess. Listing 4.15 Lesen einer Datei von der Workstation von R/3 aus mit RFC_REMOTE_ FILE DATA: command(256) DEFAULT 'echo Hello World. >> rfctest.dat' DATA: rfc_mess(128). DATA: pipedata(80) occurs 0 with header line. CALL FUNCTION 'RFC_REMOTE_FILE' DESTINATION rfcdest EXPORTING file = filename write = write "space: read the file; 'X': save filedata to filename TABLES filedata = filedata EXCEPTIONS system_failure = 1 MESSAGE rfc_mess communication_failure = 2 MESSAGE rfc_mess. 82 SAP R/3 Remote Function Call Kap04.fm Seite 83 Dienstag, 22. April 2003 2:31 14 Abbildung 4.8 Settings der vordefinierten RFC-Destination LOCAL_EXEC in SM59 Abbildung 4.9 Settings der vordefinierten RFC-Destination SERVER_EXEC in SM59 Aufruf von Remote Programs aus R/3 Via RFC 83 Kap04.fm Seite 84 Dienstag, 22. April 2003 2:31 14 4.8.4 Aufruf eines HTTP-Webservers von SAP R/3 Destination SAPHTTPA Um einen HTTP-Server aufzurufen, stellt SAP R/3 bereits geeignete RFCUtilities zur Verfügung. Dabei handelt es sich um ein Utility SAPHTTP, das auf dem SAP R/3-Applikationsserver ausgeführt wird. Alternativ kann SAPHTTP auch auf dem Frontend über SAP GUI ausgeführt werden. Funktionen HTTP_GET und HTTP_POST Standardmäßig ist eine RFC-Destination SAPHTTPA bereits mit Transaktion SM59 vordefiniert. Über diese können Sie dann HTTP-Anfragen versenden. Die beiden Funktionsbausteine HTTP_GET und HTTP_POST erleichtern Ihnen dabei die Arbeit (siehe Listing ### und Listing ###). Listing 4.16 Abruf einer URL mit ABAP DATA: ABSOLUTE_URI(128) type c. data: response_headers(80) occurs 0 with header line. data: RESPONSE_ENTITY_BODY(120) occurs 0 with header line. ABSOLUTE_URI = 'http://xml.amazon.com/onca/xml2?t=webservices-20' & '&tag=logosworldcom&dev-t=D2H3YO46KJJ615' & '&AsinSearch=3528057297&type=lite&f=xml'. CALL FUNCTION 'HTTP_GET' EXPORTING ABSOLUTE_URI = ABSOLUTE_URI RFC_DESTINATION = 'SAPHTTPA' PROXY = '192.168.69.64:8080' * IMPORTING * STATUS_CODE = * STATUS_TEXT = * RESPONSE_ENTITY_BODY_LENGTH = TABLES * REQUEST_ENTITY_BODY = RESPONSE_ENTITY_BODY = RESPONSE_ENTITY_BODY RESPONSE_HEADERS = RESPONSE_HEADERS * REQUEST_HEADERS = EXCEPTIONS CONNECT_FAILED = 1 TIMEOUT = 2 INTERNAL_ERROR = 3 TCPIP_ERROR = 4 84 SAP R/3 Remote Function Call Kap04.fm Seite 85 Dienstag, 22. April 2003 2:31 14 DATA_ERROR SYSTEM_FAILURE COMMUNICATION_FAILURE OTHERS . IF SY-SUBRC <> 0. write: / sy-subrc. ENDIF. loop at response_entity_body. write: / response_entity_body. endloop. = = = = 5 6 7 8 Listing 4.17 Ergebnis der HTTP-Anfrage mit HTTP_GET Test for function group Function module Upper/lower case SFTP HTTP_GET Import parameters ABSOLUTE_URI http://localhost/postinfo.html REQUEST_ENTITY_BODY_LENGTH RFC_DESTINATION PROXY PROXY_USER PROXY_PASSWORD USER PASSWORD BLANKSTOCRLF Value 0 SAPHTTP Export parameters STATUS_CODE STATUS_TEXT RESPONSE_ENTITY_BODY_LENGTH Value 200 OK 2.651 Tables REQUEST_ENTITY_BODY Result: RESPONSE_ENTITY_BODY Result: RESPONSE_HEADERS Value 0 Entries 0 Entries 0 Entries 14 Entries 0 Entries Aufruf von Remote Programs aus R/3 Via RFC 85 Kap04.fm Seite 86 Dienstag, 22. April 2003 2:31 14 Result: REQUEST_HEADERS Result: 11 Entries 0 Entries 0 Entries Die HTTP-Header-Daten werden in genau der gleichen Form zurückgegeben, wie sie im HTTP-Datenstrom empfangen werden: Listing 4.18 HTTP-Header in Tabelle RESPONSE_HEADERS HTTP/1.1 200 OK Server: Microsoft-IIS/5.0 Cache-Control: no-cache Expires: Sat, 08 Sep 2001 12:36:33 GMT Date: Sat, 08 Sep 2001 12:36:33 GMT Content-Type: text/html Accept-Ranges: bytes Last-Modified: Sun, 21 Jan 2001 14:24:08 GMT ETag: "0b4b1d0b583c01:96a" Content-Length: 2651 <blank line> 4.8.5 Proxy-Settings in ABAP Proxy-Settings in Tabelle THTTP Falls ABAP die HTTP-Aufrufe über einen HTTP-Proxy ausführen muss, kann der Proxy für den ganzen Mandanten in der Tabelle THTTP vordefiniert werden. Dabei geht SAP davon aus, dass der erste Eintrag in THTTP die Angaben des System-Proxy darstellt. Listing 4.19 Standardcoding zum Bestimmen des Proxy für ABAP-HTTP-Requests form set_http_proxy using uri proxy proxy_user proxy_ password. data: proxyflag type c. if proxy ne space. exit. endif. select single * from thttp. if thttp-exitfunc ne space. call function thttp-exitfunc exporting absolute_uri = uri importing proxy = proxyflag. if proxyflag eq 'X'. proxy = thttp-proxy. 86 SAP R/3 Remote Function Call Kap04.fm Seite 87 Dienstag, 22. April 2003 2:31 14 proxy_user = thttp-puser. proxy_password = thttp-ppassword. endif. endif. endform. Zum besseren Verständnis sehen Sie hier die Struktur der Tabelle THTTP: MANDT MANDT CLNT Client PROXY PROXY CHAR HTTP proxy host name PUSER PROXY_USER CHAR User name for HTTP proxy PPASSWORD PROXY_PWD CHAR Password for HTTP proxy EXITFUNC RS38L_FNAM CHAR Name of function module Tabelle 4.3 Struktur der Tabelle THTTP 4.9 RFC von SAP R/3 auf Windows 4.9.1 Kommunikation mit Excel via OLE/2 und HTTP-Server Eines der beliebtesten Tools im Enterprise-Computing ist die Tabellenkalkulation. Die einfache und einleuchtende Bedienung und die Möglichkeit, wiederkehrende Berechnungen in einer WYSIWYG-Umgebung ohne Hilfe von EDV-Spezialisten vorzunehmen, ist etwas, das Sachbearbeiter in hohem Maße schätzen. Excel ist eines der Akzeptanztools für PCs Da entsteht sehr rasch der Wunsch, die Daten in Excel zu erfassen, aber doch sofort in SAP zur Verfügung zu haben, ohne diese mehrfach zu erfassen. Zur Lösung dieses Problems gibt es wie so oft mehrere Ansätze. SAP R/3 ist sehr wohl in der Lage, auf ein beliebiges DCOM-Objekt eines Windows-Rechners zuzugreifen und darauf OLE-Befehle abzusetzen. Es gibt auch hier die Möglichkeit eines 2-Tier- und eines 3-Tier-Ansatzes. R/3 bietet eine RFC-zu-OLE-Brücke für ABAP an. Damit kann man über das SAP GUI mit einem auf der Workstation des Benutzers befindlichen OLE-Objekt kommunizieren. Hinreichende Beispiele für jemanden, der ein wenig von VBA-Programmierung in MS Excel, MS Word und so weiter versteht, finden sich in der Transaktion /nOLE2. Der Nachteil dieser Lösung ist natürlich, dass das Arbeitsblatt von der Arbeitsstation des angemeldeten SAP R/3-Benutzers aus zugreifbar sein muss. Dies kann man allerdings leicht erreichen, indem man das Arbeitsblatt auf einem von SAP R/3 zugreifbaren Netzwerk-Verzeichnis abspeichert. RFC von SAP R/3 auf Windows 2-Tier: Direktzugriff via R/3OLE auf Excel 87 Kap04.fm Seite 88 Dienstag, 22. April 2003 2:31 14 3-Tier: Zugriff via Middleware Eine Alternative ist wiederum, die Kommunikation zwischen SAP R/3 und Excel über einen Middleware-Broker durchzuführen. Dazu schickt SAP R/3 seine Anfrage an den Broker, welcher die gewünschte Aufgabe mit Excel aushandelt und das Ergebnis an SAP R/3 zurückmeldet. Diese Lösung ist in aller Regel leichter zu realisieren und vor allem ist sie deutlich stabiler. 4.9.2 Zugriff auf ein Excel-Datenblatt von R/3 Beispiel zum direkten Zugriff In diesem Kapitel stellen wir eine Möglichkeit vor, direkt mit SAP R/3 auf ein bestehendes Excel-Arbeitsblatt zuzugreifen, in diesem eine Kalkulation anzustoßen und die Ergebnisse in SAP R/3 zu importieren. Die Kommunikation findet dabei über die OLE2-Bridge des SAP GUI der Arbeitsstation des Benutzers statt. Diese Technik funktioniert in der geschilderten Weise nicht, wenn die Transaktion nicht online ausgeführt wird, weil dann kein SAP GUI im Zugriff ist. Stücklistenkalkulation als Beispiel Für das Beispiel haben wir ein Spreadsheet definiert, in dem eine Stückliste aufgeführt ist. In dieser Stückliste kann man in einer Zelle eine Menge eintragen. Es sind Formeln hinterlegt, die aus der Eingabe die Mengen der Komponenten errechnen. Die einzelnen Sektionen sind in Excel benannt, so dass wir es einfacher haben, auf die einzelnen Bereiche zuzugreifen. ! " #$%$ # " *+ , - / / )0 # ! &'() !' (.' & Abbildung 4.10 Spreadsheet zur Berechnung der Stückliste (Bill of Material) 88 SAP R/3 Remote Function Call Kap04.fm Seite 89 Dienstag, 22. April 2003 2:31 14 4& 0&&.& " " " " #$%$ # *+ , - / # 12()&3 13 &3 3 2)(3 3 &3 4& Abbildung 4.11 Benannte Bereiche im Spreadsheet $ ( / $ 2 / ! % 32 $ .6 " & )*+* ) 0"1 4 ) # '# ,-. '#, -5, Abbildung 4.12 Formeln zur Berechnung Testprogramm in VBA Zum Zweck der Entwicklung entwerfen wir erst ein Testprogramm, das die gewünschten Aktionen durch ein VBA-Programm simuliert. Die Entwicklungsumgebung von VBA hilft uns dabei, durch die Intellisense-Hilfe die richtigen Befehle, Methoden und Properties der Excel-Objekte zu finden und im Debugger eine robuste Zugriffsform zu testen. Wenn wir die Aufrufe später von SAP R/3 aus machen, wird uns der Debugger nicht mehr zur Verfügung stehen, weshalb wir das Testen außerhalb von SAP R/3 vornehmen. Zugriff auf die Daten durch ein VBA-Programm Listing 4.20 VBA-Routine zum Setzen und Auslesen der BOM-Werte Sub Read_Values_From_EXCEL() Dim ix As Integer Dim cellvalue As String Dim rowcount As Integer Dim h_appl As Excel.Application Dim h_book As Excel.Workbook Dim h_books As Excel.Workbooks RFC von SAP R/3 auf Windows 89 Kap04.fm Seite 90 Dienstag, 22. April 2003 2:31 14 Dim h_sheet As Excel.Worksheet Dim h_sheets As Excel.Sheets Dim h_range As Excel.Range Dim h_rows As Excel.Range Dim h_cell As Excel.Range Dim h_name As Excel.Name Set h_appl = New Excel.Application h_appl.Visible = False Set h_books = h_appl.Workbooks Set h_book = h_books.Open(FileName:="U:\_DEMO\BOM\BOM.XLS") Set h_sheets = h_book.Worksheets h_sheets.Select Set h_sheet = h_sheets.Item("SUPERSAVER") Set h_name = h_sheet.Names("QUANT") Set h_range = h_name.RefersToRange cellvalue = h_range(RowIndex:=1, ColumnIndex:=1).Value Debug.Print "Quant", cellvalue cellvalue = cellvalue + 5 h_range(RowIndex:=1, ColumnIndex:=1).Value = cellvalue h_appl.CalculateFull Set h_name = h_sheet.Names("RETURNS") Set h_range = h_name.RefersToRange Set h_rows = h_range.Rows rowcount = h_rows.Count For ix = 1 To rowcount cellvalue = h_range(RowIndex:=ix, ColumnIndex:=1).Value Debug.Print ix, cellvalue Next ix h_book.Close savechanges:=False h_appl.Quit Set h_appl = Nothing End Sub 90 SAP R/3 Remote Function Call Kap04.fm Seite 91 Dienstag, 22. April 2003 2:31 14 Testprogramm in ASP In einem nächsten Schritt versuchen wir, den Code aus einer anderen Umgebung aufzurufen. Dazu erzeugen wir ein ASP-Script, um die Daten als Webseite zu erhalten. Das gleiche Script können wir auch später von SAP R/3 aufrufen und haben so bereits eine 3-Tier-Lösung für den Durchgriff von SAP R/3 auf Excel. Aufruf als ASP-Skript Listing 4.21 ASP-Script zum Setzen und Auslesen der BOM-Werte via OLE <% Function Read_Values_From_EXCEL_VBS(myquant, filename) Dim Resultstr Dim ix Dim cellvalue Dim rowcount Dim h_appl Dim h_book Dim h_books Dim h_sheet Dim h_sheets Dim h_range Dim h_rows Dim h_cell Dim h_name resultstr = myquant & Chr(13) & Chr(10) Set h_appl = CreateObject("Excel.Application") ' h_appl.Visible = False Set h_books = h_appl.Workbooks Set h_book = h_books.Open(filename) Set h_sheets = h_book.Worksheets h_sheets.Select Set h_sheet = h_sheets.Item("SUPERSAVER") Set h_name = h_sheet.Names("QUANT") Set h_range = h_name.RefersToRange cellvalue = h_range(1, 1).Value cellvalue = myquant h_range(1, 1).Value = cellvalue h_appl.CalculateFull RFC von SAP R/3 auf Windows 91 Kap04.fm Seite 92 Dienstag, 22. April 2003 2:31 14 Set h_name = h_sheet.Names("RETURNS") Set h_range = h_name.RefersToRange Set h_rows = h_range.Rows rowcount = h_rows.Count For ix = 1 To rowcount cellvalue = h_range(ix, 1).Value resultstr = resultstr & CellValue cellvalue = h_range(ix, 3).Value resultstr = resultstr & "," & CellValue & chr(13) & chr(10) Next ' h_book.Save h_book.Close False h_appl.Quit Set h_appl = Nothing Read_Values_From_EXCEL_VBS = resultstr End Function Response.write Read_Values_From_EXCEL_VBS(105, "BOM.XLS") %> Direktzugriff von SAP R/3 auf Excel Zugriff via OLE-Brücke Die Syntax zum Setzen und Aufrufen von COM-Objekten aus SAP R/3 heraus ist etwas von der in Visual Basic verschieden, weshalb wir das Coding nicht 1:1 übernehmen können. In Listing ### ist die ABAP-Version zu sehen, die mit den VBA-Kommandos kommentiert ist. Listing 4.22 ABAP-Routine zum Setzen und Auslesen der BOM-Werte via OLE REPORT ydemo_excel_bom MESSAGE-ID sy. INCLUDE ole2incl. "<-- Contains the types for OLEAccess PARAMETER: filename LIKE rlgrap-filename DEFAULT 'U:\_DEMO\BOM\BOM.xls'. * Dim h_appl As Excel.Application DATA: h_appl TYPE ole2_object. * Dim h_books As Workbooks DATA: h_books TYPE ole2_object. * Dim h_book As Workbook DATA: h_book TYPE ole2_object. 92 SAP R/3 Remote Function Call Kap04.fm Seite 93 Dienstag, 22. April 2003 2:31 14 * Dim h_sheets As Excel.Sheets DATA: h_sheets TYPE ole2_object. * Dim h_sheet As Worksheet DATA: h_sheet TYPE ole2_object. DATA: x_sheet TYPE ole2_object. * Dim h_name As Name DATA: h_name TYPE ole2_object. * Dim h_range As Range DATA: h_range TYPE ole2_object. * Dim h_rows As Range DATA: h_rows TYPE ole2_object. * Dim h_cell As Range DATA: h_cell TYPE ole2_object. DATA: cellvalue(29). DATA: rowcount TYPE i. START-OF-SELECTION. * Set h_appl = New Excel.Application CREATE OBJECT h_appl 'EXCEL.APPLICATION'. IF sy-subrc NE 0. MESSAGE i002 WITH sy-msgli. ENDIF. * h_appl.Visible = True SET PROPERTY OF h_appl 'VISIBLE' = 1 . * Set h_books = h_appl.Workbooks GET PROPERTY OF h_appl 'WORKBOOKS' = h_books . * Set h_book = h_books.Open(Filename:= "\BOM.*XLS") CALL METHOD OF h_books 'OPEN' = h_book EXPORTING #filename = filename. * GET PROPERTY OF h_appl 'ACTIVEWORKBOOK' = h_book. * Set h_sheets = h_book.Worksheets GET PROPERTY OF h_book 'WORKSHEETS' = h_sheets . * Set h_sheet = h_sheets.Item("SUPERSAVER") * GET PROPERTY OF h_book 'ACTIVESHEET' = h_sheet. CALL METHOD OF h_sheets 'ITEM' = h_sheet EXPORTING #index = 'SUPERSAVER' . ****** Modify the Pivot Cell ************************** * Set h_name = h_sheet.Names("QUANT") CALL METHOD OF h_sheet 'NAMES' = h_name EXPORTING #index ='QUANT' . * Set h_range = h_name.RefersToRange GET PROPERTY OF h_name 'REFERSTORANGE' = h_range. RFC von SAP R/3 auf Windows 93 Kap04.fm Seite 94 Dienstag, 22. April 2003 2:31 14 CALL METHOD OF h_range 'CELLS' = h_cell EXPORTING #1 = 1 #2 = 1. GET PROPERTY OF h_cell 'VALUE' = cellvalue. WRITE: / 'Old value':, cellvalue. cellvalue = cellvalue + 5. WRITE: / 'New value':, cellvalue. ULINE. SET PROPERTY OF h_cell 'VALUE' = cellvalue. CALL METHOD OF h_appl 'CALCULATEFULL'. ****** Now, Pick up results *************************** * Set h_name = h_sheet.Names("SUBQUANT") CALL METHOD OF h_sheet 'NAMES' = h_name EXPORTING #index ='RETURNS'. * Set h_range = h_name.RefersToRange GET PROPERTY OF h_name 'REFERSTORANGE' = h_range. * GET PROPERTY OF h_appl 'ACTIVECELL' = h_range. * cellvalue = h_cell.Value CALL METHOD OF h_range 'ROWS' = h_rows. * Set h_rows = h_range.Rows CALL METHOD OF h_rows 'COUNT' = rowcount. * * * * * 94 DO rowcount TIMES. CALL METHOD OF h_range 'CELLS' = h_cell EXPORTING #1 = sy-index #2 = 1. EXPORTING #rowindex = 1 #columnindex = sy-index. CALL METHOD OF h_cell 'VALUE' = cellvalue. WRITE: / sy-index, cellvalue. ENDDO. h_book.Close false CALL METHOD OF h_book 'SAVE'. CALL METHOD OF h_book 'CLOSE' EXPORTING #1 = 0. h_appl.Quit CALL METHOD OF h_appl 'QUIT'. Set h_appl = Nothing FREE OBJECT h_appl. SAP R/3 Remote Function Call Kap04.fm Seite 95 Dienstag, 22. April 2003 2:31 14 4.10 Business-Objekte und BAPI Eine der großen Stärken von SAP R/3 liegt darin, dass es Geschäftsprozesse de facto standardisiert. Solche Quasi-Standards werden als Business-Objekte bezeichnet. Um auf die Business-Objekte zugreifen zu können, wurden für diese Schnittstellen geschaffen, die als BAPI bezeichnet werden. Eines der Kernziele einer modernen Business-Software ist die Geschäftsprozessmodellierung mit Hilfe einer Programmiersprache. Damit soll eine Standardisierung der Geschäftsprozesse möglich werden. SAP ist allein durch seine Marktmacht dazu prädestiniert, solche Business-Modelle vorzugeben und zu vereinheitlichen. Business-Objekte sind solche Modellierungen von realen Geschäftsprozessen innerhalb von SAP-Systemen. Business-Objekte beschreiben und implementieren die Methoden, Properties und Interfaces dieser Prozesse mit den Mitteln von ABAP Objects. Business-Objekte sind formale Modelle von realen Prozessen Aus der Sicht eines Entwicklers besteht ein Geschäftsprozessmodell aus den drei Elementen: Geschäftsmodelle aus Sicht eines Entwicklers 왘 Database Hier werden die Daten in proprietärer Form abgespeichert. 왘 Business-Objekt Eine abstrakte Projektion der Realität mit den Mitteln der Programmiersprache. 왘 Application Programming Interface (API) Schnittstelle für den Zugriff auf die Business-Objekte von einer Programmiersprache aus. Der Zugriff auf die Business-Objekte erfolgt über eine öffentlich bekannte Business-API – das Business Application Programming Interface, kurz BAPI – und ist technisch eine Untermenge der RFC-fähigen Funktionsbausteine in R/3. BAPIs sind die offizielle Außenschnittstelle, mit der Zugriffe von Fremdsystemen auf R/3 erfolgen sollen. Zugriff über Business-API 4.10.1 Business-Objekte und Modellsichten Business-Objekte sind die technische Beschreibung einer der Modellsichten auf einen Prozess. In einfachen Fällen kommen wir mit den folgenden Modellsichten aus: Business-Objekte und BAPI 95 Kap04.fm Seite 96 Dienstag, 22. April 2003 2:31 14 왘 Technische Sicht (Komponentensicht) In der Technischen Sicht werden die Objekte beschrieben, wie sie tatsächlich auf dem Computer realisiert werden. Hier bestimmen wir also, welche Tabellen in der Datenbank angelegt werden, welche Datenbank-Engine überhaupt verwendet wird, welche Protokolle zum Austausch der Daten benutzt werden, und in welcher Programmiersprache wir arbeiten. 왘 Anwendungsfall (Use Case) Die Anwendungsfälle beschreiben die Operationen, die wir auf den Daten durchführen wollen, etwa einen Logon, Aufträge speichern, nach Aufträgen suchen und so weiter. 왘 Business View (Logische Sicht) Der Business View ist die abstrakte Ebene, die versucht, unbelastet von den technischen Details die logischen Komponenten einer Applikation zu beschreiben. Dabei denken wir an die Organisation in Stammdaten, an Kundenaufträge oder einen Internetshop. Wichtig dabei ist, dass die Logische Sicht zunächst frei von Restriktionen formuliert werden darf, also so nah wie möglich dem Denken bei der realen Arbeit entsprechen soll. (!, ! $%& "# '( ( )** ) )+'" -+) ) 09)7*& ;* ;* ;) 7*& 8* &4+<11 Abbildung 4.13 Beispiel für Modellsichten 96 SAP R/3 Remote Function Call *7*& ) :4* 17*& :4* ;* ;) Kap04.fm Seite 97 Dienstag, 22. April 2003 2:31 14 Ein Business-Objekt ist nun eine formale Beschreibung eines Business Views. Demnach gibt es Business Views für jede logische Entität, zum Beispiel für den Materialstamm, Kundenstamm, Kundenaufträge oder für eine ganze Shopping-Cart-Applikation. Materialstamm, Kundenstamm und Kundenaufträge sind als Business-Objekte schon in SAP R/3 definiert und dafür gibt es auch schon die passenden BAPIs. Formale Beschreibung eines Business-View Alle öffentlichen Business-Objekte sind im SAP R/3 Business Object Repository (BOR) registriert. Dieses mit den Transaktionen BAPI und SWO1 (Business Object Builder) zugängliche Verzeichnis zeigt die vorhandenen Business-Objekte, deren Methoden und die Implementierung an. Business Object Repository – BOR Die Shopping-Cart-Applikation als Business-Objekt ist eine Sonderentwicklung von Logos! Informatik GmbH und Casabac GmbH. Näheres und einen Download der neuesten Versionen finden Sie unter shoppingcart.logosworld.com. !" #!"$ %!& % ' (& !" (&#!"$ (&%!& ! " # #$ ) !" )#!"$ )%!& + !" + #!"$ + %!& #$# '$ * '$ * Abbildung 4.14 Business-Sichten in SAP R/3 Business-Objekte und BAPI 97 Kap04.fm Seite 98 Dienstag, 22. April 2003 2:31 14 #$%%%&'%()*+ ! " #$ %& !'" " ! #$ #$#$( ! Abbildung 4.15 Business-Objekte in SAP R/3-SD Abbildung 4.16 Business-Objekt BUS1093 (ExchangeRate) in Transaktion BAPI 98 SAP R/3 Remote Function Call Kap04.fm Seite 99 Dienstag, 22. April 2003 2:31 14 Abbildung 4.17 Business-Objekt BUS1093 (ExchangeRate) in Transaktion SWO1 4.10.2 Technische Kriterien eines BAPI Ein BAPI ist eine Implementierung eines Business-Objekts, die öffentliche Methoden zur Verfügung stellt. In der Realisierung verbirgt sich hinter jeder Methode ein Funktionsbaustein. Zusätzlich erhält aber jede Methode noch eine zum SAP Business Workflow kompatible Objektschnittstelle in SWO1. Wenn von BAPI die Rede ist, ist selten das Business-Objekt selbst gemeint, sondern meistens der BAPI-Funktionsbaustein, der eine der vielen Methoden eines BAPI implementiert. So ist beispielsweise das BAPI zum Kundenauftragsmodell BUS2032, und BAPI_ SALESORDER_CREATEFROMDAT2 ist der BAPI-Funktionsbaustein zur Methode CreateFromDat2. BAPI ist die Implementierung eines BusinessObjekts Aus technischer Sicht besteht kein Unterschied zwischen einem BAPIFunktionsbaustein und gewöhnlichen RFC-Bausteinen. Allerdings unterwirft SAP ein BAPI gewissen Konventionen, die jeder beachten sollte, der standardkonforme BAPIs entwickeln möchte: Technisch ist eine BAPI-Funktion ein RFC-Funktionsbaustein 왘 Ein BAPI führt niemals ein COMMIT WORK oder einen ROLLBACK WORK aus. Der Sinn davon ist, dass eine externe Applikation eigene Transaktionen als Sequenzen von BAPI-Methoden ausführen kann. 왘 Das Ende einer Transaktion wird durch die speziellen BAPI-Funktions- bausteine BAPI_TRANSACTION_COMMIT ROLLBACK erreicht. und BAPI_TRANSACTION_ 왘 Alle Schnittstellenparameter sollen einen aussagekräftigen, englischen Namen haben. Business-Objekte und BAPI 99 Kap04.fm Seite 100 Dienstag, 22. April 2003 2:31 14 4.10.3 Anwendungsbeispiel: Das Sales-Order-BAPI Kundenaufträge sind zentral für SD Wir haben als Beispiel für die Anwendung eines BAPI das Sales-OrderObjekt ausgewählt, das ein ganz zentrales Business-Objekt in SAP ist. Dieses wird verwendet für das Anlegen, Ändern und Anzeigen von 왘 Kundenaufträgen 왘 Angebotsanfragen (Request for quotation – RFQ) 왘 Gut- und Lastschriftenanforderungen 왘 Auslieferungsaufträgen Auslieferungsaufträge sind Aufträge, die automatisch eine Lieferung erzeugen. Man verwendet sie unter anderem zum Ausliefern in mehreren Einzellieferungen. Dabei wird der Auslieferungsauftrag mit Bezug zum Originalauftrag angelegt. Der Auslieferungsauftrag ist so eingestellt, dass automatisch eine Auslieferung (Objekt LIKP) erstellt wird, gegebenenfalls mit anschließendem automatischen Versand. 왘 Anstoßen von Auslieferungen Das Business-Objekt-Konzept von SAP R/3 sieht es nicht vor, Auslieferungen direkt durch eine Schnittstelle anzulegen. Der zentrale Einstiegspunkt für jede SD-Transaktion ist das Sales-Order-BAPI. Dazu muss der Kundenauftrag bestimmte Customizing-Anforderungen erfüllen: 왘 Der Kundenauftragstyp (TVAK-AUART) muss auf automatisches Anlegen einer Auslieferung eingestellt sein. 왘 Das Anlegen der Auslieferung muss vom Status des Kundenauftrags abhängig sein, zum Beispiel durch Setzen des Feldes Liefersperre im Kopf (VBAK-LIFSK) oder Position (VBAP-LIFSP). 왘 Anstoßen von Fakturen. 왘 Der Kundenauftragstyp (TVAK-AUART) muss auf automatisches Anlegen einer Faktura eingestellt sein. 왘 Das Anlegen der Faktura muss vom Status des Kundenauftrags abhängig sein, zum Beispiel durch Setzen des Feldes Fakturasperre im Kopf (VBAK-FAKSK) oder Position (VBAP-FAKSP) Kundenauftragsbestand abfragen 왘 Methode: SalesOrder.GetList 왘 Funktion: BAPI_SALESORDER_GETLIST Die BAPI-Methode gibt eine Liste aller Kundenaufträge eines bestimmten Kunden zurück. Die Liste kann auf Materialnummern und bestimmte Zeiträume eingeschränkt werden. 100 SAP R/3 Remote Function Call Kap04.fm Seite 101 Dienstag, 22. April 2003 2:31 14 Kundenauftrag anlegen 왘 Methode: SalesOrder.CreateFromDat2 왘 Funktion: BAPI_SALESORDER_CREATEFROMDAT2 Diese BAPI-Methode legt einen Kundenauftrag komplett neu an und verwendet dazu die übergebenen Daten. Die Methode kann alle businessrelevanten Daten aufnehmen und erlaubt es, einen Kundenauftrag auf einen Schlag anzulegen. In unseren Beispielen weiter unten zeigen wir einen Mehrschritt-Ansatz, indem wir zunächst den Auftragskopf (also nur VBAK) ohne Verkaufspositionen (VBAP) anlegen. Später fügen wir die Positionen mit der Methode BAPI_SALESORDER_CHANGE hinzu. Erst Kopf, dann Position anlegen Kundenauftrag ändern 왘 Methode: SalesOrder.Change 왘 Funktion: BAPI_SALESORDER_CHANGE Diese BAPI-Methode ermöglicht es, einen bestehenden Kundenauftrag in allen Einzelheiten zu ändern, also insbesondere auch Positionen oder Einteilungsdaten hinzuzufügen oder zu löschen und selbstverständlich alle bereits bestehenden Werte einzeln zu ändern. Einzelnes Ändern von Feldwerten Kundenauftrag oder Kundenauftragspositionen löschen Löschen ist wie in jeder professionellen Datenbank oder Transaktionsumgebung nur eine Variante des Änderns. Dazu muss lediglich das Updateflag = "D" für Delete gesetzt werden. Löschen: Updateflag = »D« 4.10.4 Beispiel: BAPI_SALESORDER_GETLIST Im Folgenden sehen Sie ein fertiges Rahmenprogramm zum Aufruf des BAPIs, das eine Liste aller Kundenaufträge zu einem Kunden zurückgibt. Liste aller Kundenaufträge Listing 4.23 ABAP: Call BAPI_SALESORDER_GETLIST '**** Functions in Library Public func As SAPFunctionsOCX.Function Public XMLDOC As New MSXML2.DOMDocument Private myR3 As R3LogonObj Private XMLhelp As New XMLhelper '**** Parameters Public iCUSTOMER_NUMBER As SAPFunctionsOCX.Parameter Business-Objekte und BAPI 101 Kap04.fm Seite 102 Dienstag, 22. April 2003 2:31 14 Public Public Public Public Public meter Public Public Public Public iDOCUMENT_DATE As SAPFunctionsOCX.Parameter iDOCUMENT_DATE_TO As SAPFunctionsOCX.Parameter iMATERIAL As SAPFunctionsOCX.Parameter iPURCHASE_ORDER As SAPFunctionsOCX.Parameter iPURCHASE_ORDER_NUMBER As SAPFunctionsOCX.ParaiSALES_ORGANIZATION As SAPFunctionsOCX.Parameter iTRANSACTION_GROUP As SAPFunctionsOCX.Parameter eRETURN As SAPFunctionsOCX.Structure tSALES_ORDERS As SAPTableFactoryCtrl.Table Public Sub Class_Initialize() Set myR3 = New R3LogonObj myR3.R3Logon '*************** Creating the object reference for the RFC Functions '==================================================== Set func = myR3.Functions.Add("BAPI_SALESORDER_GETLIST") '*************** Creating the object reference for the Parameters '================================================== '**** Importing Parameters Set iCUSTOMER_NUMBER = func.Exports("CUSTOMER_NUMBER") Set iDOCUMENT_DATE = func.Exports("DOCUMENT_DATE") Set iDOCUMENT_DATE_TO = func.Exports("DOCUMENT_DATE_TO") Set iMATERIAL = func.Exports("MATERIAL") Set iPURCHASE_ORDER = func.Exports("PURCHASE_ORDER") Set iPURCHASE_ORDER_NUMBER = func.Exports("PURCHASE_ORDER_NUMBER") Set iSALES_ORGANIZATION = func.Exports("SALES_ORGANIZATION") Set iTRANSACTION_GROUP = func.Exports("TRANSACTION_GROUP") '**** Exporting Parameters Set eRETURN = func.Imports("RETURN") '**** Table Parameters 102 SAP R/3 Remote Function Call Kap04.fm Seite 103 Dienstag, 22. April 2003 2:31 14 Set tSALES_ORDERS = func.Tables("SALES_ORDERS") End Sub ' Class_Initialize() Public Sub Class_Terminate() Set func = Nothing Set myR3 = Nothing End Sub ' Class_Terminate() Public Sub Execute() func.Call End Sub ' Class_Initialize() Public Sub Connect() End Sub ' Connect Public Function toXMLDoc() As MSXML2.DOMDocument Set toXMLDoc = XMLhelp.toXMLDoc(func) End Function Public Function toXML() As String toXML = toXMLDoc.XML End Function Listing 4.24 ABAP: Anlegen eines Auftragskopfs mit BAPI_SALESORDER_ CREATEFROMDAT2 *&----------------------------------------------------* *& Report YDEMO_BAPI_2032_CREATE_HEADER *&----------------------------------------------------* *& This routines demonstrates the use of the BAPI * *& BAPI_SALESORDER_CREATEFROMDAT2 *& It calls the BAPI and creates the header of a sales *& orders *& w/o creating any items so far. *&----------------------------------------------------* REPORT YDEMO_BAPI_2032_CREATE_HEADER. TYPE-POOLS: SYDES. PARAMETERS: VBELN LIKE VBCOM-VBELN OBLIGATORY MEMORY ID AUN. PARAMETERS: AUART LIKE VBCOM-AUART OBLIGATORY MEMORY ID AUA. Business-Objekte und BAPI 103 Kap04.fm Seite 104 Dienstag, 22. April 2003 2:31 14 PARAMETERS: KUN. PARAMETERS: WEM. PARAMETERS: VKO. PARAMETERS: VTW. PARAMETERS: SPA. PARAMETERS: AUD. PARAMETERS: PARAMETERS: KUNAG LIKE VBCO2-KUNNR OBLIGATORY MEMORY ID KUNWE LIKE VBCO2-KUNWE OBLIGATORY MEMORY ID VKORG LIKE VBCOM-VKORG OBLIGATORY MEMORY ID VTWEG LIKE VBCOM-VTWEG OBLIGATORY MEMORY ID SPART LIKE VBCOM-SPART OBLIGATORY MEMORY ID AUDAT LIKE VBCOM-AUDAT OBLIGATORY MEMORY ID REFVBELN LIKE VBCOM-VBELN MEMORY ID AUB. REFVGTYP LIKE VBAK-VGTYP DEFAULT 'C'. DATA: ORDER_HEADER_IN LIKE BAPISDHD1. DATA: ORDER_HEADER_OUT LIKE BAPISDHD1. DATA: SALESDOCUMENT LIKE BAPIVBELN-VBELN. DATA: RETURN LIKE STANDARD TABLE OF BAPIRET2 WITH HEADER LINE. DATA: ORDER_ITEMS_IN LIKE STANDARD TABLE OF BAPISDITM WITH HEADER LINE. DATA: ORDER_PARTNERS LIKE STANDARD TABLE OF BAPIPARNR WITH HEADER LINE. START-OF-SELECTION. PERFORM BAPI_SALESORDER_CREATE USING VBELN AUART KUNAG KUNWE VKORG VTWEG SPART AUDAT REFVBELN REFVGTYP. *ERFORM bapi_salesorder_getcreate WRITE: / 'Returned sales order number:' , SALESDOCUMENT COLOR COL_TOTAL. ULINE. LOOP AT RETURN. CALL FUNCTION 'MESSAGE_TEXT_BUILD' EXPORTING MSGID = RETURN-ID MSGNR = RETURN-NUMBER MSGV1 = RETURN-MESSAGE_V1 MSGV2 = RETURN-MESSAGE_V2 104 SAP R/3 Remote Function Call Kap04.fm Seite 105 Dienstag, 22. April 2003 2:31 14 MSGV3 = RETURN-MESSAGE_V3 MSGV4 = RETURN-MESSAGE_V4 IMPORTING MESSAGE_TEXT_OUTPUT = SY-LISEL EXCEPTIONS OTHERS = 1. WRITE: / SY-LISEL COLOR COL_NORMAL.. ENDLOOP. *-----------------------------------------------------* FORM BAPI_SALESORDER_CREATE USING VALUE(DOCNO) LIKE VBCOM-VBELN VALUE(DOC_TYPE) LIKE VBCOM-AUART VALUE(SOLDTO) LIKE VBCOM-KUNNR VALUE(SHIPTO) LIKE VBCOM-KUNNR VALUE(SALESORG) LIKE VBCOM-VKORG VALUE(DISTRCHANNEL) LIKE VBAK-VTWEG VALUE(DIVISION) LIKE VBCOM-SPART VALUE(ORDERDATE) LIKE VBCOM-AUDAT VALUE(REFDOCNO) LIKE VBCOM-VBELN VALUE(REFDOC_TYPE) LIKE VBAK-VGTYP. REFRESH ORDER_ITEMS_IN. REFRESH ORDER_PARTNERS. CLEAR ORDER_HEADER_IN. CLEAR RETURN. CLEAR SALESDOCUMENT. SALESDOCUMENT = VBELN. IF NOT REFDOCNO IS INITIAL. ULINE. FORMAT RESET COLOR COL_NEGATIVE. WRITE:/ 'The reference does not work in 40B HP65 due to an error when'. WRITE:/ 'calling SD_SALES_DOCU_SAVE from SD_SALES_DOCUMENT_MAINTAIN. '. ULINE. FORMAT RESET. ENDIF. ORDER_HEADER_IN-REFOBJTYPE = 'BUS2032'. ORDER_HEADER_IN-REFOBJKEY = REFDOCNO. ORDER_HEADER_IN-REFDOCTYPE = REFDOC_TYPE. ORDER_HEADER_IN-DOC_TYPE = AUART. Business-Objekte und BAPI 105 Kap04.fm Seite 106 Dienstag, 22. April 2003 2:31 14 ORDER_HEADER_IN-SALES_ORG = SALESORG. ORDER_HEADER_IN-DISTR_CHAN = DISTRCHANNEL. ORDER_HEADER_IN-DIVISION = DIVISION. ORDER_HEADER_IN-REQ_DATE_H = ORDERDATE. ORDER_PARTNERS-PARTN_ROLE ORDER_PARTNERS-PARTN_NUMB APPEND ORDER_PARTNERS. ORDER_PARTNERS-PARTN_ROLE ORDER_PARTNERS-PARTN_NUMB APPEND ORDER_PARTNERS. * * * * * * * * * * * * * * * * * * * 106 = 'AG'. = SOLDTO. = 'WE'. = SHIPTO. CALL FUNCTION 'BAPI_SALESORDER_CREATEFROMDAT2' EXPORTING SALESDOCUMENT = SALESDOCUMENT ORDER_HEADER_IN = ORDER_HEADER_IN ORDER_HEADER_INX = SENDER = BINARY_RELATIONSHIPTYPE = ' ' INT_NUMBER_ASSIGNMENT = ' ' BEHAVE_WHEN_ERROR = ' ' LOGIC_SWITCH = ' ' TESTRUN = ' ' CONVERT = ' ' IMPORTING SALESDOCUMENT_EX = SALESDOCUMENT TABLES RETURN = RETURN order_items_in = order_items_in ORDER_ITEMS_INX = ORDER_PARTNERS = ORDER_PARTNERS ORDER_SCHEDULES_IN = ORDER_SCHEDULES_INX = ORDER_CONDITIONS_IN = ORDER_CFGS_REF = ORDER_CFGS_INST = ORDER_CFGS_PART_OF = ORDER_CFGS_VALUE = ORDER_CFGS_BLOB = ORDER_CFGS_VK = SAP R/3 Remote Function Call Kap04.fm Seite 107 Dienstag, 22. April 2003 2:31 14 * * * * * * ORDER_CFGS_REFINST ORDER_CCARD ORDER_TEXT ORDER_KEYS EXTENSIONIN PARTNERADDRESSES EXCEPTIONS OTHERS COMMIT WORK. = = = = = = = 1. ENDFORM. Die Auftragspositionen können jederzeit an einen bestehenden Auftrag angefügt werden. Das wird genau wie das Ändern eines Auftrags durch den BAPI BAPI_SALESORDER_CHANGE erledigt: Listing 4.25 ABAP: Zufügen von Auftragspositionen mit BAPI_SALESORDER_CHANGE TYPE-POOLS: SYDES. FORM BAPI_SALESITEM_ADD USING VALUE(DOCNO) LIKE VBCOM-VBELN VALUE(POSNR) LIKE VBAP-POSNR VALUE(MATNR) LIKE VBAP-MATNR VALUE(QUANT) LIKE VBAP-KWMENG VALUE(VRKME) LIKE VBAP-VRKME VALUE(CONDI) LIKE KONV-KSCHL VALUE(NETPR) LIKE VBAP-NETPR VALUE(CURR_ISO) LIKE BAPISDITM-CURR_ISO. DATA: ORDER_HEADER_IN LIKE BAPISDH1. DATA: ORDER_HEADER_INX LIKE BAPISDH1X. DATA: ORDER_HEADER_OUT LIKE BAPISDHD1. DATA: SALESDOCUMENT LIKE BAPIVBELN-VBELN. DATA: ORDER_ITEMS_IN LIKE STANDARD TABLE OF BAPISDITM WITH HEADER LINE. DATA: ORDER_ITEMS_INX LIKE STANDARD TABLE OF BAPISDITMX WITH HEADER LINE. DATA: SCHEDULE_LINES LIKE STANDARD TABLE OF BAPISCHDL WITH HEADER LINE. DATA: SCHEDULE_LINESX LIKE STANDARD TABLE OF BAPISCHDLX WITH HEADER LINE. DATA: CONDITIONS_IN LIKE STANDARD TABLE OF Business-Objekte und BAPI 107 Kap04.fm Seite 108 Dienstag, 22. April 2003 2:31 14 BAPICOND WITH HEADER LINE. DATA: CONDITIONS_INX LIKE STANDARD TABLE OF BAPICONDX WITH HEADER LINE. DATA: RETURN LIKE STANDARD TABLE OF BAPIRET2 WITH HEADER LINE. REFRESH: ORDER_ITEMS_IN, ORDER_ITEMS_INX. REFRESH: SCHEDULE_LINES, SCHEDULE_LINESX. CLEAR: ORDER_HEADER_IN, ORDER_HEADER_INX. CLEAR: SCHEDULE_LINES, SCHEDULE_LINESX. CLEAR: RETURN, SALESDOCUMENT. SALESDOCUMENT = DOCNO. ORDER_HEADER_INX-UPDATEFLAG = 'U'. ORDER_ITEMS_IN-ITM_NUMBER = POSNR. ORDER_ITEMS_IN-MATERIAL = MATNR. ORDER_ITEMS_IN-TARGET_QTY = QUANT. ORDER_ITEMS_IN-TARGET_QU = VRKME. ORDER_ITEMS_IN-SALES_UNIT = VRKME. ORDER_ITEMS_IN-TARGET_VAL = PRICE. ORDER_ITEMS_IN-CURR_ISO = CURR_ISO. ORDER_ITEMS_INX-ITM_NUMBER = ORDER_ITEMS_INITM_NUMBER. ORDER_ITEMS_INX-UPDATEFLAG = 'I'. APPEND ORDER_ITEMS_IN. *-----------------------------------------------------* SCHEDULE_LINES-ITM_NUMBER = ORDER_ITEMS_INITM_NUMBER. SCHEDULE_LINES-REQ_DATE = SY-DATUM + 7. SCHEDULE_LINES-REQ_QTY = QUANT. APPEND SCHEDULE_LINES. SCHEDULE_LINESX-ITM_NUMBER = SCHEDULE_LINESITM_NUMBER. SCHEDULE_LINESX-UPDATEFLAG = 'I'. APPEND SCHEDULE_LINESX. *-----------------------------------------------------* IF NOT ( PRICE IS INITIAL OR CONDI IS INITIAL ). CONDITIONS_IN-ITM_NUMBER = ORDER_ITEMS_INITM_NUMBER. CONDITIONS_IN-COND_TYPE = CONDI. CONDITIONS_IN-COND_VALUE = PRICE. CONDITIONS_IN-CURR_ISO = CURR_ISO. 108 SAP R/3 Remote Function Call Kap04.fm Seite 109 Dienstag, 22. April 2003 2:31 14 APPEND CONDITIONS_IN. CONDITIONS_INX-ITM_NUMBER = CONDITIONS_INITM_NUMBER. CONDITIONS_INX-UPDATEFLAG = 'I'. APPEND CONDITIONS_INX. ENDIF. *-----------------------------------------------------* CALL FUNCTION 'BAPI_SALESORDER_CHANGE' EXPORTING SALESDOCUMENT = SALESDOCUMENT ORDER_HEADER_IN = ORDER_HEADER_IN ORDER_HEADER_INX = ORDER_HEADER_INX TABLES RETURN = RETURN ORDER_ITEM_IN = ORDER_ITEMS_IN SCHEDULE_LINES = SCHEDULE_LINES SCHEDULE_LINESX = SCHEDULE_LINESX CONDITIONS_IN = CONDITIONS_IN CONDITIONS_INX = CONDITIONS_INX. ENDFORM. 4.10.5 VBA-Beispiel zu BAPI_SALESORDER_GETLIST Hier folgt nun zum Vergleich die Auflistung der Kundenaufträge mit Hilfe eines VBA-Programms. Liste aller Kundenaufträge Listing 4.26 VBA: Call BAPI_SALESORDER_GETLIST '**** Functions in Library Public func As SAPFunctionsOCX.Function Public XMLDOC As New MSXML2.DOMDocument Private myR3 As R3LogonObj Private XMLhelp As New XMLhelper '**** Parameters Public iCUSTOMER_NUMBER As SAPFunctionsOCX.Parameter Public iDOCUMENT_DATE As SAPFunctionsOCX.Parameter Public iDOCUMENT_DATE_TO As SAPFunctionsOCX.Parameter Public iMATERIAL As SAPFunctionsOCX.Parameter Public iPURCHASE_ORDER As SAPFunctionsOCX.Parameter Public iPURCHASE_ORDER_NUMBER As SAPFunctionsOCX.Parameter Public iSALES_ORGANIZATION As SAPFunctionsOCX.Parameter Business-Objekte und BAPI 109 Kap04.fm Seite 110 Dienstag, 22. April 2003 2:31 14 Public iTRANSACTION_GROUP As SAPFunctionsOCX.Parameter Public eRETURN As SAPFunctionsOCX.Structure Public tSALES_ORDERS As SAPTableFactoryCtrl.Table Public Sub Class_Initialize() Set myR3 = New R3LogonObj myR3.R3Logon '*************** Creating the object reference for the RFC Functions '==================================================== Set func = myR3.Functions.Add("BAPI_SALESORDER_GETLIST") '*************** Creating the object reference for the Parameters '================================================== '**** Importing Parameters Set iCUSTOMER_NUMBER = func.Exports("CUSTOMER_NUMBER") Set iDOCUMENT_DATE = func.Exports("DOCUMENT_DATE") Set iDOCUMENT_DATE_TO = func.Exports("DOCUMENT_DATE_TO") Set iMATERIAL = func.Exports("MATERIAL") Set iPURCHASE_ORDER = func.Exports("PURCHASE_ORDER") Set iPURCHASE_ORDER_NUMBER = func.Exports("PURCHASE_ORDER_NUMBER") Set iSALES_ORGANIZATION = func.Exports("SALES_ORGANIZATION") Set iTRANSACTION_GROUP = func.Exports("TRANSACTION_GROUP") '**** Exporting Parameters Set eRETURN = func.Imports("RETURN") '**** Table Parameters Set tSALES_ORDERS = func.Tables("SALES_ORDERS") End Sub ' Class_Initialize() Public Sub Class_Terminate() Set func = Nothing Set myR3 = Nothing End Sub ' Class_Terminate() 110 SAP R/3 Remote Function Call Kap04.fm Seite 111 Dienstag, 22. April 2003 2:31 14 Public Sub Execute() func.Call End Sub ' Class_Initialize() Public Sub Connect() End Sub ' Connect Public Function toXMLDoc() As MSXML2.DOMDocument Set toXMLDoc = XMLhelp.toXMLDoc(func) End Function Public Function toXML() As String toXML = toXMLDoc.XML End Function 4.11 SAP Interface Repository Alle offiziell von SAP freigegebenen BAPIs, IDocs und RFC-Bausteine sind im Interface Repository von SAP dokumentiert, das unter der URL http:/ /ifr.sap.com abrufbar ist. Es ist für jedermann zugänglich und gibt die Struktur der BAPIs sowohl in Textform als auch als formales XML-Dokument zurück. Alle BAPIs sind im IFR dokumentiert Zum Abrufen der Information verwendet SAP so genannte Canonical URLs, die im Wesentlichen eine vom W3C abgesegnete Variante der CGISpezifikation zur Codierung von Parametern in eine URI sind. Eine Version des IFR kann auch mit der Transaktion BAPI in einer SAP R/3-Instanz aufgerufen werden. Anfrage in Form einer Canonical URL Das nachstehende Beispiel ist eine typische Anfrage an das IFR, die uns ein XML-Schema für die GetList-Methode des Sales-Order-BAPIs zurückgibt. Typische Anfrage an das IFR Listing 4.27 Eine typische Anfrage an das IFR http://ifr.sap.com/catalog/query.asp? namespace=urn:sap-com:ifr:LO:46C &type=bapi&name=SalesOrder.GetList &xml=schema.w3c-2000-04&xdir=response Tabelle 4.4 erläutert die Bestandteile der Canonical URLs, Tabelle 4.5 listet die möglichen Werte für die einzelnen Elemente auf, und Tabelle 4.6 die verwendeten Objekttypen. SAP Interface Repository 111 Kap04.fm Seite 112 Dienstag, 22. April 2003 2:31 14 Element Bedeutung http://ifr.sap.com/catalog/query.asp? Einstiegs-URL ins IFR namespace=urn:sapcom:ifr:LO:46C Namespace für das XML-Dokument &type=bapi Anfrage nach einem BAPI &name=SalesOrder.GetList Name des BAPI &xml=schema.w3c-2000-04 Anfrage nach XML-Schema gemäß W3C-Spezifikation &xdir=response Anfrage nach Schema für die Antwort Tabelle 4.4 Objekt-Typen, die in den Canonical URLs von SAP verwendet werden Element Erlaubte Werte type= bobj | bapi | rfc | imsg | idoc | iseg | area | docu name= Name des Objekts key= Schlüssel des Dokuments, zum Beispiel Auftragsnummer param= Parameter, die kein Schlüsselfeld sind, zum Beispiel Auftragsdatum language= Sprache des Ergebnisdokuments, falls sinnvoll xml= schema | template [W3C-Version] xdir= send | response Tabelle 4.5 Mögliche Werte innerhalb der Canonical URL type= Bedeutung area Area bapi BAPI bobj Business Object idoc Intermediate Document (IDoc) imsg IDoc message type iseg IDoc segment Rfc Remote Function Call Tabelle 4.6 Objekt-Typen, die in den Canonical URLs von SAP verwendet werden 112 SAP R/3 Remote Function Call Kap04.fm Seite 113 Dienstag, 22. April 2003 2:31 14 type= Bedeutung Name Name Param Parameter docu Documentation Tabelle 4.6 Objekt-Typen, die in den Canonical URLs von SAP verwendet werden (Forts.) Folgendermaßen gestaltet sich die Abfrage des XSD-Schemas für RFC IDOCTYPE_READ-COMPLETE: http://ifr.sap.com/catalog/query.asp?namespace=urn:sapcom:ifr:BASIS:46C&type=rfc&name=IDOCTYPE_READ_COMPLETE&xml=SCHEMA&xdir=SEND <?xml version="1.0" encoding="utf-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:sap-com:document:sap:rfc:functions" targetNamespace="urn:sap-com:document:sap:rfc:functions" version="1.0"> <xsd:element name="IDOCTYPE_READ_COMPLETE"> <xsd:annotation> <xsd:documentation>Read IDoc type with segments (RFC-compatible)</xsd:documentation> </xsd:annotation> <xsd:complexType> <xsd:all> <xsd:element name="PI_IDOCTYP"> <xsd:annotation> <xsd:documentation>Basic type</xsd:documentation> </xsd:annotation> <xsd:simpleType> <xsd:restriction base="xsd:string"> <xsd:maxLength value="30"/> </xsd:restriction> </xsd:simpleType> </xsd:element> <xsd:element name="PI_CIMTYP" minOccurs="0"> ... SAP Interface Repository 113 Kap04.fm Seite 114 Dienstag, 22. April 2003 2:31 14