Eigenschaften der CIS ABAP Schnittstelle

Die CIS ABAP Schnittstelle bietet eine einfach handhabbare und schnelle Möglichkeit, SAP-Daten in ABAP zu besorgen und die Visualisierung anschließend in VB.NET und HTML zu implementieren.

Aus VB.NET wird eine ABAP-Routine des Funktionsbausteins /GUIXT/CISADDON_INTERFACE aufgerufen. Alle Input-Parameter werden als Strings in festgelegter Reihenfolge übergeben, z.B.

1. Parameter: Kundennummer
2. Parameter: Auftragsnummer

Zurückgegeben wird eine Tabelle, in der in der Regel jede Tabellenzeile, abgeteilt durch Tabulator-Zeichen, mehrere Einzelfelder enthält (CSV-Format). Sie können auch Langtexte oder Daten in XML-Format zurückliefern.

Vor- und Nachteile:

  • ABAP-Kenntnisse werden benötigt
  • Sehr gute Performance erzielbar
  • Komplexe Select-Zugriffe und Aufruf von SAP Funktionsbausteinen möglich
  • Bündelung mehrerer Zugriffe in einen physischen Request möglich
  • Zusätzliche Berechtigungsprüfungen können im ABAP-Coding erforderlich sein
  • Relativ schnelle Implementierung
  • Sehr variabel
  • Insbesondere für CIS Reports sehr gut geeignet

In der aufgerufenen ABAP-Routine können Sie alle Daten besorgen, die für die spezielle Anzeige benötigt werden, insbesondere auch die anzuzeigenden Texte zu den jeweiligen Schlüsseln.

Beispiel 1:
Daten für Bestandsübersicht besorgen

Zu einer Materialnummer wollen wir den Lagerbestand tabellarisch anzeigen:

Im Artikel Add-on Bestandsübersicht ist beschrieben, wie Sie dieses Add-on in CIS mobile integrieren; hier konzentrieren wir uns nun auf das Besorgen der Daten.

Bekannt ist die Materialnummer; zurückgegeben werden soll eine Tabelle mit Werk, Lager, Name von Werk und Lager und die jeweiligen Mengen ("Frei verfügbar" etc.).

Als erstes wählen wir einen Namen für die Datenbeschaffung, z.B. "MaterialStock".
Als nächstes legen wir die Schnittstelle fest:

Eingabe: Materialnummer

Ausgabe: Jeweils eine Zeile pro Werk/Lager mit den Angaben: Werk, Bezeichnung Werk, Lager, Bezeichnung Lager, Lagerbestand frei verfügbar, Bestand in Umlagerung, Bestand Q-Prüfung, Bestand Retouren.

Als nächstes überlegen wir, wie die Datenbeschaffung  in ABAP aussieht. Bei Daten, die 1:1 in der Datenbank zu finden sind, bietet sich ein ABAP-Select an. Sind es dagegen Informationen, die eine komplexere Logik erfordern, halten wir eher nach existierenden Funktionsbausteinen (oft BAPIs) Ausschau.

In unserem Fall ist die Datenbeschaffung mit Select aus der Datenbank relativ einfach:

  • Die Lagerbestände stehen in Tabelle MARD
  • Die Werksbezeichnung in Tabelle T001W
  • Die Lagerbezeichnung in Tabelle T001L

Aus Performancegründen ist es ratsam, mit einem oder wenigen Select-Zugriffen auszukommen. Hierzu stehen in ABAP komplexe Varianten der Select-Anweisung zur Verfügung, z.B. Subquery und Join. Bei Tabelle, von denen wir annehmen können, dass sie im Cache des Applikationsservers zu finden sind (z.B. kleinere Customizingtabellen), können wir statt eines Joins auch Einzelzugriffe durchführen.

In unserem Fall nehmen wir an, dass T001W und T001L im SAP-Cache liegen, im Unterschied zu MARD, die sehr groß werden kann (viele Materialstämme). Wir entscheiden uns deshalb für die Lösung, zunächst Tabelle MARD zu lesen und dann jeweils pro Werk/Lagerort die Texte dazu zu lesen. Alternativ könnten wir auch alle Daten mit einem einzigen "inner Join" der drei Tabellen besorgen, was in diesem Fall aber wohl nicht schneller wäre und auf jeden Fall die Datenbank etwas stärker belastet.

Insgesamt sieht unser ABAP-Coding wie folgt aus:

ABAP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
* Stock quantities for each plant
*
* In
* 1 MATNR Material number
*
* Out
*  Stok quantities

form MaterialStock tables reqparm resparm   
           changing   rc type c   msg type c.

  data: wa(8000).
  data: parmatnr like mard-matnr.

  Read Table reqparm index 1 into parmatnr.

* Stock quantities
  data: begin of stock occurs 10,
          werks like mard-werks,
          lgort like mard-lgort,
          labst like mard-labst,
          umlme like mard-umlme,
          insme like mard-insme,
          retme like mard-retme,
       end of stock.

* Texts
  Data: werks_text like t001w-name1,
        lgort_text like t001l-lgobe.

* Quantities, character format
  Data: labst_char(16),
        umlme_char(16),
        insme_char(16),
        retme_char(16).

  select *  from mard into corresponding fields of table stock
                    where matnr = parmatnr and labst > 0.


  Sort stock.
  Loop at stock.

* Authority check
    AUTHORITY-CHECK OBJECT 'M_MATE_WRK'
      ID 'WERKS' FIELD STOCK-WERKS
      ID 'ACTVT' FIELD '03'.

* not authorized?
    if sy-subrc NE 0.
      UNPACK 4 to rc.
      if msg = space.
        Concatenate 'Missing authorization for plant:' STOCK-WERKS 
               into msg separated by space.
      else.
        Concatenate MSG STOCK-WERKS into msg separated by ','.
      endif.

    else.
* read texts
      Select single name1 from t001w into werks_text
               where werks = stock-werks.

      Select single lgobe from t001l into lgort_text
                where werks = stock-werks and lgort = stock-lgort.

* character format of quantities

      Write:
        stock-labst to labst_char decimals 0,
        stock-umlme to umlme_char decimals 0,
        stock-insme to insme_char decimals 0,
        stock-retme to retme_char decimals 0.


* output result
      Concatenate
          stock-werks
          werks_text
          stock-lgort
          lgort_text
          labst_char
          umlme_char
          insme_char
          retme_char
             into wa 
                separated by cl_abap_char_utilities=>horizontal_tab.

      Append wa to resparm.

    endif.
  Endloop.
Endform.

Durch "Authority-Check" prüfen wir dabei die Berechtigung des Benutzers zur Anzeige von Daten in dem jeweiligen Werk.
Beschreibung der Schnittstelle in ABAP

Alle ABAP-Routinen liegen im Funktionsbaustein /GUIXT/CISADDON_INTERFACE. Die Schnittstellen der aufgerufenen Routinen sind immer gleich:

form xxxx tables reqparm resparm   changing   rc type c   msg type c.

Dabei ist reqparm die Eingabetabelle und resparm die Ausgabetabelle. Mit Returncode rc=0 teilen wir mit, dass die Routine normal abgelaufen ist, während rc>0 einen Fehler anzeigt; in diesem Fall können wir in msg einen Fehlertext zurückgegeben.

Die Eingabeparameter werden über die Parameternummer gelesen. Wenn zum Beispiel an eine Routine drei Schlüsselfelder VKORG, VTWEG und SPART aus VB.NET übergeben werden, lesen wir sie mit

  Read Table reqparm index into vkorg.
  Read Table reqparm index into vtweg.
  Read Table reqparm index into spart.

in die drei ABAP-Felder vkorg, vtweg und spart ein.

Die Ausgabetabelle resparm enthält Zeilen des ABAP-Typs C mit maximaler Länge 8000. Der Inhalt ist im Prinzip beliebig;  es hat sich aber als praktikabel erwiesen pro Zeile einzelne Werte, durch ein TAB-Zeichen getrennt, abzulegen. Beim Auslesen von SAP-Langtexten, die ja  größer als 8000 Zeichen sein dürfen, ist die Übergabe etwas anders, siehe Beispiel 3.
 

Beschreibung der Schnittstelle in VB.NET

Zum Aufruf der ABAP-Routine in VB.NET stellt man die Eingabeparameter als einzelne Zeilen der Eingabetabelle bereit, ruft die Routine auf und verarbeitet dann die Ausgabetabelle. Für das Beispiel oben ("MATERIALSTOCK") sieht das wie folgt aus:
 
VB.net
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    ' Stock overview 
    Public Function BuildMaterialStock(ByVal keys_ 
          As Dictionary(Of String, String), _
           ByRef buttons As String) As String

        ' We use CIS addon ABAP function to read the stock quantities

        ' Clear input/output
        rfc_input.Clear()
        rfc_output.Clear()

        ' Build up input
        Dim s As addonsimplestring = _
               DirectCast(rfc_input.AddNew(), addonsimplestring)
        s.content = GetItem(keys, "MATNR")

        ' Request data
        ic.RfcRequest("CISADDON.MATERIALSTOCK", "S", rfc_input,  _
               rfc_output, deferred:=True)

        ' execute all requests (one only in our case)
        ic.RfcExecuteDeferredCalls(rfcmessages)

       
        For Each line As addonsimplestring In rfc_output

           ...

        Next

        

    End Function

Das gesamte VB.NET-Coding finden Sie im Artikel Add-on Bestandsübersicht und in dem mit CIS mobile ausgelieferten VB.NET Add-on-Projekt.

Beispiel 2:
Preisfindung pro Kunde

Unser nächstes Beispiel ist die Datenbeschaffung des Reports "Preisfindung":

Wir gehen wie in Beispiel 1 vor:

Als erstes wählen wir einen Namen für die Datenbeschaffung, z.B. "PRICING".
Als nächstes legen wir die Schnittstelle fest:

Eingabe: Verkaufsorganisation, Vertriebsweg, Sparte, Kundennummern (beliebig viele, jeweils eine Kundennummer pro Eingabezeile)

Ausgabe: Jeweils eine Zeile pro Kunde mit den Angaben: Kundennummer, Preisgruppe, Text dazu, Kundenschema, Text dazu, Preisliste, Text dazu.

Als nächstes überlegen wir, wie die Datenbeschaffung in ABAP aussieht:

  • Die Preisfindungsangaben pro Kunde stehen in Tabelle KNVV
  • Die Bezeichnungen von Preisgruppe, Kundenschema und Preisliste stehen in den Tabellen T188T, TVKDT und T189T.

Wie bekommen Sie heraus, wie die benötigten SAP-Tabellen heißen? Hilfreich ist oft die Suche im Internet, da in Foren oft derartige Fragestellungen besprochen werden. Zweitens die Hilfefunktion F1 im SAP-System auf den entsprechenden Feldern der jeweiligen Transaktion. Drittens die SAP-Trace-Funktion, mit der Sie pro Transaktion die Datenbankzugriffe aufzeichnen können. Das sind in der Regel allerdings ziemlich viele. Viertens Sekundärliteratur zum SAP-System (Bücher, Internet).

Zum Nachprüfen, ob ein Tabellenname stimmt, mit SE11 die Tabellenstruktur und dort die Inhalte anzeigen.

Zum Lesen der Daten pro Kunde ist bei allen Datenbeschaffungsroutinen für CIS mobile Reports wichtig, den Einzelzugriff pro Kunde zu vermeiden, da dann bei z.B. 100 Kunden auch 100 einzelne Datenbankaufrufe stattfinden. Stattdessen nutzen wir die Möglichkeit der ABAP-Select-Anweisung, eine Menge von Datenbankschlüsseln auf einen Schlag an die Select-Schnittstelle weiterzureichen (Option "FOR ALL ENTRIES IN ...").

Insgesamt sieht unser ABAP-Coding wie folgt aus:

ABAP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
* Pricing
* In
* 1 VKORG
* 2 VTWEG
* 3 SPART
* 4 KUNNR and following: customer numbers
*
* Out
*  KUNNR  KONDA KONDATEXT  KALKS KALKSTEXT  PLTYP PLTYPTEXT
form PRICING tables reqparm resparm   
        changing   rc type c   msg type c.

  data: wa(8000).
  data: vkorg like knvv-vkorg,
        vtweg like knvv-vtweg,
        spart like knvv-spart.




  Read Table reqparm index 1 into vkorg.
  Read Table reqparm index 2 into vtweg.
  Read Table reqparm index 3 into spart.
  Read Table reqparm index 4 into wa.

* Customer numbers
  data: begin of customers occurs 10,
          kunnr like kna1-kunnr,
        end of customers.

  Data: k type i value 4.
  Read Table reqparm index k into customers-kunnr.
  While sy-subrc EQ 0.
    Append customers.

    k = k + 1.
    Read Table reqparm index k into customers-kunnr.
  Endwhile.

* no customers?
  if   customers[] is initial.
    exit.
  endif.

* Result tables
  data: begin of r1 occurs 10,
          kunnr like knvv-kunnr,
          konda like knvv-konda,
          kalks like knvv-kalks,
          pltyp like knvv-pltyp,
       end of r1.


  Select kunnr konda kalks pltyp
           from knvv
             into corresponding fields of table r1
               for all entries in customers
              where vkorg = vkorg
    and vtweg = vtweg
    and spart = spart
    and kunnr = customers-kunnr.

  sort r1.

  Data: kondatext like t188t-vtext,
        kalkstext like tvkdt-vtext,
        pltyptext like t189t-ptext.

  Loop at r1.

    kondatext = ''.
    kalkstext = ''.
    pltyptext = ''.

    Select single vtext from t188t into kondatext 
       where spras = sy-langu and konda = r1-konda.
    Select single vtext from tvkdt into kalkstext 
       where spras = sy-langu and kalks = r1-kalks.
    Select single ptext from t189t into pltyptext 
       where spras = sy-langu and pltyp = r1-pltyp.

* output result
    Concatenate
        r1-kunnr
        r1-konda
        kondatext
        r1-kalks
        kalkstext
        r1-pltyp
        pltyptext
         into wa separated by cl_abap_char_utilities=>horizontal_tab.

    Append wa to resparm.


  Endloop.
Endform.

 


Bitte im ABAP-Coding beachten:

Die Abfrage
* no customers?

  if   customers[] is initial.
    exit.
  endif
ist erforderlich, da bei einer leeren Tabelle die Option "FOR ALL ENTRIES IN ..:" in der Select-Anweisung ignoriert wird und damit alle Kunden gelesen würden.
Beispiel 3:
Lesen des Langtextes zum Fertigungsauftrag

Zum Lesen von Langtexten zu SAP-Objekten ist das folgende Beispiel eine nützliche Vorlage. Wir lesen im Add-on "Fertigungsauftrag" den Langtext zum Fertigungsauftrag:

Wir gehen wieder wie in Beispiel 1 vor:

Als erstes wählen wir einen Namen für die Datenbeschaffung, z.B. "ADDON_AUFK_LONGTEXT".
Als nächstes legen wir die Schnittstelle fest:

Eingabe: Auftragsnummer

Ausgabe: Langtext, jeweils eine Textzeile in einer Ausgabezeile

Als nächstes überlegen wir, wie die Datenbeschaffung in ABAP aussieht:

  • Zum Lesen des Textes nutzen wir den Funktionsbaustein READ_TEXT
  • Zur Umwandlung vom internen SAP-textformat in reinen Text (ohne Formatierung) nehmen wir den Standardbaustein CONVERT_ITF_TO_ASCII

Wir suchen den Text zunächst in der Anmeldesprache des Benutzers und, falls nicht gefunden, dann in Englisch und in Deutsch.

ABAP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

* Read longtext of productionorder (CO03)
form ADDON_AUFK_LONGTEXT tables reqparm resparm   
         changing   rc type c   msg type c.


  DATA: AUFNR like aufk-aufnr.

* 1st parameter: document number
  Read Table reqparm index 1 into AUFNR.

* texts
  DATA: BEGIN OF T_HEAD.
          INCLUDE STRUCTURE THEAD.
  DATA: END OF T_HEAD.

* text lines
  DATA: BEGIN OF T_LINES OCCURS 0.
          INCLUDE STRUCTURE TLINE.
  DATA: END OF T_LINES.

* Order number with client
  DATA: BEGIN OF CLIENTAUFNR,
          CLIENT LIKE SY-MANDT,
          AUFNR LIKE AUFK-AUFNR,
        END OF CLIENTAUFNR.

  CLIENTAUFNR-CLIENT = SY-MANDT.
  CLIENTAUFNR-AUFNR  = AUFNR.


*  Read long text
  Perform set_textkeys using 'AUFK' CLIENTAUFNR 'KOPF'.

  T_HEAD-TDID = textkeys-tdid.
  T_HEAD-TDNAME = textkeys-tdname.
  T_HEAD-TDSPRAS = textkeys-tdspras.
  T_HEAD-TDOBJECT = textkeys-tdobject.

  CALL FUNCTION 'READ_TEXT'
    EXPORTING
      ID       = T_HEAD-TDID
      LANGUAGE = T_HEAD-TDSPRAS
      NAME     = T_HEAD-TDNAME
      OBJECT   = T_HEAD-TDOBJECT
    IMPORTING
      HEADER   = T_HEAD
    TABLES
      LINES    = T_LINES
    EXCEPTIONS
      OTHERS   = 1.


  CALL FUNCTION 'CONVERT_ITF_TO_ASCII'
    TABLES
      ITF_LINES = T_LINES.


  LOOP AT T_LINES.
    Append T_LINES-TDLINE TO RESPARM.
  ENDLOOP.



endform.

 


Dabei benutzen wir eine Hilfsroutine "set_textkeys"

Perform set_textkeys using 'AUFK' CLIENTAUFNR 'KOPF'.

die bereits in dem Programm enthalten ist und den Textzugriff zunächst mit der Anmeldesprache, dann in Englisch und Deutsch realisiert:


ABAP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
 
Form set_textkeys using tdobject tdname tdid.

  Clear textkeys.
  Select single * from stxh into corresponding fields of textkeys
      where tdobject = tdobject and tdname = tdname 
        and tdid = tdid and tdspras = sy-langu.

* try English
  if sy-subrc ne 0 and sy-langu ne 'E'.
    Select single * from stxh into corresponding fields of textkeys
    where tdobject = tdobject and tdname = tdname 
     and tdid = tdid and tdspras = 'E'.
  endif.

* try German
  if sy-subrc ne 0 and sy-langu ne 'D'.
    Select single * from stxh into corresponding fields of textkeys
    where tdobject = tdobject and tdname = tdname 
     and tdid = tdid and tdspras = 'D'.
  endif.


Endform.