Wir erweitern das Beispiel aus Tutorial 11 (Serviceaufträge) um die Rückmeldungen. Dazu verwenden wir eine Reihe typischer S10 Techniken:
Die fertige Anwendung können Sie über den Link
https://www.mycismobile.com/sap/bc/bsp/s10/pmconfirmation/default.htm
abrufen. Für das Ausprobieren empfehlen wir den Auftrag mit der Nummer 4006542 "Wartung 100.000 km", da dieser viele Vorgänge enthält.
Alle Dateien und den Sourcecode der ABAP-Programme können Sie hier herunterladen: pmconfirmation.zip
Das Tutorial enthält keine grundlegend neuen Techniken, zeigt jedoch beispielhaft, wie eine Anwendung (in diesem Fall die Serviceliste aus Tutorial 11) schrittweise um weitere Elemente erweitert werden kann.
Unser Ausgangspunkt ist die Grundliste "Serviceaufträge" aus Tutorial 11. Als erstes definieren wir die Kurzanzeige der Vorgänge als scrollbaren Bereich:
Das geht in HTML über die Stil-Angaben "max-height:200px;overflow:auto". Am besten dabei nur die Tabelle ohne die Spaltenüberschriften scrollbar machen, damit die Spaltenüberschriften immer sichtbar bleiben:
<div class="infoblock" style="height: auto; width: auto" data-s10title="Vorgangstabelle" name="tabafvc"> <!-- column headers --> <div class="colheaders" style="background-color:transparent;"> <div class='colhead' style="width: 60px;">Vorgang</div> <div class='colhead' style="width: 400px;">Kurztext</div> </div> <!-- list rows --> <form class='table' name='myviaufk.tabafvc' style="max-height: 200px; overflow: auto; background-color: #2196f31a;"> <div class="tablerow"> <div class='outputcelldiv ' style="width: 60px;" name="vornr"></div> <div class='outputcelldiv ' style="width: 400px;" name="ltxa1"></div> </div> </form> </div>
Beide Informationen sind nicht unmittelbar über SAP-Schnittstellen zugreifbar, sodass wir eine eigene Programmlogik benötigen, in der wir direkt die SAP-Tabellen lesen.
Als Technik benutzen wir die "build-Methoden" wie in Tutorial 2 "Abgeleitete Werte" beschrieben. Unsere Vorgangstabelle "tabafvc" aus Tutorial 11 besteht aus Objekten der Klasse "afvc_short":
data: tabafvc type table of ref to afvc_short.
class afvc_short definition inheriting from /s10/any. public section. .... data: afrudate type d, " Letzte Rückmeldung status type string. "Vorgangsstatus
methods: build_afrudate importing rueck type afvc-rueck "Rückmeldenummer exporting afrudate type d, build_status importing rueck type afvc-rueck "Rückmeldenummer exporting status type string.
In der Regel sollten bei "importing" genau die Attribute des Objekts angegeben werden, von denen das oder die zu berechnenden Attribute abhängen. Falls die Berechnung der Attribute allerdings andere Objekte oder Datenbanktabellen erfordert, ist das nicht möglich. Es ist sinnvoll, dann zumindest passende Schlüsselfelder anzugeben, sodass bei Einlesen eines anderen Datenbankobjekts eine Neuberechnung der Werte erfolgt. Nach Update-Operationen, zum Beispiel Hinzufügen oder Stornieren einer Rückmeldung, muss man die Neuberechnung solcher Attribute selbst durch s10rebuild() anstossen oder das gesame Objekt ganz neu anlegen. Dieser Punkt wird weiter unten noch ausgeführt.
Das Datum der letzten Rückmeldung "afrudate" wird aus der Datenbanktabelle "afru" ermittelt. Zu beachten ist dabei, dass wir stornierte Rückmeldungen sowie die Storno-Rückmeldungen nicht berücksichtigen dürfen. Technisch sind das die Bedingungen stokz = "" sowie "stzhl = "". Es wäre schöner, wenn SAP eine Schnittstelle (BAPI z.B.) anbieten würde, welche das letzte Rückmeldedatum liefert; das scheint aber nicht der Fall zu sein. Alternativ zu dem hier verwendeten direkten Lesen der Datenbanktabelle "afru" über eine "select"-Anweisung können wir die Rückmeldungen auch über einen Aufruf von "BAPI_ALM_CONF_GETLIST" einlesen, was das Coding allerdings komplizierter und etwas langsamer machten würde.
method build_afrudate. clear afrudate. select single max( isdd ) from afru into afrudate where rueck = rueck and stokz = '' and stzhl = ''. endmethod.
Zur Ermittlung des Status lesen wir ebenfalls die Rückmeldungen zum Vorgang und prüfen, ob mindestens eine Rückmeldung eine "Endrückmeldung" ist:
method build_status. * Status zurücksetzen clear status. data: aueru type afru-aueru. "X = Endrückmeldung * Rückmeldungen lesen select aueru from afru into aueru where rueck = rueck and stokz = '' and stzhl = ''. * mindestens eine Endrückmeldung ? if aueru = 'X'. status = 'Endrückgemeldet'. return. endif. endselect. * mindestens eine Rückmeldung ? if sy-subrc = 0. status = 'Teilrückgemeldet'. return. endif. * keine Rückmeldung status = 'Offen'. endmethod.
Um vor dem Statustext noch das entsprechende Icon auszugeben, definieren wir in der HTML-Datei drei CSS-KLassen:
<style> .itemcomplete { padding-left: 18px; background-image: url(../../../icons/itemcomplete.png); background-size: 16px; background-repeat: no-repeat; } .iteminwork { padding-left: 18px; background-image: url(../../../icons/iteminwork.png); background-size: 16px; background-repeat: no-repeat; } .itemopen { padding-left: 18px; background-image: url(../../../icons/itemopen.png); background-size: 16px; background-repeat: no-repeat; } </style>
method build_status. * Status zurücksetzen clear status. s10removecss( attrname = 'status' ). data: aueru type afru-aueru. "X = Endrückmeldung * Rückmeldungen lesen select aueru from afru into aueru where rueck = rueck and stokz = '' and stzhl = ''. * mindestens eine Endrückmeldung ? if aueru = 'X'. status = 'Endrückgemeldet'. s10addcss( attrname = 'status' cssclassname = 'itemcomplete' ). return. endif. endselect. * mindestens eine Rückmeldung ? if sy-subrc = 0. status = 'Teilrückgemeldet'. s10addcss( attrname = 'status' cssclassname = 'iteminwork' ). return. endif. * keine Rückmeldung status = 'Offen'. s10addcss( attrname = 'status' cssclassname = 'itemopen' ). endmethod.
Nun erscheint das entsprechende Icon als Hintergrundbild:
Bei Klick auf einen der Vorgänge zeigen wir die Rückmeldungen des Vorgangs an. Hier am Beispiel Vorgang 0160, "Klimaanlage prüfen":
Auch hier gibt es einige Besonderheiten, die wir wieder über "abgeleitete Werte" implementieren konnen:
Zunächst zur Tabelle der Rückmeldungen. Wir generieren hierzu mit den S10 Utilities eine S10 Klasse "afru_db" mit den benötigten Feldern:
class afru_db definition inheriting from /s10/any. public section. * table fields for detail view, plus key fields data: rueck type afru-rueck, " Rückmeldungsnummer rmzhl type afru-rmzhl, " Zähler ltxa1 type afru-ltxa1, " Rückmeldetext arbid type afru-arbid, " ObjektId isdd type afru-isdd, " Start Durchführen isdz type afru-isdz, " Iststart (Uhrzeit) iedd type afru-iedd, " Ende Durchführen iedz type afru-iedz, " Istende (Zeit) ismnw type afru-ismnw, " Istarbeit ismne type afru-ismnu, " Einheit Arbeit aueru type afru-aueru, " X=Endrückmeldung werks type afru-werks. " Werk
data: tabafru type table of ref to afru_db.
methods: build_afrutab importing rueck type afvc-rueck " Rückmeldungsnummer exporting tabafru type table.
class afvc_detail implementation. method build_afrutab. s10databaseselect( exporting condition = |rueck = @rueck and stokz = '' and stzhl = ''| changing folder = tabafru ). endmethod.
WIr ergänzen jetzt unsere drei Felder
Dazu beachten, dass diese Felder in die Klasse "afru_db" gehören, da sie zur Rückmeldung gehören.
data: isstart type string, arbpllong type string, htmlstatus type string.
methods: build_isstart importing isdd type afru-isdd " Start Durchführen isdz type afru-isdz " Iststart (Uhrzeit) exporting isstart type string, build_arbpllong importing arbid type afru-arbid " ObjektId werks type afru-werks " Werk exporting arbpllong type string, build_htmlstatus importing aueru type afru-aueru exporting htmlstatus type string.
method build_isstart. isstart = s10getuservalue( 'isdd' ) && | | && isdz(2) && |:| && isdz+2(2). endmethod.
Etwas komplizierter wird es bei dem Arbeitsplatz. Hier speichert SAP in der Tabelle "afru" nur eine technische Arbeitsplatz-Id "arbid", aus der wir über den SAP-Helpview "m_cramn" den Arbeitsplatz mit Namen und Text ermitteln:
method build_arbpllong. data: arbidarbpl type string, arbidktext type string. select single arbpl ktext into (arbidarbpl,arbidktext) from m_cramn where spras = sy-langu and werks = werks and objid = arbid. arbpllong = arbidarbpl && | | && arbidktext. endmethod.
Die Ausgabe des Status-Icons zur Rückmeldung (Teilrückmeldung oder Endrückmeldung) könnten wir durch eine CSS-Klasse wie bei der Statusspalte der Vorgänge implementieren. Um eine andere Möglichkeit zu zeigen, verwenden wir aber einen HTML-String mit einer HTML-Image-Ausgabe über das <img>-Tag:
method build_htmlstatus. if aueru is initial. htmlstatus = |<img src="../../../icons/iteminwork.png" style="height:16px;" />|. else. htmlstatus = |<img src="../../../icons/itemcomplete.png" style="height:16px;" />|. endif. endmethod.
<div class='outputcelldiv '
style="width: 60px; text-align: center"
name="htmlstatus">
</div>
Wir müssen dem S10 Framework noch mitteilen, dass die Spalte "htmlstatus" als HTML-Code zu interpretieren ist, was über die CSS-Klasse "outputcellhtmldiv" geschieht:
<div class='outputcellhtmldiv ' style="width: 60px; text-align: center" name="htmlstatus"> </div>
Von den beiden Techniken (CSS in der HTML-Seite oder Generierung von HTML-Code in ABAP) entspricht der CSS-Ansatz besser der Trennung von Layout (HTML) und Anwendungslogik (ABAP). Es gibt aber einige Fälle, die mit CSS nicht implementiert werden können, zum Beispiel eine dynamische Quickinfo (Title-Attribut in HTML) oder ein dynamisch gesetzter Link. In diesen Fällen bietet die HTML-Generierung alle Möglichkeiten von HTML.
Nun fehlt uns noch die Implementierung der Aktionen "Stornieren" und "Rückmeldung erfassen". In HTML sind das jeweils Drucktasten, bei "Stornieren" rechts in jeder Rückmeldungszeile und bei "Rückmeldung erfassen" nach der Rückmeldungstabelle.
Wir beginnen mit "Rückmeldung erfassen". Zunächst in HTML die Definition der Drucktaste:
<div class="infoblock" style="padding: 4px;" data-s10title="Rückmeldung erfassen" name="create_confirmation"> <button type="button" class="button" onclick="S10Apply('create_confirmation');"> Rückmeldung erfassen </button> </div>
Durch die Drucktaste wird die Methode "create_confirmation" aufgerufen, und zwar in der Klasse "afvc_manager", zu der die HTML-Seite gehört. Sie zeigt eine Erfassungsmaske für die Rückmeldung an:
Die HTML-Seite dazu haben wir in der Rückmeldeklasse "afru_db" angelegt; der Dateipfad in unserem Projekt ist "classes/afru_db/views.de/afru_db.create.html" . Sie können die S10 Generierung /s10/util nutzen, um sich eine erste Version der Erfassungsmaske aus der Tabelle "afru" generieren zu lassen.
WIr besprechen zunächst die Eingabefelder der Erfassungsmaske. Das Datumsfeld "isdd":
<label class="label output" for="isdd" name="isdd"></label> <br> <input type="date" max="9999-12-31" class="input" required id="isdd" name="isdd" style="width: 140px;">
Der Parameter max="9999-12-31" wird von den S10 Utilities generiert, um eine irrtümlich eingegebene 5-stellige Jahreszahl zu verhindern. Für Rückmeldungen kann es zusätzlich sinnvoll sein, Datumsangaben zu verhindern, die in der Zukunft liegen. Das erreichen Sie in HTML mit etwas JavaScript, in dem die "max" Option des <input> Tags dynamisch auf das Tagesdatum gesetzt wird:
<input type="date" class="input" required id="isdd" name="isdd" style="width: 140px;"> <script> var today = new Date().toISOString().substring(0, 10); document.getElementById("isdd").max = today; </script>
Die Browser bieten dann keine Datumseingaben aus der Zukunft an:
Entsprechend bei direkter Datumseingabe:
Prüfungen dieser Art direkt in der HTML-Seite sind für den Benutzer hilfreich, wir können uns aber leider nicht darauf verlassen, da im Debugging-Modus des Browsers eine Änderung der max= Angabe möglich ist:
Wenn eine Prüfung für die Korrektheit der Anwendung wichtig ist, müssen Sie sie in Ihrem ABAP-Programm daher zusätzlich durchführen. In den meisten Fällen führen die SAP-Schnittstellen (BAPI-Aufruf oder Call Transaction) alle Prüfungen durch, wobei in diesem Fall das SAP-System Rückmeldungen mit Zukunftsdatum toleriert, aber zum Beispiel die Prüfung "Anfangsdatum vor Endedatum" durchführt.
WIe in Tutorial 4 beschrieben, können wir die Prüfung wie folgt mit einer "validate"-Methode durchführen:
methods: validate_isdd importing isdd type afru-isdd. .... method validate_isdd. if isdd > sy-datum. s10errormessage( |Arbeitsbeginn liegt in Zukunft, bitte korrigieren| ). endif. endmethod.
Für die Zeiteingabe verwenden wir in HTML den Typ "time":
<input type="time" class="input" required id="isdz" name="isdz" style="width: 100px;">
Falls die Eingabe mit Sekunden erfolgen soll (bei Rückmeldungen wohl eher nicht), können Sie <input ... step=1> verwenden. Die step-Angabe bezieht sich immer auf Sekunden; der Defaultwert für "step" ist 60, also eine Minute. Bei step=600 z.B. prüft der Browser, dass die Zeitangabe in 10-Minuten-Schritten erfolgt:
Auch hier beachten, dass zusätzlich eine Prüfung in ABAP erforderlich ist, falls Sie sicherstellen wollen, dass auch über Browser-Debugging keine anderen Zeiten eingegeben werden.
Die Eingabe des Arbeitsplatzes erfolgt über eine dropdown-Liste:
Da es keine direkt verwendbare Standard-Dropdownliste zu dem Datenelement "arbpl" gibt, verwenden wir hier das in der Dokumentation von s10dropdownlist() beschriebene Verfahren, die Methode s10dropdownlist() des S10 Framework zu überschreiben. HTML-Code:
<label class="label" for="arbpl">Arbeitsplatz</label> <br> <select class='inputselect' size="1" name="arbpl" data-s10dropdownlist="arbpl@dropdownlist" data-s10options="noEmptyEntry" id="arbpl" style='width: 240px;'> </select>
Im ABAP-Programm, Klasse "afru_db", implementieren wir s10dropdownlist() mit einer eigenen Methode, die für "arbpl" den View "m_cramn" liest und für andere Felder die Standardmethode des S10 Frameworks aufruft:
methods: s10dropdownlist redefinition. ... method s10dropdownlist. case attrname. when 'ARBPL'. data: arbidarbpl type string, arbidktext type string. select distinct arbpl ktext into (arbidarbpl,arbidktext) from m_cramn where werks = werks and spras = sy-langu order by arbpl. ddlstring = ddlstring && arbidarbpl && cl_abap_char_utilities=>horizontal_tab && arbidktext && cl_abap_char_utilities=>cr_lf. endselect. return. endcase. * default ddlstring = super->s10dropdownlist( attrname = attrname valuelist = valuelist condition = condition ). endmethod.
Nun zum Aufruf der Erfassungsmaske. Die Drucktaste "Rückmeldung erfassen" ruft die Methode "create_confirmation" der Klasse "afvc_manager" auf. Da wir eine Liste von Vorgängen haben, müssen wir zunächst in der Vorgangstabelle auf den richtigen Vorgang positionieren. Dazu nutzen wir die Methode s10contextinfo() des S10 Frameworks, die uns nach Benutzeraktionen darüber informiert, auf welche HTML-Elemente sich die Aktion bezieht. Insbesondere enthält s10contextinfo()->rownumber die Nummer der Tabellenzeile, in der sich die Drucktaste befindet. Damit lesen wir die Vorgangstabelle "tabafvc":
method create_confirmation. * read current table row. data: tabindex type i. tabindex = s10contextinfo( )->rownumber. read table tabafvc index tabindex assigning field-symbol(<row>).
Anschliessend aus der Datenbank alle aktuellen Werte zum Vorgang. Das ist nötig, da in der Vorgangstabelle "tabafvc" zwar immer die Schlüsselspalten der Tabelle gesetzt sind, aber die übrigen Felder je nach Tabellenlayout nicht eingelesen wurden. Beispielsweise ist das Werk "werks" nicht in der Anzeige der Vorgänge enthalten (Tabellenlayout) und wird daher auch nicht gelesen. Durch den expliziten Aufruf von s10databaseread(() sind wir sicher, dass alle Werte gefüllt sind. Für die Rückmeldung benötigen wir die technische Rückmeldenummer "afvc-rueck" sowie das Werk "afvc-werks". Wir legen ein Objekt der Rückmeldeklasse "afru_db" an, setzen "rueck" und "werks" sowie einige Default-Werte, und rufen den Dialog "create" des Objekts auf:
method create_confirmation. * read current table row. data: tabindex type i. tabindex = s10contextinfo( )->rownumber. read table tabafvc index tabindex assigning field-symbol(<row>). * read table row data <row>->s10databaseread( ). data: myafru type ref to afru_db. create object myafru. * set some defaults myafru->rueck = <row>->rueck. myafru->isdd = sy-datum. myafru->isdz(2) = sy-uzeit(2) - 2. myafru->isdz+2(4) = '0000'. myafru->iedd = sy-datum. myafru->iedz(2) = sy-uzeit(2) - 1. myafru->iedz+2(4) = '0000'. myafru->ismnw = '1'. myafru->ismne = 'STD'. myafru->werks = <row>->werks. myafru->arbpl = 'ST006'. "demo value, user parameter in real applications data: rc type string. rc = myafru->s10dialog( 'create' ).
...
Im Rückmeldedialog kann der Benutzer die eingegebenen Daten über die "Sichern" Drucktaste abspeichern:
<button type="button" class="toolbarbutton" onclick="S10Apply('save');"> Sichern </button>
Die Drucktaste ruft die Methode "save" des aktuellen Objekts "myafru", in der wir die Rückmeldung über SAP-Schnittstellen abspeichern. Wir nutzen einen "Call Transaction" auf Transaktion IW41, wie in Tutorial 3 beschrieben:
method save. * bdc data data: bdcdatawa type bdcdata, bdcdata type table of bdcdata. * message table for call transaction data: messtabwa type bdcmsgcoll, messtab type table of bdcmsgcoll. * create bdc data * screen 3000 clear bdcdatawa. bdcdatawa-program = 'SAPLCORU'. bdcdatawa-dynpro = '3000'. bdcdatawa-dynbegin = 'X'. append bdcdatawa to bdcdata. clear bdcdatawa. bdcdatawa-fnam = 'CORUF-RUECK'. bdcdatawa-fval = s10getuservalue( 'rueck' ). append bdcdatawa to bdcdata. clear bdcdatawa. bdcdatawa-fnam = 'BDC_OKCODE'. bdcdatawa-fval = '=ENTR'. append bdcdatawa to bdcdata. * screen 3200 clear bdcdatawa. bdcdatawa-program = 'SAPLCORU'. bdcdatawa-dynpro = '3200'. bdcdatawa-dynbegin = 'X'. append bdcdatawa to bdcdata. clear bdcdatawa. bdcdatawa-fnam = 'AFRUD-ISDD'. bdcdatawa-fval = s10getuservalue( 'isdd' ). append bdcdatawa to bdcdata. bdcdatawa-fnam = 'AFRUD-ISDZ'. bdcdatawa-fval = s10getuservalue( 'isdz' ). append bdcdatawa to bdcdata. bdcdatawa-fnam = 'AFRUD-IEDD'. bdcdatawa-fval = s10getuservalue( 'iedd' ). append bdcdatawa to bdcdata. bdcdatawa-fnam = 'AFRUD-IEDZ'. bdcdatawa-fval = s10getuservalue( 'iedz' ). append bdcdatawa to bdcdata. bdcdatawa-fnam = 'AFRUD-LTXA1'. bdcdatawa-fval = s10getuservalue( 'ltxa1' ). append bdcdatawa to bdcdata. bdcdatawa-fnam = 'AFRUD-ISMNW_2'. bdcdatawa-fval = s10getuservalue( 'ismnw' ). append bdcdatawa to bdcdata. bdcdatawa-fnam = 'AFRUD-ISMNU'. bdcdatawa-fval = s10getuservalue( 'ismne' ). append bdcdatawa to bdcdata. bdcdatawa-fnam = 'AFRUD-ARBPL'. bdcdatawa-fval = arbpl. append bdcdatawa to bdcdata. if aueru ne '1'. bdcdatawa-fnam = 'AFRUD-AUERU'. bdcdatawa-fval = ''. . append bdcdatawa to bdcdata. bdcdatawa-fnam = 'AFRUD-LEKNW'. bdcdatawa-fval = ''. . append bdcdatawa to bdcdata. else. bdcdatawa-fnam = 'AFRUD-AUERU'. bdcdatawa-fval = 'X'. . append bdcdatawa to bdcdata. bdcdatawa-fnam = 'AFRUD-LEKNW'. bdcdatawa-fval = 'X'. . append bdcdatawa to bdcdata. endif. clear bdcdatawa. bdcdatawa-fnam = 'BDC_OKCODE'. bdcdatawa-fval = '=BU'. append bdcdatawa to bdcdata. call transaction 'IW41' with authority-check using bdcdata mode 'N' update 'S' messages into messtab. data: messagetext type string. loop at messtab into messtabwa. case messtabwa-msgtyp. when 'E' or 'A'. * get message text message id messtabwa-msgid type messtabwa-msgtyp number messtabwa-msgnr with messtabwa-msgv1 messtabwa-msgv2 messtabwa-msgv3 messtabwa-msgv4 into messagetext.
s10errormessage( messagetext ). when 'W'. when others. s10exitdialog( 'X' ). endcase. endloop. endmethod.
Wenn wir in "create_confirmation" nun nichts weiter tun, ist zwar die Rückmeldung gespeichert, aber die Liste zeigt noch den alten Stand, d.h. die neue Rückmeldung fehlt in der Tabelle der Rückmeldungen und auch eine eventuelle Statusänderung des Vorgangs, zum Beispiel bei einer Endrückmeldung, sowie das Datum der letzten Rückmeldung werden nicht korrekt angezeigt:
Hier soll bei Rückkehr der Vorgangsstatus sofort auf "Endrückgemeldet" stehen und die neue Rückmeldung in die Tabelle der Rückmeldungen eingefügt sein:
Im S10 Framework ist die Technik dazu vorgesehen und in dem Artikel Update bei Tabellen beschrieben. In unserem Fall sieht das Coding dazu wie folgt aus, hier mit der gesamten Methode "create_confirmation":
method create_confirmation. * read current table row. data: tabindex type i. tabindex = s10contextinfo( )->rownumber. read table tabafvc index tabindex assigning field-symbol(<row>). * read table row data <row>->s10databaseread( ). data: myafru type ref to afru_db. create object myafru. * set some defaults myafru->rueck = <row>->rueck. myafru->isdd = sy-datum. myafru->isdz(2) = sy-uzeit(2) - 2. myafru->isdz+2(4) = '0000'. myafru->iedd = sy-datum. myafru->iedz(2) = sy-uzeit(2) - 1. myafru->iedz+2(4) = '0000'. myafru->ismnw = '1'. myafru->ismne = 'STD'. myafru->werks = <row>->werks. myafru->arbpl = 'ST006'. "demo value, user parameter in real applications data: rc type string. rc = myafru->s10dialog( 'create' ). * no save ? if rc ne 'X'. return. endif. * refresh item detail data create object myafvc. myafvc->aufpl = <row>->aufpl. myafvc->aplzl = <row>->aplzl. myafvc->s10databaseread( ). * read table row data and invalidate build fields <row>->s10databaseread( ). <row>->s10rebuild( ). * force transport of current detail area <row>->s10detailview = 'X'. endmethod.
Wir fragen zunächst durch "if rc='X' " ab, ob der Benutzer die Rückmeldung gesichert hat, denn andernfalls müssen wir in der Liste nichts ändern. Anschliessend wird das Objekt "myafvc", das in HTML zur Anzeige der Vorgangsdetails genutzt wird, neu angelegt, die Schlüsselwerte gesetzt und die Werte aus der Datenbank gelesen. Da es ein neues Objekt ist, läuft die build-Methode zur Ermittlung der Rückmeldungen erneut ab und zeigt die frisch erfasste Rückmeldung an.
Für die Statusänderung in der Vorgangszeile, also "Endrückgemeldet" in unserem Beispiel, lesen wir ebenfalls die Vorgangsdaten der Tabellenzeile (Klasse afvc_short) neu aus der Datenbank. Zusätzlich werden durch s10rebuild() alle bisher ermittelten build-Felder invalidiert, sodass der Status neu ermittelt wird. Das ist hier nötig, da der Status nicht direkt aus Felder der Tabelle "afvc" ermittelt werden kann, sondern sich aus den Rückmeldungen in der Datenbanktabelle "afru" bestimmt.
Als Alternative zu s10rebuild() können wir das Vorgangsobjekt in der Vorgangstabelle mit "create object <row>" neu anlegen:
* refresh item detail data create object myafvc. myafvc->aufpl = <row>->aufpl. myafvc->aplzl = <row>->aplzl. myafvc->s10databaseread( ). * refresh table row data create object <row>. <row>->aufpl = myafvc->aufpl. <row>->aplzl = myafvc->aplzl. <row>->s10databaseread( ). * force transport of current detail area <row>->s10detailview = 'X'.
Rückmeldung stornieren
Die Funktion "Stornieren" implementieren wir ähnlich, aber hier gibt es eine kleine Zusatzschwierigkeit: Die Drucktaste "Stornieren" befindet sich in jeder Zeile der Rückmeldungstabelle, und wir erhalten mit s10contextinfo()->rownumber die Zeilennummer in der Rückmeldungstabelle, aber nicht die in der übergeordneten Vorgangstabelle. Das Problem entsteht also durch die geschachtelte Tabellenanzeige, d.h. die Tabelle der Rückmeldungen pro Vorgang wird innerhalb der Tabelle der Vorgänge angezeigt, und über s10contextinfo()->rownumber kann nur die Zeilennumber der aktuellen Tabelle (Rückmeldungen) ermittelt werden.
Wir verwenden daher statt "s10contextinfo()->rownumber" eine andere Funktion von s10contextinfo(), nämlich die Rückgabe von Feldinhalten, die in der HTML-Seite enthalten sind und die CSS-Klasse "linkkey" haben. Der Mechanismus ist dabei wie folgt:
Konkret sieht das in unserem Fall so aus: In der HTML-Seite nehmen wir im Detailbereich des aufgeklappten Vorgangs gleich am Anfang die Schlüsselfelder "aufpl" und "aplzl" des Vorgangs auf:
<div id="tabafvc_detail" class='tabledetail'> <!-- hidden keys --> <span class='output linkkey' name='myafvc.aufpl'></span> <span class='output linkkey' name='myafvc.aplzl'></span> ...
Zusätzlich zu Beginn der Rückmeldungszeile die Schlüsselfelder "rueck" und "rmzhl" der Rückmeldung:
<form class='table' name='myafvc.tabafru'> <div class="tablerow"> <!-- hidden keys --> <span class="outputcell linkkey" name="rueck"></span> <span class="outputcell linkkey" name="rmzhl"></span> ...
HTML-Definition der Drucktaste "Stornieren":
<button type="button" class="button" onclick="S10Apply('cancel_afru');" style="height: 16px; border-color: #a7a6a6; color: red; font-size: 9pt;" title="Rückmeldung stornieren"> Stornieren </button>
ABAP-Methode "cancel_afru":
method cancel_afru. * hidden key fields from HTML page data: aufpl type afvc-aufpl, aplzl type afvc-aplzl, rueck type afru-rueck, rmzhl type afru-rmzhl. s10fromcontextinfo( exporting key = 'aufpl' changing result = aufpl ). s10fromcontextinfo( exporting key = 'aplzl' changing result = aplzl ). s10fromcontextinfo( exporting key = 'rueck' changing result = rueck ). s10fromcontextinfo( exporting key = 'rmzhl' changing result = rmzhl ). * read current table row. read table tabafvc with key table_line->aufpl = aufpl table_line->aplzl = aplzl assigning field-symbol(<row>). * user warning if s10confirmation( |Möchten Sie die Rückmeldung | && rmzhl && | wirklich stornieren?| ) ne 'X'. return. endif. * cancel confirmation via BAPI data: bapiret2 type bapiret2. call function 'BAPI_ALM_CONF_CANCEL' exporting confirmation = rueck confirmationcounter = rmzhl importing return = bapiret2. * any error? if bapiret2-type eq 'E' or bapiret2-type eq 'A'. call function 'BAPI_TRANSACTION_ROLLBACK'. s10errormessage( conv string( bapiret2-message ) ). return. endif. * commit with wait call function 'BAPI_TRANSACTION_COMMIT' exporting wait = 'X'. * read table row data again and invalidate build fields <row>->s10databaseread( ). <row>->s10rebuild( ). * force transport of detail area <row>->s10detailview = 'X'. * set key fields create object myafvc. myafvc->aufpl = aufpl. myafvc->aplzl = aplzl. * read detail data again myafvc->s10databaseread( ). endmethod.
Die beiden letzten Schritte stellen sicher, dass der aktuelle Zustand unmittelbar nach dem Stornieren einer Rückmeldung sichtbar ist:
Wir drücken "Stornieren" bei der zweiten Rückmeldung und erhalten folgende Anzeige, in der die Rückmeldung gelöscht und der Vorgangsstatus auf "Teilrückgemeldet" geändert ist:
Die Anwendung ist nun abgeschlossen. Für den praktischen Einsatz könnte es sinnvoll sein, die Erfassung von Materialbewegungen in der Rückmeldung zu integrieren. Dies kann mithilfe der im Tutorial vorgestellten Techniken auf eine modulare und übersichtliche Weise umgesetzt werden.
Das S10 Framework, das konsequent auf Objektorientierung setzt und mit ABAP OO arbeitet, erfordert zunächst etwas Denkarbeit, um das Zusammenspiel der HTML-Seiten im Browser mit den ABAP-Objekten auf dem Server zu verstehen. Es bietet jedoch den richtigen Rahmen für die modulare Entwicklung komplexer Anwendungen, die schnell laufen und einen hohen Benutzerkomfort bieten.