This is a test message to test the length of the message box.
Login
ABAP Refactoring alter Reports
Erstellt von Software-Heroes

ABAP - Refactoring alter Reports (1)

119

Du willst einen alten Report mal komplett Überarbeiten oder sollst diesen auf ein neues System bringen, da ihr gerade eine HANA Migration macht? Kein Problem, wir zeigen dir ein paar Schritte, wie dir das gelingt.

Werbung


In dieser zweiteiligen Reihe wollen wir dir 12 Schritte näher bringen, die dir helfen sollen bestehendes Coding aufzuräumen und zu verbessern. Wenn du gerade bei der Migration einen alten Reports auf ein neues System bist, wird dir dieser kleine Guide eine ungefähre Richtung geben.

 

Start

Du hast dich dazu entschieden einen alten Report zu migrieren oder willst ihn einmal komplett aufräumen, dafür gibt es verschiedene Methoden. In diesem Aritkel zeigen wir dir die 4 Phasen, die ein Report durchlaufen kann und die dir helfen sollen. Jede Phase besteht dabei aus 3 groben Schritten die dir eine Richtung geben sollen.

Dazu ein kleiner Report als Beispiel, den wir nun Schritt für Schritt überarbeiten werden und dir so die einzelnen Pfasen etas näher bringen möchten.

 

REPORT ztest_determine_bookings.

* INSERT START - BS20110506
TYPES:
  BEGIN OF ts_booking,
    bukrs TYPE bkpf-bukrs,
    belnr TYPE bkpf-belnr,
    gjahr TYPE bkpf-gjahr,
    cpudt TYPE bkpf-cpudt,
    xblnr TYPE bkpf-xblnr,
* DELETED START - BS20110506
*    aedat type bkpf-aedat,
*    awkey type bkpf-awkey,
* DELETED END - BS20110506
    buzei TYPE bseg-buzei,
    wrbtr TYPE bseg-wrbtr,
    waers TYPE bkpf-waers,
    kostl TYPE bseg-kostl,                                  "BS20110315
    prctr TYPE bseg-prctr,                                  "BS20110315
    butxt TYPE t001-butxt,
  END OF ts_booking.
* INSERT END - BS20110506
TYPES: tt_booking TYPE STANDARD TABLE OF ts_booking.

DATA:
  gt_data TYPE STANDARD TABLE OF t001,
  gs_data TYPE t001.
* INSERT START - BS20110506
DATA: gs_booking TYPE ts_booking.
* INSERT END - BS20110506
DATA:
  gt_booking TYPE tt_booking,
  gd_date    TYPE d.                                        "BS20110315

DATA go_my_table TYPE REF TO cl_salv_table.

field-SYMBOLS:
  <gs_booking> type ts_booking.

SELECTION-SCREEN BEGIN OF BLOCK b01 WITH FRAME TITLE TEXT-t01.
PARAMETERS:
  p_bukrs TYPE bkpf-bukrs,
  p_belnr TYPE bkpf-belnr,
  p_gjahr TYPE bkpf-gjahr.
SELECT-OPTIONS:
*  s_bukrs for gs_data-bukrs,
  s_date FOR gd_date.
SELECTION-SCREEN END OF BLOCK b01.


INITIALIZATION.
  DATA ls_date LIKE LINE OF s_date.
*  data ls_bukrs like line of s_bukrs.                      "BS20110410

  ls_date-sign = 'I'.
  ls_date-option = 'BT'.
  ls_date-low = '20200101'.
  ls_date-high = sy-datum.
  APPEND ls_date TO s_date.

AT SELECTION-SCREEN ON p_bukrs.
  IF p_bukrs IS NOT INITIAL.
    SELECT SINGLE * FROM t001 INTO gs_data WHERE bukrs = p_bukrs.
    IF sy-subrc <> 0.
      MESSAGE 'Company Code is not valid!' TYPE 'E'.
    ENDIF.
  ENDIF.

START-OF-SELECTION.
  DATA:
    ls_bkpf TYPE bkpf,
    ls_bseg TYPE bseg.

  IF p_bukrs IS INITIAL OR p_belnr IS INITIAL OR p_gjahr IS INITIAL.
    MESSAGE 'Required fields not filled!' TYPE 'E'.
  ELSE.
    SELECT SINGLE * FROM t001 INTO gs_data WHERE bukrs = p_bukrs.
    IF sy-subrc <> 0.
      MESSAGE 'Company Code is not valid!' TYPE 'E'.
    ELSE.
      SELECT * FROM bkpf INTO ls_bkpf
        WHERE bukrs = p_bukrs
          AND belnr = p_belnr
          AND gjahr = p_gjahr.

        SELECT * FROM bseg INTO ls_bseg
          WHERE bukrs = p_bukrs
          AND belnr = p_belnr
          AND gjahr = p_gjahr.

          CLEAR gs_booking.
          MOVE-CORRESPONDING ls_bkpf TO gs_booking.
          gs_booking-buzei = ls_bseg-buzei.
          gs_booking-wrbtr = ls_bseg-wrbtr.
          gs_booking-kostl = ls_bseg-kostl.
          gs_booking-prctr = ls_bseg-prctr.
          APPEND gs_booking TO gt_booking.

        ENDSELECT.

      ENDSELECT.

*      select single * from t001 into gs_data where bukrs = p_bukrs. "BS20110410
      SELECT * FROM t001 INTO TABLE gt_data.

      loop at gt_booking ASSIGNING <gs_booking>.
        READ TABLE gt_data INTO gs_data
          WITH KEY bukrs = <gs_booking>-bukrs.
        IF sy-subrc = 0.
          <gs_booking>-butxt = gs_data-butxt.
        ENDIF.
      ENDLOOP.

      PERFORM output.
    ENDIF.
  ENDIF.


FORM output.
* DELETED START - BS20110506
*  LOOP AT gt_booking INTO gs_booking.
*    write: /, gs_booking-bukrs, gs_booking-belnr, gs_booking-gjahr,
*    gs_booking-buzei, gs_booking-wrbtr, gs_booking-kostl.
*  ENDLOOP.
* DELETED END - BS20110506

* INSERT START - BS20110506
  cl_salv_table=>factory(
    IMPORTING
      r_salv_table = go_my_table
    CHANGING
*      t_table = gt_t001                                     "BS20110315
      t_table = gt_booking                                  "BS20110315
  ).

  go_my_table->display( ).
* INSERT END - BS20110506

ENDFORM.

 

Phase: Analyse

In der Analysephase geht es vor allem um die Sichtung des bestehenden Codes und die Vorbereitung der Kopie, sowie das Aufräumen. Hier soll die Grundlage für das Refactoring geschaffen werden und noch einmal die nötigen Schritte überblickt werden. Dabei gibt es dir folgenden Schritte in dieser Phase:

 

 

Objekt kopieren

Wir gehen davon aus, dass wir den Report in ein neues System überführen. Deshalb wir im ersten Schritt der Report und alle abhängigen Objekte auf das neue System kopiert. Es sollten in diesem Schritt noch keine Änderungen am bestehenden Code durchgeführt werden und der Report sollte sich zum Abschluss normal aktivieren lassen.

 

Aufbau prüfen

Wenn du den Report nicht kennst, geht es in diesem Schritt um die Sichtung der Bestandteile und sich eine Übersicht zu schaffen. Damit sollte der ungefähre Arbeitsaufwand herauskommen und auf welche Besonderheiten geachtet werden sollte.

  • Wie gut ist der Report strukturiert (Methoden, Forms)?
  • Sind viele Daten global?
  • Werden Dynpros verwendet und muss auf PBO/PAI geachtet werden?

 

Aufräumen

Im ersten echten Schritt beginnen wir mit dem Aufräumen des Quellcodes. Alles was auskommentiert ist, sowie Änderungsvermerke, können gelöscht werden, da sie im neuen Report auch nicht benötigt werden. Einmal Pretty-Printer drücken sollte auch nicht verkehrt sein.

 

Nach der ersten Phase sollte dann der Quellcode in etwa so aussehen:

REPORT ztest_determine_bookings.

TYPES:
  BEGIN OF ts_booking,
    bukrs TYPE bkpf-bukrs,
    belnr TYPE bkpf-belnr,
    gjahr TYPE bkpf-gjahr,
    cpudt TYPE bkpf-cpudt,
    xblnr TYPE bkpf-xblnr,
    buzei TYPE bseg-buzei,
    wrbtr TYPE bseg-wrbtr,
    waers TYPE bkpf-waers,
    kostl TYPE bseg-kostl,
    prctr TYPE bseg-prctr,
    butxt TYPE t001-butxt,
  END OF ts_booking.
TYPES: tt_booking TYPE STANDARD TABLE OF ts_booking.

DATA:
  gt_data TYPE STANDARD TABLE OF t001,
  gs_data TYPE t001.
DATA: gs_booking TYPE ts_booking.
DATA:
  gt_booking TYPE tt_booking,
  gd_date    TYPE d.

DATA go_my_table TYPE REF TO cl_salv_table.

FIELD-SYMBOLS:
  <gs_booking> TYPE ts_booking.

SELECTION-SCREEN BEGIN OF BLOCK b01 WITH FRAME TITLE TEXT-t01.
PARAMETERS:
  p_bukrs TYPE bkpf-bukrs,
  p_belnr TYPE bkpf-belnr,
  p_gjahr TYPE bkpf-gjahr.
SELECT-OPTIONS:
  s_date FOR gd_date.
SELECTION-SCREEN END OF BLOCK b01.


INITIALIZATION.
  DATA ls_date LIKE LINE OF s_date.
  ls_date-sign = 'I'.
  ls_date-option = 'BT'.
  ls_date-low = '20200101'.
  ls_date-high = sy-datum.
  APPEND ls_date TO s_date.

AT SELECTION-SCREEN ON p_bukrs.
  IF p_bukrs IS NOT INITIAL.
    SELECT SINGLE * FROM t001 INTO gs_data WHERE bukrs = p_bukrs.
    IF sy-subrc <> 0.
      MESSAGE 'Company Code is not valid!' TYPE 'E'.
    ENDIF.
  ENDIF.

START-OF-SELECTION.
  DATA:
    ls_bkpf TYPE bkpf,
    ls_bseg TYPE bseg.

  IF p_bukrs IS INITIAL OR p_belnr IS INITIAL OR p_gjahr IS INITIAL.
    MESSAGE 'Required fields not filled!' TYPE 'E'.
  ELSE.
    SELECT SINGLE * FROM t001 INTO gs_data WHERE bukrs = p_bukrs.
    IF sy-subrc <> 0.
      MESSAGE 'Company Code is not valid!' TYPE 'E'.
    ELSE.
      SELECT * FROM bkpf INTO ls_bkpf
        WHERE bukrs = p_bukrs
          AND belnr = p_belnr
          AND gjahr = p_gjahr.

        SELECT * FROM bseg INTO ls_bseg
          WHERE bukrs = p_bukrs
          AND belnr = p_belnr
          AND gjahr = p_gjahr.

          CLEAR gs_booking.
          MOVE-CORRESPONDING ls_bkpf TO gs_booking.
          gs_booking-buzei = ls_bseg-buzei.
          gs_booking-wrbtr = ls_bseg-wrbtr.
          gs_booking-kostl = ls_bseg-kostl.
          gs_booking-prctr = ls_bseg-prctr.
          APPEND gs_booking TO gt_booking.

        ENDSELECT.

      ENDSELECT.

      SELECT * FROM t001 INTO TABLE gt_data.

      LOOP AT gt_booking ASSIGNING <gs_booking>.
        READ TABLE gt_data INTO gs_data
          WITH KEY bukrs = <gs_booking>-bukrs.
        IF sy-subrc = 0.
          <gs_booking>-butxt = gs_data-butxt.
        ENDIF.
      ENDLOOP.

      PERFORM output.
    ENDIF.
  ENDIF.


FORM output.
  cl_salv_table=>factory(
    IMPORTING
      r_salv_table = go_my_table
    CHANGING
      t_table = gt_booking
  ).

  go_my_table->display( ).
ENDFORM.

 

Phase: Strukturierung

In der zweiten Phase geht es um die Aufteilung des Codes in entsprechende Includes, sowie den Aufbau der Klasse und das verschieben des Codings. In dieser Phase werden noch keine größeren Dinge am Code geändert.

 

 

Includes erstellen

In diesem Schritt wird der Code entsprechend der Bestandteile zerlegt und in einzelne Includes aufgeteilt, damit auch bei viel Quellcode die Übersicht gewahrt bleibt. Die Includes werden wie das Hauptprogramm benannt, bekommen aber einen Zusatz, der die Funktion darstellt:

  • Hauptprogramm - alle programmspezifischen Events
  • TOP-Include (z.B. _TOP) - hier befinden sich alle globalen Datendefinitionen, Feldsymbole, Tabellen, Klassenbekanntgaben
  • Selektionsbild (z.B. _SEL) - alle Selektionsbilder und Abschnitte sollten in ein eigenes Include verschoben werden
  • Forms und Module (z.B. _F01) - hier befinden sich die letzten Forms oder Module, wenn du noch eine Dynproverarbeitung im Report hast
  • Klassen (z.B. _C01) - pro Klasse sollte ein eigenes Include genommen werden, diese können durchnummeriert werden

 

Klasse anlegen

Nun geht es um die Strukturierung der Klasse und der Daten. Dazu wird die Klasse angelegt und alle Events die bisher im Report angelegt wurden. Für jedes Form und jede Methode wird nun ein entsprechendes Gegenstück angelegt, diese dürfen durchaus auch schon auf Private gesetzt werden. Die Definition der Klasse könnte dann entsprechend aussehen:

 

CLASS lcl_report DEFINITION FINAL.
  PUBLIC SECTION.
    METHODS:
      initialization,

      main,

      on_bukrs.

  PRIVATE SECTION.
    METHODS:
      output.
ENDCLASS.

 

Code verschieben

Im nächsten Schritt verschieben wir den entsprechenden Code in die einzelnen Methoden und übernehmen die globalen Variablen/Typen in den globalen Teil der Klasse. Hier solltest du darauf achten, dass Feldsymbole nicht verschoben werden können, da diese nicht im globalen Bereich der Klasse definiert werden können. In unserem Fall können wir also die Typdefinition und das Feldsymbol erst einmal nicht übernehmen.

 

REPORT ztest_determine_bookings.

TYPES:
  BEGIN OF ts_booking,
    bukrs TYPE bkpf-bukrs,
    belnr TYPE bkpf-belnr,
    gjahr TYPE bkpf-gjahr,
    cpudt TYPE bkpf-cpudt,
    xblnr TYPE bkpf-xblnr,
    buzei TYPE bseg-buzei,
    wrbtr TYPE bseg-wrbtr,
    waers TYPE bkpf-waers,
    kostl TYPE bseg-kostl,
    prctr TYPE bseg-prctr,
    butxt TYPE t001-butxt,
  END OF ts_booking.
TYPES: tt_booking TYPE STANDARD TABLE OF ts_booking.

DATA:
  gd_date TYPE d.
FIELD-SYMBOLS:
  <gs_booking> TYPE ts_booking.


SELECTION-SCREEN BEGIN OF BLOCK b01 WITH FRAME TITLE TEXT-t01.
PARAMETERS:
  p_bukrs TYPE bkpf-bukrs,
  p_belnr TYPE bkpf-belnr,
  p_gjahr TYPE bkpf-gjahr.
SELECT-OPTIONS:
  s_date FOR gd_date.
SELECTION-SCREEN END OF BLOCK b01.


CLASS lcl_report DEFINITION FINAL.
  PUBLIC SECTION.
    METHODS:
      initialization,

      main,

      on_bukrs.

  PRIVATE SECTION.
    DATA:
      gt_data     TYPE STANDARD TABLE OF t001,
      gs_data     TYPE t001,
      gt_booking  TYPE tt_booking,
      gs_booking  TYPE ts_booking,
      go_my_table TYPE REF TO cl_salv_table.

    METHODS:
      output.
ENDCLASS.

CLASS lcl_report IMPLEMENTATION.
  METHOD initialization.
    DATA ls_date LIKE LINE OF s_date.
    ls_date-sign = 'I'.
    ls_date-option = 'BT'.
    ls_date-low = '20200101'.
    ls_date-high = sy-datum.
    APPEND ls_date TO s_date.
  ENDMETHOD.


  METHOD on_bukrs.
    IF p_bukrs IS NOT INITIAL.
      SELECT SINGLE * FROM t001 INTO gs_data WHERE bukrs = p_bukrs.
      IF sy-subrc <> 0.
        MESSAGE 'Company Code is not valid!' TYPE 'E'.
      ENDIF.
    ENDIF.
  ENDMETHOD.


  METHOD main.
    DATA:
      ls_bkpf TYPE bkpf,
      ls_bseg TYPE bseg.

    IF p_bukrs IS INITIAL OR p_belnr IS INITIAL OR p_gjahr IS INITIAL.
      MESSAGE 'Required fields not filled!' TYPE 'E'.
    ELSE.
      SELECT SINGLE * FROM t001 INTO gs_data WHERE bukrs = p_bukrs.
      IF sy-subrc <> 0.
        MESSAGE 'Company Code is not valid!' TYPE 'E'.
      ELSE.
        SELECT * FROM bkpf INTO ls_bkpf
          WHERE bukrs = p_bukrs
            AND belnr = p_belnr
            AND gjahr = p_gjahr.

          SELECT * FROM bseg INTO ls_bseg
            WHERE bukrs = p_bukrs
            AND belnr = p_belnr
            AND gjahr = p_gjahr.

            CLEAR gs_booking.
            MOVE-CORRESPONDING ls_bkpf TO gs_booking.
            gs_booking-buzei = ls_bseg-buzei.
            gs_booking-wrbtr = ls_bseg-wrbtr.
            gs_booking-kostl = ls_bseg-kostl.
            gs_booking-prctr = ls_bseg-prctr.
            APPEND gs_booking TO gt_booking.

          ENDSELECT.

        ENDSELECT.

        SELECT * FROM t001 INTO TABLE gt_data.

        LOOP AT gt_booking ASSIGNING <gs_booking>.
          READ TABLE gt_data INTO gs_data
            WITH KEY bukrs = <gs_booking>-bukrs.
          IF sy-subrc = 0.
            <gs_booking>-butxt = gs_data-butxt.
          ENDIF.
        ENDLOOP.

        output( ).
      ENDIF.
    ENDIF.
  ENDMETHOD.


  METHOD output.
    cl_salv_table=>factory(
      IMPORTING
        r_salv_table = go_my_table
      CHANGING
        t_table = gt_booking
    ).

    go_my_table->display( ).
  ENDMETHOD.
ENDCLASS.


INITIALIZATION.
  DATA(go_report) = NEW lcl_report( ).
  go_report->initialization( ).

AT SELECTION-SCREEN ON p_bukrs.
  go_report->on_bukrs( ).

START-OF-SELECTION.
  go_report->main( ).

 

Nach Abschluss dieser Phase hat sich vor allem in der Struktur des Reports viel geändert, doch noch nicht alles ist perfekt und so wie wir es wollen. Es sollten weiterhin keine Fehler im Report zu finden sein und die Aktivierung nach der Phase sollte kein Problem sein, ansonsten müsstest du hier suchen, was noch nicht ganz funktioniert. Auch nicht vergessen die neue Klasse zu instanziieren und zu verwenden.

 

Dabei können vielleicht die folgenden Punkte helfen:

  • Werden globale Variablen noch für das Selektionsbild benötigt?
  • Werden Typen noch für Feldsymbole verwendet?
  • Hast du alle Form Aufrufe durch die Methodenaufrufe ausgetauscht?

 

Fazit

In den ersten beiden Phasen ging es vor allem um das Verstehen des Quellcodes, das Aufräumen und das Überführen in eine neue Struktur, mit der wir nun arbeiten können. Der Report ist damit für die nächsten Schritte des Refactoring vorbereitet. Im nächsten Artikel durchlaufen wir dann die beiden letzten Phasen und bringen den Report in unsere gewünschte Form.


Enthaltene Themen:
HANA MigrationAltes CodingRefactoring
Kommentare (0)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


ABAP - Refactoring alter Reports (2)

Kategorie - ABAP

Hier geht es um den zweiten Teil der Refactoring-Reihe. Wir zeigen dir die letzten Schritte zur Überführung des alten Codings in eine neue Form.

25.09.2020

ABAP Tools - Arbeiten mit Eclipse (Refactoring)

Kategorie - ABAP

Im heutigen Artikel geht es um das Arbeiten mit Eclipse und wie du effizient und schnell dein Refactoring am Quellcode durchführen kannst

24.07.2020