In der Anleitung Tabellen wird die Tabellentechnik des S10 Frameworks beschrieben. In diesem Tutorial wollen wir die Technik anhand eines Beispiels kennenlernen:

In einer Tabelle sollen beschädigte Lagerartikel erfasst werden. Die Eingabemaske sieht wie folgt aus:

Zur Eingabe der Materialnummer kann neben der Tastatureingabe auch ein Barcode oder QR-Code abfotografiert werden.  Die Spalten  "Menge"  (Anzahl beschädigter Artikel) sowie "Schadenstyp" (Dropdown-Liste) werden manuell erfasst. Materialkurztext, Lagermengeneinheit und der Lagerbestand werden aus dem SAP-System dazugelesen.

Die Anwendung kann auf dem Smartphone wie oben angezeigt im Querformat oder auch im Hochformat  bedient werden. Im Hochformat sind die Anzeigespalten "Materialkurztext", "Bestand" und "ME" (Mengeneinheit) ausgeblendet, aber die Erfassung ist auch dann problemlos möglich:

Falls zusätzliche Informationen angezeigt oder eingegeben werden sollen, etwa eine Chargen- oder Serialnummer oder der genaue Lagerplatz, kann die Tabelle um eine mehrzeilige Ausgabe oder ein gesondert aufrufbares Detailbild pro Zeile ergänzt werden. Wir beschränken uns hier auf die einzeilige Ansicht.

Die Entwicklung beginnen wir mit der Definition einer lokalen ABAP-Klasse zur Beschreibung der Tabellenzeile. Generell beruht die Tabellendarstellung im S10 Framework darauf,  jeder Tabellenzeile ein ABAP Objekt zuzuordnen. Das hat den Vorteil, dass die Techniken der "build"- und "validate"-Methoden (Tutorials 2 und 4)  nun pro Tabellenzeile zur Verfügung stehen, was wir gleich verwenden werden.

Wir können die lokale Klasse für die Tabellenzeilen entweder per Hand eingeben oder den Klassengenerator aus den S10 Utilities nutzen. Die  Klasse sieht wie folgt aus:

class mdr definition inheriting from /s10/any.

  public section.

    data:
      matnr type mard-matnr,  " Materialnummer
      werks type mard-werks,  " Werk
      lgort type mard-lgort,  " Lagerort
      speme type mard-speme,  " Anzahl beschädigte Artikel
      meins type mara-meins,  " Mengeneinheit 
      labst type mard-labst,  " Lagerbestand
      ecode type string.      " Schadenscode

*  Einheiten zuordnen
    constants:
      unit_speme type string value 'meins',
      unit_labst type string value 'meins'.
endclass.

Der Klassenname, hier "mdr" für "Material Damage Report" ist frei wählbar. Die Konstanten "unit_speme" und "unit_labst" sorgen dafür, dass die Ausgabe mit der zur Mengeneinheit passenden Anzahl Dezimalstellen erfolgt.

Verglichen mit der angezeigten Tabelle, haben wir

Den Materialtext können wir direkt in HTML durch die Notation "matnr@text" ausgeben, siehe s10standardname, sodass wir hierfür kein eigenes Klassenattribut benötigen.

Die Informationen "Werk" und "Lagerort" sind  für den Datenbankzugriff auf die Lagerortdaten des Materials (SAP-Tabelle MARD) gedacht. Alternativ könnten wir in jeder Tabellenzeile auch eine Referenz zu dem Kopfobjekt unterbringen und darüber auf Werk und Lagerort zugreifen.

Die Lagermengeneinheit und der aktuelle Lagerbestand werden aus SAP-Tabellen gelesen. Dazu implementieren wir wie in Tutorial 2 beschrieben zwei "build"-Methode, denen wir die Namen "build_meins" und "build_labst"geben:

   methods:
      build_meins
        importing
          matnr type mard-matnr
        exporting
          meins type mara-meins,

      build_labst
        importing
          matnr type mard-matnr
        exporting
          labst type mard-labst.
...

class mdr implementation.

  method build_meins.
    clear meins.
    if matnr is initial.
      return.
    endif.

    select single meins from mara into meins
      where matnr = matnr.

  endmethod.


  method build_labst.
    clear labst.
    if matnr is initial.
      return.
    endif.

    select single labst from mard into labst
      where matnr = matnr
      and   werks = werks
      and  lgort = lgort.

  endmethod.

Die beiden build-Methoden werden vom S10 Framework automatisch pro Tabellenzeile aufgerufen. Damit ist sichergestellt, dass die Mengeneinheit und der Lagerbestand jeweils korrekt für die eingegebene Materialnummer besorgt und angezeigt werden.

Jede eingegebene Materialnummer soll auf Gültigkeit geprüft werden, was wir durch Implementierung der folgenden Methode "validate_matnr" erreichen (siehe Tutorial 4):

  methods:
      validate_matnr
        importing
          matnr type mard-matnr.
...

class mdr implementation.

  method validate_matnr.
    data: mymatnr type mara-matnr.
    select single matnr from mard into mymatnr
       where matnr = matnr
       and   werks = werks
       and   lgort = lgort.
    if sy-subrc ne 0.
      s10errormessage( |Materialnummer | && s10getuservalue( 'matnr' ) && | existiert nicht in Werk/Lagerort |).
    endif.

  endmethod.

In der Fehlermeldung arbeiten wir mit der S10-Methode "s10getuservalue", um die Materialnummer korrekt formatiert anzuzeigen, also zum Beispiel "98" statt des internen Formats "000000000000000098". Die Positionierung der Meldung auf die richtige Zeile und das Setzen des Eingabefokus auf die ungültige Materialnummer erfolgen automatisch durch das S10 Framework, das die Verbindung zwischen der Tabellenzeile und dem mdr-Objekt kennt:

Nun zur Definition der Tabelle im ABAP. Sie erfolgt in einer separaten Klasse, die wir  "mdr_manager" nennen; der Name ist beliebig wählbar.

class mdr_manager definition inheriting from /s10/any.

  public section.

    data:
      werks  type mard-werks,
      lgort  type mard-lgort,
 
       tabmdr type table of ref to mdr.

    methods:
      start,
      addrow,
      delrow,
       check, 
      save.

endclass.


DIe eigentliche Anwendung besteht aus den Methoden start,  addrow, delrow, check und save.

Die Methode "start" wird aus der logon-Methode aufgerufen.  Sie liest die SAP-Benutzerparameter für Werk und Lageort, initialisiert die Erfassungstabelle mit fünf leeren Zeilen und ruft den Screen "create" auf:
  method start.

* user parameters
    get parameter:
     id 'WRK' field werks,
     id 'LAG' field lgort.

* add 5 empty rows
    do 5 times.
      addrow( ).
    enddo.

* display main screen
    s10nextscreen( 'create').
  endmethod.



Durch "addrow" wird eine leere Zeile in der Erfassungstabelle hinzugefügt. Wir können die Methode wie oben in "start" intern aufrufen, aber auch der Benutzer kann durch einen Klick auf die Drucktaste "Neue Zeile" eine leere Zeile hinzufügen.


* add row in item table
  method addrow.
    data: mymdr type ref to mdr.
    create object mymdr.

* set general key values
    mymdr->werks =  werks.
    mymdr->lgort =  lgort.

    append mymdr to tabmdr.
  endmethod.

 

"delrow" wird vom Benutzer aufgerufen, indem er auf das Icon rechts in der Tabellenzeile klickt. In der Methode müssen wir zum Löschen der richtigen Zeile wissen, in welcher Zeile der Benutzer geklickt hat. Das geht über die  einen Aufruf von s10contextinfo( ):

* delete row in item table
  method delrow.
    delete tabmdr index s10contextinfo( )->rownumber.
  endmethod.

Bei "check" prüfen wir bei allen belegten Zeilen der Tabelle, ob Menge und Schadenstyp eingegeben wurden:

 method check.
    loop at tabmdr assigning field-symbol(<mdr>)
      where table_line->matnr is not initial.

      if <mdr>->speme le 0.
        <mdr>->s10setfocus( 'speme' ).
        s10errormessage( 'Bitte die Menge eingeben' ).
      endif.

      if <mdr>->ecode is initial.
        <mdr>->s10setfocus( 'ecode' ).
        s10errormessage( 'Bitte den Schadenstyp auswählen' ).
      endif.

    endloop.

    s10infomessage( 'Daten geprüft' ).
  endmethod.

Zwei Details sind dabei interessant:

 

Die "save" Methode ist für das Tutorial nicht näher ausgeführt, da es uns im Moment um die Dialogtechnik der Erfassungstabelle geht:

* save data
  method save.

* check data first
    check( ).

    s10infomessage( 'Save: to be implemented' ).
  endmethod.

Denkbar ist hier der Aufruf des SAP-BAPI "BAPI_GOODSMVT_CREATE" zum Erstellen eines Materialbelegs.

Die HTML-Seite "create" enthält den Kopfbereich mit Titel, den Drucktasten "Prüfen", "Sichern" und "Logoff", das Werk und den Lagerort, dann die Erfassungstabelle sowie den "Neue Zeile" Button. Da das Erstellen der HTML-Datei einige Schreibarbeit bedeutet, können Sie das S10 Generierungstool dazu nutzen und sich eine halbwegs passende Tabelle als Ausgangspunkt generieren  lassen. Fast alle HTML-Optionen sind HTML5-Standard. Das S10 Framework interpretiert nur die speziellen CSS-Klassen der Elemente, um die Verbindung mit den ABAP-Objekten herzustellen.

   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
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=400">
    <link rel='stylesheet' type='text/css' href='../../../style/s10.style.css'>
    <link rel='stylesheet' type='text/css' href='../../../style/custom.style.css'>
    <script src='../../synactiveS10/synactiveS10.java.js'></script>
    <title>Lagermaterial: Schaden erfassen</title>
</head>

<body style="width: 100%; margin: 0px; padding: 0px;"
    onload='init();' class="colorscheme9">

    <div class="headerarea"
        style="width: 100%; font-size: 20px; font-weight: bold; padding: 10px;">
        Lagermaterial: Schaden erfassen
    </div>

    <div class="toolbar">

        <button type="button" class="toolbarbutton" onclick="S10Apply('check');">
            Prüfen
        </button>

        <button type="button" class="toolbarbutton" onclick="S10Apply('save');">
            Sichern
        </button>

        <button type="button" class="toolbarbutton" style="float: right;"
            onclick="S10Logoff();">
            Logoff
        </button>

    </div>


    <!-- header fields -->
    <div style="background-color: lightgray;">

        <div class="infoblock" style="width: 200px; height: 50px;">
            <label class="label output" for="werks" name="werks"></label>
            <br>
            <span class='output' name="werks"></span>
            <span class='output' name='werks@text'></span>
        </div>


        <div class="infoblock" style="width: 200px; height: 50px;">
            <label class="label output" for="lgort" name="lgort"></label>
            <br>
            <span class='output' name="lgort"></span>
            <span class='output' name='lgort@text'></span>
        </div>
    </div>



    <!-- column headers -->
    <div class="colheaders nosort">

        <!-- Material -->
        <div class='colhead output ' style="width: 100px;" name="matnr"></div>
        <div class='colhead output landscape'
            style="width: 180px;" name="matnr@text">
        </div>

        <!-- Menge -->
        <div class='colhead' style="width: 60px;" name="speme">
            Menge
        </div>

        <!-- Schadenscode-->
        <div class='colhead' style="width: 170px;" name="ecode">
            Schadenstyp
        </div>

        <!-- Lagerbestand -->
        <div class='colhead landscape'
            style="width: 60px;" name="labst">
            Bestand
        </div>

        <!-- ME -->
        <div class='colhead  landscape' style="width: 60px;" name="meins">
            ME
        </div>


    </div>

    <!-- list rows -->
    <form class='table' name='tabmdr'>

        <div class="tablerow">
            <input type="text" class="inputcell valuehelp" name="matnr"
                style="float: left; width: 100px;">

            <div class='outputcelldiv landscape'
                style="width: 180px; font-size: 13px; font-weight: normal;"
                name="matnr@text">
            </div>

            <input type="text" class="inputcell" name="speme"
                style="float: left; width: 60px;">

            <select class="inputcellselect" name="ecode"
                style="float: left; width: 170px;">
                <optgroup label="Wasserschaden">
                    <option value="W01">Wasserschaden</option>
                    <option value="W02">Wasserspuren</option>
                </optgroup>
                <optgroup label="Verpackung">
                    <option value="V01">Verpackung beschädigt</option>
                    <option value="V02">Verpackung verschmutzt</option>
                    <option value="V03">Verpackung fehlt</option>
                    <option value="V04">Verpackung leer</option>
                </optgroup>
                <optgroup label="Sonstiges">
                    <option value="S01">Sonstiger Schaden</option>
                </optgroup>
            </select>


            <div class='outputcelldiv landscape'
                style="width: 60px;" name="labst">
            </div>

            <div class='outputcelldiv landscape'
                style="width: 60px;" name="meins@text">
            </div>

            <!-- delete row -->
            <img style="width: 16px; height: 16px; margin-left: 10px;"
                src="../../../icons/delete_64x64.png"
                onclick="S10Apply('delrow')" />

        </div>

    </form>

    <button type="button" class="button-small"
        style='margin: 4px; background-color: #937a7a; color: white;'
        onclick="S10Apply('addrow'); ">
        Neue Zeile
    </button>

</body>
</html>
Die Erfassungstabelle beginnt in Zeile 59 mit den Spaltenüberschriften.

Zeile 59:    <div class="colheaders nosort"

Die Option "nosort" sorgt dafür, dass die Umsortierung der Tabellenzeilen, die normalerweise durch Klick auf eine Spaltenüberschrift möglich ist, unterbleibt. Bei Erfassungstabellen macht es meist nicht viel Sinn, eine Umsortierung anzubieten, und der Pfeil für die Sortierreihenfolge verbraucht etwas Platz in der Überschrift.

Zeile 63:  <div class='colhead output ' style="width: 100px;" name="matnr"></div>

Bei der CSS-Klasse "colhead output" entnimmt das S10 Framework die Überschrift aus dem SAP Repository gemäss dem bei name= genannten Klassenattribut .

Zeile 64   <div class='colhead output landscape'
                    style="width: 180px;" name="matnr@text">
               </div>

Durch "landscape" wird diese Spalte im Hochformat ("portrait") nicht angezeigt

Zeile 93   <form class='table' name='tabmdr'>

Nach den Überschriften nun die eigentliche Tabelle, die durch ein <form>-Tag mit class='table' eingeleitet wird. Bei name= den ABAP-Namen der Tabelle nennen, hier "tabmdr". Alle in der nachfolgenden Tabellenzeile stehenden Elemente werden pro Tabellenzeile wiederholt und mit den Werten des Tabellenzeilen-Objekts gefüllt.

Ab Zeile 96 die einzelnen Spalten der Tabelle. Wichtig ist hier, dass die Breite genau mit der Breite der jeweiligen Spaltenüberschrift identisch ist. Für Hoch- und Querformat können Sie abweichende Breiten wählen, oder Spalten ganz unterdrücken. Das ist in der Anleitung "Responsive Webdesign"  beschrieben.

Zeile 96 <input type="text" class="inputcell valuehelp" name="matnr"
                style="float: left; width: 100px;">

Die Spalte "matnr" ist eingabebereit und gestattet die Auswahl über eine Suchhilfe, siehe die Anleitung "Suchhilfe".

Zeile 99  <div class='outputcelldiv landscape'
                style="width: 180px; font-size: 13px; font-weight: normal;"
                name="matnr@text">
            </div>

Der Kurztext zur eingegebenen Materialnummer wird durch name="matnr@text" in dieser Spalte angezeigt. Im Hochformat ist die Spalte wegen der CSS-Klasse "landscape" unterdrückt.

 

Zeile 107 <select class="inputcellselect" name="ecode"
                style="float: left; width: 170px;">
                <optgroup label="Wasserschaden">
                    <option value="W01">Wasserschaden</option>
                    <option value="W02">Wasserspuren</option>
                </optgroup>
                <optgroup label="Verpackung">
                    <option value="V01">Verpackung beschädigt</option>
                    <option value="V02">Verpackung verschmutzt</option>
                    <option value="V03">Verpackung fehlt</option>
                    <option value="V04">Verpackung leer</option>
                </optgroup>
                <optgroup label="Sonstiges">
                    <option value="S01">Sonstiger Schaden</option>
                </optgroup>
            </select>

Eingabe über eine fest in der HTML-Seite programmierte 2-stufige Dropdown-Liste. Alternativ können Sie Dropdown-Listen auch in ABAP bereitstellen oder das S10 Repository als Grundlage nehmen, siehe die Anleitung "Dropdown-Listen".

 

Zeile 129  <div class='outputcelldiv landscape'
                   style="width: 60px;" name="meins@text">
               </div>

Zur Mengeneinheit geben wir die sprachabhängige Bezeichnung aus, die durch name="meins@text" durch das S10 Framework zur Verfügung gestellt wird.

Zeile 133  <!-- delete row -->
               <img style="width: 16px; height: 16px; margin-left: 10px;"
                    src="../../../icons/delete_64x64.png"
                   onclick="S10Apply('delrow')" />

Ausgabe eines Icons zum Löschen der Zeile. Über S10Apply( "delrow" ) wird die ABAP-Methode "delrow" aufgerufen.

 

Zeile 142  <button type="button" class="button-small"
                    style='margin: 4px; background-color: #937a7a; color: white;'
                   onclick="S10Apply('addrow'); ">
                      Neue Zeile
              </button>

Der "Neue Zeile" Button, der im Anschluss an die Erfassungstabelle erscheint. Alternativ können wir in jeder Tabellenzeile ein  Icon "Neue Zeile einfügen"  und dann vor der betreffenden Zeile eine neue Zeile einfügen. Da es bei dieser Anwendung nicht auf die Reihenfolge der erfassten Materialien ankommt, haben wir uns auf die platzsparende Variante, Zeilen immer am Ende anzufügen, beschränkt.