本篇来实现一个综合案例:管理费用明细表。报表在实际项目中,也有一定的参考意义,一方面展示类似的报表,比如管理费用、研发费用等费用的明细,使用业务比较习惯的展示格式;另一方面正好综合运用前面学习的动态编程知识点。
本系列博客的链接:
ABAP语言的动态编程(2) - field symbol 的典型用法_filed symbol-CSDN博客
ABAP语言的动态编程(3) - data reference 对象_abap for in a reference table-CSDN博客
本篇的主要要点:
- 期间费用等类似报表的取数方法
- 通过 RTTS 实现动态内表
- 如何删除动态内表的空值
- 通过 SALV 输出动态内表
本例实现的功能:
选择屏幕:

输出界面:

程序总体逻辑
- frm_get_data: 获取数据
- frm_add_dyn_fields:根据成本中心设置动态内表格式
- frm_process_data: 将数据加工成输出格式所需的格式
- frm_alv_show: 在 ALV 中显示数据
数据获取
数据比较简单,直接从 FAGLFLEXT 汇总表获取,也可以直接从 ACDOCA 表获取。
form frm_get_data .
ranges: lr_racct for faglflext-racct.
lr_racct[] = value #( ( sign = 'I' option = 'CP' low = '0080*' ) ).
* 从FAGLFLEXT获取数据
select rbukrs "公司代码
, racct as saknr "科目号
, rcntr as kostl "成本中心
, hsl01 "1月
, hsl02 "2月
, hsl03
, hsl04
, hsl05
, hsl06
, hsl07
, hsl08
, hsl09
, hsl10
, hsl11
, hsl12
from v_faglflext_view
where rbukrs in @s_bukrs
and ryear eq @p_ryear
and rldnr eq '0L'
and rrcty eq '0'
and rvers eq '001'
and racct in @lr_racct
into table @data(lt_faglflext).
* 按照gs_list格式对数据进行合计
loop at lt_faglflext into data(ls_faglflext).
move-corresponding ls_faglflext to gs_list.
collect gs_list into gt_list.
clear: ls_faglflext, gs_list.
endloop.
" 获取会计科目描述
select distinct saknr from @lt_faglflext as t into table @data(lt_saknr) .
select s~saknr,
s~txt20
from skat as s
inner join @lt_saknr as t on s~saknr eq t~saknr
where s~ktopl eq '1000'and s~spras eq @sy-langu
into table @data(lt_skat).
sort lt_skat by saknr.
" 获取成本中心描述
select distinct kostl from @lt_faglflext as t into table @data(lt_cntr). " cost center
select c~kostl, c~ktext
from cskt as c
inner join @lt_cntr as d on c~kostl eq d~kostl
where c~kokrs eq '1000' and c~spras eq @sy-langu and c~datbi ge @sy-datum
into table @gt_cskt.
sort gt_cskt by kostl.
" 将科目描述和成本中心描述填充到gt_list
loop at gt_list into gs_list.
if line_exists( lt_skat[ saknr = gs_list-saknr ] ).
gs_list-txt20 = lt_skat[ saknr = gs_list-saknr ]-txt20.
endif.
if line_exists( gt_cskt[ kostl = gs_list-kostl ] ).
gs_list-ltext = gt_cskt[ kostl = gs_list-kostl ]-ktext.
endif.
modify gt_list from gs_list.
clear gs_list.
endloop.
endform.
设置动态内表
输出的格式中,有些字段是确定的,通过 data 语句直接定义。而每一个成本中心都需要增加一个动态列,在程序中动态设定,关键就是构建动态内表的组件 (gt_component, 类型为 cl_abap_structdescr=>component_table
)
预先定义的部分:
* 用于数据输出
data: begin of gs_output,
rbukrs type faglflext-rbukrs, "公司代码
racct type faglflext-racct, "科目号
txt20 type skat-txt20, "科目名称
hsl_sum type faglflext-hsl12, "合计
end of gs_output.
data gt_output like standard table of gs_output.
程序动态设置的部分:
*--------------------------------------------------------
* 基于有数据发生的成本中心,将成本中心加到输出字段中
*--------------------------------------------------------
form frm_add_dyn_fields .
data: ls_dref type ref to data,
lt_dref type ref to data.
data: lo_strcut_descr type ref to cl_abap_structdescr,
lo_table_descr type ref to cl_abap_tabledescr.
data: lv_index type string,
lv_fieldname type string.
data lv_reffield type faglflext-hsl01.
free: gt_component.
* 基于gs_output
lo_strcut_descr ?= cl_abap_structdescr=>describe_by_data( gs_output ).
gt_component = lo_strcut_descr->get_components( ).
* 根据成本中心列表增加动态字段
loop at gt_cskt into gs_cskt.
lv_index = sy-tabix.
lv_fieldname = gc_dyn_prefix && lv_index .
condense lv_fieldname no-gaps.
clear gs_component.
gs_component-name = lv_fieldname.
gs_component-type = cast #( cl_abap_elemdescr=>describe_by_data( lv_reffield ) ).
append gs_component to gt_component.
endloop.
"根据字段目录创建动态结构类型
lo_strcut_descr ?= cl_abap_structdescr=>create( p_components = gt_component ).
lo_table_descr ?= cl_abap_tabledescr=>create( p_line_type = lo_strcut_descr ).
create data lt_dref type handle lo_table_descr.
create data ls_dref type handle lo_strcut_descr.
assign ls_dref->* to <dyn_wa>.
assign lt_dref->* to <dyn_table>.
endform.
程序运行的时候,gt_component 的结构和内容如下 (每一行的关键是 name 和 type):


根据输出格式加工数据
因为程序只是获取单月的数据,而从 FAGLFLEXT 表取数底表格式(每个月 1 列)决定了有可能当月并没有数据,所以代码中增加了删除空行的代码。静态内表删除内表比较简单,而动态内表因为列名是在程序中生成的,所以处理起来相对麻烦一些。
form frm_process_data .
data lv_src_fldname type fieldname.
data lv_target_fldname type fieldname.
data gv_tabix like sy-tabix.
lv_src_fldname = 'HSL' && p_rpmax. " 基于选择屏幕的月份
condense lv_src_fldname.
*-------------------------------------------------------------
* 行转列,同时进行聚合计算
*-------------------------------------------------------------
loop at gt_list into gs_list.
move-corresponding gs_list to <dyn_wa>.
" 从1到12月份,获取选择屏幕所在月份的数据
assign component lv_src_fldname of structure gs_list to field-symbol(<lfs_source>).
" 找到对应成本中心的index
read table gt_cskt transporting no fields with key kostl = gs_list-kostl binary search.
if sy-subrc = 0.
gv_tabix = sy-tabix.
lv_target_fldname = gc_dyn_prefix && gv_tabix.
condense lv_target_fldname. " 对应的动态列名
" 找到成本中心对应的字段
assign component lv_target_fldname of structure <dyn_wa> to field-symbol(<lfs_target>).
<lfs_target> = <lfs_source>.
" 合计字段
assign component 'HSL_SUM' of structure <dyn_wa> to field-symbol(<lfs_sum>).
<lfs_sum> = <lfs_source>.
endif.
collect <dyn_wa> into <dyn_table>.
clear: gs_list, <dyn_wa>.
endloop.
*-----------------------------------------------------
* 删除空行
*-----------------------------------------------------
data lt_component like gt_component.
lt_component[] = gt_component[].
" 删除不相关行
delete lt_component where name = 'RBUKRS'.
delete lt_component where name = 'SAKNR'.
delete lt_component where name = 'TXT20'.
loop at <dyn_table> assigning <dyn_wa>.
data(lv_is_empty) = abap_true.
" lt_component保存了所有gt_output的字段,删除了RBUKRS, SAKNR和TXT20
loop at lt_component assigning field-symbol(<ls_comp>).
assign component <ls_comp>-name of structure <dyn_wa> to field-symbol(<ls_field>).
if <ls_field> is not initial.
lv_is_empty = abap_false.
exit.
endif.
endloop.
if lv_is_empty = abap_true.
delete <dyn_table>.
endif.
endloop.
endform.
ALV 输出
使用 SALV (CL_SALV_TABLE)在 ALV 中显示数据。
form frm_alv_set_style tables t_data type standard table.
data ls_program type salv_s_layout_key."结构包含布局变式所属程序名
data lv_tabix like sy-tabix.
data lv_fieldname type fieldname.
ls_program-report = sy-repid.
* 实例化SALV
cl_salv_table=>factory(
importing r_salv_table = gr_alv_table
changing t_table = t_data[] ).
* 设置动态字段的field catalog
data(lr_alv_columns) = gr_alv_table->get_columns( ).
loop at gt_cskt into gs_cskt.
lv_tabix = sy-tabix.
lv_fieldname = gc_dyn_prefix && lv_tabix.
condense lv_fieldname no-gaps.
perform frm_set_field using lr_alv_columns lv_fieldname gs_cskt-ktext '' ''.
clear gs_cskt.
endloop.
* 设置合计字段的显示
perform frm_set_field using lr_alv_columns 'HSL_SUM' '金额合计' '' ''.
* 优化列宽
lr_alv_columns->set_optimize( 'X' ).
* 设置 toolbar
gr_alv_table->get_functions( )->set_all( ).
* 设置ALV布局
data(lr_layout) = gr_alv_table->get_layout( ).
lr_layout->set_key( ls_program ). "设置布局保存为变式时基于此Key
lr_layout->set_save_restriction( cl_salv_layout=>restrict_none )."允许保存布局为变式
* 设置条纹显示
gr_alv_table->get_display_settings( )->set_striped_pattern( abap_true ).
endform.
frm_alv_set_style 调用了 frm_set_field:
form frm_set_field using uo_cols type ref to cl_salv_columns_table
value(uv_fieldname) " 字段名
value(uv_text) " 字段文本
value(uv_length) " 输出宽度
value(uv_istechnical). " 是否隐藏
data: lo_col type ref to cl_salv_column_table,
lo_agg type ref to cl_salv_aggregations,
lv_text_s type scrtext_s,
lv_text_m type scrtext_m,
lv_text_l type scrtext_l.
lo_col ?= uo_cols->get_column( uv_fieldname ).
* 是否隐藏
if not uv_istechnical is initial.
lo_col->set_technical( abap_true ). "隐藏
endif.
* 设置显示字段文本
if not uv_text is initial.
lv_text_s = uv_text.
lv_text_m = uv_text.
lv_text_l = uv_text.
lo_col->set_short_text( lv_text_s ).
lo_col->set_medium_text( lv_text_m ).
lo_col->set_long_text( lv_text_l ).
endif.
* 设置输出宽度
if not uv_length is initial.
lo_col->set_output_length( uv_length ).
endif.
lo_col->set_zero( '' ).
endform.
调用 display() 方式实现输出:
form frm_alv_show tables t_data type standard table.
perform frm_alv_set_style tables t_data[].
gr_alv_table->display( ).
endform.
为了方便阅读,正文给出主要的代码,完整的代码请参考链接。