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.
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.
* 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:
In der where-Bedingung der loop-Anweisung können wir in ABAP durch "table_line" auf das Zeilenobjekt zugreifen:
loop at tabmdr assigning field-symbol(<mdr>) where table_line->matnr is not initial.
Der Aufruf von s10setfocus erfolgt über das Zeilenobjekt:
<mdr>->s10setfocus( 'speme' ).
Wenn wir stattdessen nur s10setfocus( 'speme' ) aufrufen, versucht das S10 Framework, ein Eingabefeld "speme" des aktuellen Objekts der Klasse "mdr_manager" zu finden, was es aber nicht gibt. Die Fehlermeldung erscheint dann bei dem auslösenden "Prüfen"-Button, was dem Benutzer die Zuordnung zu der fehlenden Mengeneingabe erschwert:
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> |
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.