Копание в данных SAP

в 9:20, , рубрики: abap, ERP-системы, sap, макросы, метки: , ,

В качестве промо-довеска к ранее опубликованной статье приложу немного опыта и исходного кода для специфической аудитории, а именно пользователей SAP. Однажды мне пришлось полгода позаниматься изучением и программированием в этой чудесной системе, а точнее в модуле CRM. Поскольку, с одной стороны, средства для броузинга данных в SAP крайне скудны, а, с другой, я был избалован собственными привычками делать все мышкой, однажды от скуки я занялся любимым делом — «Свободой выборки», но в ABAP & light версии.
В SAPе таблиц какие-то жуткие тысячи штук, а названия у них не самые удобные и очевидные. Нормализация присутствует в изобилии. Поэтому понять, что же лежит в этой записи таблицы, скажем, BUT_000, можно лишь с некоторой натяжкой. Недолго думая, используя богатые средства рефлексии и метапрограммирования древнего как мамонт языка ABAP и сдабривая это всеми любимым alv_grid'ом я «слабал» некий код, позволяющий просматривать значения ссылок едва ли не на любую глубину, используя метаданные связей.
На память, интерфейс z-отчета это не про кассы, о, случайно зашедший! следующий:

  • имя стартовой таблицы
  • дополнительное условие ограничения
  • количество строк

В открывшемся гриде, равно как и во вложенных, двойной клик по «ссылочному» полю проваливается в таблицу. Остальное уже забыл, не исключено, что это не все вкусности.Изучайте, внедряйте рефакторите, надеюсь вы сможете сами. Удачи!

Итак, исходный код на ABAP
*&---------------------------------------------------------------------*
*& Report  Z_GREAT_ALV_TMP
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*

REPORT  z_great_alv_tmp.

DATA:
      gv_ok             LIKE sy-ucomm,
      gv_container      TYPE REF TO cl_gui_custom_container,
      gv_container_name TYPE scrfname VALUE 'CONTAINER_ALV',
      gv_grid           TYPE REF TO cl_gui_alv_grid,
      gt_fieldcat       TYPE lvc_t_fcat,
      gs_layout         TYPE lvc_s_layo.

*&---------------------------------------------------------------------*
*&  Include           Z_GREAT_ALV_F01
*&---------------------------------------------------------------------*

FORM create_alv
  USING
      container_name TYPE scrfname
  CHANGING
      container TYPE REF TO cl_gui_custom_container
      grid TYPE REF TO cl_gui_alv_grid.
  "если грида еще нет
  IF container IS NOT BOUND.

    "создать контейнер
    CREATE OBJECT container
      EXPORTING
        container_name = container_name
      EXCEPTIONS
        OTHERS         = 1.
    IF sy-subrc NE 0.
    ENDIF.

  ENDIF.

  IF container IS BOUND.
    "создать грид
*    if grid is bound.
*      free grid.
*    endif.

    IF grid IS NOT BOUND.
      CREATE OBJECT grid
        EXPORTING
          i_parent = container
        EXCEPTIONS
          OTHERS   = 1.
      PERFORM set_great_handler.
    ENDIF.

    IF sy-subrc NE 0.
    ENDIF.

  ENDIF.

ENDFORM.                    "create_alv

*&---------------------------------------------------------------------*
*&      Form  set_great_handlers
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*

*&---------------------------------------------------------------------*
*&      Form  display_alv
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->GRID       text
*      -->TAB        text
*----------------------------------------------------------------------*
FORM display_alv
  CHANGING
        grid TYPE REF TO cl_gui_alv_grid
        tab TYPE ANY TABLE.

  IF grid IS BOUND.
    "заполнить свойства колонок
*    PERFORM prepare_field_catalog CHANGING gt_fieldcat.
*
    "главный метод для показа грида
    CALL METHOD grid->set_table_for_first_display
      EXPORTING" i_structure_name = gv_tablename
        is_layout                     = gs_layout
      CHANGING
        it_outtab                     = tab
        it_fieldcatalog               = gt_fieldcat
      EXCEPTIONS
        invalid_parameter_combination = 1
        program_error                 = 2
        too_many_lines                = 3
        OTHERS                        = 4.
    IF sy-subrc NE 0.
    ENDIF.

*    "обновить данные
*    CALL METHOD grid->refresh_table_display
*      EXCEPTIONS
*        finished       = 1
*        OTHERS         = 2.
*
*    IF sy-subrc NE 0.
*    ENDIF.

  ENDIF.

ENDFORM.                    "display_alv

*&---------------------------------------------------------------------*
*&      Form  prepare_field_catalog
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->PT_FIELDCAT  text
*----------------------------------------------------------------------*
FORM prepare_field_catalog
  USING
    tabname TYPE dd02l-tabname
    itab TYPE rs_bool
  CHANGING
    fieldcat TYPE lvc_t_fcat .

  REFRESH fieldcat.

  IF itab IS INITIAL.
    CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
      EXPORTING
        i_structure_name = tabname
      CHANGING
        ct_fieldcat      = fieldcat
      EXCEPTIONS
        OTHERS           = 1.
    FIELD-SYMBOLS: <fs_fcat> TYPE lvc_s_fcat.
    LOOP AT fieldcat ASSIGNING <fs_fcat>.
      <fs_fcat>-edit = 'X'.
    ENDLOOP.
  ELSE.
    DATA: lv_field TYPE lvc_s_fcat.

    lv_field-fieldname = 'FK_TABNAME'.
    lv_field-scrtext_s = 'TABNAME'.
    lv_field-inttype = 'C'.
    lv_field-outputlen = 50.
    APPEND lv_field TO fieldcat.

    lv_field-fieldname = 'FIELDNAME'.
    lv_field-scrtext_s = 'FIELDNAME'.
    lv_field-outputlen = 30.
    APPEND lv_field TO fieldcat.

  ENDIF.
  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.                    "prepare_field_catalog

*&---------------------------------------------------------------------*
*&      Form  great_alv
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*

*&---------------------------------------------------------------------*
*&  Include           Z_GREAT_ALV_CL_DICTSERVICES
*&---------------------------------------------------------------------*

CLASS z_cl_great_alv_dictservices DEFINITION.
  PUBLIC SECTION.
    TYPES:
      BEGIN OF z_fk_pair,
        pk_name TYPE dd03l-fieldname,
        fk_name TYPE dd03l-fieldname,
      END OF z_fk_pair,
      z_fk_pair_list TYPE STANDARD TABLE OF z_fk_pair,
      BEGIN OF z_dependent_rec,
        pk_tabname TYPE dd02l-tabname,
        fk_tabname TYPE dd02l-tabname,
        fieldname TYPE dd03l-fieldname,
        frkart TYPE dd08l-frkart,
        fieldlist TYPE REF TO data,"z_fk_pair_list,
      END OF z_dependent_rec,
      z_dependent_rec_list TYPE STANDARD TABLE OF z_dependent_rec.
    CLASS-METHODS:
      get_fk_list
        IMPORTING
          tabname TYPE dd02l-tabname
          fieldname TYPE dd03l-fieldname
        EXPORTING
          dependent_rec TYPE z_dependent_rec,
      get_check_fk_list
        IMPORTING
          tabname TYPE dd02l-tabname
          fieldname TYPE dd03l-fieldname
        EXPORTING
          dependent_rec TYPE z_dependent_rec,
      get_dependent_list
        IMPORTING
          tabname TYPE dd02l-tabname
        EXPORTING
          tablist TYPE z_dependent_rec_list.
ENDCLASS.                    "z_cl_great_alv_dictservices DEFINITION

*----------------------------------------------------------------------*
*       CLASS z_cl_great_alv_dictservices IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS z_cl_great_alv_dictservices IMPLEMENTATION.
  METHOD get_fk_list.

    dependent_rec-fk_tabname = tabname.
    dependent_rec-fieldname = fieldname.

    SELECT SINGLE checktable frkart FROM dd08l INTO (dependent_rec-pk_tabname, dependent_rec-frkart) WHERE tabname = tabname AND fieldname = fieldname.
    IF sy-subrc = 0 AND dependent_rec-pk_tabname <> '*'.

      DATA: ls_fk_pair TYPE z_fk_pair,
            lv_primpos TYPE dd05s-primpos.
      FIELD-SYMBOLS: <fs_pair_list> TYPE z_fk_pair_list.
      CREATE DATA dependent_rec-fieldlist TYPE z_fk_pair_list.
      ASSIGN dependent_rec-fieldlist->* TO <fs_pair_list>.

      SELECT primpos forkey FROM dd05s INTO (lv_primpos, ls_fk_pair-fk_name) WHERE tabname = tabname AND fieldname = fieldname.
        SELECT SINGLE fieldname FROM dd03l INTO ls_fk_pair-pk_name WHERE tabname = dependent_rec-pk_tabname AND position = lv_primpos.
        IF sy-subrc = 0.
          APPEND ls_fk_pair TO <fs_pair_list>.
        ENDIF.
      ENDSELECT.

    ENDIF.

  ENDMETHOD.                    "get_fk_list

  METHOD get_check_fk_list.
    dependent_rec-fk_tabname = tabname.
    dependent_rec-fieldname = fieldname.

    DATA: lv_domname TYPE dd03l-domname.
    SELECT SINGLE domname FROM dd03l INTO lv_domname WHERE tabname = tabname AND fieldname = fieldname.
    IF sy-subrc = 0 AND lv_domname IS NOT INITIAL.

      SELECT SINGLE entitytab FROM dd01l INTO dependent_rec-pk_tabname WHERE domname = lv_domname.
      IF sy-subrc = 0 AND dependent_rec-pk_tabname IS NOT INITIAL.

        DATA: ls_fk_pair TYPE z_fk_pair.
        FIELD-SYMBOLS: <fs_pair_list> TYPE z_fk_pair_list.
        CREATE DATA dependent_rec-fieldlist TYPE z_fk_pair_list.
        ASSIGN dependent_rec-fieldlist->* TO <fs_pair_list>.

        SELECT SINGLE fieldname FROM dd03l INTO ls_fk_pair-pk_name WHERE tabname = dependent_rec-pk_tabname AND domname = lv_domname AND keyflag = 'X'.
        IF sy-subrc = 0.
          ls_fk_pair-fk_name = fieldname.
          APPEND ls_fk_pair TO <fs_pair_list>.
        ENDIF.

      ENDIF.
    ENDIF.
  ENDMETHOD.                    "get_check_fk_list

  METHOD get_dependent_list.
    DATA: ls_rec TYPE z_dependent_rec.
    REFRESH tablist.
    SELECT tabname fieldname frkart FROM dd08l INTO (ls_rec-fk_tabname, ls_rec-fieldname, ls_rec-frkart) WHERE checktable = tabname.
      get_fk_list( EXPORTING
        tabname = ls_rec-fk_tabname
        fieldname = ls_rec-fieldname
      IMPORTING
        dependent_rec = ls_rec ).
      IF ls_rec-fieldlist IS BOUND.
        APPEND ls_rec TO tablist.
        CLEAR ls_rec.
      ENDIF.
    ENDSELECT.
  ENDMETHOD.                    "get_dependent_list
ENDCLASS.                    "z_cl_great_alv_dictservices IMPLEMENTATION

** METHOD forward_zalx.
**    DATA:
**          lv_rowtype TYPE zalx_clnt_genfld-rowtype,
**          lv_fkrowtype TYPE zalx_clnt_genfld-fkrowtype,
**          lv_fkrowpos TYPE zalx_clnt_genfld-fkrowpos,
**          lv_structname TYPE zalx_clnt_genrel-structname,
**          lv_tablename TYPE dd02l-tabname,
**          lv_condition TYPE string,
**          ls_nav_point TYPE z_nav_point.
**    READ TABLE nav_stack INTO ls_nav_point INDEX nav_position.
**    ls_nav_point-row_id = row_id.
**    ls_nav_point-key_field = fieldname.
**    MODIFY nav_stack INDEX nav_position FROM ls_nav_point.
**
**    IF sy-subrc = 0.
**      SELECT SINGLE rowtype FROM zalx_clnt_genrel INTO lv_rowtype WHERE structname = ls_nav_point-tab_name.
**      IF sy-subrc = 0.
**        SELECT SINGLE fkrowtype fkrowpos FROM zalx_clnt_genfld INTO (lv_fkrowtype, lv_fkrowpos) WHERE rowtype = lv_rowtype AND fieldname = fieldname.
**        IF sy-subrc = 0.
**          SELECT SINGLE structname FROM zalx_clnt_genrel INTO lv_structname WHERE rowtype = lv_fkrowtype.
**          IF sy-subrc = 0.
**
**            table_name = lv_structname.
**            SELECT SINGLE fieldname FROM zalx_clnt_genfld INTO lv_condition WHERE rowtype = lv_fkrowtype AND rowpos = lv_fkrowpos.
**            IF sy-subrc  = 0.
**              WRITE lv_structname.
**              FIELD-SYMBOLS: <ff> TYPE ANY,
**                             <fs> TYPE ANY,
**                             <lfs_table> TYPE STANDARD TABLE.
**
**              ASSIGN ls_nav_point-tab_table->* TO <lfs_table>.
**              READ TABLE <lfs_table> ASSIGNING <fs> INDEX row_id-row_id.
**              ASSIGN COMPONENT fieldname OF STRUCTURE <fs> TO <ff>.
**              CONCATENATE lv_condition ' = ''' <ff> '''' INTO lv_condition.
**
**              CLEAR ls_nav_point.
**
**              lv_tablename = lv_structname.
**              doselect( EXPORTING tablename = lv_tablename condition = lv_condition
**                IMPORTING data_table = ls_nav_point-tab_table ).
**
**              data_table = ls_nav_point-tab_table.
**              ls_nav_point-tab_name = lv_structname.
**
**              APPEND ls_nav_point TO nav_stack.
**              ADD 1 TO nav_position.
**
**            ENDIF.
**
**          ENDIF.
**
**        ENDIF.
**      ENDIF.
**    ENDIF.
**
**  ENDMETHOD.                    "forward
*&---------------------------------------------------------------------*
*&  Include           Z_GREAT_ALV_CL
*&---------------------------------------------------------------------*

*&---------------------------------------------------------------------*
*&  Include           Z_GREAT_ALV_CL_NAVSTACK
*&---------------------------------------------------------------------*

CLASS z_cl_great_alv_navstack DEFINITION.
  PUBLIC SECTION.
    TYPES:
      BEGIN OF z_nav_point,
        tab_name TYPE dd02l-tabname,
        key_field TYPE lvc_s_col,
        row_id TYPE lvc_s_roid,
        tab_table TYPE REF TO data,
        dependent TYPE rs_bool,
      END OF z_nav_point.
    CLASS-METHODS:
      init
        IMPORTING
          tablename TYPE dd02l-tabname
          reccount TYPE i
          condition TYPE string
        RETURNING
          value(ok) TYPE rs_bool,
      follow_fk
        IMPORTING
          fieldname TYPE lvc_s_col
          row_id TYPE lvc_s_roid
        RETURNING
          value(ok) TYPE rs_bool,
      choose_dependent
        IMPORTING
          fieldname TYPE lvc_s_col
          row_id TYPE lvc_s_roid
          table_name TYPE dd02l-tabname OPTIONAL
        RETURNING
          value(ok) TYPE rs_bool,
      follow_pk
        IMPORTING
          deprec TYPE z_cl_great_alv_dictservices=>z_dependent_rec
          row_id TYPE lvc_s_roid
        RETURNING
          value(ok) TYPE rs_bool,
      follow_text
        IMPORTING
          row_id TYPE lvc_s_roid
        RETURNING
          value(ok) TYPE rs_bool,
      back,
      current_nav_point RETURNING
        value(point) TYPE z_nav_point,
      follow_zalx
        IMPORTING
          fieldname TYPE lvc_s_col
          row_id TYPE lvc_s_roid
        RETURNING
          value(ok) TYPE rs_bool,
      get_condition_string IMPORTING
        row_id TYPE i
        deprec TYPE z_cl_great_alv_dictservices=>z_dependent_rec
        data_table TYPE REF TO data OPTIONAL
        dependent TYPE rs_bool OPTIONAL
        RETURNING value(condition) TYPE string.


  PRIVATE SECTION.
    CLASS-DATA:
      nav_stack TYPE TABLE OF z_nav_point,
      nav_position TYPE i,
      rec_count TYPE i.
    CLASS-METHODS:
      doselect
        IMPORTING
          tablename TYPE dd02l-tabname
          condition TYPE string OPTIONAL
        EXPORTING
          data_table TYPE REF TO data.

ENDCLASS.                    "Z_CL_GREAT_ALV_NAVSTACK DEFINITION

*----------------------------------------------------------------------*
*       CLASS Z_CL_GREAT_ALV_NAVSTACK IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS z_cl_great_alv_navstack IMPLEMENTATION.
  METHOD init.
    REFRESH: nav_stack.
    DATA:  ls_nav_point TYPE z_nav_point.
    rec_count = reccount.

    doselect( EXPORTING tablename = tablename condition = condition
      IMPORTING data_table = ls_nav_point-tab_table ).

    IF ls_nav_point-tab_table IS BOUND.
      ls_nav_point-tab_name = tablename.
      APPEND ls_nav_point TO nav_stack.
      nav_position = 1.
      ok = 'X'.
    ENDIF.

  ENDMETHOD.                    "init

  METHOD doselect.
    CREATE DATA data_table TYPE TABLE OF (tablename).
    FIELD-SYMBOLS: <fsv_table> TYPE STANDARD TABLE.
    ASSIGN data_table->* TO <fsv_table>.

    IF condition IS SUPPLIED.
      SELECT * UP TO rec_count ROWS FROM (tablename) INTO TABLE <fsv_table> WHERE (condition).
    ELSE.
      SELECT * UP TO rec_count ROWS FROM (tablename) INTO TABLE <fsv_table>.
    ENDIF.
  ENDMETHOD.                    "doselect


  METHOD follow_fk.

    DATA: ls_dep TYPE z_cl_great_alv_dictservices=>z_dependent_rec,
          ls_nav_point TYPE z_nav_point,
          lv_colname TYPE lvc_s_col,
          lv_condition TYPE string.

    ls_nav_point = current_nav_point( ).
    ls_nav_point-row_id = row_id.
    ls_nav_point-key_field = fieldname.
    MODIFY nav_stack INDEX nav_position FROM ls_nav_point.

    IF ls_nav_point-dependent IS INITIAL.

      z_cl_great_alv_dictservices=>get_fk_list( EXPORTING
          tabname = ls_nav_point-tab_name
          fieldname = fieldname-fieldname
        IMPORTING
          dependent_rec = ls_dep ).

      IF ls_dep-fieldlist IS NOT BOUND.

        z_cl_great_alv_dictservices=>get_check_fk_list( EXPORTING
            tabname = ls_nav_point-tab_name
            fieldname = fieldname-fieldname
          IMPORTING
            dependent_rec = ls_dep ).

      ENDIF.

      IF ls_dep-fieldlist IS BOUND.

        lv_condition = get_condition_string(
          row_id = row_id-row_id
          deprec = ls_dep ).

        CLEAR ls_nav_point.

        doselect( EXPORTING tablename = ls_dep-pk_tabname condition = lv_condition
          IMPORTING data_table = ls_nav_point-tab_table ).

        IF ls_nav_point-tab_table IS BOUND.
          ls_nav_point-tab_name = ls_dep-pk_tabname.

          APPEND ls_nav_point TO nav_stack.
          ADD 1 TO nav_position.
          ok = 'X'.
        ENDIF.

      ENDIF.

    ELSE.

      FIELD-SYMBOLS: <fs_dep> TYPE z_cl_great_alv_dictservices=>z_dependent_rec_list.
      ASSIGN ls_nav_point-tab_table->* TO <fs_dep>.
      IF <fs_dep> IS ASSIGNED.
        READ TABLE <fs_dep> INTO ls_dep INDEX row_id-row_id.
        IF sy-subrc = 0.
          back( ).
          ls_nav_point = current_nav_point( ).
          lv_colname-fieldname = ls_dep-fieldname.
          ok = follow_pk( deprec = ls_dep
            row_id = ls_nav_point-row_id
             ).
        ENDIF.
      ENDIF.

    ENDIF.

  ENDMETHOD.                    "follow_fk

  METHOD choose_dependent.
    DATA: ls_nav_point TYPE z_nav_point,
          lv_dep_list TYPE REF TO data.
    FIELD-SYMBOLS <fs_deplist> TYPE z_cl_great_alv_dictservices=>z_dependent_rec_list.
    ls_nav_point = current_nav_point( ).
    ls_nav_point-row_id = row_id.
    ls_nav_point-key_field = fieldname.
    MODIFY nav_stack INDEX nav_position FROM ls_nav_point.

    CREATE DATA lv_dep_list TYPE z_cl_great_alv_dictservices=>z_dependent_rec_list.
    ASSIGN lv_dep_list->* TO <fs_deplist>.
    z_cl_great_alv_dictservices=>get_dependent_list(
      EXPORTING
        tabname = ls_nav_point-tab_name
      IMPORTING
        tablist = <fs_deplist> ).

    IF lv_dep_list IS NOT INITIAL.
      SORT <fs_deplist>  BY fk_tabname ASCENDING.
      CLEAR ls_nav_point.

      ls_nav_point-tab_table = lv_dep_list.
      ls_nav_point-dependent = 'X'.

      APPEND ls_nav_point TO nav_stack.
      ADD 1 TO nav_position.
      ok = 'X'.
    ENDIF.

  ENDMETHOD.                    "choose_dependent

  METHOD follow_pk.
    DATA: lv_condition TYPE string,
          ls_nav_point TYPE z_nav_point.

    ls_nav_point = current_nav_point( ).
    ls_nav_point-row_id = row_id.
    MODIFY nav_stack INDEX nav_position FROM ls_nav_point.

    lv_condition = get_condition_string(
      row_id = row_id-row_id
      deprec = deprec
      dependent = 'X' ).

    CLEAR ls_nav_point.

    doselect( EXPORTING tablename = deprec-fk_tabname
      condition = lv_condition
      IMPORTING data_table = ls_nav_point-tab_table ).

    IF ls_nav_point-tab_table IS BOUND.
      ls_nav_point-tab_name = deprec-fk_tabname.

      APPEND ls_nav_point TO nav_stack.
      ADD 1 TO nav_position.
      ok = 'X'.
    ENDIF.
  ENDMETHOD.                    "follow_pk

  METHOD follow_text.
    DATA: ls_nav_point TYPE z_nav_point,
          lv_colname TYPE lvc_s_col,
          lt_dep_list TYPE z_cl_great_alv_dictservices=>z_dependent_rec_list,
          ls_dep_list TYPE LINE OF z_cl_great_alv_dictservices=>z_dependent_rec_list.
    ls_nav_point = current_nav_point( ).
    z_cl_great_alv_dictservices=>get_dependent_list(
      EXPORTING
        tabname = ls_nav_point-tab_name
      IMPORTING
        tablist = lt_dep_list ).

    LOOP AT lt_dep_list INTO ls_dep_list WHERE frkart = 'TEXT'.
      lv_colname-fieldname = ls_dep_list-fieldname.
      ok = follow_pk( deprec = ls_dep_list
        row_id = row_id
         ).
      EXIT. "only one text table, if any
    ENDLOOP.

  ENDMETHOD.                    "follow_text

  METHOD back.
    IF nav_position > 1.
      DELETE nav_stack INDEX nav_position.
      SUBTRACT 1 FROM nav_position.
    ENDIF.
  ENDMETHOD.                    "back

  METHOD current_nav_point.
    READ TABLE nav_stack INTO point INDEX nav_position.
  ENDMETHOD.                    "current_table_name

  METHOD get_condition_string.

    FIELD-SYMBOLS: <ff> TYPE ANY,
                   <fs> TYPE ANY,
                   <lfs_table> TYPE STANDARD TABLE,
                   <lfs_fkrec> TYPE z_cl_great_alv_dictservices=>z_fk_pair_list.
    DATA: ls_navpoint TYPE z_nav_point,
          ls_fkpair TYPE z_cl_great_alv_dictservices=>z_fk_pair,
          lv_condition TYPE string,
          lt_condition TYPE TABLE OF string.
    ls_navpoint = current_nav_point( ).
    ASSIGN ls_navpoint-tab_table->* TO <lfs_table>.
    READ TABLE <lfs_table> ASSIGNING <fs> INDEX row_id.

    ASSIGN deprec-fieldlist->* TO <lfs_fkrec>.
    LOOP AT <lfs_fkrec> INTO ls_fkpair.
      IF dependent IS INITIAL.
        ASSIGN COMPONENT ls_fkpair-fk_name OF STRUCTURE <fs> TO <ff>.
        CONCATENATE ls_fkpair-pk_name ' = ''' <ff> '''' INTO lv_condition.
      ELSE.
        ASSIGN COMPONENT ls_fkpair-pk_name OF STRUCTURE <fs> TO <ff>.
        CONCATENATE ls_fkpair-fk_name ' = ''' <ff> '''' INTO lv_condition.
      ENDIF.
      APPEND lv_condition TO lt_condition.
    ENDLOOP.

    CONCATENATE LINES OF lt_condition INTO condition SEPARATED BY ' AND '.

  ENDMETHOD.                    "get_condition_string


  METHOD follow_zalx.
    DATA:
          lv_rowtype TYPE zalx_clnt_genfld-rowtype,
          lv_fkrowtype TYPE zalx_clnt_genfld-fkrowtype,
          lv_fkrowpos TYPE zalx_clnt_genfld-fkrowpos,
          lv_structname TYPE zalx_clnt_genrel-structname,
          lv_tablename TYPE dd02l-tabname,
          lv_condition TYPE string,
          ls_nav_point TYPE z_nav_point.
    READ TABLE nav_stack INTO ls_nav_point INDEX nav_position.
    ls_nav_point-row_id = row_id.
    ls_nav_point-key_field = fieldname.
    MODIFY nav_stack INDEX nav_position FROM ls_nav_point.

    IF sy-subrc = 0.
      SELECT SINGLE rowtype FROM zalx_clnt_genrel INTO lv_rowtype WHERE structname = ls_nav_point-tab_name.
      IF sy-subrc = 0.
        SELECT SINGLE fkrowtype fkrowpos FROM zalx_clnt_genfld INTO (lv_fkrowtype, lv_fkrowpos) WHERE rowtype = lv_rowtype AND fieldname = fieldname.
        IF sy-subrc = 0.
          SELECT SINGLE structname FROM zalx_clnt_genrel INTO lv_structname WHERE rowtype = lv_fkrowtype.
          IF sy-subrc = 0.

            "table_name = lv_structname.
            SELECT SINGLE fieldname FROM zalx_clnt_genfld INTO lv_condition WHERE rowtype = lv_fkrowtype AND rowpos = lv_fkrowpos.
            IF sy-subrc  = 0.
              WRITE lv_structname.
              FIELD-SYMBOLS: <ff> TYPE ANY,
                             <fs> TYPE ANY,
                             <lfs_table> TYPE STANDARD TABLE.

              ASSIGN ls_nav_point-tab_table->* TO <lfs_table>.
              READ TABLE <lfs_table> ASSIGNING <fs> INDEX row_id-row_id.
              ASSIGN COMPONENT fieldname OF STRUCTURE <fs> TO <ff>.
              CONCATENATE lv_condition ' = ''' <ff> '''' INTO lv_condition.

              CLEAR ls_nav_point.

              lv_tablename = lv_structname.
              doselect( EXPORTING tablename = lv_tablename condition = lv_condition
                IMPORTING data_table = ls_nav_point-tab_table ).

              IF ls_nav_point-tab_table IS BOUND.
                ls_nav_point-tab_name = lv_structname.

                APPEND ls_nav_point TO nav_stack.
                ADD 1 TO nav_position.
                ok = 'X'.
              ENDIF.

            ENDIF.

          ENDIF.

        ENDIF.
      ENDIF.
    ENDIF.

  ENDMETHOD.                    "forward
ENDCLASS.                    "Z_CL_GREAT_ALV_NAVSTACK IMPLEMENTATION

*----------------------------------------------------------------------*
*       CLASS z_great_alv_handler DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS z_great_alv_handler DEFINITION.
  PUBLIC SECTION.
    CLASS-DATA:
      zf_texting TYPE stb_button-function VALUE 'TEXTING',
      zf_zalx TYPE stb_button-function VALUE 'ZALX',
      zf_depend TYPE stb_button-function VALUE 'DEPEND',
      zf_follow TYPE stb_button-function VALUE 'FOLLOW'.
    METHODS:
      double_click FOR EVENT double_click OF cl_gui_alv_grid
        IMPORTING e_row e_column es_row_no,
      toolbar_modify FOR EVENT toolbar OF cl_gui_alv_grid
        IMPORTING e_object e_interactive,
      user_command FOR EVENT user_command OF cl_gui_alv_grid
        IMPORTING e_ucomm,
      data_changed FOR EVENT data_changed OF cl_gui_alv_grid
        IMPORTING er_data_changed e_onf4 e_onf4_before e_onf4_after e_ucomm.
ENDCLASS.                    "Z_GREAT_ALV_HANDLER DEFINITION

*----------------------------------------------------------------------*
*       CLASS Z_GREAT_ALV_HANDLER IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS z_great_alv_handler IMPLEMENTATION.
  METHOD double_click.
    DATA: lv_ok TYPE rs_bool.
    lv_ok = z_cl_great_alv_navstack=>follow_fk(
      fieldname = e_column
      row_id = es_row_no ).

    IF lv_ok = 'X'.
      CALL SCREEN 100.
    ENDIF.

  ENDMETHOD.                    "DOUBLE_CLICK

  METHOD toolbar_modify.
    DATA: lv_tbutton TYPE stb_button.

    LOOP AT e_object->mt_toolbar INTO lv_tbutton WHERE function = zf_texting.
    ENDLOOP.

    IF sy-subrc <> 0.

      CLEAR lv_tbutton.
      MOVE 0 TO lv_tbutton-butn_type.
      MOVE zf_follow TO lv_tbutton-function.
      MOVE 'Follow foreign key/check table' TO lv_tbutton-text.
      MOVE 'Follow foreign key/check table' TO lv_tbutton-quickinfo.
      MOVE space TO lv_tbutton-disabled.

      APPEND lv_tbutton TO e_object->mt_toolbar.

      CLEAR lv_tbutton.
      MOVE 0 TO lv_tbutton-butn_type.
      MOVE zf_texting TO lv_tbutton-function.
      MOVE 'Text table record' TO lv_tbutton-text.
      MOVE 'Text table record' TO lv_tbutton-quickinfo.
      MOVE space TO lv_tbutton-disabled.

      APPEND lv_tbutton TO e_object->mt_toolbar.

      CLEAR lv_tbutton.
      MOVE 0 TO lv_tbutton-butn_type.
      MOVE zf_depend TO lv_tbutton-function.
      MOVE 'Dependent tables' TO lv_tbutton-text.
      MOVE 'Dependent tables' TO lv_tbutton-quickinfo.
      MOVE space TO lv_tbutton-disabled.

      APPEND lv_tbutton TO e_object->mt_toolbar.

      CLEAR lv_tbutton.
      MOVE 0 TO lv_tbutton-butn_type.
      MOVE zf_zalx TO lv_tbutton-function.
      MOVE 'ZALX' TO lv_tbutton-text.
      MOVE 'ZALX' TO lv_tbutton-quickinfo.
      MOVE space TO lv_tbutton-disabled.

      APPEND lv_tbutton TO e_object->mt_toolbar.

    ENDIF.

  ENDMETHOD.                    "toolbar_modify

  METHOD user_command.

    DATA: lv_table  TYPE REF TO data,
          lv_column TYPE lvc_s_col,
          lv_rowno  TYPE lvc_s_roid,
          lv_ok     TYPE rs_bool.
    gv_grid->get_current_cell( IMPORTING es_col_id = lv_column
      es_row_no = lv_rowno ).

    CASE e_ucomm.
      WHEN zf_texting.
        lv_ok = z_cl_great_alv_navstack=>follow_text(
          row_id = lv_rowno ).
      WHEN zf_zalx.
        lv_ok = z_cl_great_alv_navstack=>follow_zalx(
        fieldname = lv_column
        row_id = lv_rowno ).
      WHEN zf_depend.
        lv_ok = z_cl_great_alv_navstack=>choose_dependent(
          fieldname = lv_column
          row_id = lv_rowno ).
      WHEN zf_follow.
        lv_ok = z_cl_great_alv_navstack=>follow_fk(
        fieldname = lv_column
        row_id = lv_rowno ).
      WHEN OTHERS.
        EXIT.
    ENDCASE.

    IF lv_ok = 'X'.
      CALL SCREEN 100.
    ENDIF.

  ENDMETHOD.                    "user_command

  METHOD data_changed.
    DATA: ls_nav_point TYPE z_cl_great_alv_navstack=>z_nav_point,
          ls_deleted_row TYPE lvc_s_moce.
    FIELD-SYMBOLS: <fst> TYPE STANDARD TABLE,
                   <fs> TYPE ANY.
    "modify
    ls_nav_point = z_cl_great_alv_navstack=>current_nav_point( ).
    ASSIGN er_data_changed->mp_mod_rows->* TO <fst>.
    MODIFY (ls_nav_point-tab_name) FROM TABLE <fst>.

    "delete
    ASSIGN ls_nav_point-tab_table->* TO <fst>.
    LOOP AT er_data_changed->mt_deleted_rows INTO ls_deleted_row.
      READ TABLE <fst> ASSIGNING <fs> INDEX ls_deleted_row-row_id.
      IF sy-subrc = 0.
        DELETE (ls_nav_point-tab_name) FROM <fs>.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.                    "data_changed

ENDCLASS.                    "Z_GREAT_ALV_HANDLER IMPLEMENTATION

*----------------------------------------------------------------------*
*  MODULE PAI_01 INPUT
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
MODULE pai_01 INPUT.

  CASE gv_ok.
    WHEN 'BACK'.
      z_cl_great_alv_navstack=>back( ).
      PERFORM great_alv.
    WHEN '%EX'.
      LEAVE PROGRAM.
  ENDCASE.

ENDMODULE.                 " PAI_01  INPUT

*----------------------------------------------------------------------*
*  MODULE PBO_01 OUTPUT
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
MODULE pbo_01 OUTPUT.

  CLEAR gv_ok.
  SET PF-STATUS ''.
  PERFORM great_alv.

ENDMODULE.                 " PBO_01  OUTPUT

*&---------------------------------------------------------------------*
*&      Form  set_great_handler
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM set_great_handler.
  DATA lv_handler TYPE REF TO z_great_alv_handler.
  CREATE OBJECT lv_handler.
  IF gv_grid IS BOUND AND lv_handler IS BOUND.
    SET HANDLER lv_handler->double_click FOR gv_grid.
    SET HANDLER lv_handler->toolbar_modify FOR gv_grid.
    SET HANDLER lv_handler->user_command FOR gv_grid.
    SET HANDLER lv_handler->data_changed FOR gv_grid.
    "grid->set_toolbar_interactive( ).
  ENDIF.
ENDFORM.                    "set_great_handlers

*&---------------------------------------------------------------------*
*&      Form  great_alv
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM great_alv.

  DATA: lv_nav_point TYPE z_cl_great_alv_navstack=>z_nav_point.

  lv_nav_point = z_cl_great_alv_navstack=>current_nav_point( ).

  PERFORM create_alv USING gv_container_name CHANGING gv_container gv_grid.
  .
  PERFORM prepare_field_catalog USING lv_nav_point-tab_name lv_nav_point-dependent CHANGING gt_fieldcat.

  FIELD-SYMBOLS: <lfs_table> TYPE ANY TABLE.
  ASSIGN  lv_nav_point-tab_table->* TO <lfs_table>.

  PERFORM display_alv USING gv_grid <lfs_table>.

  gv_grid->set_current_cell_via_id( EXPORTING "is_row_no = lv_nav_point-row_id
     is_column_id = lv_nav_point-key_field
     is_row_no = lv_nav_point-row_id ).

ENDFORM.                    "great_alv



PARAMETERS: tabname LIKE dd02l-tabname DEFAULT 'but050',
            rccount TYPE i DEFAULT 100,
            condit TYPE string DEFAULT ''.

START-OF-SELECTION.

  DATA: lv_ok TYPE rs_bool.

  lv_ok = z_cl_great_alv_navstack=>init(
      tablename = tabname
      reccount = rccount
      condition = condit
     ).

  IF lv_ok = 'X'.
    CALL SCREEN 100.
  ENDIF.


Автор: NMM

Источник

  1. Diamond:

    Спасибо Автору ! Прекрасная статья !

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js