ABAP 소스 테스트에 유용한 SALV 간단 출력 Class
CL_SALV_TABLE
ZCL_SALV_TEST
ZCL_SALV_OUTPUT
# 2023.09.20 수정 : Internal Table뿐만 아니라 Structure도 사용할 수 있도록 기능 개선
# 2023.09.12 수정 : ALV 리스트 더블클릭시 SE16N 처럼 '변환값'/'비변환값'을 보여주는 팝업 기능 추가
# 2025.04.10 수정 : fieldname이 없는 table_line 형식의 Internal Table로 표시되도록 로직 추가
로컬 오브젝트로 ABAP 소스 테스트 할때 결과를 확인하기 위해 WRITE 문이나 CL_DEMO_OUTPUT=>DISPLAY( ) 를 많이들 애용합니다.
간단한 내용은 WRITE나 CL_DEMO_OUTPUT을 사용해도 충분하지만, Internal Table의 데이터를 보기에는 ALV만큼 편한게 없죠.
그래서 자주 사용하는 만큼 최대한 사용하기 간단하고 구조도 심플하게 SALV를 이용하여 ALV간단출력 Class를 만들었습니다.
참고로, SQL 쿼리문만 테스트를 하는 경우 ZTOAD 사용 추천드립니다!!
New Open SQL에 대해 안되는게 좀 있긴 하지만 그래도 급한게 사용할때 아주 유용하죠
http://quelquepart.biz/article7/ztoad-requeteur-open-sql
ZTOAD : Requêteur Open SQL - Quelquepart
quelquepart.biz
아래 이미지는 SALV 간단출력 Class의 출력 화면 예시입니다.
Internal Table 내용 확인이 주 목적인 만큼 최대한 심플하게 만들었습니다.
#1. SALV 간단 출력 Class
열 텍스트는 테스트 편의성을 위해 필드명으로 변경하여 표시됩니다.
class ZCL_SALV_OUTPUT definition
public
create public .
public section.
class-methods DISPLAY
importing
value(T_TABLE) type ANY .
class-methods DISPLAY_NO_CONV
importing
value(T_TABLE) type ANY .
PROTECTED SECTION.
private section.
class-data MD_TABLE type ref to DATA .
class-methods SALV_OUTPUT
importing
value(T_TABLE) type ANY
!I_NO_CONV type C .
class-methods HANDLE_DOUBLE_CLICK
for event DOUBLE_CLICK of CL_SALV_EVENTS_TABLE
importing
!ROW
!COLUMN .
ENDCLASS.
CLASS ZCL_SALV_OUTPUT IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_SALV_OUTPUT=>DISPLAY
* +-------------------------------------------------------------------------------------------------+
* | [--->] T_TABLE TYPE ANY
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD display.
salv_output( EXPORTING t_table = t_table
i_no_conv = space ).
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_SALV_OUTPUT=>DISPLAY_NO_CONV
* +-------------------------------------------------------------------------------------------------+
* | [--->] T_TABLE TYPE ANY
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD display_no_conv.
salv_output( EXPORTING t_table = t_table
i_no_conv = 'X' ).
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_SALV_OUTPUT=>HANDLE_DOUBLE_CLICK
* +-------------------------------------------------------------------------------------------------+
* | [--->] ROW LIKE
* | [--->] COLUMN LIKE
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD handle_double_click.
TYPES: BEGIN OF t_popup,
position TYPE tabfdpos,
fieldname TYPE fieldname,
fieldtext TYPE as4text,
value_wc TYPE string, "변환값
value_nc TYPE string, "비변환값
cfield_v TYPE string, "통화/단위 값
convexit TYPE convexit,
tabname TYPE tabname,
reftable TYPE reftable,
reffield TYPE reffield,
END OF t_popup.
DATA: lt_popup TYPE STANDARD TABLE OF t_popup,
ls_popup LIKE LINE OF lt_popup.
DATA: lv_field(255) TYPE c,
lv_funcname TYPE rs38l_fnam.
DATA: lv_char(1024) TYPE c,
lv_datum TYPE sy-datum,
lv_uzeit TYPE sy-uzeit,
lv_currency TYPE tcurc-waers,
lv_unit TYPE mara-meins.
DATA lv_rowidx(20) TYPE c.
FIELD-SYMBOLS: <lft> TYPE table,
<lfv> TYPE any,
<lfv_waers> TYPE any.
IF row IS INITIAL.
EXIT.
ENDIF.
WRITE row TO lv_rowidx. CONDENSE lv_rowidx.
CLEAR lt_popup.
ASSIGN md_table->* TO <lft>.
DATA(lt_dfies) = cl_salv_ddic=>get_by_data( <lft> ).
READ TABLE <lft> ASSIGNING FIELD-SYMBOL(<lfs>) INDEX row.
IF sy-subrc NE 0.
EXIT.
ENDIF.
DELETE lt_dfies WHERE inttype EQ cl_abap_typedescr=>typekind_table
OR inttype EQ cl_abap_typedescr=>typekind_struct1
OR inttype EQ cl_abap_typedescr=>typekind_struct2.
LOOP AT lt_dfies INTO DATA(ls_dfies).
ls_popup = CORRESPONDING #( ls_dfies ).
ls_popup-position = sy-tabix.
lv_field = |<LFS>-{ ls_dfies-fieldname }|.
UNASSIGN <lfv>.
ASSIGN (lv_field) TO <lfv>.
IF <lfv> IS NOT ASSIGNED.
APPEND ls_popup TO lt_popup. CLEAR ls_popup.
CONTINUE.
ENDIF.
MOVE <lfv> TO ls_popup-value_wc.
MOVE <lfv> TO ls_popup-value_nc.
TRY.
IF ls_popup-convexit IS NOT INITIAL.
lv_funcname = |CONVERSION_EXIT_{ ls_popup-convexit }_OUTPUT|.
TRY.
CALL FUNCTION lv_funcname
EXPORTING
input = <lfv>
IMPORTING
output = lv_char
EXCEPTIONS
OTHERS = 1.
MOVE lv_char TO ls_popup-value_wc.
CATCH cx_root INTO DATA(lo_cx_alpha1).
ENDTRY.
ELSEIF ls_popup-reffield IS NOT INITIAL AND ls_dfies-reftable EQ ls_dfies-precfield.
lv_field = |<LFS>-{ ls_popup-reffield }|.
UNASSIGN <lfv_waers>.
ASSIGN (lv_field) TO <lfv_waers>.
IF <lfv_waers> IS NOT ASSIGNED.
APPEND ls_popup TO lt_popup. CLEAR ls_popup.
CONTINUE.
ENDIF.
MOVE <lfv_waers> TO ls_popup-cfield_v.
IF ls_dfies-datatype EQ 'CURR'.
MOVE <lfv_waers> TO lv_currency.
WRITE <lfv> TO lv_char CURRENCY lv_currency. CONDENSE lv_char.
MOVE lv_char TO ls_popup-value_wc.
ELSEIF ls_dfies-datatype EQ 'QUAN'.
MOVE <lfv_waers> TO lv_unit.
WRITE <lfv> TO lv_char UNIT lv_unit. CONDENSE lv_char.
MOVE lv_char TO ls_popup-value_wc.
ENDIF.
ELSEIF ls_dfies-inttype EQ cl_abap_typedescr=>typekind_int
OR ls_dfies-inttype EQ cl_abap_typedescr=>typekind_int1
OR ls_dfies-inttype EQ cl_abap_typedescr=>typekind_int8
OR ls_dfies-inttype EQ cl_abap_typedescr=>typekind_int2
OR ls_dfies-inttype EQ cl_abap_typedescr=>typekind_float
OR ls_dfies-inttype EQ cl_abap_typedescr=>typekind_packed.
WRITE <lfv> TO lv_char. CONDENSE lv_char.
MOVE lv_char TO ls_popup-value_wc.
ELSEIF ls_dfies-inttype EQ cl_abap_typedescr=>typekind_num.
SHIFT ls_popup-value_wc LEFT DELETING LEADING '0'.
ELSEIF ls_dfies-inttype EQ cl_abap_typedescr=>typekind_date.
MOVE <lfv> TO lv_datum.
ls_popup-value_wc = |{ lv_datum DATE = USER }|.
ELSEIF ls_dfies-inttype EQ cl_abap_typedescr=>typekind_time.
MOVE <lfv> TO lv_uzeit.
ls_popup-value_wc = |{ lv_uzeit TIME = USER }|.
ENDIF.
CATCH cx_root INTO DATA(ls_cx).
ENDTRY.
APPEND ls_popup TO lt_popup. CLEAR ls_popup.
ENDLOOP.
IF lt_popup[] IS INITIAL.
EXIT.
ENDIF.
TRY.
cl_salv_table=>factory(
IMPORTING
r_salv_table = DATA(lr_salv)
CHANGING
t_table = lt_popup ). "ALV로 출력할 Internal Table
CATCH CX_SALV_MSG.
RETURN.
ENDTRY.
lr_salv->get_functions( )->set_all( ). "ALV의 기능키 표시
lr_salv->get_display_settings( )->set_list_header( CONV #( |Selected row index : { lv_rowidx }| ) ). "TITLE
DATA: lv_ltext TYPE scrtext_l,
lr_column TYPE REF TO cl_salv_column_table.
DATA(lr_columns) = lr_salv->get_columns( ).
LOOP AT lr_columns->get( ) INTO DATA(ls_columns).
CLEAR lv_ltext.
TRY.
lr_column ?= lr_columns->get_column( ls_columns-columnname ).
CATCH CX_SALV_NOT_FOUND.
CONTINUE.
ENDTRY.
CASE ls_columns-columnname.
WHEN 'FIELDNAME'.
lr_column->set_key( abap_true ).
WHEN 'VALUE_WC'.
lv_ltext = SWITCH #( sy-langu WHEN '3' THEN '값' ELSE 'Value' ).
lr_column->set_color( VALUE #( col = 5 int = 0 inv = 0 ) ).
WHEN 'VALUE_NC'.
lv_ltext = SWITCH #( sy-langu WHEN '3' THEN '비변환값' ELSE 'Value Unconverted' ).
lr_column->set_color( VALUE #( col = 5 int = 0 inv = 0 ) ).
WHEN 'CFIELD_V'.
lv_ltext = SWITCH #( sy-langu WHEN '3' THEN '참조값' ELSE 'Ref. value' ).
lr_column->set_color( VALUE #( col = 3 int = 0 inv = 0 ) ).
WHEN 'REFTABLE' OR 'REFFIELD'.
lr_column->set_color( VALUE #( col = 3 int = 0 inv = 0 ) ).
ENDCASE.
IF lv_ltext IS NOT INITIAL.
lr_column->set_long_text( CONV #( lv_ltext ) ).
lr_column->set_medium_text( CONV #( lv_ltext ) ).
lr_column->set_short_text( CONV #( lv_ltext ) ).
ENDIF.
ENDLOOP.
lr_salv->get_columns( )->set_optimize( abap_true ). "열 너비 최적화
lr_salv->set_screen_popup(
start_column = 1
end_column = 140
start_line = 1
end_line = 30 ).
lr_salv->display( ).
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_SALV_OUTPUT=>SALV_OUTPUT
* +-------------------------------------------------------------------------------------------------+
* | [--->] T_TABLE TYPE ANY
* | [--->] I_NO_CONV TYPE C
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD salv_output.
FIELD-SYMBOLS: <lft> TYPE STANDARD TABLE.
DATA: ld_itab TYPE REF TO data.
DATA: lo_type TYPE REF TO cl_abap_typedescr,
lo_line_type TYPE REF TO cl_abap_typedescr,
lo_table TYPE REF TO cl_abap_tabledescr,
lo_struc TYPE REF TO cl_abap_structdescr,
lo_data TYPE REF TO cl_abap_datadescr.
lo_type = cl_abap_typedescr=>describe_by_data( t_table ).
CASE lo_type->kind.
WHEN cl_abap_typedescr=>kind_table.
lo_table ?= lo_type.
lo_line_type = lo_table->get_table_line_type( ).
IF lo_line_type->kind EQ cl_abap_typedescr=>kind_struct.
lo_struc ?= lo_line_type.
lo_table = cl_abap_tabledescr=>create( lo_struc ).
CREATE DATA ld_itab TYPE HANDLE lo_table.
ASSIGN ld_itab->* TO <lft>.
<lft> = CORRESPONDING #( t_table ).
ELSE.
lo_data ?= lo_line_type.
lo_struc = cl_abap_structdescr=>create( VALUE #( ( name = 'TABLE_LINE_VALUE'
type = lo_data ) ) ).
lo_table = cl_abap_tabledescr=>create( lo_struc ).
CREATE DATA ld_itab TYPE HANDLE lo_table.
ASSIGN ld_itab->* TO <lft>.
LOOP AT t_table ASSIGNING FIELD-SYMBOL(<lfs_table>).
APPEND INITIAL LINE TO <lft> ASSIGNING FIELD-SYMBOL(<lfs>).
ASSIGN COMPONENT 1 OF STRUCTURE <lfs> TO FIELD-SYMBOL(<lfv>) ELSE UNASSIGN.
IF <lfv> IS ASSIGNED.
<lfv> = <lfs_table>.
ENDIF.
ENDLOOP.
ENDIF.
WHEN cl_abap_typedescr=>kind_struct.
CREATE DATA ld_itab LIKE TABLE OF t_table.
ASSIGN ld_itab->* TO <lft>.
APPEND t_table TO <lft>.
WHEN OTHERS.
cl_demo_output=>display( t_table ).
RETURN.
ENDCASE.
GET REFERENCE OF <lft> INTO md_table.
TRY.
cl_salv_table=>factory(
IMPORTING
r_salv_table = DATA(lr_salv)
CHANGING
t_table = <lft> ). "ALV로 출력할 Internal Table
CATCH cx_salv_msg.
RETURN.
ENDTRY.
DATA lv_rowcnt(20) TYPE c.
WRITE lines( <lft> ) TO lv_rowcnt. CONDENSE lv_rowcnt.
lr_salv->get_functions( )->set_all( ). "ALV의 기능키 표시
lr_salv->get_selections( )->set_selection_mode( if_salv_c_selection_mode=>row_column ). "선택 모드
lr_salv->get_display_settings( )->set_striped_pattern( cl_salv_display_settings=>true ). "줄무늬 패턴
lr_salv->get_display_settings( )->set_list_header( CONV #( |{ sy-cprog } - Result : { lv_rowcnt } rows| ) ). "TITLE
DATA(lr_columns) = lr_salv->get_columns( ).
LOOP AT lr_columns->get( ) INTO DATA(ls_columns). "열 텍스트를 필드명으로 변경
TRY.
DATA(lr_column) = lr_columns->get_column( ls_columns-columnname ).
CATCH cx_salv_not_found.
CONTINUE.
ENDTRY.
DATA(lv_inttype) = lr_column->get_ddic_inttype( ).
IF lv_inttype EQ cl_abap_typedescr=>typekind_table OR
lv_inttype EQ cl_abap_typedescr=>typekind_struct1 OR
lv_inttype EQ cl_abap_typedescr=>typekind_struct2.
lr_column->set_technical( abap_true ).
ENDIF.
lr_column->set_short_text( CONV #( ls_columns-columnname ) ).
lr_column->set_medium_text( CONV #( ls_columns-columnname ) ).
lr_column->set_long_text( CONV #( ls_columns-columnname ) ).
lr_column->set_sign( abap_true ).
IF i_no_conv EQ 'X'.
lr_column->set_edit_mask( space ).
TRY.
lr_column->set_quantity_column( space ).
CATCH cx_salv_not_found
cx_salv_data_error.
ENDTRY.
TRY.
lr_column->set_currency_column( space ).
CATCH cx_salv_not_found
cx_salv_data_error.
ENDTRY.
ENDIF.
ENDLOOP.
lr_salv->get_columns( )->set_optimize( abap_true ). "열 너비 최적화
DATA(lr_event_tab) = lr_salv->get_event( ).
SET HANDLER handle_double_click FOR lr_event_tab.
lr_salv->display( ). "ALV 출력
ENDMETHOD.
ENDCLASS.
#2. 사용예시
인터널테이블의 내용을 출력하고자 할때, ZCL_SALV_OUTPUT=>DISPLAY( 인터널테이블 ). 만 있으면 됩니다.
REPORT YTEMP_SANDBOX.
WITH +VBRP AS ( SELECT
A~VBELN,
A~FKDAT,
B~POSNR,
B~PAOBJNR,
B~NETWR,
A~WAERK
FROM VBRK AS A
INNER JOIN VBRP AS B ON A~VBELN EQ B~VBELN
WHERE A~WAERK EQ 'USD' ),
+BSEG AS ( SELECT
A~AWKEY,
B~BUZEI,
B~PAOBJNR AS PAOBJNR_FI,
B~WRBTR,
A~WAERS,
B~DMBTR
FROM BKPF AS A
INNER JOIN BSEG AS B ON A~BUKRS EQ B~BUKRS AND A~BELNR EQ B~BELNR AND A~GJAHR EQ B~GJAHR
WHERE A~AWTYP EQ 'VBRK' )
SELECT
*
FROM +VBRP AS A
LEFT OUTER JOIN +BSEG AS B ON A~VBELN EQ B~AWKEY AND A~PAOBJNR EQ B~PAOBJNR_FI
WHERE A~FKDAT BETWEEN '20221201' AND '20221231'
INTO TABLE @DATA(LT_TMP).
"Internal Table의 내용 SALV로 출력
ZCL_SALV_OUTPUT=>DISPLAY( LT_TMP[] ). "Conversion Routine 적용
ZCL_SALV_OUTPUT=>DISPLAY_NO_CONV( LT_TMP[] ). "Conversion Routine 미적용
'ABAP > 소스코드' 카테고리의 다른 글
[BTE] PROCESS_00001120 - 회계전표 품목 텍스트 대체 (0) | 2023.07.10 |
---|---|
[Report] ZBDC - 미리보기 기능 추가된 BDC 실행 프로그램 (0) | 2023.06.26 |
[Class] Progress Indicator 처리용 Common Class (0) | 2023.05.24 |
[Report] Client copy 후 번호범위 자동 조정 프로그램 (1) | 2023.05.24 |
[Include] Progress Indicator 처리용 Common Include (0) | 2023.05.10 |
댓글