Showing posts with label ALV Grid. Show all posts
Showing posts with label ALV Grid. Show all posts

Monday, September 7, 2015

ALV Grids, Repors and a common Data Model

I recently had to program a simple monitor doing quality checks on outbound deliveries.

Basically the user should be able to scan a delivery using a Dynpro Input mask. Then an ALV Grid would Show all the LIPS Positions and he could scan Materials by EAN to confirm that the quality of the position is ok.


Basically that is what i came up with:
 


Since I do not program Module Pools every day one tricky part was to have a clean data Model of the delivery we just work with. You can see that there are manipulating Inputs in the Dynpro as well as in the ALV toolbar, which can be used.

The manipulation of ALV Data is very well protected and always problematic. Also I consider using globals problematic whenever a program manipulates more than one data item(e.g. the delivery), since there will be problems with clean reloads of the next item.


I was not able to split the Controller from the view for the ALV composes both in our framework, but I wanted to have a common data model for both the module pool and the ALV Grid. So in he end I came up with this:



Its kind of M(V&C).  All the Data Manipulation is done by Methods of the Model Class itself to minimize  manipulation overwriting. The Model will lock itself once all QS Positions are confirmed, and needs an explicit reset to be opened again.


The only problem is, that the ALV is embedded in the Dynpro. So whenever controls of the ALV are clicked, I can manipulate the Data Model cleanly (by reference). Afterwards I need to render the ALV again. Simple refresh Grid is not enough, since the ALV is inside an Container. So I need to get into the PBO of the report, which I only managed by calling  CALL METHOD CL_GUI_CFW=>SET_NEW_OK_CODE (any better solutions are welcome as comments J ) ALV should refresh itself if you use it right, not like i did it at the start.

Monday, March 16, 2015

ALV Tree Refresh (Delete all Nodes) - The Node Key Problem

Recently i stumbled across a problem in the ALV Tree. When you refresh the Tree you are supposed to delete all nodes

CALL METHOD REF_TREE->DELETE_ALL_NODES.

and add the new Nodes after. The problem here is, that the Tree implementation does not reset the  Node Key Counter. So when you had 248 Elements before the new Tree will start at Key 249(-1?).

The thing is, i wanted to have the same UI after the refresh, meaning all the expanded Nodes should be expanded again. You can read expanded Nodes with REF_TREE->GET_EXPANDED_NODES but it will only give you a list of Keys, which are pretty worthless after the Refresh if you dont want to do complicated calculations, which might fail due to complexity.

You can later Expand the Nodes in the new tree with REF_TREE->EXPAND_NODES. But now we need a connection between the old node keys and the new ones.

Given that you have an itab it_data with unique keys that builds up the tree ist is possible by using that table and an extra itab l_it_expanded with the same structure that holds the expanded data lines instead of only the node keys.


  1. First you read all the expanded nodes GET_EXPANDED_NODES 
  2. Then you read the lines of your data structure into it_expanded using the node keys
    • You need to save the Node Keys of the Tree in the it_data structure when you build it, so you can now find the corresponding line
    • The mt_outtab of the tree would also work, but its protected in the class and i dont know how to access it. Comment if you know how
  3. You do your refresh now
    • Your it_data contains the new data and the new nodekeys now. 
    • The l_it_expanded contains the old expanded data with the worthless node keys
  4. You can now read the new data itab using the real key tuple of your it_expanded
  5. Because you have the node keys saved inside the it_data after the read you kann append to a node key table to add all expanded
    • Also you are able to react on changes like missing lines 
  6. You can now use EXPAND_NODES with you node key table.
here is a code sample how i worked it out. It might be more difficult to read since we are using a framework between ALV Tree and our code.



      "Tabelle für Expandierte Knoten
      DATAL_WA_OUTLINEOLD TYPE TP_T_TREE_TAB.

........

Inside a function for e.g. Text updates

   "Speichern der Expandierten Knoten für Refresh
          CLEAR L_WA_OUTLINEOLD.
          PERFORM CUST_TREE_SAVE_EXPANDED CHANGING L_WA_OUTLINEOLD.

          "Texte über Lagernummer ermitteln
          PERFORM CUST_GET_TXTS_FOR_TRANSPORT USING C_WA_TREE_TAB-TKNUM.

          "LayoutRefresh und Laden der Expandierten Knoten
          PERFORM STD_REFRESH_SELECTION.
          PERFORM CUST_TREE_EXPAND_SAVED_NODES USING L_WA_OUTLINEOLD.



The important functions here are:

*&---------------------------------------------------------------------*
*&      Form  CUST_TREE_SAVE_EXPANDED
*&---------------------------------------------------------------------*
*       Ausgeklappte Knoten für den Refresh speichern
*       Das Problem ist, dass ein Refresh alle Knoten löscht und neu
*       anlegt aber die Schlüssel der Knoten einfach fortzählt.
*       Deshalb sind die gespeicherten Schlüssel unbrauchbar und
*       eine Verbindung muss über die Wertetupel des eigentlichen
*       Tabelleninhalte geschaffen werden um die Zeilen nach dem
*       neuaufbau wieder zu finden.
*----------------------------------------------------------------------*
*      <--C_IT_OUTLINEOLD  Tabelle mit den Realen ausgeklappten zeilen
*----------------------------------------------------------------------*
FORM CUST_TREE_SAVE_EXPANDED
          CHANGING C_IT_OUTLINEOLD TYPE TP_T_TREE_TAB.

  DATA:       L_IT_NODES      TYPE LVC_T_NKEY,
              L_WA_NODE       TYPE LVC_NKEY,
              L_WA_OUTLINEOLD TYPE TP_TREE_TAB.

  "Expandierte Knoten lesen
  CALL METHOD REF_TREE->GET_EXPANDED_NODES
    CHANGING
      CT_EXPANDED_NODES L_IT_NODES
    EXCEPTIONS
      CNTL_SYSTEM_ERROR 1
      DP_ERROR          2
      FAILED            3
      OTHERS            4.
  IF SY-SUBRC <> 0.
    MESSAGE ID SY-MSGID TYPE 'S' NUMBER SY-MSGNO
                       WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4
                         DISPLAY LIKE SY-MSGTY.
  ENDIF.

  "Knoten in Inbhaltstabelle Übersetzen
  LOOP AT L_IT_NODES INTO L_WA_NODE.
    REF_TREE->GET_OUTTAB_LINE(
      EXPORTING
        I_NODE_KEY     =   L_WA_NODE  " node key
      IMPORTING
        E_OUTTAB_LINE  L_WA_OUTLINEOLD
      EXCEPTIONS
        NODE_NOT_FOUND 1
        OTHERS         2
    ).

    IF SY-SUBRC <> 0.
      MESSAGE ID SY-MSGID TYPE 'S' NUMBER SY-MSGNO
                 WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4
                 DISPLAY LIKE SY-MSGTY.
    ENDIF.

    "Durch die Inhaltstabelle mit Wertetupeln wird der Bezug zu den
    "neuen Knoten hergestellt
    APPEND L_WA_OUTLINEOLD TO C_IT_OUTLINEOLD.

  ENDLOOP.


ENDFORM.                    " CUST_TREE_SAVE_EXPANDED
*&---------------------------------------------------------------------*
*&      Form  CUST_TREE_EXPAND_SAVED_NODES
*&---------------------------------------------------------------------*
*       Ausklappen der gespeicherten Tree Zeilen. Ausfühliche besch-
*       reibung des Problem unter CUST_TREE_SAVE_EXPANDED.
*----------------------------------------------------------------------*
*      -->F_IT_OUTLINEOLD  Zeilen zum Ausklappen
*----------------------------------------------------------------------*
FORM CUST_TREE_EXPAND_SAVED_NODES
              USING    F_IT_OUTLINEOLD TYPE TP_T_TREE_TAB.


  DATA:       L_IT_NODES      TYPE LVC_T_NKEY,
              L_WA_NODE       TYPE LVC_NKEY,
              L_WA_OUTLINEOLD TYPE TP_TREE_TAB,
              L_WA_TREETABNEW TYPE TP_TREE_TAB.

  "Inhaltstabelle lesen und die neuen entsprechenden Knoten ausklappen
  LOOP AT F_IT_OUTLINEOLD INTO L_WA_OUTLINEOLD.

    READ TABLE  IT_TREE_TAB
                INTO L_WA_TREETABNEW
                WITH KEY  TKNUM L_WA_OUTLINEOLD-TKNUM
                          VENUM L_WA_OUTLINEOLD-VENUM
                          EXIDV L_WA_OUTLINEOLD-EXIDV
                          LINETYPE L_WA_OUTLINEOLD-LINETYPE.

    IF SY-SUBRC 0.
      APPEND L_WA_TREETABNEW-NODE_KEY TO L_IT_NODES.
    ENDIF.

  ENDLOOP.

  CALL METHOD REF_TREE->EXPAND_NODES
    EXPORTING
      IT_NODE_KEY             L_IT_NODES
    EXCEPTIONS
      FAILED                  1
      CNTL_SYSTEM_ERROR       2
      ERROR_IN_NODE_KEY_TABLE 3
      DP_ERROR                4
      NODE_NOT_FOUND          5
      OTHERS                  6.
  IF SY-SUBRC <> 0.
    MESSAGE ID SY-MSGID TYPE 'S' NUMBER SY-MSGNO
                   WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4
                         DISPLAY LIKE SY-MSGTY.
  ENDIF.

  "Update der Darstellung
  REF_TREE->FRONTEND_UPDATE).

ENDFORM.                    " CUST_TREE_EXPAND_SAVED_NODES


Feel free to comment, especially for better solutions for this problem. I need to add, since the framework is in between, we are not able to use other refresh functions, but i've heard that there is a different solution where you don't use DELETE_ALL_NODES but i think any solutions using the keys might be faulty due to possible changes in the structure resulting in wrong node keys for a line.


Friday, November 7, 2014

ALV Grids in Reports

Sometimes you want to use an ALV Grid directly as output in a report (not a Dynpro).

The magic function to this is:

**ALVGrid 
  CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
    EXPORTING
      IT_FIELDCAT   L_IT_FIELDCAT
      I_GRID_TITLE  L_VAR_TITLE
    TABLES
      T_OUTTAB      <L_VAR_OUTTAB>
    EXCEPTIONS
      PROGRAM_ERROR 1
      OTHERS        2.
  IF SY-SUBRC <> 0.
    MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
            WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
  ENDIF.

You can generate your Fieldcat wth another Helpful function:
**Generate Fieldcat
  CALL FUNCTION 'REUSE_ALV_FIELDCATALOG_MERGE'
    EXPORTING
      I_PROGRAM_NAME         SY-CPROG
      I_INTERNAL_TABNAME     L_VAR_TABNAME
      I_INCLNAME             SY-CPROG
    CHANGING
      CT_FIELDCAT            L_IT_FIELDCAT
    EXCEPTIONS
      INCONSISTENT_INTERFACE 1
      PROGRAM_ERROR          2
      OTHERS                 3.
  IF SY-SUBRC <> 0.
    MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
            WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
  ENDIF.

As you can see here,the Fieldcat is generated from a local internam table. It is mandatory to give the include the where the table is defined. If the data structure is not in DDIC but local as well you need to give the include where it is defined, or include it in TOP. Very important is, that local structures are only read correctly when defined with LIKE and INCLUDE structure as Datatypes. TYPES and normalINCLUDE wont work, since the ddic information is not read there. Its also written in the Manual of the function. So always RTFM cause i did it way too late ;)

Here a sample as an MBEWH Extension is locally defined:

DATABEGIN OF TP_MBEWH_EXTENDED.
" MBEWH
        INCLUDE STRUCTURE MBEWH.
" MARA with Key MATNR
DATA:     YYBEZBE   LIKE  MARA-YYBEZBE,
          MTART     LIKE  MARA-MTART,
          MATKL     LIKE  MARA-MATKL,
          MEINS     LIKE  MARA-MEINS,
"T001 with Key BUKRS from T001K  with Key BWKEY
          WAERS     LIKE  T001-WAERS,
"T023T with Key MATKL
          WGBEZ     LIKE  T023T-WGBEZ.
DATAEND OF TP_MBEWH_EXTENDED.

I also discussed reordering the Catalog from the selection screen here:
http://abapify.blogspot.de/2014/10/alv-sorting-with-reordering-of-fieldcat.html



As Update here is a small piece of Code to dynamically alocate Tables and Fieldcats to 3 different Tables:

FORM BUILD_ALV.

  CHECK P_TABLE IS NOT INITIAL.

  DATAL_IT_FIELDCAT       TYPE SLIS_T_FIELDCAT_ALV,
        L_VAR_TABNAME       TYPE SLIS_TABNAME,
        L_VAR_TITLE         TYPE LVC_TITLE,
        L_VAR_LINE_CNT_OUT  TYPE LENGTH 20.

  FIELD-SYMBOLS<L_VAR_OUTTAB> TYPE STANDARD TABLE.

**Je nach Auwahl der Tabelle wird die Struktur für den Feldkatalog und
* die zugehörige Ergebnis iTab  für das ALV gewählt.
  CASE P_TABLE.
    WHEN 'S31'.
      L_VAR_TABNAME 'TP_S031_EXTENDED'.
      ASSIGN IT_S031_EXTENDED  TO <L_VAR_OUTTAB>.
    WHEN 'S32'.
      L_VAR_TABNAME 'TP_S032_EXTENDED'.
      ASSIGN IT_S032_EXTENDED  TO <L_VAR_OUTTAB>.
    WHEN 'MBH'.
      L_VAR_TABNAME 'TP_MBEWH_EXTENDED'.
      ASSIGN IT_MBEWH_EXTENDED TO <L_VAR_OUTTAB>.
  ENDCASE.

**Feldkatalog schlicht nach Tabelle erstellen
  CALL FUNCTION 'REUSE_ALV_FIELDCATALOG_MERGE'
    EXPORTING
      I_PROGRAM_NAME         SY-CPROG
      I_INTERNAL_TABNAME     L_VAR_TABNAME
      I_INCLNAME             SY-CPROG
    CHANGING
      CT_FIELDCAT            L_IT_FIELDCAT
    EXCEPTIONS
      INCONSISTENT_INTERFACE 1
      PROGRAM_ERROR          2
      OTHERS                 3.
  IF SY-SUBRC <> 0.
    MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
            WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
  ENDIF.

  "Feldkatalog individuelle Sortierung
  CASE P_TABLE.
    WHEN 'S31'.
      PERFORM REORDER_FIELDCAT_S031 CHANGING L_IT_FIELDCAT.
    WHEN 'S32'.
      PERFORM REORDER_FIELDCAT_S032 CHANGING L_IT_FIELDCAT.
    WHEN 'MBH'.
      PERFORM REORDER_FIELDCAT_MBEWH CHANGING L_IT_FIELDCAT.
  ENDCASE.

*Anzahl der Zeilen
  DESCRIBE TABLE <L_VAR_OUTTAB> LINES L_VAR_LINE_CNT_OUT.

***Tabellenname in den Titel nehmen
*  CONCATENATE TEXT-100 L_VAR_TABNAME L_VAR_LINE_CNT_OUT TEXT-101
*      INTO L_VAR_TITLE RESPECTING BLANKS.


**ALVGrid generieren.
  CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
    EXPORTING
      IT_FIELDCAT   L_IT_FIELDCAT
      I_GRID_TITLE  L_VAR_TITLE
    TABLES
      T_OUTTAB      <L_VAR_OUTTAB>
    EXCEPTIONS
      PROGRAM_ERROR 1
      OTHERS        2.
  IF SY-SUBRC <> 0.
    MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
            WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
  ENDIF.

ENDFORM.                    "DISPLAY_ALV_REPORT



Then at last you might want to reorder the Fieldcat positions in your Output afterwards. Therem ight be better ways to do this (comments welcome), but most of what i tried before was very complicated an not error prone. The problem are some Entry which are also hidden an not seen and mostly have empty Tabnames. So i just went and took the First entry i knew, reordered everything from this Sy-Tabix on forward and renumerated everythin in the end.

*&---------------------------------------------------------------------*
*&      Form  REORDER_FIELDCAT_MBEWH
*&---------------------------------------------------------------------*
* Feldkatalog anpassungen:
*----------------------------------------------------------------------*
*      <--P_L_IT_FIELDCAT  Feldcatalog zu ändern
*----------------------------------------------------------------------*
FORM REORDER_FIELDCAT_MBEWH CHANGING C_IT_FIELDCAT
                                    TYPE SLIS_T_FIELDCAT_ALV.


  DATAL_WA_FIELDCAT_TMP TYPE SLIS_FIELDCAT_ALV,
        L_VAR_TABIX       TYPE SY-INDEX,
        L_VAR_COL_POS_CNT TYPE VALUE 1.

  FIELD-SYMBOLS <FS_WA_FIELDCAT_POS> TYPE SLIS_FIELDCAT_ALV.

  "Index der Material Spalte lesen (MATNR)
  READ TABLE  C_IT_FIELDCAT
           WITH KEY FIELDNAME 'MATNR' TRANSPORTING NO FIELDS.
  L_VAR_TABIX SY-TABIX + .

  "WGBEZ
  READ TABLE  C_IT_FIELDCAT
            INTO L_WA_FIELDCAT_TMP
            WITH KEY FIELDNAME 'WGBEZ'.
  IF SY-SUBRC 0.
    "Löschen und neu einfügen
    DELETE C_IT_FIELDCAT
        WHERE FIELDNAME 'WGBEZ'.
    INSERT L_WA_FIELDCAT_TMP  INTO C_IT_FIELDCAT INDEX L_VAR_TABIX.
  ENDIF.

  "MATKL verschieben
  READ TABLE  C_IT_FIELDCAT
              INTO L_WA_FIELDCAT_TMP
              WITH KEY FIELDNAME 'MATKL'.
  IF SY-SUBRC 0.
    "Löschen und neu einfügen
    DELETE C_IT_FIELDCAT
        WHERE FIELDNAME 'MATKL'.
    INSERT L_WA_FIELDCAT_TMP  INTO C_IT_FIELDCAT INDEX L_VAR_TABIX.
  ENDIF.

  "MTART
  READ TABLE  C_IT_FIELDCAT
            INTO L_WA_FIELDCAT_TMP
            WITH KEY FIELDNAME 'MTART'.
  IF SY-SUBRC 0.
    "Löschen und neu einfügen
    DELETE C_IT_FIELDCAT
        WHERE FIELDNAME 'MTART'.
    INSERT L_WA_FIELDCAT_TMP  INTO C_IT_FIELDCAT INDEX L_VAR_TABIX.
  ENDIF.

  "MAKTX
  READ TABLE  C_IT_FIELDCAT
            INTO  L_WA_FIELDCAT_TMP
            WITH KEY FIELDNAME 'MAKTX'.
  IF SY-SUBRC 0.
    "Löschen und neu einfügen
    DELETE C_IT_FIELDCAT
        WHERE FIELDNAME 'MAKTX'.
    INSERT L_WA_FIELDCAT_TMP  INTO C_IT_FIELDCAT INDEX L_VAR_TABIX.
  ENDIF.



  "Index der Periode Spalte lesen (LFMON)
  READ TABLE  C_IT_FIELDCAT
           WITH KEY FIELDNAME 'LFMON' TRANSPORTING NO FIELDS.
  L_VAR_TABIX SY-TABIX + .

  "WAERS
  READ TABLE  C_IT_FIELDCAT
            INTO L_WA_FIELDCAT_TMP
            WITH KEY FIELDNAME 'WAERS'.
  IF SY-SUBRC 0.
    "Löschen und neu einfügen
    DELETE C_IT_FIELDCAT
        WHERE FIELDNAME 'WAERS'.
    INSERT L_WA_FIELDCAT_TMP  INTO C_IT_FIELDCAT INDEX L_VAR_TABIX.
  ENDIF.

  "MEINS
  READ TABLE  C_IT_FIELDCAT
            INTO  L_WA_FIELDCAT_TMP
            WITH KEY FIELDNAME 'MEINS'.
  IF SY-SUBRC 0.
    "Löschen und neu einfügen
    DELETE C_IT_FIELDCAT
        WHERE FIELDNAME 'MEINS'.
    INSERT L_WA_FIELDCAT_TMP  INTO C_IT_FIELDCAT INDEX L_VAR_TABIX.
  ENDIF.

  "Positionen neu schreiben
  LOOP AT C_IT_FIELDCAT ASSIGNING <FS_WA_FIELDCAT_POS>
     WHERE TABNAME NE ''.
    <FS_WA_FIELDCAT_POS>-COL_POS L_VAR_COL_POS_CNT.
    L_VAR_COL_POS_CNT L_VAR_COL_POS_CNT + 1.
  ENDLOOP.

ENDFORM.                    " REORDER_FIELDCAT_MBEWH