Quantcast
Channel: Tricktresor
Viewing all 214 articles
Browse latest View live

SAP und der Rechtsklick

$
0
0

Jeder kennt sicherlich ein paar Einstellungen im GUI oder im ABAP-Editor, die für das eine oder andere gut sind. Die Optionen und Möglichkeiten sind jedoch recht vielfältig und ständigen Veränderungen unterworfen. Das führt dazu, dass

  • man schnell eine Option übersieht
  • eine Änderung gar nicht ins Auge fällt
  • man selektiv schaut, ob eine Option gerade für das aktuelle Problem passend sein könnte

In jedem Fall ist es schwierig, alle Optionen, Einstellungen, Tricks, Tastaturkürzel und Kniffe parat zu haben. Aus diesen Grund - und weil ein Kollege mich gerade auf einen Klick aufmerksam machte, den ich noch nicht kannte - möchte ich euch gerne ein paar Klicks und Einstellungen aus verschiedenen Bereichen vorstellen.

ABAP-Editor

Zeile verschieben

Eine Zeile - oder markierter Bereich - kann im Quelltext durch Drücken der Tastenkombination STRG+ALT+Cursor hoch nach oben geschoben werden. Gleiches funktioniert mit STRG+ALT+Cursor runter natürlich auch in die andere Richtung.

Oha! Da habe ich euch ja was erzählt... Ich habe die Tastenkombination in einer VM ausprobiert und dort funktionieren sie. Leider ist die Tastenkombination von Windows vorbelegt: Schwenken in die Richtung der Pfeiltasten. Damit erlebt man sein blaues Wunder und der Bildschirm steht Kopf.

Um die Funktion sinnvoll nutzen zu können, musst du im Editor den Befehlen Edit.MoveLinesUp und Edit.MoveLinesDown eine andere Tastenkombination zuweisen:

EditMoveLines Ich habe mich für STRG+SHIFT+Cursor Hoch/ Runter entschieden

TRANSLATE TO UPPER

Den ABAP-Befehl kennt sicherlich jeder Programmierer. Im ABAP-Editor gibt es eine Tastenkombination dafür: STRG+U (Upper). Die Verwandlung in Kleinbuchstaben erfolgt durch STRG+L (Lower). Das ist besonders dann hilfreich, wenn man einen Teil des Quelltextes als Übergabeparameter für eine Funktion benötigt, zum Beispiel den Strukturnamen zur Übergabe als String an den ALV-Grid. Text zwischen die Hochkommas kopieren, STRG+U und der Strukturname steht in Großbuchstaben dort.

Copy and Paste mit Historie

Eine von mir häufig verwendete Option ist die Tastenkombination STRG+SHIFT+V zum Einfügen von zuvor kopierten Texten. Mit dieser Option kannst du im Editor einen Text aus einer Methode kopieren (zum Beispiel die Datendeklaration), einen zweiten Text kopieren (zum Beispiel der zugehörige Funktionsaufruf) und kannst dann in einer anderen Methode die Daten nacheinander mittels STRG+SHIFT+V wieder einfügen.

Additives Copy And Paste

Theoretisch gibt es in den Einstellungen des Editors die Möglichkeit, die Funktion CopyAndAppend einer Tastenkombination zuzuweisen. Hiermit ist es - theoretisch - möglich, mehrere Textstellen durch CopyAndAppend an den bereits im Zwischenspeicher vorhandenen Text anzufügen.

CopyAndAppend

In der Praxis funktioniert dies jedoch nicht zuverlässig. Schade eigentlich...

ALV-Grid

Der ALV-Grid steckt voller Geheimnisse. Nicht nur bei der Programmierung desselben sondern auch an der Oberfläche. Einige vorgestellten Tricks dürften vom aktuellen Release abhängig sein. Von daher kann es sein, dass die Tricks bei dir leider nicht funktionieren...

Innereien

Für die Analyse von Fehlern kann es notwendig sein, etwas über die verwendeten Einstellungen eines ALV-Grids zu wissen. Klicke hierfür auf einen nicht verwendeten Bereich im Grid, also zum Beispiel ganz nach unten scrollen und dann in den grauen Bereich, bei gedrückter SHIFT-Taste mit der rechten Maustaste doppelt. Es werden dir dann Informationen und Fehlermeldungen zum Grid angezeigt, die normalerweise nur Programmierern zugänglich sind:

Grid-Einstellungen

Feldkatalog

Bei der Filterung oder Sortierung im ALV-Grid-Layout wird nur die Beschreibung der Feldnamen angezeigt. Das ist manchmal irreführend oder nicht eindeutig. Inzwischen gibt es die Möglichkeit, sich per Kontextmenü die technischen Feldnamen einblenden zu lassen:

Feldkatalog1

Die Anzeige sieht dann aus wie folgt:

Feldkatalog2

Control-Services

In den SAPGUI-Optionen (ALT+F12) lassen sich Control-Services einstellen:

Control-Services

Im Kontextmenü des ALV-Grid erscheint dann das zusätzliche Menü "Services - Suchen". Der Text in der markierten Zelle wird damit über - in diesem Beispiel - Google gesucht.

Dynpro

Eingabefelder und Historie

Der SAPGUI entscheidet selbst, für welche Felder eine Eingabehistorie aktiviert wird und für welche nicht. Mit STRG+Rechtsklick kannst du dies selbst steuern:

Eingabehistorie

Entwicklung

SE24 - Class Browser

Eventuell lohnt sich mal wieder ein Blick in die Einstellungen der Workbench? Hier kannst du einstellen, dass Methoden aus Interfaces gruppiert dargestellt werden:

Einstellungen SE24

SE24 Gruppierung INTF

Ich finde die Option sehr hilfreich.

Sonstiges

Hast du auch Kniffe, die sonst kaum jemand kennt, oder die du sehr hilfreich findest? Dann hinterlasse bitte einen Kommentar! Ich freue mich über geheime Einstellungen; gerne auch aus anderen Bereichen (MM, SD, etc)!

 

 

 

 

 


High Definition SAP? Ein Jahr später

$
0
0

Vor ziemlich genau einem Jahr habe ich mich darüber ausgelassen, dass der SAPGUI mit einer Auflösung größer als FullHD leider nicht zu verwenden ist: High Definition SAP? Ein Erfahrungsbericht
Jetzt, ein Jahr später, führt die SAP im SAPGUI das SAP Blue Crystal Design ein:
Blue Crystal Design
SAP Blue Crystal is a new visual design theme which succeeds Corbu. It provides a consistent design that allows users to seamlessly experience SAP GUI and NWBC elements. It is the standard theme of Fiori applications and comes with a new color palette and icons which are better scalable. The background texture has a stroke pattern in white and light blue with a gradient layer.

SAP-Note 2022335

Hat sich was geändert?

Die SAP hat tatsächlich nachgebessert und für das Blue Crystal Design die Icons komplett neu gebaut. Ich persönlich finde das Design extrem unübersichtlich und hässlich. Leider wurden die Icons für das Signature Theme nicht überarbeitet.
Leider sind jedoch nach wie vor Elemente vergessen worden.
Folgend ein paar Screenshots mit hoher Auflösung und dem neuen SAP Blue Crystal Design.

Der erste Blick gilt der SE80. Hier ist gut erkennbar, dass zwar die Icons gut skaliert wurden, der Auswahlbereich links oben jedoch immer noch nicht korrekt skaliert wird.

SAP Blue Crystal Design - SE24

 

Ich wollte es kaum glauben: Das Calendar-Control wurde gar nicht angepasst:

SAP Blue Crystal Design - Calendar

 

Das ALV-Grid sieht ordentlich aus. Die Icons finde ich jedoch gelinde gesagt schrecklich.

SAP Blue Crystal Design - ALV Grid

 

Im Tree-Control-Demo sieht man, dass die "Anfasser" für die Ordner immer noch extrem klein sind.

SAP Blue Crystal Design - Tree

 

Die Dynamischen Dokument werden gut skaliert. Allerdings nur der Text. Die Icons sind so klein, dass sie kaum zu erkennen sind.

SAP Blue Crystal Design - HTML

 

 

The background texture has a stroke pattern in white and light blue with a gradient layer. Hier in vierfacher Vergrößerung. Mich hat der schraffierte Hintergrund sofort irritiert:

SAP Crystal Design - Hintergrund

 

Das SAP-Logon ebenfalls mit einem sehr kleinen "Auf- und Zuklapper":

 

SAPlogon

 

 

Zuguter letzt bin ich  noch über die F4-Eingabehilfe gestolpert. Hier funktioniert das Skalieren so gar nicht.

f4

 

Fazit

Ich werde weiterhin mit meiner auf FullHD runter skalierten Auflösung arbeiten und weiterhin das Signature Theme verwenden. Schließlich will ich mit dem SAPGUI arbeiten...

Ach so: Für alle, die an der genauen Info über den SAPGUI-Patch interessiert sind:

SAPLOGON Info

MAIN MODULE INFORMATION:
saplogon.exe
SAP Logon für Windows
740 Final Release
7400.1.3.1104
1588321

MD04 Start/Freigabedatum anzeigen

$
0
0

In der Bedarfs/Bestandsliste stehen alle Banfen und Planaufträge. Schön nach Datum sortiert mit dem Hinweis wann das Material im Werk sein soll.
Speziell bei plangesteuerter Disposition - mit Bedarfen in der Zukunft - stellt sich aber oftmals die Frage wann man denn die Banf in eine Bestellung umwandeln muss.

Hierzu kann man die im Standard ausgeblendete Spalte "Start-/Freigabedatum" einblenden lassen.
Dort steht dann das Datum des Bedarfstermins minus der Wiederbeschaffungszeit, also der Tag an dem die Banf spätestens umgesetzt werden soll.

MD04

Zum Einblenden muss man mit dem Mauszeiger genau zwischen 2 Spalten bis sich der Mauszeiger von "verschieben/Größe ändern" zu "einblenden" ändert.
(2 parallele Balken mit Pfeilen nach links und rechts) (neben dem Feld "Dispoelement"). Dieses Vorgehen muss 2 mal wiederholt werden, bis die Spalte "Start-/Freigabedatum" erscheint.

Danach kann das Layout gespeichert werden.

Layout speichern MD04

wie immer "aktivieren" und schon wird die Spalte dauerhaft eingeblendet.

Layout aktivieren

Kleiner Trick - große Hilfe :-)

AWI, 06/2015

Transaktionen

MD04

SimDia² – Neue Version des besten SAP-Import-Tools

$
0
0

Über ein Jahr sind seit der Veröffentlichung meines Test-Berichts zu SimDia² vergangen. Dort habe ich die Version 2.5 getestet. Seitdem wurden vom Hersteller ERSAsoft bereits zwei neue Versionen veröffentlicht. Höchste Zeit also für ein Update. Die aktuelle Version ist 2.7.

Update

Neben vielen Kleinigkeiten wurden zwei recht große Funktionen implementiert. Zunächst zu den "Kleinigkeiten", die jedoch das Leben noch einfacher machen:

  • Pause-Button: Die aktuelle Aufzeichnung kann unterbrochen werden, um zum Beispiel nachschauen zu können, ob bereits bestimmte Daten vorhanden sind.
  • Gesteigerte Performance, insbesondere bei großen Datenmengen
  • Benutzung der F4-Hilfe während der Aufzeichnung
  • Mehrsprachigkeit
  • Verbesserte Unterstützung von eingabebereiten ALV-Grid

Die zwei großen Änderungen sind:

  • Dynamische Maßnahmen
  • Berechtigungsverwaltung

Mit den dynamischen Maßnahmen ist es nun möglich, SAP-Aktionen dynamisch (sprich: für jeden Datensatz der Excel-Datei unterschiedlich) in Abhängigkeit der Datenkonstellation in SAP und/oder Excel auszuführen - oder dies eben nicht zu tun.

Zusammen mit der verbesserten Auswahlmöglichkeit bzw. Positionierbarkeit auf einem TableControl (s.u.) lassen sich z.B. Materialstämme sehr einfach anpassen. Siehe Hersteller-Video "MM02 - komplexe Änderung von Materialstammsätzen".

Bereits mit der vorigen Version wurde SimDia² ein recht weit reichendes Verwaltungstool spendiert, um die Berechtigungen für das Programm sehr differenziert festzulegen. Dies dürfte vor allem in größeren Organisationen nicht ganz unwichtig sein.

Die Berechtigungsverwaltung erlaubt es einem Administrator, festzulegen, welcher Windows-Benutzer Skripte erstellen oder lediglich ausführen darf.

Simdia² - Admin

Eine Übersicht über alle Verbesserungen findest du im Newsletter-Archiv von SimDia².

Technik

SimDia² ist zwar nach wie vor ein reines Windows-Programm. Das Programm-Fenster legt sich mittlerweile aber nicht mehr nur einfach über den SAP GUI sondern wird fest ans SAP GUI „angetackert“. Für mich ein kleiner, für die einfache Bedienung aber nicht unwichtiger Punkt.

Anwendung - Funktionalität

In TableControls ist die Suche nach dem Identifikations-Merkmal jetzt nicht mehr auf die erste Spalte eingeschränkt. Vielmehr kann jetzt in einer beliebigen Spalte nach einer zu ändernden Tabellenzeile gesucht werden. Darüber hinaus kann jetzt auch z.B. in SUB-Fenstern, wie z.B. der Sichtenauswahl der Transaktion MM01/MM02...) gezielt nach der auszuwählenden Sicht gesucht werden, auch wenn sich diese je nach Material an unterschiedlichsten Positionen befindet.

Pro und Contra

Zwar können mit SimDia² immer noch keine Abfragen oder Verzweigungen programmiert werden (was ja eigentlich auch der Programm-Philosophie „Daten importieren ohne programmieren“ widersprechen würde). Aber immerhin lassen sich jetzt auch auf einfache Weise Buttons (und auch Tabs) abhängig von den Excel-Daten betätigen. Und damit sind wir dann doch schon (fast) bei der Möglichkeit, Abfragen oder Verzweigungen zu erstellen (das Wort „Programmieren“ vermeide ich bewusst, denn da gehört doch noch ein wenig mehr dazu.).

Anhand eines Videos auf der SimDia²-Homepage wird z.B. gezeigt, wie aufgrund der Excel-Daten ein Button betätigt werden kann, um fallweise in einem dadurch ausgelösten Popup zusätzliche Daten zu erfassen.

Fazit

Nach wie vor ist Simdia ein einfach gehaltenes Tool, das jedoch sehr mächtig ist. Als Programmierer möchte ich natürlich für jede wiederkehrende Aufgabe ein ABAP-Programm schreiben. Solange es jedoch lediglich darum geht, aus einer Excel-Tabelle einfache Daten in ein SAP-System zu bekommen, ist Simdia fast jeder Programmierung überlegen.

Schon wenn nur ein paar wenige (einfache) Datenübernahmeprogramme geschrieben werden müssen, komme ich wahrscheinlich bereits in Erklärungsnot, warum ich denn dafür nicht eine Simdia-Lizenz gekauft hätte.

Alleine dadurch, dass der Fachbereich selbst schnell und einfach definieren kann, welche Daten wo abgelegt werden sollen, können zwei Fliege mit einer (Simdia-) Klappe geschlagen werden:

  1. Die IT wird entlastet. Es muss keine Anforderung definiert werden, die dann geprüft und umgesetzt werden muss.
  2. Der Fachbereich kann sich selbst helfen und ist nicht auf freie Kapazitäten der IT angewiesen

Häufig werden Anforderungen gar nicht an die IT herangetragen, weil von vornherein feststeht, dass für solche kleinen Aufgaben kaum Zeit übrig ist.

Ich habe von Fachabteilungen gehört, die sich mit einer geeigneten Batchinput-Datei und der Word-Serienbrief-Funktion geholfen haben, um Daten in ihr SAP-System zu bekommen. Im Gegensatz dazu ist Simdia wirklich ein Kinderspiel!

Tricktresor- Bonus

Nach wie vor bietet ERSAsoft allen Tricktresor-Lesern einen Nachlass von 5% an. Fülle einfach das unten stehende Formular aus und du bekommst den erwähnten Nachlass gewährt.

Du wirst dich wundern, was mit Simdia in deinem Unternehmen möglich ist!

Sichere dir einen 5%-Rabatt auf SimDia²!

Schreibe uns eine Mail und berufe dich auf diesen Artikel im Tricktresor.

Beim Kauf von SimDia² erhältst du dann einen Nachlass von fünf Prozent!!

* indicates required field

Manchmal, aber nur manchmal…♫

$
0
0

hält SAP Überraschungen bereit, das glaubt man kaum...

Aber von vorne.

In einem Projekt haben wir uns gewundert, warum es in einer dynamisch generierten internen Tabelle einen CONVERSION OVERFLOW gab, obwohl das Feld vom Typ DEC ausreichend groß dimensioniert war. Die Lösung war offensichtlich. Hinterher...

2015-08-10_15-31-23

Erzeugung

Wir sind bei der Erzeugung der internen Tabelle wie folgt vorgegangen:

  • Definition des Feldkataloges
  • Erzeugung der internen Tabelle mit Hilfe von cl_alv_table_create=>create_dynamic_table

Beispielhaft kann die Erzeugung folgendermaßen veranschaulicht werden:

  FIELD-SYMBOLS <fcat>  TYPE lvc_s_fcat.
  DATA gt_table         TYPE lvc_t_fcat.

  APPEND INITIAL LINE TO gt_fcat ASSIGNING .
  <fcat>-fieldname = 'FELD1'.
  <fcat>-scrtext_l = 'Feld 1: CHAR'.
  <fcat>-inttype   = 'C'.
  <fcat>-intlen    = 20.
  <fcat>-datatype  = 'CHAR'.

  APPEND INITIAL LINE TO gt_fcat ASSIGNING .
  <fcat>-fieldname = 'FELD2'.
  <fcat>-scrtext_l = 'Feld 2: DEC'.
  <fcat>-inttype   = 'P'.
  <fcat>-intlen    = 16.
  <fcat>-datatype  = 'DEC'.
  <fcat>-decimals  = 2.

  CALL METHOD cl_alv_table_create=>create_dynamic_table
    EXPORTING
      it_fieldcatalog           = gt_fcat
      i_length_in_byte          = abap_true
    IMPORTING
      ep_table                  = gd_table
    EXCEPTIONS
      generate_subpool_dir_full = 9.

Das macht SAP

Im Debugger sieht man die erzeugte Datenstruktur im Detail.

2015-08-10_15-40-25

Hier ist auch erkennbar, dass die gepackte Zahl im internen Format nicht 16 Bytes groß ist, sondern nur 8. Dadurch passt nur eine vierzehnstellige Zahl (plus Vorzeichen) in das Feld. Im Coding wurde jedoch die maximale Größe von intern 16 Bytes verwendet.

Versteckte Parameter

Ich habe mir die Erzeugung der internen Tabelle durch cl_alv_table_create=>create_dynamic_table genauer angesehen und bin dann recht schnell auf den Parameter I_LENGTH_IN_BYTE gestoßen. Dieser Parameter ist in der Schnittstelle - wie so oft - sehr anschaulich und gut dokumentiert:

boolsche Variable (X=true, space=false)

Der Parameter ist optional und hat als Vorgabewert ABAP_FALSE.

Um es vorweg zu nehmen: Nachdem wir den Parameter auf ABAP_TRUE gesetzt haben, funktionierte alles, wie erwartet.

Beim Debuggen bin ich irgendwann auf folgende Stelle gestossen:

if ls_fieldcat-inttype eq 'P'.
  if r_length_in_byte eq abap_true.
    l_leng = ls_fieldcat-intlen.
  else.
    l_leng =  ( ls_fieldcat-intlen + 1 ) / 2.
  endif.
endif.

Meiner Meinung nach ist das Coding hier verkehrt, da gepackte Zahlen immer aus Halbbytes bestehen. An dieser Stelle darf nicht einfach die Länge halbiert werden. Warum genau diese "Halbierung" statt findet, habe ich auch nicht verstanden. Es hat wahrscheinlich mit Unicode zu tun.

Fazit

Die Erzeugung von internen Tabellen sollte meiner Meinung nach eh nicht mehr mit dem erwähnten Funktion erzeugt werden, da hier im Hintergrund Subroutine-Pools generiert werden. Die Verwendung ist nur eingeschränkt möglich (maximale Anzahl Aufrufe: 36). Inzwischen sind die Möglichkeiten mit CREATE DATA & RTTC deutlich eleganter und zukunftssicherer. Allerdings habe ich die Verwendung von CREATE_DYNMIC_TABLE auch schon mal als elegant bezeichnet...

Die Methode mit CREATE_DYNAMIC_TABLE hat auch einen großen Vorteil: Bei dem Aufbau der internen Tabelle kann ich gleich semantische Informationen mitgeben/ anreichern (Titel, Texte, Suchhilfen etc.), die nicht automatisch übernommen werden. Ich dann diesen Feldkatalog nicht nur zur Erzeugung der internen Tabelle verwenden, sondern auch für die Anzeige.

Bei der Variante über CREATE DATA und RTTC werden fast ausschließlich die technischen Informationen ausgewertet. Wenn ein verwendetes Datenelement korrekte Überschriften hat, ist es okay, aber wenn ich eigene Felder mit einem generischen Datenelement aufbaue (FELD1, FELD2) und diese im gleichen Zug benennen will, dann muss ich dies separat tun.

Berechtigungen inklusive erlaubter Aktivitäten

$
0
0

Ich habe keinen Report gefunden, der mir zu den Berechtigungsobjekten anzeigt, welche Aktivitäten erlaubt sind. Kurzerhand habe ich diesen natürlich schnell selbst geschrieben.

Der Report kann zur Dokumentation eigener Objekte oder als Information zu Standardobjekten dienen.

2015-08-11_17-02-48

Viel Spaß. Soweit das mit Berechtigungen möglich ist...

Code

REPORT z_disp_auth_activity.
*== Daten
DATA gt_tobj  TYPE STANDARD TABLE OF tobj.
DATA gs_tobj  TYPE tobj.

DATA gt_actz  TYPE STANDARD TABLE OF tactz.
DATA gs_actz  TYPE tactz.

DATA gv_ltext TYPE text30.
DATA gv_ttext TYPE xutext.
DATA gv_stext TYPE scrtext_l.

FIELD-SYMBOLS <feld> TYPE any.

*== Selektionsbild
PARAMETERS p_oclss TYPE xuobjclass DEFAULT 'MDG'.

START-OF-SELECTION.

*== Berechtigungsobjekte lesen
 SELECT * FROM tobj INTO TABLE gt_tobj WHERE oclss = p_oclss.
 CHECK sy-subrc = 0.

*== Aktivitäten zu den Berechtigungsobjekten
 SELECT * FROM tactz INTO TABLE gt_actz FOR ALL ENTRIES IN gt_tobj WHERE brobj = gt_tobj-objct.

 LOOP AT gt_tobj INTO gs_tobj.
*== Berechtigungsobjekt + Text
   FORMAT COLOR COL_GROUP.
   WRITE: / gs_tobj-objct.
   SELECT SINGLE ttext FROM tobjt INTO gv_ttext WHERE langu = sy-langu AND object = gs_tobj-objct.
   IF sy-subrc = 0.
     WRITE gv_ttext.
   ENDIF.
   WRITE AT sy-linsz space.
   FORMAT COLOR OFF.

 DO.
*== Berechtigungsfelder
   ASSIGN gs_tobj-objct INCREMENT sy-index TO <feld> RANGE gs_tobj.
   IF sy-subrc > 0 OR <feld> IS INITIAL.
     EXIT.
   ELSE.
     WRITE: /3 <feld>.
*== Text zum Berechtigungsfeld
     SELECT SINGLE dd04t~scrtext_l
       FROM dd04t
      INNER JOIN authx ON authx~rollname = dd04t~rollname
       INTO gv_stext
      WHERE dd04t~ddlanguage = sy-langu
        AND authx~fieldname = <feld>
        AND dd04t~as4local = 'A'
        AND as4vers = space.
     IF sy-subrc = 0.
       WRITE gv_stext.
     ENDIF.
   ENDIF.
 ENDDO.

*== Aktivitäten zum Berechtigungsobjekt
   LOOP AT gt_actz INTO gs_actz WHERE brobj = gs_tobj-objct.
     WRITE: /6 gs_actz-actvt.
     SELECT SINGLE ltext FROM tactt INTO gv_ltext WHERE spras = sy-langu AND actvt = gs_actz-actvt.
     IF sy-subrc = 0.
       WRITE gv_ltext.
     ENDIF.
   ENDLOOP.
   SKIP 1.
 ENDLOOP.

Ranges Tabellentyp definieren

$
0
0

Ranges sind eine starke Waffe im SAP im Kampf um zu viele Daten (hach wie poetisch...). Leider gibt es keinen allgemeinen Datentyp RANGES. Im Coding kann ich zwar eine Ranges-Tabelle definieren, aber ich kann dies nicht in Parametern von Methoden oder Funktionsbausteinen verwenden. Hierfür muss ein separater Tabellentyp im Dictionary angelegt werden.

RANGES Definition im Quelltext

Im Quelltext wird eine Ranges-Tabelle üblicherweise auf drei Arten definiert:

  1. SELECT-OPTIONS s_bukrs FOR t001-bukrs.
  2. RANGES r_bukrs FOR t001-bukrs.
  3. DATA r_bukrs TYPE RANGE OF bukrs.

Mit jedem Befehl wird eine interne Tabelle mit folgenden Feldern angelegt:

  • SIGN: Wert einschließen (I = include) oder ausschließen (E = exclude)
  • OPTION: EQ = EQual, BT = BeTween etc.
  • LOW: niedrigster Wert des Intervalls
  • HIGH: höchster Wert des Intervalls (leer, wenn OPTION = EQ)

Bei 1. und 2. wird diese Tabelle mit impliziter Kopfzeile (!) definiert.

Bei 3. muss der Arbeitsbereich separat definiert werden:

DATA s_bukrs LIKE LINE OF r_bukrs.

Namenskonvention

Die Namensgebung bei RANGES-Tabellen finde ich aus folgenden Gründen immer etwas schwierig:

  • Den Prefix R für RANGES benutze ich in der Regel für Objektreferenzen. Eine lokal definierte Objektreferenz heisst dann zum Beispiel LR_GRID. Hier könnte ich mir behelfen, indem ich das Prefix O für Objektreferenz verwende, also LO_GRID.
  • Eine RANGES-Tabelle besteht immer aus der Tabelle selbst und dem zugehörigen Arbeitsbereich. Der Arbeitsbereich kann eine Struktur sein oder aber auch ein strukturiertes Feldsymbol. Dementsprechend müsste der Prefix aus drei Stellen bestehen (Wenn man, so wie ich, jedoch immer einen zweistelligen Prefix hat, dann sieht ein dreistelliger "doof" aus):
    • LTR_BUKRS für eine lokal definierte Ranges-Tabelle
    • LSR_BUKRS für eine lokal definierte Ranges-Workarea
  • SELECT-OPTIONS haben in der Regel einen Sonderstatus, weil ein Parameter im Selektionsbild (SELECT-OPTIONS und PARAMETER) nur acht Stellen haben darf (im Gegensatz zu Variablen, die 30 Stellen lang sein dürfen). Deswegen arbeite ich hier häufig mit S_BUKRS für SELECT-OPTIONS und P_BUKRS für PARAMETERS.
  • Die Definition mit RANGES ist inzwischen als obsolet gekennzeichnet, da eine implizite Workarea definiert wird. Eine Benennung im Typ nach Prefix ist also schwer, da es ein Tabellentyp und eine Struktur gleichzeitig ist.

Definition im Dictionary

Durch Zufall habe ich entdeckt, dass die Anlage von RANGES-Strukturen im Dictionary (Transaktion SE11) unterstützt wird. Folgend eine kleine Bilderserie, die die Anlage einer RANGES-Tabelle im Dictionary dokumentiert:

Aufruf Transaktion SE11 und Datentyp anlegen

2015-08-12_13-48-47

Auswahl Tabellentyp:

2015-08-12_13-49-15

Die Kurzbeschreibung füllen (Mussfeld!) und dann im Menü Bearbeiten • Als Ranges Tabellentyp definieren.

2015-08-12_14-26-04

Es erscheint ein anderes Dynpro und du kannst das Datenelement angeben, auf das sich die Rangestabelle beziehen soll:

2015-08-12_13-51-40

Den Namen des zugehörigen strukturierten Zeilentyps kannst du bereits angeben und durch Anlegen auch direkt anlegen lassen. Die notwendigen Felder werden bereits vorgegeben und du musst nur noch den Kurztext eingeben:

2015-08-12_13-54-32

Tipp

Die Ranges-Tabellen müssen nicht Typ-genau sein! Du kannst für den Datentyp BUKRS auch eine Ranges-Tabelle vom Typ CHAR10 definieren. Das referenzierte generische Datenelement muss natürlich entsprechend groß sein. Für die Verwendung mit der Materialnummer, die 18 Stellen lang ist, reicht CHAR10 selbstverständlich nicht aus.

Im Dictionary gibt es bereits ein paar generische RANGES-Tabellentypen, wie zum Beispiel

  • TAB_RANGE_C10
  • TAB_RANGE_C18

Oder entsprechende RANGES-Arbeitsbereiche:

  • RANGE_C10
  • RANGE_C18
  • RANGE_C50

Log-Points zur Performancemessung

$
0
0

Performance ist ein komplexes Thema. Häufig ist es schwer überhaupt einen Ansatzpunkt zu finden, wo man mit der Performanceoptimierung beginnen soll. Ich habe ein einfaces Testprogramm geschrieben, um das Problem zu verdeutlichen. Es berechnet in einer Schleife ein Ergebnis mit unterschiedlichen Rechenoperationen.

Der folgende Tipp zeigt in erster Linie, wie man die Log-Points zur Protokollierung einsetzen kann. eine zuverlässige Laufzeitanalyse ist so nicht unbedingt möglich.

Die Transaktion SAT bietet die Möglichkeit, eine definierte Laufzeitmessung durchzuführen. Mittels SET RUN TIME ANALYZER ON/ OFF können explizit Blöcke für die Messung definiert werden. In der Variante kannst du definieren, dass nur diese Blöcke analysiert werden sollen. Zudem kann die Aggregation ausgestellt werden.

Testprogramm

Zuerst wird die Tabellen mit Testdaten gefüllt (TESTDATA). Diese Tabelle wird per LOOP durchlaufen und die Berechnung durchgeführt (CALCULATE). Bei Bedarf kann das Ergebnis ausgegeben werden (OUTPUT).

Testprogramm anzeigen
REPORT zzenno38. CLASS lcl_test DEFINITION. PUBLIC SECTION. CLASS-METHODS: main, output. PROTECTED SECTION. TYPES: BEGIN OF ty_data, type TYPE c LENGTH 1, count TYPE i, result TYPE f, p1 TYPE f, p2 TYPE f, END OF ty_data. CLASS-DATA gt_data TYPE STANDARD TABLE OF ty_data. CLASS-METHODS: testdata, calculate IMPORTING type TYPE char1 count TYPE i p1 TYPE f p2 TYPE f RETURNING VALUE(result) TYPE f . ENDCLASS. CLASS lcl_test IMPLEMENTATION. METHOD testdata. FIELD-SYMBOLS <data> TYPE ty_data. APPEND INITIAL LINE TO gt_data ASSIGNING <data>. <data>-type = 'A'. <data>-count = 1000. <data>-p1 = 123. <data>-p2 = 456. APPEND INITIAL LINE TO gt_data ASSIGNING <data>. <data>-type = 'A'. <data>-count = 10000. <data>-p1 = 123. <data>-p2 = 456. APPEND INITIAL LINE TO gt_data ASSIGNING <data>. <data>-type = 'A'. <data>-count = 10000. <data>-p1 = 123456. <data>-p2 = 4567890. APPEND INITIAL LINE TO gt_data ASSIGNING <data>. <data>-type = 'B'. <data>-count = 100000. <data>-p1 = 123. <data>-p2 = 456. APPEND INITIAL LINE TO gt_data ASSIGNING <data>. <data>-type = 'C'. <data>-count = 10000. <data>-p1 = 1234567. <data>-p2 = 4. ENDMETHOD. METHOD main. FIELD-SYMBOLS <data> TYPE ty_data. testdata( ). LOOP AT gt_data ASSIGNING <data>. <data>-result = calculate( type = <data>-type count = <data>-count p1 = <data>-p1 p2 = <data>-p2 ). ENDLOOP. ENDMETHOD. METHOD output. DATA lr_grid TYPE REF TO cl_salv_table. cl_salv_table=>factory( IMPORTING r_salv_table = lr_grid CHANGING t_table = gt_data ). lr_grid->display( ). ENDMETHOD. METHOD calculate. DO count TIMES. CASE type. WHEN 'A'. result = p1 * p2. WHEN 'B'. result = p1 / p2. WHEN 'C'. result = p1 ** p2. ENDCASE. ENDDO. ENDMETHOD. ENDCLASS. PARAMETERS p_output AS CHECKBOX DEFAULT space. START-OF-SELECTION. lcl_test=>main( ). IF p_output = abap_true. lcl_test=>output( ). ENDIF.

Auswertung

In der Transaktion SAT erhält man leider zu wenig aussagekräftige Informationen:

2015-08-19_01-14-26

2015-08-19_01-14-44

Wir brauchen also eine andere Möglichkeit, um die Performance weiter zu analysieren.

Eigene Performanceprotokollierung

Die Protokollierung der Performance ist mit GET RUN TIME möglich. Der Befehl muss jedoch zu Beginn des Tests aufgerufen werden und zum Ende der Messung. Die Zeitendifferenz - also die Laufzeit - muss noch berechnet werden. Das ist umständlich, wenn man mehrere Routinen protokollieren möchte.

Es liegt also nah, diese Funktion auszulagern:

CLASS lcl_performance DEFINITION.
 PUBLIC SECTION.
 CLASS-METHODS:
   start IMPORTING id TYPE any,
   stopp IMPORTING id TYPE any.
 PROTECTED SECTION.
   TYPES: BEGIN OF ty_id_table,
            id TYPE string,
            count TYPE i,
            runtime TYPE i,
          END OF ty_id_table.
  CLASS-DATA gt_id_table TYPE STANDARD TABLE OF ty_id_table.
ENDCLASS.

CLASS lcl_performance IMPLEMENTATION.
  METHOD start.
    FIELD-SYMBOLS <id_table> TYPE ty_id_table.
    READ TABLE gt_id_table ASSIGNING <id_table> WITH KEY id = id.
    IF sy-subrc > 0.
      APPEND INITIAL LINE TO gt_id_table ASSIGNING <id_table>.
      <id_table>-id = id.
    ENDIF.
    ADD 1 TO <id_table>-count.
    GET RUN TIME FIELD <id_table>-runtime.
  ENDMETHOD.
METHOD stopp.
FIELD-SYMBOLS <id_table> TYPE ty_id_table.
DATA lv_runtime TYPE i.
DATA lv_subkey TYPE c LENGTH 200.
READ TABLE gt_id_table ASSIGNING <id_table> WITH KEY id = id.
CHECK sy-subrc = 0.
GET RUN TIME FIELD lv_runtime.
<id_table>-runtime = lv_runtime - <id_table>-runtime.
lv_subkey = |{ id }, Run { <id_table>-count }: { <id_table>-runtime NUMBER = USER }ms|.
LOG-POINT ID z_performance SUBKEY lv_subkey FIELDS <id_table>-runtime.
ENDMETHOD.
ENDCLASS.

Aufruf der Protokollierung

Wir müssen die Methoden unserer Protokollklasse nun nur noch einbinden:

METHOD main.
  FIELD-SYMBOLS <data> TYPE ty_data.
  testdata( ).
  LOOP AT gt_data ASSIGNING <data>.
    lcl_performance=>start( <data>-type ).
    <data>-result = calculate( type = <data>-type
                              count = <data>-count
                                 p1 = <data>-p1
                                 p2 = <data>-p2 ).
    lcl_performance=>stopp( <data>-type ).
  ENDLOOP.
ENDMETHOD.

Log-Points

In dem Coding zur Performanceprotokollierung benutze ich bereits eines Log-Point. Diesen kannst du mit der Transaktion SAAB anlegen. Log-Points haben den Vorteil, dass man diese in jedem System bei Bedarf aktivieren kann. Du benötigst also keine separate Tabelle, um einstellen zu können, was wann protokolliert werden muss, sondern kannst du Standardfunktionalität nutzen.

Mit der Transaktion SAAB musst du den Log-Point auch einschalten. Du kannst wählen, für welchen Zeitraum die Protokollierung aktiv sein soll:

2015-08-19_00-46-37

Normalerweise würde man den Log-Point in dieser Form verwenden:

LOG-POINT ID z_performance SUBKEY id FIELDS <id_table>-runtime.

Diese Protokollierung ist jedoch sehr umständlich auszuwerten: Man sieht erstens nur den letzten Aufruf des Log-Points und zweitens muss man die Hierarchie aufklappen.

2015-08-19_01-50-34

Aber ich wollte die Auswertung ja einfacher machen. Also machen wir uns den Umstand zunutze, dass die Log-Point-ID bis zu 200 Zeichen lang sein darf und basteln uns für jeden Aufruf eine eigene ID. Das Ergebnis ist viel aussagekräftiger:

2015-08-19_00-50-19

Variante

Es ist auch möglich, einem Log-Point eine interne Tabelle zu übergeben! Da jeweils nur der letzte Log-Point gesichert wird, können wir uns diesen Umstand ebenfalls gut zunutze machen:

Wir rufen den Log-Point einfach mit einem festen Wert auf und übergeben bei FIELDS die interne Tabelle:

LOG-POINT ID z_performance SUBKEY 'Default' FIELDS gt_id_table.

Das Ergebnis sieht folgendermaßen aus:

2015-08-19_02-01-47

Erweiterungsmöglichkeiten

Die Protokollierung ist natürlich auch noch sehr begrenzt. Du kannst die Methoden jedoch um Parameter erweitern, so dass bei jedem Aufruf zusätzliche Informationen übergeben werden können. Auch könnte die Protokollklasse sich selbst je Log-Id instanziieren, so dass je Log-Id nur die eigenen Aufrufe an den Log-Point übergeben werden. So würde die Messung von mehreren Abschnitten wahrscheinlich übersichtlicher werden.

Doku

Für die genauen Möglichkeiten der Log-Points solltest du dir die Hilfe durchlesen: LOG-POINT.


STA Technologies

$
0
0

In der Regel beschränke ich mich bei meinen Beiträgen auf Deutsch und Deutschland. Die Regel mit "Deutsch" soll auch weiterhin gelten, allerdings möchte ich diesmal inhaltlich nach Ungarn. Genauer gesagt nach Budapest. Zu einer Firma, die sich STA Technologies nennt.

STA Technlogies Logo

STA Technologies

Die Firma wurde 2008 in Ungarn, Budapest von Tamas Holics gegründet. Der Grund, warum ich Tamas und seine Firma hier vorstellen möchte, ist der folgende: Tamas hat SAP-AddOns geschrieben, die mich vom Handling und von der Qualität her überzeugt haben. Kleine Tools, denen man ansieht, dass viel Arbeit und Ideen in Ihnen stecken.

Tamas ist mit seiner Firma offizieller SAP Partner und hat sich auf die Programmierung von SAP-AddOns spezialisiert. Sein Anspruch sind innovative Produkte, die höchsten Qualitätskriterien entsprechen. Zusätzlich bietet Tamas Holic mit STA Consulting auch kundenindividuelle Beratung und Programmierung an.

STA ALV Enhancer

Der STA ALV Enhancer erweitert Standard-ALVs und natürlich ALVs in kundeneigenen Programmen um folgende Funktionen:

  • Neue Felder hinzufügen
  • Automatische Währungsumrechnung
  • Formeln
  • Bezeichnungen für SAP-Objekte (Werke, Lagerorte etc)
  • Änderung der Zellenfarbe anhand von Bedingungen
  • Navigation zu SAP-Objekten (Materialstamm, Kundenauftrag, ...)
  • Generic Object Services

Zudem muss ich zugeben, dass das Video wirklich unterhaltsam ist! Ich habe noch nie so ein gutes Produktvideo für ein SAP-Addon gesehen:

STA Ticket System

Das STA Ticket System macht ebenfalls einen sehr guten Eindruck. Viele Funktionen, ordentlicher Ausdruck der Meldungen per Formular und vieles mehr. Im folgenden ein paar Highlights:

  • E-Mail-Benachrichtigung
  • Eigene Templates
  • Integration in den SAPGUI
  • Automatisches Einbetten von Laufzeitinfos
  • Hinzufügen von Kurzdumps, Screenshots, SU53-Trace
  • Auswahl aktueller Felder mit Feldinhalten

Auch dieses Produktvideo ist wirklich super!

Tricktresor-Rabatt

Ein Blick (und auch einer mehr) lohnt sich in jedem Fall.

* indicates required field

Espresso-Tutorials

$
0
0

SAP-Wissen zum Mitnehmen: Espresso-Tutorials. Klein. Stark. SAP. Umfassende und doch auf das Wesentliche beschränkte Lektüre zu verschiedenen SAP-Themen.

Espresso-Tutorials

Espresso-Tutorials konzentrieren sich auf ein Thema. Kurz und knackig. Die Bücher liegen in der Regel in einem digitalen Format vor, so dass sie bequem am Tablet oder PC gelesen werden können.

Flatrate-Wissen

Die große Besonderheit bei Espresso-Tutorials: Die eBook-Flatrate für EUR 99,00 pro Jahr.

In dieser Flatrate sind alle Bücher aus der Expresso-Bibliothek enthalten. Zudem auch alle, die noch erscheinen. Für Firmen gibt es spezielle Angebote, um den Mitarbeitern das gesamte Wissen der Espresso-Fachbücher online zur Verfügung stellen zu können.

Eingebettete Videos

Zu vielen Büchern gibt es erklärende Videos, die direkt aus dem eBook heraus angesehen werden können. Eine wertvolle Hilfe zum Verständnis!

Stöbern

Die Espresso-Bibliothek

Vorteil

Mit dem Gutscheincode "915-885-313" sparst du 10 Euro Gebühr im ersten Jahr!

Recht so – Links rum!

$
0
0

Wer aus dem ALV-Grid Zahlen kopieren möchte, der erlebt eine Überraschung, wenn im Grid negative Werte dargestellt werden:

2015-10-05_19-53-12

Copy&Paste oder Export

Die Ausrichtung wird korrekt vorgenommen, wenn du das gesamte Grid exportierst oder Excel-Inplace öffnest.

Manchmal möchte man aber nur einen markierten Teil kopieren (STRG+Y / Markieren / STRG+C).

Mit folgendem Visual-Basic-Makro "mivorenali - MInus VOn REchts NAch LInks" kannst du das Vorzeichen von rechts nach links setzen und das Feld wird wieder als Zahl formatiert. Einfach den gewünschten Zellenbereich markieren und das Makro starten.

Sollte es ein sehr großer Bereich sein, ist es evtl. sinnvoll, die Aktualisierung des Arbeitsblattes auszuschalten:

Application.ScreenUpdating = False

Code

Sub mivorenali()
    
   
  Dim objCell As Range
  Dim vntValue As Variant
 
  If TypeOf Selection Is Excel.Range Then
    'Application.ScreenUpdating = False
    
    'Durchlaufe alle markierten Zellen
        For Each objCell In Selection

            'Wert der Zelle an Variable übergeben
            vntValue = objCell.Value
            
            'Wenn der Wert nicht leer ist
            If Not IsEmpty(vntValue) Then
            If Right(vntValue, 1) = "-" Then
             objCell.Value = "-" & Left(vntValue, Len(vntValue) - 1)
             objCell.NumberFormat = "General"
             objCell.Value = CDbl(objCell.Value)
             End If
            End If
            
        Next
            
    'Application.ScreenUpdating = True
  End If
    
    
End Sub
image_pdfimage_print

Redefinierte Methoden?

$
0
0

Heute habe ich in einer Klassenhierarchie (Superklasse -> vererbte Klassen) eine Klasse gesucht habe, bei der ich eine bestimmte Methode redefiniert habe.

Als anständiger Programmierer war ich natürlich zu faul, mich auch nur durch drei vererbte Klassen durchzuklicken... Also bin ich auf die Suche nach einem passenden Funktionsbaustein gegangen und dabei auf SEO_INHERITANC_READ gestossen. Diesem Funktionsbaustein kannst du eine Klasse übergeben und erhältst alle redefinierten Methoden.

Nicht ganz das, was ich gesucht habe, aber die verwendete Tabelle SEOREDEF lieferte mir die gewünschten Informationen im Handumdrehen.

Hier ein Beispiel in dem du siehst, welche Methoden von CL_GUI_CONTAINER in direkt geerbten Klassen redefiniert wurden:

2015-11-09_18-17-16

Allerdings ist diese Methode nicht 100%ig, denn es wird natürlich nur die direkte Vererbung gespeichert. Von der Klasse CL_GUI_DOCKING_CONTAINER zum Beispiel erben weitere Klassen. Deren Redefinitionen sind nur sichtbar, wenn du die Klasse CL_GUI_DOCKING_CONTAINER direkt eingibst.

Um zu sehen, wo entlang einer Vererbungshierarchie welche Methoden redefiniert wurden, muss also noch ein Programm geschrieben werden.

Programm

Hier ist das kleine Programm. Quick and dirty. Methode GET_SUBCLASSES der Klasse CL_OO_CLASS gibt alle vererbten Klassen zurück.

von diesen Klassen werden die redefinierten Methoden ermittelt.

Um zu sehen, welche Methode in welcher Klasse redefiniert wurde, muss nach Methode gefiltert oder sortiert werden:

2015-11-09_18-49-58

Code

REPORT.

PARAMETERS p_class TYPE seoclsname DEFAULT 'CL_GUI_CONTAINER'.

DATA gt_redef TYPE STANDARD TABLE OF seoredef.

START-OF-SELECTION.

 PERFORM do USING p_class 'X'.
 PERFORM display.

*&---------------------------------------------------------------------*
*& Form display
*&---------------------------------------------------------------------*
FORM display.

 DATA lr_table TYPE REF TO cl_salv_table.
 DATA lr_funcs TYPE REF TO cl_salv_functions_list.

 CALL METHOD cl_salv_table=>factory
 IMPORTING
 r_salv_table = lr_table
 CHANGING
 t_table = gt_redef.

 lr_funcs = lr_table->get_functions( ).
 lr_funcs->set_all( ).

 lr_table->display( ).
ENDFORM. "display


*&---------------------------------------------------------------------*
*& Form do
*&---------------------------------------------------------------------*
FORM do USING i_class TYPE seoclsname
 i_start TYPE boolean.

 DATA lr_class TYPE REF TO cl_oo_class.
 DATA lt_subclasses TYPE seo_relkeys.
 DATA ls_subclass LIKE LINE OF lt_subclasses.

 TRY .
 lr_class ?= cl_oo_class=>get_instance( i_class ).

 APPEND LINES OF lr_class->redefinitions TO gt_redef.
 lt_subclasses = lr_class->get_subclasses( ).

 IF i_start = abap_true.
 LOOP AT lt_subclasses INTO ls_subclass.
 PERFORM do USING ls_subclass-clsname space.
 ENDLOOP.
 ENDIF.

 CATCH cx_class_not_existent.

 ENDTRY.

ENDFORM. "do

image_pdfimage_print

Befehlsverkettung – Don’t try this at home!

$
0
0

Den CL_SALV_TABLE kann man leider nicht "elegant" erzeugen. Darunter verstehe ich, dass man die SALV-Referenz per RECEIVING bekommt. Sobald ein Exporting- oder Changing-Parameter in der Methode verwendet wird, kann RETURNING nicht mehr benutzt werden.

Der Aufruf ist zwar kompakt:

CALL METHOD cl_salv_table=>factory
 EXPORTING
 r_container = gr_dock
 IMPORTING
 r_salv_table = gr_salv
 CHANGING
 t_table = gt_data.
gr_salv->display( ).

aber man kann ihn nicht wie folgt verwenden:

cl_salv_table=>factory( ... )->display( ).

Ich wollte nun wissen, ob man die Erzeugung eines SALV-Tables plus Anzeige trotzdem in einen funktionalen Aufruf schreiben kann. Geklappt hat es zwar, jedoch wiederum mit einem Nachteil.

Befehlsverkettung

Methoden von durch RETURNING übergebenen Objektreferenzen können direkt durch Befehlsverkettung ausgeführt werden.

Dazu ein kleines Testprogramm mit einer FACTORY-Methode:

*----------------------------------------------------------------------*
* CLASS lcl_test DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_test DEFINITION.
 PUBLIC SECTION.
 CLASS-METHODS factory RETURNING value(object) TYPE REF TO lcl_test.
 METHODS show.
ENDCLASS. "lcl_salv DEFINITION

*----------------------------------------------------------------------*
* CLASS lcl_salv IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_test IMPLEMENTATION.
 METHOD factory.
 CREATE OBJECT object.
 ENDMETHOD. "factory

 METHOD show.
 MESSAGE 'Hallo' TYPE 'I'.
 ENDMETHOD. "show

ENDCLASS. "lcl_test IMPLEMENTATION

Normalerweise würde man die Methode SHOW folgendermaßen aufrufen:

DATA lr_test TYPE REF TO lcl_test.
lr_test = lcl_test=>factory( ).
lr_test->show( ).

Durch Verwendung einer Befehlsverkettung kann der Aufruf wie folgt aussehen:

lcl_test=>factory( )->show( ).

In diesem Umfang ist die Befehlsverkettung auch vertretbar. Sie kann jedoch Auswüchse annehmen, die ich nicht mehr warten möchte...

CL_SALV_TABLE

Zurück zum meinem eigentlich Problem: Die Anzeige des SALV-Grid mit einem Aufruf. Hindernis dabei ist, dass die SALV-Referenz eben nicht per RETURNING zurück gegeben wird, sondern als IMPORTING-Parameter.

Ich schrieb also ein kleines Wrapper-Programm. Ich habe die Übergabe der einzelnen Parameter in einzelne Methoden ausgelagert um diese nacheinander "funktional" aufrufen zu können. Dafür muss die eigene Instanz jeweils an den Aufrufer übergeben werden.

Auch dabei musste ich tricksen, denn die anzuzeigende Tabelle kann nicht als CHANGING übergeben werden. Ich musste deswegen mit einer Referenz auf die Tabelle arbeiten.

 DATA gd_table TYPE REF TO data.
 GET REFERENCE OF gt_data INTO gd_table.

Dadurch erreiche ich natürlich nicht das, was ich eigentlich wollte, nämlich einen kompakten Methodenaufruf. Immerhin: Der Aufruf sieht nun wirklich interessant aus:

lcl_salv=>factory( )->set_table( gd_table )->create_grid( )->display( ).

DISPLAY ist hierbei bereits die Methode des SALV-Table, die durch CREATE_GRID erzeugt wird!

Sicherlich nicht elegant, aber es lässt sich gut demonstrieren, wie die funktionalen Aufrufe funktionieren.

Diese Methode ist selbstverständlich nur dann sinnvoll, wenn man sicher ist, dass man das SALV-Objekt nicht mehr benötigt, denn bei diesem Aufruf erzeugt man das Objekt, benutzt es, aber die Referenz darauf ist sofort wieder weg.

Code

*----------------------------------------------------------------------*
* CLASS lcl_salv DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_salv DEFINITION.
 PUBLIC SECTION.
 DATA mt_table TYPE REF TO data.
 CLASS-METHODS factory RETURNING value(object) TYPE REF TO lcl_salv.
 METHODS create_grid RETURNING value(salv) TYPE REF TO cl_salv_table.
 METHODS set_table IMPORTING table TYPE REF TO data
 RETURNING value(object) TYPE REF TO lcl_salv.
ENDCLASS. "lcl_salv DEFINITION

*----------------------------------------------------------------------*
* CLASS lcl_salv IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_salv IMPLEMENTATION.
 METHOD factory.
 create object object.
 ENDMETHOD. "factory

 METHOD create_grid.

 FIELD-SYMBOLS <table> TYPE ANY TABLE.
 ASSIGN mt_table->* TO <table>.

 CALL METHOD cl_salv_table=>factory
 IMPORTING
 r_salv_table = salv
 CHANGING
 t_table = <table>.

 ENDMETHOD. "factory

 METHOD set_table.
 mt_table = table.
 object = me.
 ENDMETHOD. "set_table
ENDCLASS. "lcl_salv IMPLEMENTATION
image_pdfimage_print

Befehlsverkettung mit Strukturzugriff

$
0
0

Jeder kennt inzwischen die Möglichkeit der funktionalen Methodenaufrufe, bei denen man das Ergebnis einer Funktion direkt einer Variablen zuweisen

rnd = CL_ABAP_RANDOM_INT=>CREATE( ).

oder direkt in Vergleichen verwenden kann:

CHECK CL_ABAP_DEMO_SERVICES=>IS_PRODUCTION_SYSTEM( ) = abap_false.

Direkter Zugriff

Was die wenigsten wissen ist, dass man direkt auf einzelne Felder einer zurück gegebenen Struktur zugreifen kann. Durch die Befehlsverkettung können Methoden direkt aneinander gereiht werden:

layout = gr_salv->get_layout( )->get_current_layout( ).

Handelt es sich bei dem Übergabeparameter um eine Struktur, so kann auch hierauf direkt zugegriffen  werden:

default = gr_salv->get_layout( )->get_current_layout( )-default.

 

image_pdfimage_print

Smart Filter

$
0
0

Vor einiger Zeit habe ich euch den Magic Filter vorgestellt. Hier habe ich den aktuellen Filter eines Grids auf mehrere Objekte angewendet.

Heute möchte ich euch eine weitere Möglichkeit der Filterung vorstellen: Die Filterung anhand einer Mehrfachselektion. Damit es ins Konzept passt und weil jedes Kind einen Namen braucht, habe ich es Smart Filter genannt.

Smart Filter: Anwendung

Am Anfang war das Grid (oder genauer: Der SALV-Table). Als Beispieldaten dienen die Länderbezeichnungen.

Das Grid besitzt eine selbst definierte Funktion "Filter":

filter_sel1

Um diese Funktion zu verwenden, müssen mindestens eine Zeile und mindestens eine Spalte markiert werden (STRG gedrückt halten für Mehrfachselektion!):

filter_sel2

Mit einem Klick auf "Filter" wird dann die "Schnittmenge" gefiltert:

filter_sel3

Die Anwendungsmöglichkeiten sind sicherlich beschränkt, aber bei größeren Listen, die anhand bestimmter Felder und Feldwerte kontrolliert werden müssen, sicherlich sehr hilfreich.

Anmerkungen zum Code

Da der zusätzliche Button in dieser Variante nicht im Fullscreen-Grid funktioniert, muss die Darstellung in einem Docking-Container erfolgen.

Um dies einfach zu tun, habe ich den PARAMETER dummy verwendet, damit ein Selektionsbild angezeigt wird.

Mit Druck auf die Enter-Taste wird das Grid angezeigt.

Um den Zusatzbutton anzuzeigen, wird Command Chaining verwendet:

mr_salv->get_functions( )->add_function( ... )

Code

REPORT.

PARAMETERS dummy.

*----------------------------------------------------------------------*
* CLASS lcl_grid DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_grid DEFINITION.
 PUBLIC SECTION.
 CLASS-METHODS start.
 PROTECTED SECTION.
 CLASS-DATA mt_data TYPE STANDARD TABLE OF t005t.
 CLASS-DATA mr_salv TYPE REF TO cl_salv_table.
 CLASS-METHODS add_filter_value
 IMPORTING
 structure TYPE any
 fieldname TYPE fieldname
 CHANGING
 filter TYPE lvc_t_filt.
 CLASS-METHODS add_filter_selection.
 CLASS-METHODS set_filter
 IMPORTING
 filter TYPE lvc_t_filt.
 CLASS-METHODS add_function.
 CLASS-METHODS on_function FOR EVENT added_function
 OF cl_salv_events_table
 IMPORTING e_salv_function.


ENDCLASS. "lcl_grid DEFINITION

*----------------------------------------------------------------------*
* CLASS lcl_grid IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_grid IMPLEMENTATION.
 METHOD start.

*== Daten lesen
 SELECT * FROM t005t INTO TABLE mt_data.

*== Docker erzeugen
 DATA lr_docker TYPE REF TO cl_gui_docking_container.
 CREATE OBJECT lr_docker
 EXPORTING
 side = cl_gui_docking_container=>dock_at_bottom
 ratio = 80.

*== SALV-Table erzeugen
 CALL METHOD cl_salv_table=>factory
 EXPORTING
 r_container = lr_docker
 IMPORTING
 r_salv_table = mr_salv
 CHANGING
 t_table = mt_data.

*== alle Funktionen einblenden, um Filter löschen zu können
 DATA lr_funcs TYPE REF TO cl_salv_functions_list.
 lr_funcs = mr_salv->get_functions( ).
 lr_funcs->set_all( ).
*== Eigene Funktion einblenden
 add_function( ).

*== ON_Usercommand registrieren
 DATA lr_events TYPE REF TO cl_salv_events_table.
 lr_events = mr_salv->get_event( ).
 SET HANDLER on_function FOR lr_events.

*== Anzeige
 mr_salv->display( ).

 ENDMETHOD. "start

 METHOD add_function.

*== Eigene Funktion einfügen
 mr_salv->get_functions( )->add_function(
 name = 'ZFilterSel'
 icon = |{ icon_filter }|
 text = |Filter|
 tooltip = |Filter anhand Selektion|
 position = if_salv_c_function_position=>right_of_salv_functions ).

 ENDMETHOD. "add_function

 METHOD on_function.

 CASE e_salv_function.
 WHEN 'ZFilterSel'.
*== Filter anhand Selektion setzen
 add_filter_selection( ).
 ENDCASE.

 ENDMETHOD. "on_function

 METHOD add_filter_value.

 DATA ls_filter TYPE lvc_s_filt.
 FIELD-SYMBOLS <value> TYPE any.

*== Filterzeile hinzufügen
 ls_filter-fieldname = fieldname.
 ls_filter-sign = 'I'.
 ls_filter-option = 'EQ'.
 ASSIGN COMPONENT fieldname OF STRUCTURE structure TO <value>.
 IF sy-subrc = 0.
 ls_filter-low = <value>.
 SHIFT ls_filter-low LEFT DELETING LEADING space.
 READ TABLE filter WITH KEY fieldname = ls_filter-fieldname
 sign = ls_filter-sign
 option = ls_filter-option
 low = ls_filter-low
 TRANSPORTING NO FIELDS.
 IF sy-subrc > 0.
 APPEND ls_filter TO filter.
 ENDIF.
 ENDIF.

 ENDMETHOD. "add_filter_field

 METHOD add_filter_selection.

*== Lokale Daten
 DATA lt_filter TYPE lvc_t_filt.

 DATA lt_rows TYPE salv_t_row.
 FIELD-SYMBOLS <row> LIKE LINE OF lt_rows..
 DATA lt_cols TYPE salv_t_column.
 FIELD-SYMBOLS <col> LIKE LINE OF lt_cols.
 DATA lr_selection TYPE REF TO cl_salv_selections.

 FIELD-SYMBOLS <table_line> TYPE any.


*== Selektierte Spalten und Zeilen ermitteln
 lr_selection = mr_salv->get_selections( ).
 lt_rows = lr_selection->get_selected_rows( ).
 lt_cols = lr_selection->get_selected_columns( ).

*== Filter anhand der aktuellen Selektion setzen
 LOOP AT lt_rows ASSIGNING <row>.
 IF sy-subrc = 0.
 READ TABLE mt_data ASSIGNING <table_line> INDEX <row>.
 IF sy-subrc = 0.
 LOOP AT lt_cols ASSIGNING <col>.
 add_filter_value( EXPORTING structure = <table_line> fieldname = <col>
 CHANGING filter = lt_filter ).

 ENDLOOP.
 ENDIF.
 ENDIF.
 ENDLOOP.

*== Filter setzen
 set_filter( filter = lt_filter ).

 ENDMETHOD. "add_filter_selection

 METHOD set_filter.

*== Filter auf SAL-Table anwenden
 DATA lv_index TYPE i.
 DATA lr_display TYPE REF TO cl_salv_display_settings.
 DATA lv_title TYPE lvc_title.
 FIELD-SYMBOLS <filter> TYPE lvc_s_filt.

 cl_salv_controller_metadata=>set_lvc_filter( t_filter = filter
 r_filters = mr_salv->get_filters( ) ).
 lr_display = mr_salv->get_display_settings( ).

 LOOP AT filter ASSIGNING <filter>.
 IF lv_title IS INITIAL.
 lv_title = <filter>-fieldname.
 ELSE.
 CONCATENATE '/' <filter>-fieldname INTO lv_title.
 ENDIF.
 ENDLOOP.

 CONCATENATE 'Filter ist aktiv (' lv_title ')' INTO lv_title .

 lr_display->set_list_header( lv_title ).

 mr_salv->refresh( ).
 ENDMETHOD. "set_filter

ENDCLASS. "lcl_grid IMPLEMENTATION

AT SELECTION-SCREEN.

 lcl_grid=>start( ).

 

image_pdfimage_print

Serialize me

$
0
0

Das Interface IF_SERIALIZABLE_OBJECT läuft einem hin und wieder in SAP-Standardklassen über den Weg.

Was ist Serialisierung?

Wikipedia sagt über Serialisierung:

Die Serialisierung ist in der Informatik eine Abbildung von strukturierten Daten auf eine sequenzielle Darstellungsform.

Eine der bekanntesten Anwendungen für die Serialisierung ist das JSON-Format, in dem komplexe Daten in einer lesbaren Form dargestellt werden können.

Eine andere Form der Serialisierung lässt sich mit XML bewerkstelligen.

Interface IF_SERIALIZABLE_OBJECT

Damit ein Objekt (Klasse) serialisierbar ist, muss es das Interface IF_SERIALIZABLE_OBJECT implementieren:

CLASS lcl_serialize_me DEFINITION.
 PUBLIC SECTION.
 INTERFACES if_serializable_object.

 DATA mt_t005  TYPE STANDARD TABLE OF t005.
 DATA mt_t005t TYPE STANDARD TABLE OF t005t.

ENDCLASS.

Zur Demonstration habe ich die zwei öffentlichen Attribute MT_T005 und MT_T005T hinzugefügt.

Um die Klasse nutzen zu können, muss sie instantiiert werden:

DATA ref TYPE REF TO lcl_serialize_me.
CREATE OBJECT ref.

In die Tabellen laden wir nun alle EG-Länder inklusive Texte:

 SELECT * FROM t005 INTO TABLE ref->mt_t005 WHERE xegld = abap_true.
 IF sy-subrc = 0.
   SELECT * FROM t005t INTO TABLE ref->mt_t005t
      FOR ALL ENTRIES IN ref->mt_t005
    WHERE land1 = ref->mt_t005-land1
      AND spras = sy-langu.
 ENDIF.

Somit haben wir eine Objektreferenz erzeugt, die ein paar Daten enthält.

Diese Daten sind auch serialisierbar. Andere Daten, wie zum Beispiel Attribute mit Referenzen zu anderen Klassen, sind nicht serialisierbar.

Deswegen darf das Interface IF_SERIALIZABLE_OBJECT nur dann implementiert werden, wenn alle Attribute der Klasse für die Serialisierung geeignet sind.

Serialisierung

Nun soll das Objekt mittels XML serialisiert werden. Das geht schnell und einfach:

DATA ser TYPE string.
CALL TRANSFORMATION id
     SOURCE model = ref
     RESULT XML ser.

Das Ergebnis ist ein lesbarer XML-String (Ausschnitt):

2015-12-02_18-20-37

Tipp: Im Debugger ist es möglich, einen XML-String komplett darstellen zu lassen:

2015-12-02_18-21-08

Speicherplatz sparen

Da die Tabellen gefüllt sind, ist das Objekt recht groß geworden. Mit GZIP schrumpfen wir es auf eine kleinere Größe:

DATA zip TYPE xstring.
cl_abap_gzip=>compress_text(
     EXPORTING text_in  = ser
     IMPORTING gzip_out = zip ).

Dies aber nur nebenbei...

Deserialisierung

Nun möchten wir das serialisierte Objekt natürlich irgendwo speichern, ablegen oder verschicken.

Das ist jedoch nur sinnvoll, wenn wir es auch wieder deserialisieren können...

Die Objektreferenz ist in dem Fall natürlich leer:

CLEAR ref.

Die Deserialisierung funktioniert ebenfalls mittel CALL TRANSFORMATION in der Standardvariante:

CALL TRANSFORMATION id
     SOURCE XML ser
     RESULT model = ref.

Im Debugger kannst du überprüfen, dass die komplette Referenz wiederhergestellt wurde. Das ist fast schon Zauberei... :)

Vielen Dank an Haubi für diese Idee!

Nutzen

Wozu die Serialisierung und Deserialisierung tatsächlich nützlich ist, wird man wahrscheinlich erst wissen, wenn man es braucht. Daher ist es in jedem Fall gut zu wissen, dass es funktioniert.

DEMO

Ein einfaches Demoprogramm ist DEMO_SERIALIZABLE_OBJECT. Hier wird ebenfalls die Serialisierung demonstriert.

2015-12-02_18-41-07

Wer ein komplexes Demoprogramm zur Konvertierung von (Daten-) Typen sehen möchte, sollte sich das Programm STRANSDEMO_FLIGHTS anschauen.

2015-12-02_18-37-23

Viel Spaß damit; mir ist es zu kompliziert!

image_pdfimage_print

Der Doppelpunkt…

$
0
0

Eine Kleinigkeit aus der Welt des Doppelpunkts.

Jeder ABAP-Programmierer kennt den Doppelpunkt und weiß, dass er damit Befehlsausführungen - durch Komma voneinander getrennt - verketten kann:

DATA: a TYPE string,
      b TYPE c LENGTH 3.
MOVE: a TO b, c TO d.

Was die wenigsten jedoch wissen ist, dass man auch Methodenaufrufe durch einen Doppelpunkt unübersichtlicher machen kann:

o_ref->umbenennen( : alt = 'A' neu = 'B' ),
                     alt = 'X' neu = 'Y' ).

Das Ganze noch mit "echter" Befehlsverkettung verbunden und das Chaos ist perfekt.

PS: Das habe ich in dem sehr guten Buch von Paul Hardy entdeckt: ABAP To The Future

image_pdfimage_print

Debugger-Scripting (1)

$
0
0

Lange habe ich mich vor den umfangreichen Funktionen es Debugger-Scripting gedrückt. Durch das sensationelle Buch von Paul Harding "ABAP To The Future" habe ich mich nun endlich getraut.

Mein erstes Debugger-Skript

Es passiert des Öfteren, dass einem Authority-Checks in den Weg geworfen werden. Einem einzelnen kann man schnell Herr werden, in dem man sich einen Break-Point auf die Anweisung "AUTHORITY-CHECK" setzt, F5 (Einzelschritt) drückt, den SY-SUBRC auf "0" ändert und dann weiter macht.

Wenn es mehrere Checks sind, kann es schnell nerven.

Mein erstes Debugger-Skript habe ich genau hierfür geschrieben. Es macht genau das, was ich eben beschrieben habe.

METHOD script.

  debugger_controller->debug_step( 
       p_command = cl_tpda_script_debugger_ctrl=>debug_step_over ).

  cl_tpda_script_data_descr=>change_value(
         p_new_value = '4'
         p_varname = 'SY-SUBRC' ).

ENDMETHOD. "script

Damit das Skript funktioniert, musst du an geeigneter Stelle den Debugger anschalten (/h, geht auch vor dem Aufruf einer Transaktion!!) und zum Tab "Script" wechseln.

Dort setzt du einen Break-Point bei der Anweisung AUTHORITY-CHECK:

2015-12-03_17-55-27

Dann musst du nur noch das oben vorgestellte Coding in der Methode SCRIPT einfügen.

"Skript starten" und auf einmal werden alle Berechtigungsprüfungen wahr...

Leider...

funktioniert der Trace bei dieser Methode nicht. Normalerweise kann man das aktuelle Ereignis tracen:

trace->add_src_info( ).

Entweder ist ein AUTHORITY-CHECK kein Ereignis, oder es funktioniert aus anderen Gründen nicht.

Wahrscheinlich letzteres, denn auch ein eigener Eintrag in den Trace bleibt erfolglos:

 DATA trace_entry TYPE tpda_trace_custom.
 trace_entry-value = 'hier steht was...'.
 trace->add_custom_info( p_trace_entry = trace_entry ).

Wer hier noch Tipps hat: Immer her damit in die Kommentare!

Wizard

Wer das Debugger-Skripting weiter erforschen möchte, kann das sehr komfortabel über den Wizard machen:

2015-12-03_18-01-43

Alle möglichen Befehle werden hier als Muster eingefügt. Alle möglichen Konstanten, die in diesem Zusammenhang möglich sind, werden als Kommentar eingebunden:

*************************************************
* debugger commands (p_command):
* Step into(F5) -> CL_TPDA_SCRIPT_DEBUGGER_CTRL=>DEBUG_STEP_INTO
* Execute(F6) -> CL_TPDA_SCRIPT_DEBUGGER_CTRL=>DEBUG_STEP_OVER
* Return(F7) -> CL_TPDA_SCRIPT_DEBUGGER_CTRL=>DEBUG_STEP_OUT
* Continue(F8) -> CL_TPDA_SCRIPT_DEBUGGER_CTRL=>DEBUG_CONTINUE
*************************************************
****************************************************************
*Interface (CLASS = CL_TPDA_SCRIPT_DEBUGGER_CTRL / METHOD = DEBUG_STEP )
*Importing
* REFERENCE( P_COMMAND ) TYPE I
****************************************************************

*TRY.
DEBUGGER_CONTROLLER->DEBUG_STEP( P_COMMAND = P_COMMAND ).
* CATCH cx_tpda_scr_rtctrl_status .
* CATCH cx_tpda_scr_rtctrl .
*ENDTRY.

Speichern & Laden

Die Skripts können - inklusive erstellter Breakpoints!! - gespeichert werden. Entweder direkt im System im ABAP-Repository oder lokal auf dem Rechner.

Da es sich bei den Skripten um "normale" Programme handelt (Programmtyp "Subroutinenpool"), ist es sinnvoll, sich an Namenskonventionen zu halten. Alle SAP-eigenen vorgefertigten Skripte beginnen mit "RSTPDA_SCRIPT".

image_pdfimage_print

Xing Profile Pic Enlarger [chrome extension]

$
0
0

Wieder eine Premiere. Meine erste Chrome Extension: Der XING Profile Picture Enlarger

In Chrome hat man die Möglichkeit, ein Bild mit Rechtsklick anzeigen zu lassen. Bei Xing sind die Profilbilder skaliert wobei die Skalierung, bzw. die tatsächliche Größe in der URL angegeben ist:

Mein Profilfoto heißt zum Beispiel:

https://x1.xingassets.com/image/a_d_3_1ed9891b3_2296_29/enno-wulff-foto.64x64.jpg

Wenn man nun in der URL die Größenangabe durch 1024x1024 ersetzt, bekommt man sozusagen die Originaldatei des Profilfotos angezeigt.

Chrome Extension

Für den Browser Chrome können Erweiterungen programmiert werden.

Was liegt da näher, als das Suchen & Ersetzen von einer Erweiterung vornehmen zu lassen...?

Hier ist ein Getting-started von Google.

XING Profile Pic Enlarger

Ich habe mir aus der Beispielbibliothek das Beispiel imageinfo herunter geladen und modifiziert. Herausgekommen ist ein Skript, dass sich in das Kontextmenü einklinkt, die darunter liegende URL wie oben beschrieben modifiziert und sie in einem separaten Fenster anzeigt.

Xing speichert die Bilder in festen Größen. Der Einfachheit halber ersetze ich einfach jede Größe, die ich gefunden habe durch "1024x1024". Irgendeine wird schon passen... 😉

xppe-128

Hier gibt es die Erweiterung zum Download:

xing profile pic enlarger

Zum Aktivieren muss du die Datei zuerst entpacken. Um sie in den Chrome Browser einzubinden, gehe ins Menü "Weitere Tools - Erweiterungen" und klicke den Button "Entpackte Erweiterung laden". Danach sollte die Erweiterung aktiv sein und kann sofort genutzt werden.

2015-12-04_17-43-24

Evtl. muss der Entwickler-Modus aktiviert sein.

image_pdfimage_print

Gruppensummenstufenberechnung

$
0
0

Heute mal wieder ein Work-around ganz besonderer Güte: Das Beeinflussen von Gruppensummenstufen.  Das ist leider nicht ganz so einfach, wie es sich anhört, da bei einem Refresh des Grids die aufgebauten Gruppenstufen wieder zerstört werden. Also muss ein kleiner Trick herhalten...

Vielen Dank an Stefan, der sich die Mühe gemacht hat, ein Minimaldemo zu erstellen.

Gruppenstufen

Nach dem Start des Demoprogramms erscheint ein "normaler" ALV mit Daten aus der Flugdatenbank:

2015-12-15_22-48-55

Bei normaler Summierung und Bildung von Gruppenstufen, gibt es keine Bezeichnung der gebildeten Gruppen:

2015-12-15_22-50-50

Die Bildung der Gruppenbezeichnung kann sehr komplex werden. Das Beispiel demonstriert die Bezeichnung der Gruppenstufen im Feld "PLANETYPE":

2015-12-15_22-41-44

 

Code

REPORT zdemo_alv_summenzeilen.

*----------------------------------------------------------------------*
* CLASS lcl_helper DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_helper DEFINITION FINAL.
 PUBLIC SECTION.

 CLASS-METHODS: read_data,
 display,
 handle_after_user_command FOR EVENT after_user_command OF cl_gui_alv_grid,
 summenzeilen_anpassen.

 CLASS-DATA: mo_grid TYPE REF TO cl_gui_alv_grid,
 mt_data TYPE STANDARD TABLE OF saplane WITH NON-UNIQUE DEFAULT KEY.
ENDCLASS. "lcl_helper DEFINITION


START-OF-SELECTION.
 lcl_helper=>read_data( ).

END-OF-SELECTION.
 lcl_helper=>display( ).


*----------------------------------------------------------------------*
* CLASS lcl_helper IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_helper IMPLEMENTATION.

 METHOD read_data.

 SELECT *
 INTO TABLE mt_data
 FROM saplane.

 ENDMETHOD. "read_data

 METHOD display.

 DATA: ls_variant TYPE disvariant.
 WRITE:/ 'Wenn man das hier liest, ist ein interner Fehler aufgetreten'. "#EC NOTEXT

*--------------------------------------------------------------------*
* ALV erzeugen
*--------------------------------------------------------------------*
 CREATE OBJECT mo_grid
 EXPORTING
 i_parent = cl_gui_container=>screen0
 EXCEPTIONS
 OTHERS = 1.

*--------------------------------------------------------------------*
* Event AFTER_USER_COMMAND nutzbar machen
* Da sehr viele Usercommands ( auch SAP-Usercommands ) implizit einen
* full-refresh des Grid durchführen, müssen wir uns stets dahinter klemmen
* um unsere eigene Zwischensummenzeilengenerierung zu erhalten
*--------------------------------------------------------------------*
 SET HANDLER handle_after_user_command FOR mo_grid.

*--------------------------------------------------------------------*
* Defaultlayouts ermöglichen, um Zwischensummen ohne Userinteraktion zu demonstrieren
*--------------------------------------------------------------------*
 ls_variant-handle = '0001'.
 ls_variant-report = sy-repid.

*--------------------------------------------------------------------*
* Anzeigen des grid
*--------------------------------------------------------------------*
 mo_grid->set_table_for_first_display( EXPORTING
 i_structure_name = 'SAPLANE'
 is_variant = ls_variant
 i_save = 'A'
 i_default = 'X'
 CHANGING
 it_outtab = mt_data
 EXCEPTIONS
 OTHERS = 1 ).

*--------------------------------------------------------------------*
* Summen- oder Zwischensummenzeilen manipulieren
*--------------------------------------------------------------------*
 summenzeilen_anpassen( ).

 ENDMETHOD. "display

 METHOD handle_after_user_command.
*--------------------------------------------------------------------*
* SAP hat evtl. noch keinen Refresh gemacht.
* Daher würden Änderungen, die in der Methode summenzeilen_anpassen
* gemacht und dann mit soft-refresh an den Grid gereicht würden im
* Nachgang durch den ausstehenden full-refresh zunichte gemacht, da
* der Grid beim full refresh auch die Summen- und Zwischensummenzeilen
* neu generiert
* Daher wird der full-refresh jetzt explizit vor unserer Anpassung
* ausgeführt und der nachfolgende soft_refresh lässt unsere
* Summenzeilen stehen.
*--------------------------------------------------------------------*
 mo_grid->refresh_table_display( i_soft_refresh = ' ' ).


*--------------------------------------------------------------------*
* Summen- oder Zwischensummenzeilen manipulieren
*--------------------------------------------------------------------*
 summenzeilen_anpassen( ).

 ENDMETHOD. "handle_AFTER_USER_COMMAND

 METHOD summenzeilen_anpassen.

 DATA: lr_data_summe TYPE REF TO data,
 lr_data_zwischensumme TYPE REF TO data,
 lt_grouplevels TYPE lvc_t_grpl, "#EC NEEDED Normalerweise braucht man das um gezielt die Zwischensummen zu manipulieren
 lv_tabix TYPE numc2.

 FIELD-SYMBOLS: <lt_data> LIKE mt_data,
 <ls_data> LIKE LINE OF <lt_data>.
*--------------------------------------------------------------------*
* Zwischensummenzeilen holen -
*--------------------------------------------------------------------*
 mo_grid->get_subtotals( IMPORTING
 ep_collect00 = lr_data_summe " Summenzeile
 ep_collect01 = lr_data_zwischensumme " Zwischensummenzeile - Stufe 1
* EP_COLLECT02 - EP_COLLECT09 Zwischensummenzeilen - Stufe 2-9
 et_grouplevels = lt_grouplevels ). " Informationen welche Zwischensummenzeile(n) zu welchen Gridzeilen gehören

*--------------------------------------------------------------------*
* Hier kann das jetzt hinreichend komplex werden
* Zur Demo werde ich in alle Summen und Zwischensummen im Feld
* "PLANETYPE" etwas einfüllen
*--------------------------------------------------------------------*
 IF lr_data_summe IS BOUND.
 ASSIGN lr_data_summe->* TO <lt_data>.
 LOOP AT <lt_data> ASSIGNING <ls_data>.
 lv_tabix = sy-tabix.
 CONCATENATE 'Stufe1-' lv_tabix INTO <ls_data>-planetype. "#EC NOTEXT
 ENDLOOP.
 ENDIF.

 IF lr_data_zwischensumme IS BOUND.
 ASSIGN lr_data_zwischensumme->* TO <lt_data>.
 LOOP AT <lt_data> ASSIGNING <ls_data>.
 lv_tabix = sy-tabix.
 CONCATENATE 'Stufe2-' lv_tabix INTO <ls_data>-planetype. "#EC NOTEXT
 ENDLOOP.
 ENDIF.


*--------------------------------------------------------------------*
* ALV-Anzeige neu aufbauen lassen, ohne Zwischensummen vom ALV generieren zu lassen
*--------------------------------------------------------------------*
 mo_grid->refresh_table_display( i_soft_refresh = 'X' ).

 ENDMETHOD. "summenzeilen_anpassen
ENDCLASS. "lcl_helper IMPLEMENTATION
image_pdfimage_print
Viewing all 214 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>