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>

Die detaillierte Anzeige der Vorgänge erscheint bei Klick auf den Button "Vorgänge anzeigen", was bereits in Tutorial 11 als separater Screen implementiert ist. Wir ergänzen die Liste um folgende Informationen:  

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.

Die zwei Zusatzfelder kommen daher in die Klasse "afvc_short", sodass die Informationen  pro Listzeile angezeigt werden können:
class afvc_short definition inheriting from /s10/any.

  public section.
  
  ....      
      
      data: 
            afrudate      type d,       " Letzte Rückmeldung
            status        type string.   "Vorgangsstatus


Zu "afrudate" und "status" nun jeweils eine build-Methode zur Berechnung des Wertes. Als importing-Parameter verwenden wir die technische Rückmeldenummer "afvc-rueck", die den Vorgang eindeutig identifiziert:
    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.

Damit wird der entsprechende Text in der Tabelle ausgegeben:

 

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>

Technisch arbeiten wir hier mit einem Hintergrundbild und verschieben den Statustext um 18 Pixel nach rechts, sodass links das Icon als Hintergrundbild erscheint. In unserer ABAP-build-Methode ordnen wir nun durch "s10addcss" die richtige Klasse zu:
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

In der Klasse "afvc_detail" der Vorgangsdetails nehmen wir eine Tabelle "tabafru" der Rückmeldungen zum Vorgang auf:
data: tabafru type table of ref to afru_db.

Zum Füllen der Rückmeldungstabelle eine build-Methode:
    methods:
      build_afrutab
        importing
          rueck   type afvc-rueck " Rückmeldungsnummer
        exporting
          tabafru type table.

Die zugehörige Implementierung:
class afvc_detail implementation.

  method build_afrutab.
    s10databaseselect(
     exporting
       condition = |rueck = @rueck and stokz = '' and stzhl = ''|
   changing
      folder = tabafru ).

  endmethod.

Damit wird die Tabelle der Rückmeldungen bei Anzeige der Vorgangsdetails aufgebaut, sobald wir sie in HTML definiert haben und sie in der Layout-Defintion der Tabellendetails aktiv ist.

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.

Zu jedem Feld definieren wir eine build-Methode, in der wir die Felder benennen, die wir zur Berechnung des Werts verwenden:
    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.

Das Feld "isstart" (Ist-Start) setzen wir aus Datum und Uhrzeit (ohne Sekunden) zusammen:
 method build_isstart.
    isstart = s10getuservalue( 'isdd' ) && | | && isdz(2) && |:| && isdz+2(2).
endmethod.
Dabei haben wir die Funktion "s10getuservalue" zur Aufbereitung des Datums genutzt, damit das im SAP-Benutzerstammsatz eingestellte Datumsformat hergestellt wird. Die Zeit wird immer im Format HH:MM ausgegeben.

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.

Den Arbeitsplatz, also im Beispiel "ST006",  könnten wir auch über den Funktionsbaustein "BAPI_ALM_CONF_GETDETAIL" erhalten.  Er liefert aber leider nicht den Text dazu, z.B. "Ted Schmitt", und der Text ist wiederum nur über die technische Arbeitsplatz-Id aus Tabelle CRTX zu erhalten, aber die technische Arbeitsplatz-Id liefert der Funktionsbaustein nicht zurück. Insgesamt sind die existierenden SAP-Schnittstellen nützlich, aber in vielen Fällen nicht ausreichend. Und sobald ein Feld in der Ausgabe des BAPI fehlt, ist dann doch ein direktes Lesen von SAP-Tabellen nötig.

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.

Wenn wir die Statusspalte in der Tabelle nun wie gewohnt mit der Klasse "outputcelldiv" ausgeben:
<div class='outputcelldiv ' 
      style="width: 60px; text-align: center" 
      name="htmlstatus">
</div>

dann erfolgt die Ausgabe des HTML-Strings und nicht wie gewünscht des Icons:

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>

Damit wird das img-Tag nun als Bildausgabe interpretiert und unser Icon erscheint:

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>

Indem wir die Drucktaste als "infoblock" anlegen, erscheint sie in der Layoutdefinition und kann damit separat ein- und ausgeblendet werden:

 

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.

Bei fehlerhafter Eingabe wird dann automatisch der Fokus auf das fehlerhafter Eingabefeld gesetzt:

 

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' ).
     ...
Nun erscheint die Eingabemaske für die Rückmeldung und das Objekt "myafru" ist aktiv. Die Methode "create_confirmation" ist noch nicht beendet, sondern wird nach Aufruf des Rückmeldung-dialogs fortgesetzt.

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.
Der Dialog wird durch s10exitdialog( 'X' ) mit dem Rückgabewert 'X' beendet und die Verarbeitung wird in unserer Methode "create_confirmation" nach dem Aufruf von s10dialog( ) fortgesetzt.

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.


Erläuterungen

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'.
Es ist dabei nicht nötig, in der Vorgangstabelle "tabafvc" noch ein Update durchzuführen, da "tabafvc" aus Objektreferenzen besteht, die wir  durch das Feldsymbol <row> direkt ansprechen können.

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>
...
s10contextinfo() stellt uns diese Werte nun unter dem bei name= angegebenen Namen bereit, wobei vorangestellte Objektnamen nicht berücksichtigt werden, also zum Beispiel "aufpl" und nicht "myafvc.aufpl". In der über die Drucktaste aufgerufenen Methode "cancel_afru" können wir uns alle Werte beschaffen und mit den Schlüsselfeldern des Vorgangs den aktuell ausgewählten Vorgang lesen:

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.


Erläuterungen

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.