ABAP OO-ALV 客制报表呈现

随着产业日渐成熟,客户对於需求也越来困难,ABAPER 为了生存不可避免也必须不断精进自我,而这篇文章也是自己在学习路上的一份积累。
报表程序是 ABAP 中最常开发的功能之一,其中 ALV 报表必是ABAPER 无法避免的课题,在使用者没有要求时,大多会CALL Function 来呈现,下方列出部分常用 ALV fcnction:

  1. REUSE_ALV_FIELDCATALOG_MERGE
  2. REUSE_ALV_GRID_DISPLAY
  3. REUSE_ALV_LIST_DISPLAY
  4. REUSE_ALV_GRID_DISPLAY_LVC

然而在部分需求中,如:搜寻条件要与 ALV 报表在相同介面时,此时便须要ALV-OO 才能达成目的
实现介面如下
https://ithelp.ithome.com.tw/upload/images/20210210/2013500885vS4WOcTw.png
https://ithelp.ithome.com.tw/upload/images/20210210/20135008PNa1Ow7FfD.png

在开始介绍范例程序时,会运用到下方观念

  1. Dynamic program
  2. Dialog program
  3. OOP 基础概念
    如想更加了解此类型程序执行方式,可以往此方向做研究。
    首先先附上程序码
    TOP 区
TYPE-POOLS:abap.


DATA: lt_table         LIKE TABLE OF dntab,
      ls_table         TYPE dntab,
      d_ref            TYPE REF TO data,
      lt_comp          TYPE abap_component_tab,
      lt_alv_cat       TYPE TABLE OF lvc_s_fcat,
      ls_alv_cat       LIKE LINE OF lt_alv_cat,
      gv_noinput       TYPE c,
      gv_modify        TYPE c.

*----- For Dialog Program Logic flow
FIELD-SYMBOLS <gv_screen_no> TYPE any ."VALUE '1000'.
DATA ok_code LIKE sy-ucomm.

*----- Field Symbol
FIELD-SYMBOLS: <dyn_table>     TYPE STANDARD TABLE ,
               <dyn_tmp_struc> TYPE any,
               <dyn_wa>        TYPE any ,
               <dyn_tmp_table> TYPE table.

*-------- For ALV
DATA: g_container           TYPE scrfname VALUE 'ALV_P',
      g_container_1         TYPE scrfname VALUE 'ALV_P2',
      grid1                 TYPE REF TO cl_gui_alv_grid,
      grid2                 TYPE REF TO cl_gui_alv_grid,
      gs_print              TYPE lvc_s_prnt,
      gs_print_1            TYPE lvc_s_prnt,
      gs_layout             TYPE lvc_s_layo,
      gs_layout_1           TYPE lvc_s_layo,
      g_custom_container_ma TYPE REF TO cl_gui_custom_container,
      g_custom_container    TYPE REF TO cl_gui_custom_container,
      lt_fieldcat           TYPE slis_t_fieldcat_alv WITH HEADER LINE,
      ir_data_changed       TYPE REF TO cl_alv_changed_data_protocol.
*----Excel open
DATA: lv_rc TYPE i.
DATA: lt_file_table TYPE filetable. 

**SEREEN **

SELECTION-SCREEN BEGIN OF SCREEN 1100 AS SUBSCREEN.
PARAMETERS: p_tab_n  LIKE tabname .
PARAMETERS: p_infile TYPE localfile DEFAULT 'C:\temp\upload.xlsx' .
  SELECTION-SCREEN  BEGIN OF  BLOCK lc_2 WITH FRAME TITLE TEXT-002 .
     PARAMETERS: r_ins   TYPE c RADIOBUTTON GROUP g1.
     PARAMETERS: r_mod   TYPE c RADIOBUTTON GROUP g1.
  SELECTION-SCREEN END OF  BLOCK lc_2.
SELECTION-SCREEN END OF SCREEN 1100 .

CALL SCREEN 1000.
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_infile.

  CALL METHOD cl_gui_frontend_services=>file_open_dialog
    EXPORTING
      window_title = '打开文件'
    CHANGING
      file_table   = lt_file_table
      rc           = lv_rc.
  IF sy-subrc = 0.
    READ TABLE lt_file_table INTO DATA(wa_file_table) INDEX 1.
    p_infile = wa_file_table-filename.
  ENDIF.

FROM and MODULE

*&---------------------------------------------------------------------*
*&      Form  authorization_check
*&---------------------------------------------------------------------*
*& Begin Date  : 2021/02/02
*& Author      : Kevin Yu
*& Description : 权限及输入条件确认及卡控
*----------------------------------------------------------------------*
*& Version     :
*----------------------------------------------------------------------*
FORM authorization_check.
 "  UNASSIGN <dyn_table>.
 "  UNASSIGN <dyn_tmp_struc>.
  IF p_tab_n IS INITIAL OR p_infile IS INITIAL.
     MESSAGE 'Table or Path cannot be null!!' TYPE 'I' DISPLAY LIKE 'E'.
  ELSE.

    SELECT SINGLE dd02l~tabname FROM dd02l INTO @DATA(lv_tabname)
           WHERE dd02l~tabname EQ @p_tab_n.

    IF sy-subrc EQ 0.
       PERFORM get_dynamic_itab.
       PERFORM excel_upload TABLES <dyn_table> .
       "CHECK <dyn_table> IS NOT INITIAL.
       PERFORM display_data USING <dyn_table> .
       IF     r_ins EQ 'X' AND r_mod  EQ ''.

       ELSEIF r_mod EQ 'X' AND r_ins  EQ ''.

       ENDIF.
    ELSE.
      MESSAGE 'Table Not Active, Please check!!' TYPE 'I' DISPLAY LIKE 'E'.
    ENDIF.
  ENDIF.
ENDFORM.

*&---------------------------------------------------------------------*
*&      Form  get_dynamic_itab
*&---------------------------------------------------------------------*
*& Begin Date  : 2021/02/02
*& Author      : Kevin Yu
*& Description : 依输入 TABLE 名产出对应栏位
*----------------------------------------------------------------------*
*& Version     :
*----------------------------------------------------------------------*
FORM get_dynamic_itab.

*取出结构字段目录
CALL FUNCTION 'NAMETAB_GET'
  EXPORTING
    langu                     = sy-langu
    tabname                   = p_tab_n

  TABLES
    nametab                   = lt_table.

  IF sy-subrc NE 0.
     MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
     WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

*依字段目录产出参考目录

CLEAR lt_alv_cat.
LOOP AT lt_table ASSIGNING FIELD-SYMBOL(<fs_table>).
   ls_alv_cat-fieldname = ls_alv_cat-ref_field = <fs_table>-fieldname.
   ls_alv_cat-ref_table = p_tab_n.
   APPEND ls_alv_cat TO lt_alv_cat.
   CLEAR: <fs_table>,ls_alv_cat.
ENDLOOP.
UNASSIGN <fs_table>.


CALL METHOD cl_alv_table_create=>create_dynamic_table
  EXPORTING
    it_fieldcatalog           = lt_alv_cat

  IMPORTING
    ep_table                  = d_ref.

  IF sy-subrc NE 0.
     MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
     WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.
ASSIGN d_ref->* TO <dyn_table>.
ASSIGN d_ref TO <dyn_tmp_struc>.

ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  excel_upload
*&---------------------------------------------------------------------*
*& Begin Date  : 2021/02/02
*& Author      : Kevin Yu
*& Description : 将 Excel data 从本地端传至宣告的 Table 中
*----------------------------------------------------------------------*
*& Version     :
*----------------------------------------------------------------------*
FORM excel_upload TABLES table.

 DATA: lt_data_in_file TYPE TABLE OF          zalsmex_tabline,
       ls_data_in_file TYPE                   zalsmex_tabline,
       lv_count        TYPE i,
       lv_count_2      TYPE i.
DATA truxs_t_text_data(4096) TYPE c OCCURS 0. "不影响回传资料,仅因 FUNCTION 需求而设
"DATA: gt_data         TYPE alsmex_tabline OCCURS 0 WITH HEADER LINE."不影响回传资料,仅因 FUNCTION 需求而设

CLEAR table.
CALL FUNCTION 'TEXT_CONVERT_XLS_TO_SAP'
    EXPORTING
*     I_FIELD_SEPERATOR          =
      i_line_header              =  'X'
      i_tab_raw_data             =  truxs_t_text_data
      i_filename                 =  p_infile
    TABLES
      i_tab_converted_data       =  table
   EXCEPTIONS
     conversion_failed          = 1
     OTHERS                     = 2
            .
  IF sy-subrc NE 0.
     MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
     WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.
ENDFORM.

*&---------------------------------------------------------------------*
*&      Form  display_data
*&---------------------------------------------------------------------*
*& Begin Date  : 2021/02/02
*& Author      : Kevin Yu
*& Description : 呈现 Excel 上传至 table 中的资料 --OO ALV
*----------------------------------------------------------------------*
*& Version     :
*----------------------------------------------------------------------*
FORM display_data USING table.



"如要再 PAI 做 OO ALV 跳转时,要REFRESH ALV 物件及 GUI
" refresh_table_display & SAPGUI_SET_FUNCTIONCODE
   IF grid1 IS INITIAL.
           g_custom_container       = NEW cl_gui_custom_container( g_container ).
           grid1                    = NEW cl_gui_alv_grid( g_custom_container  ).
           CALL METHOD grid1->set_table_for_first_display
               EXPORTING i_structure_name = p_tab_n
                         is_print         = gs_print
                         is_layout        = gs_layout
               CHANGING  it_outtab        = table.

           CALL METHOD grid1->refresh_table_display
              EXCEPTIONS
                 finished = 1
                 OTHERS   = 2.

    ELSE.
        CALL METHOD grid1->set_table_for_first_display
               EXPORTING i_structure_name = p_tab_n
                         is_print         = gs_print
                         is_layout        = gs_layout
               CHANGING  it_outtab        = table.

        CALL METHOD grid1->refresh_table_display
              EXCEPTIONS
                 finished = 1
                 OTHERS   = 2.

    ENDIF.
        IF sy-subrc <> 0.
* Implement suitable error handling here
    ENDIF.

CALL FUNCTION 'SAPGUI_SET_FUNCTIONCODE' " 人为触发 PBO
           EXPORTING
             functioncode           = 'REFRESH'
           EXCEPTIONS
             function_not_supported = 1
             OTHERS                 = 2.

ENDFORM.
*&---------------------------------------------------------------------*
*& Module PBO OUTPUT
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
MODULE pbo OUTPUT.
  SET PF-STATUS 'STANDARD'.
  IF gv_noinput EQ cl_abap_typedescr=>true.
   ASSIGN '1200' TO <gv_screen_no>.
  ELSE.
   ASSIGN '1100' TO <gv_screen_no>.
  ENDIF.
  CHECK grid1 IS NOT INITIAL.
*  IF  gv_modify EQ 'X'.
*    PERFORM alv_modify.
*  ENDIF.
  CALL METHOD grid1->refresh_table_display
  EXCEPTIONS
  finished = 1
  OTHERS   = 2.

ENDMODULE.
*&---------------------------------------------------------------------*
*&      Module  PAI  INPUT
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
MODULE pai INPUT.

"CALL METHOD cl_gui_cfw=>dispatch. "人为触发 PAI
  CLEAR gv_noinput.
 CASE ok_code.

    WHEN  'ENDE' OR 'ECAN'.
      PERFORM exit_program.
    WHEN  'EXE' OR 'ONLI' .
     PERFORM authorization_check.
    WHEN  'GET'.
      PERFORM exit_program.
    WHEN  'E'.
      IF gs_layout-edit EQ cl_abap_typedescr=>true. "'X'.
      gs_layout-edit = cl_abap_typedescr=>false.
      PERFORM authorization_check.
      ENDIF.
    WHEN  'CHANGE_MODE'.
      CHECK grid1 IS NOT INITIAL.
      gv_noinput = cl_abap_typedescr=>true. "X"
      gv_modify  = cl_abap_typedescr=>true. "X"
      PERFORM alv_modify TABLES <dyn_table>.
    WHEN  'SPOS'.
    PERFORM alv_modify TABLES <dyn_table>.
    WHEN 'ENTRY_DATA'.
    PERFORM data_entry.

  ENDCASE.
  CLEAR ok_code.
ENDMODULE.
FORM exit_program.
* CALL METHOD G_CUSTOM_CONTAINER->FREE.
*  CALL METHOD CL_GUI_CFW=>FLUSH.
  LEAVE TO SCREEN 0.
ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  alv_modify
*&---------------------------------------------------------------------*
*& Begin Date  : 2021/02/03
*& Author      : Kevin Yu
*& Description :
*----------------------------------------------------------------------*
*& Version     :
*----------------------------------------------------------------------*
FORM    alv_modify  TABLES pt_list .

IF gs_layout-edit = cl_abap_typedescr=>true.
   gs_layout-edit = cl_abap_typedescr=>false.
ELSE.
   gs_layout-edit = cl_abap_typedescr=>true.
ENDIF.

 CALL METHOD grid1->register_edit_event
    EXPORTING i_event_id = cl_gui_alv_grid=>mc_evt_enter.

 CALL METHOD grid1->set_table_for_first_display
               EXPORTING i_structure_name = p_tab_n
                         is_print         = gs_print
                         is_layout        = gs_layout
               CHANGING  it_outtab        = <dyn_table>.

 CALL METHOD grid1->refresh_table_display
              EXCEPTIONS
                 finished = 1
                 OTHERS   = 2.
ENDFORM.

首先为了使Selection Screen 与 ALV 呈现在相同介面
必须要先自制自己的 Screen,并将ALV 包入物件中,并使用Custom Control 来呈现 ALV 物件
https://ithelp.ithome.com.tw/upload/images/20210210/20135008RcKnUo20Ze.png
https://ithelp.ithome.com.tw/upload/images/20210210/20135008OyMxKsz5Jn.png

而为了使介面呈现Selection Screen 的部分,在PBO 及 PAI 皆要调用其作为subscreen,如此便可让两者同时
存在於同一画面中
https://ithelp.ithome.com.tw/upload/images/20210210/201350089i4v15mM1F.png

而接着便是创建 ALV 相关物件
可参照程序TOP做宣告类型 ,运用宣告类型如下

g_container           TYPE scrfname VALUE 'ALV_P'
grid1                 TYPE REF TO cl_gui_alv_grid
gs_print              TYPE lvc_s_prnt
gs_layout             TYPE lvc_s_layo
g_custom_container    TYPE REF TO cl_gui_custom_container

物件创建如下

           g_custom_container       = NEW cl_gui_custom_container( g_container ).
           grid1                    = NEW cl_gui_alv_grid( g_custom_container  ).

准备好相关参数後接着调用 Method->set_table_for_first_display
便可在所设计的custom control 位置中呈现 alv

 CALL METHOD grid1->set_table_for_first_display
               EXPORTING i_structure_name = p_tab_n
                         is_print         = gs_print
                         is_layout        = gs_layout
               CHANGING  it_outtab        = <dyn_table>.

而其中要注意的便是 alv 刷新问题
通常在刷新 alv 会调用Method->refresh_table_display

 CALL METHOD grid1->refresh_table_display
              EXCEPTIONS
                 finished = 1
                 OTHERS   = 2.

然而因此范例并非再 PBO 处理,故要自行触发PBO 刷新 GUI 介面

CALL FUNCTION 'SAPGUI_SET_FUNCTIONCODE' " 人为触发 PBO
           EXPORTING
             functioncode           = 'REFRESH'
           EXCEPTIONS
             function_not_supported = 1
             OTHERS                 = 2.

至於其他细节就提供给各位看官自行研究啦~


<<:  做一份与众不同的简历 (Create a Compelling CV)

>>:  番外篇(2)一起来做 To Do List!- 实作篇(1)

Day 10 Eventrouter + ELK + Filebeat 来收集k8s丛集的events

2021 铁人赛 DAY10 昨天介绍了eventrouter收集k8s丛集中的event,今天则是...

17.MYSQL AND指令

AND和&&是相同的意思 AND运算子是左右两边结果为1,就回传1,否则回传0 Ti...

CSS微动画 - Loading来了!终於要出款文字版本的了!

Q: 说好的文字Loading呢? A: 客倌这里上菜罗~ 前几篇的Loading都是以图案循环为...

风险的决策应在投资评估过程中行使

-什麽是风险? 选项B提供了最佳视角,但正确的版本应为“剩余风险低於董事会的风险承受能力。” 基於...