Die Anzeige der Kundenadresse aus Tutorial 1 ergänzen wir nun um eine weitere Information: wir zeigen den Saldo des Kunden aus der Buchhaltung an, also die Summe der aktuell offenen Posten abzüglich eventueller Anzahlungen.

Als Resultat sieht die Anzeige der Kundendaten dann so aus:

Zur Implementierung nehmen wir als erstes zwei Attribute in unserer ABAP-Klasse "customer" auf, nämlich den Betrag und die Währung des Saldos:

   data:
      open_balance_amount   type dmbtr,
      open_balance_currency type waers.

Als nächstes implementieren wir eine ABAP-Methode, welche den Saldo berechnet. Dazu nutzen wir den Standard-Funktionsbaustein "BAPI_AR_ACC_GETOPENITEMS":

 method build_open_balance.

    clear:
    open_balance_amount,
    open_balance_currency.

    data:
      items type table of bapi3007_2.

    call function 'BAPI_AR_ACC_GETOPENITEMS'
      exporting
        customer    = kunnr
        companycode = '1010'
        keydate     = sy-datum
      tables
        lineitems   = items.

    loop at items assigning field-symbol(<item>)

*     add debit/credit 
      if <item>-db_cr_ind = 'S'.
        open_balance_amount = open_balance_amount + <item>-lc_amount.
      else.
        open_balance_amount = open_balance_amount - <item>-lc_amount.
      endif.

* set company currency
      open_balance_currency = <item>-loc_currcy.

    endloop.

  endmethod.

In der HTML-Datei geben wir den Saldo aus:

 <!-- Saldo -->
<div class="infoblock">
    <label class='label'>Saldo</label>
    <br />
    <span class='output' name='open_balance_amount'></span>
    <span class='output' name='open_balance_currency'></span>
</div>

Bis auf den Aufruf der Methode sind wir nun fertig. An welcher Stelle im ABP Coding sollen wir nun die Methode aufrufen? Naheliegend ist, den Aufruf gleich durchzuführen, wenn der Benutzer den "Anzeigen" Button drückt, also in unserer Methode "display":

* display customer data
  method display.
    if not s10databaseread( ).
      s10errormessage( 'Bitte eine gültige Kundennummer eingeben' ).
    endif.
    
* Buchhaltungs-Saldo berechnen
    build_open_balance( ).

    s10nextscreen( 'display').

  endmethod.

So kann man es machen und es funktioniert auch. Bei der Entwicklungen komplexerer Anwendungen gerät man allerdings in Schwierigkeiten, wenn man derartige aus den Schlüsselwerten abgeleitete Information zum frühestmöglichen Zeitpunkt bereitstellen will:

In der Regel fängt man ja mit wenigen Daten an, hat aber später eine Anwendung, in der z.B. 50 Informationen, darunter Tabellen und Diagramme, auf verschiedenen Screens dargestellt werden, wobei die Reihenfolge vom Benutzer gewählt wird, indem er über ein Menü oder über einzelne Buttons Informationen gezielt ansteuert. Eventuell gibt es auf einigen der Screens noch zusätzliche Parameter, in unserem Beispiel etwa die Angabe eines Zeitraums, sodass nur  bereits länger fällige Posten berücksichtigt werden.

Damit funktioniert der Ansatz, alle Informationen auf einmal am Anfang zu besorgen, nicht mehr. Als Abhilfe können wir die Lese-Strategie verändern und die Daten  erst dann besorgen,  wenn ein bestimmter Screen wie z.B.  "Buchhaltungsdaten" angezeigt wird. Damit wird das Coding allerdings stark von der Struktur der Benutzeroberfläche abhängig, und Änderungen der Benutzeroberfläche erfordern Logikänderungen in der ABAP-Klasse. Zum Beispiel zeigen wir vielleicht anfangs alle Buchhaltungsdaten auf einem separaten Screen an, und nun kommt die Anforderung,  den Saldo bereits auf dem Übersichtsscreen anzuzeigen. Das führt leicht dazu, dass entweder Daten mehrfach gelesen werden, oder es werden Schalter eingeführt mit der Bedeutung "Daten x schon gelesen", um das mehrfache Besorgen der Daten zu vermeiden. Diese Schalter müssen korrekt verwaltet werden, zum Beispiel zurückgesetzt wenn sich  die Kundennummer oder ein Selektionsparameter ändert.

Als Lösung bietet das S10 Framework einen Mechanismus, bei dem automatisch Daten erst dann gelesen werden, wenn sie in einer HTML-Seite benötigt werden, und nur dann neu gelesen, wenn sich der Inhalt der hierfür relevanten Schlüsselfelder verändert hat. Eine detaillierte Beschreibung dazu finden Sie in der Anleitung "build-Methoden"; wir zeigen den Mechanismus hier an unserem Saldo-Beispiel.

Das S10 Framework benötigt die Angabe, von welchen Schlüsselfeldern die zu ermittelnden Informationen abhängen und welche Methode dazu aufgerufen werden muss. Dazu werden bei der Deklaration der Methode  bei "importing" diejenigen Attribute genannt, von denen die Daten abhängen, und bei" exporting" diejenigen, die in der Methode berechnet werden. In unserem Beispiel sieht das so aus:

   methods:
      build_open_balance
        importing
          kunnr                 type kna1-kunnr
        exporting
          open_balance_amount   type dmbtr
          open_balance_currency type waers.


Den Aufruf "build_open_balance()" können wir nun einfach weglassen: das S10 Framework führt die Methode dann durch, wenn eines der exporting-Attribute in einer HTML Seite angezeigt werden soll. Sie können nun problemlos die Ausgabe einzelner Attribute von einem Screen zu einem anderen verschieben und sind sicher, dass die zugehörige build-Methode rechtzeitig und nur einmal aufgerufen wird, solange sich die Schlüsselwerte nicht ändern.

Um das korrekte Neuberechnen nach Änderung der Kundennummer zu überprüfen, fügen wir auf dem "display" Screen einen "Zurück"-Button ein:

Das funktioniert genau wie der "Anzeigen" Button aus Tutorial 1:

<button type="button" class="toolbarbutton" onclick="S10Apply('start');">
    Zurück
</button>

In unserer ABAP-Klasse dann noch die Methode "start", die zum Screen "start" navigiert:

* start again with customer number input
  method start.
    s10nextscreen( 'start').
  endmethod.



Jetzt können wir innerhalb der Anwendung den Kunden wechseln. Die build-Methode "build_open_balance" wird durch das S10 Framework automatisch zur Anzeige des Saldos neu aufgerufen, falls sich die Kundennummer geändert hat:




Man könnte nun pro Attribut eine eigene build-Methode schreiben. Das ist aber nicht sinnvoll, da sehr oft Attribute gemeinsam ohne grossen Zusatzaufwand besorgt werden können, zum Beispiel durch Aufruf eines BAPI oder mit einem Datenbankzugriff. Deshalb fassen Sie in einer "build"-Methode besser diese Attribute zusammen und geben bei "exporting" alle Attribute an, die gemeinsam berechnet werden.

Auch Tabellen können bei einer "build"-Methode unter "exporting" genannt werden. Als Beispiel geben wir in einem separaten Screen eine Tabelle aller offenen Posten des Kunden aus.  Als Resultat wollen wir folgendes erreichen:



 

Zur generellen Technik der Tabellenausgabe finden Sie alle Details in der Anleitung Tabellen. Für unser Beispiel sieht es wie folgt aus:

In dem "display" Screen fügen wir einen weiteren Button "Offene Posten" hinzu:

 <button type="button" class="toolbarbutton" onclick="S10Apply('to_openitems');">
     Offene Posten
</button>

Der Button führt die ABAP-Methode "to_openitems" aus, mit der wir auf den neuen Screen "openitems" verzweigen:
* display open items
  method to_openitems.
    s10nextscreen( 'openitems').
  endmethod.


In unserem ABAP Programm definieren wir eine Klasse "openitem", welche die anzuzeigenden Felder der offenen Buchhaltungsposten enthält, also einen Ausschnitt aus der BAPI-Struktur:

class openitem definition inheriting from /s10/any.

  public section.

    data:
      doc_no     type bapi3007_2-doc_no, " Document Number
      item_num   type bapi3007_2-item_num, " Line item
      pstng_date type bapi3007_2-pstng_date, " Posting Date
      doc_type   type bapi3007_2-doc_type, " Document type
      lc_amount  type dmbtr, " Amount in LC with 2 decimals
      pmnttrms   type bapi3007_2-pmnttrms, " Payment Terms
      loc_currcy type bapi3007_2-loc_currcy. "Currency

endclass.

Sie können sich diese Klasse in Transaktion /s10/util generieren lassen, indem Sie aus der BAPI-Struktur "bapi3007_2" die gewünschten Felder ankreuzen.

In Klasse "customer" legen wir eine Tabelle "openitems" von Objekten der Klasse "openitem" an:
 data:
    openitems  type table of ref to openitem.

Wir füllen die Tabelle in der Methode "build_open_balance", die jetzt wie folgt deklariert ist:

methods:
      build_open_balance
        importing
          kunnr                 type kna1-kunnr
        exporting
          open_balance_amount   type dmbtr
          open_balance_currency type waers
          openitems             type table.

Das heisst, es ist jetzt ein weiterer exporting-Parameter hinzugekommen, die Tabelle "openitems". Hierbei bitte beachten, dass sie nur mit "type table" deklariert wird.

In der Implementierung von "build_open_balance" füllen wir die Tabelle. Da das BAPI die Beträge ohne Vorzeichen zurückliefert, muss man das Soll/Haben-Kennzeichen als Vorzeichen interpretieren:

 method build_open_balance.

    clear:
    open_balance_amount,
    open_balance_currency,
    openitems.

    data:
      items type table of bapi3007_2.

    call function 'BAPI_AR_ACC_GETOPENITEMS'
      exporting
        customer    = kunnr
        companycode = '1010'
        keydate     = sy-datum
      tables
        lineitems   = items.

* sort items
    sort items by  doc_no item_num.

    loop at items assigning field-symbol(<item>).

* correct sign
      if <item>-db_cr_ind = 'H'.
        <item>-lc_amount = - <item>-lc_amount.
      endif.

*     add to total amount
      open_balance_amount = open_balance_amount + <item>-lc_amount.

* append to open items table
      data(oi) = new openitem( ).
      oi->s10copy( <item> ).
      append oi to openitems.

* set company currency
      open_balance_currency = <item>-loc_currcy.

    endloop.

  endmethod.


Damit ist das ABAP-Programm fertig und es fehlt uns nur noch die HTML-Datei zum Screen "openitems". Wer sich Schreibarbeit sparen möchte, kann hierzu wieder Transaktion /s10/util nutzen, sich eine Listanzeige für die BAPI-Struktur "bapi3007_2" generieren lassen und diese Anzeige dann in die HTML-Datei "openitems" übernehmen.

  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
<!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>Kundendaten</title>
</head>
<body style="width: 100%; margin: 0px; padding: 0px;" onload='init();' class="colorscheme9">

    <div class="headerarea" style="width: 100%; padding: 10px;">
        <b>Offene Posten  <span class='output' type="text" name='name1'></span></b>
        <br />
        <br />


        <button type="button" class="toolbarbutton" onclick="S10Apply('to_display');">
            Übersicht
        </button>


        <button type="button" class="toolbarbutton" onclick="S10Logoff();">
            Beenden
        </button>


    </div>


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


        <!-- Belegnummer -->
        <div class='colhead colheadup output ' style="width: 90px;" name="doc_no"></div>

        <!-- Position -->
        <div class='colhead output ' style="width: 36px;" name="item_num"></div>

        <!-- Buchungsdatum -->
        <div class='colhead output ' style="width: 80px;" name="pstng_date"></div>

        <!-- Zahlungsbedingung -->
        <div class='colhead output ' style="width: 48px;" name="pmnttrms"></div>

        <!-- Betrag -->
        <div class='colhead output totals' style="width: 200px; --portrait-width: 90px; text-align: right;" name="lc_amount"></div>


        <div class="colhead" style="width: 20px; float: right; margin-right: 4px;" onclick="S10FilterTable(this);">
            <img src="../../../icons/filter.png" style="width: 18px; height: 18px;">
        </div>

    </div>

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

        <div class="tablerow">

            <!-- Belegnummer -->
            <div class='outputcelldiv ' style="width: 90px;" name="doc_no"></div>
            <!-- Position -->
            <div class='outputcelldiv ' style="width: 36px;" name="item_num"></div>
            <!-- Buchungsdatum -->
            <div class='outputcelldiv ' style="width: 80px;" name="pstng_date"></div>
            <!-- Zahlungsbedingung -->
            <div class='outputcelldiv ' style="width: 48px;" name="pmnttrms"></div>
            <!-- Betrag -->
            <div class='outputcelldiv ' style="width: 200px; --portrait-width: 90px; text-align: right;" name="lc_amount"></div>

        </div>

    </form>

    <!-- nothing selected -->
    <div class="tablenocontent" style="display: block;">Es wurde nichts selektiert</div>



</body>
</html>

Für die Navigation zurück zur Übersicht haben wir hier den Button "Übersicht" definiert, der wie bei den vorherigen Navigations-Buttons eine kleine ABAP-Methode aufruft:

* display general attributes
  method to_display.
    s10nextscreen( 'display').
  endmethod.


Damit ist die Listanzeige der offenen Posten fertig und in unsere Anwendung integriert.

Es ist empfehlenswert, von Anfang an mit dem Mechanismus der "build"-Methoden zu arbeiten, da komplexe Dialoganwendungen dadurch erheblich übersichtlicher werden und nachträgliche Änderungen der Benutzeroberfläche sehr viel leichter und sicherer möglich sind.