前言

ERP项目上线后,标准的系统配置,我们很少去做改动了,随着使用的逐渐深入,自开发的功能会越来越多,需要运维顾问进行管理的自定义配置也会越来越多。为此,我提供了一个类似于SPRO的配置工具,用来管理自定义的配置项。

说明:这个配置工具里,也可以将标准的视图维护进来,比如:配置公司代码的维护视图V_T001。


1/6.效果展示

之前做了一个模块自定义配置项的管理工具,比较low一些。

现在做了一下升级,效果如下:

小工具:管理各模块自定义配置项,升级版_自定义


上面展示的层级结构,通过自定义表进行配置,配置内容大概如下图:

小工具:管理各模块自定义配置项,升级版_ico_02

小工具:管理各模块自定义配置项,升级版_配置项_03


2/6.使用说明

1.需要创建Z+模块+CONFIG的事务代码,事务代码对应的REPORT程序都是一个,即:ZCUSTOM_CONFIG(代码在最后),如下图:

小工具:管理各模块自定义配置项,升级版_自定义_04

2.需要按照下面的步骤,依次创建数据库表、视图、视图簇(SE54,不会的请百度)、程序。

3.最后的程序里,有一个管理增强有效性的配置项,这个是我这边项目上跟另外一个开发管理的工具关联在一起的,各位部署完程序后,可自行修改代码,屏蔽此配置项。

4.点击配置项的执行按钮时,支持跳转到SM30、SM34,以及无比灵活的某程序的某个FORM


3/6.数据字典

为实现上述效果,需要自己创建两张表,如下图:

小工具:管理各模块自定义配置项,升级版_配置项_05

小工具:管理各模块自定义配置项,升级版_自定义_06


4/6.视图

创建两个视图

小工具:管理各模块自定义配置项,升级版_配置项_07

小工具:管理各模块自定义配置项,升级版_自定义_08


5/6.视图簇

创建视图簇如下:

小工具:管理各模块自定义配置项,升级版_自定义_09


6/6.代码

*&---------------------------------------------------------------------*
*& 程序名称:ZCUSTOM_CONFIG
*& 程序描述:各模块通用配置程序
*&===============================*
*&创建日期:2021.06.21 程序员:孙亮
*&===============================*
*&修改日期 请求号 修改人 业务提交人 修改描述 *
*&---------------------------------------------------------------------*
REPORT zcustom_config.


CLASS lcl_tree_event_receiver DEFINITION.
PUBLIC SECTION.
METHODS:
handle_tree_link_click FOR EVENT link_click OF cl_gui_column_tree IMPORTING node_key item_name.
ENDCLASS.


TYPES:BEGIN OF ty_item,
dir_no TYPE zcus_conf_dir-dir_no,
dir_text TYPE zcus_conf_dir-dir_text,
item_no TYPE zcus_conf_item-item_no,
item_text TYPE zcus_conf_item-item_text,
tabname TYPE zcus_conf_item-tabname,
ztype TYPE zcus_conf_item-ztype,
progname TYPE zcus_conf_item-progname,
formname TYPE zcus_conf_item-formname,
END OF ty_item.


CONSTANTS:
gc_enh_conf TYPE string VALUE 'ENH_CONF',
gc_cus_conf TYPE string VALUE 'CUS_CONF',
gc_column_btn TYPE tv_itmname VALUE 'BUTTON',
gc_column_item TYPE tv_itmname VALUE 'ITEM'.
DATA: gv_module TYPE string,
gt_item TYPE SORTED TABLE OF ty_item WITH UNIQUE KEY dir_no item_no,
go_tree TYPE REF TO cl_gui_column_tree,
go_docking_tree TYPE REF TO cl_gui_docking_container,
go_event_tree TYPE REF TO lcl_tree_event_receiver,
gt_exclude TYPE TABLE OF sy-ucomm,
gt_sel_condition TYPE TABLE OF vimsellist WITH HEADER LINE.


CLASS lcl_tree_event_receiver IMPLEMENTATION.
METHOD handle_tree_link_click.
PERFORM handle_tree_link_click USING node_key item_name.
ENDMETHOD.
ENDCLASS.


SELECTION-SCREEN PUSHBUTTON 1(20) refresh USER-COMMAND refresh.


INITIALIZATION.
refresh = '刷新'.
DATA(lv_tcode) = sy-tcode+1.
REPLACE FIRST OCCURRENCE OF 'CONFIG' IN lv_tcode WITH ''.
IF lv_tcode = 'FI'.
gv_module = 'FICO'.
ELSE.
gv_module = lv_tcode.
ENDIF.
sy-title = |{ gv_module }自定义配置|.


PERFORM get_data.


AT SELECTION-SCREEN OUTPUT.
CLEAR gt_exclude.
APPEND 'ONLI' TO gt_exclude.
APPEND 'SPOS' TO gt_exclude.
CALL FUNCTION 'RS_SET_SELSCREEN_STATUS'
EXPORTING
p_status = sy-pfkey
p_program = sy-cprog
TABLES
p_exclude = gt_exclude.


PERFORM create_tree.
PERFORM display_data.


AT SELECTION-SCREEN.
IF sy-ucomm = 'REFRESH'.
PERFORM get_data.
PERFORM display_data.
ENDIF.


FORM get_data.
SELECT
a~dir_no
a~dir_text
b~item_no
b~item_text
b~tabname
b~ztype
b~progname
b~formname
INTO TABLE gt_item
FROM zcus_conf_dir AS a
LEFT JOIN zcus_conf_item AS b ON b~zmodule = a~zmodule AND b~dir_no = a~dir_no
WHERE a~zmodule = gv_module.
ENDFORM.
FORM create_tree.
DATA: ls_header TYPE treev_hhdr,
lt_events TYPE cntl_simple_events.


CHECK go_docking_tree IS INITIAL.
CREATE OBJECT go_docking_tree
EXPORTING
ratio = 93
side = cl_gui_docking_container=>dock_at_bottom.


ls_header-heading = |{ gv_module }自定义配置项|.
ls_header-width = 50.


"创建树
CREATE OBJECT go_tree
EXPORTING
parent = go_docking_tree
node_selection_mode = cl_gui_column_tree=>node_sel_mode_single
item_selection = abap_true
hierarchy_column_name = gc_column_item
hierarchy_header = ls_header.


"获取事件并添加事件
go_tree->get_registered_events( IMPORTING events = lt_events ).
APPEND VALUE #( eventid = cl_gui_column_tree=>eventid_link_click appl_event = 'X' ) TO lt_events.
go_tree->set_registered_events( events = lt_events ).


"创建事件处理类
CREATE OBJECT go_event_tree.
SET HANDLER go_event_tree->handle_tree_link_click FOR go_tree.


"在树中增加一列按钮
go_tree->insert_hierarchy_column( name = gc_column_btn ).
ENDFORM.
FORM display_data.
DATA: lt_nodes TYPE treev_ntab,
lt_items TYPE STANDARD TABLE OF streeitm WITH DEFAULT KEY,
lv_node_key TYPE tv_nodekey.


DEFINE macro_append_node.
IF &2 IS INITIAL.
APPEND VALUE #( node_key = &1
n_image = &3
isfolder = &4 ) TO lt_nodes.
ELSE.
APPEND VALUE #( node_key = &1
relatkey = &2
relatship = cl_gui_column_tree=>relat_last_child
n_image = &3
isfolder = &4 ) TO lt_nodes.
ENDIF.
END-OF-DEFINITION.


DEFINE macro_append_btn.
APPEND VALUE #( node_key = &1
item_name = gc_column_btn
class = cl_gui_column_tree=>item_class_link
t_image = icon_execute_object ) TO lt_items.
END-OF-DEFINITION.


DEFINE macro_append_item.
APPEND VALUE #( node_key = &1
item_name = gc_column_item
text = &2
disabled = 'X' ) TO lt_items.
END-OF-DEFINITION.


go_tree->delete_all_nodes( ).


"增加两个固定项
macro_append_node gc_enh_conf '' icon_space ''.
macro_append_btn gc_enh_conf.
macro_append_item gc_enh_conf '增强有效性配置'.


macro_append_node gc_cus_conf '' icon_space ''.
macro_append_btn gc_cus_conf.
macro_append_item gc_cus_conf '管理自定义配置项'.


LOOP AT gt_item ASSIGNING FIELD-SYMBOL(<ls_item>).
AT NEW dir_no.
macro_append_node <ls_item>-dir_no '' '' 'X'.
macro_append_item <ls_item>-dir_no <ls_item>-dir_text.
ENDAT.


IF <ls_item>-item_no IS NOT INITIAL OR <ls_item>-item_text IS NOT INITIAL.
lv_node_key = |{ <ls_item>-dir_no }-{ <ls_item>-item_no }|.
macro_append_node lv_node_key <ls_item>-dir_no icon_space ''.
macro_append_btn lv_node_key.
macro_append_item lv_node_key <ls_item>-item_text.
ENDIF.
ENDLOOP.


CALL METHOD go_tree->add_nodes_and_items
EXPORTING
node_table = lt_nodes
item_table = lt_items
item_table_structure_name = 'STREEITM' "MTREEITM'
EXCEPTIONS
OTHERS = 1.
go_tree->expand_root_nodes( ).
ENDFORM.
FORM handle_tree_link_click USING p_node_key TYPE lvc_nkey
p_item_name TYPE tv_itmname.
CASE p_node_key.
WHEN gc_enh_conf.
SUBMIT zabap_enha_config WITH p_module = gv_module AND RETURN.


WHEN gc_cus_conf.
REFRESH gt_sel_condition.
APPEND VALUE #( viewfield = 'ZMODULE' operator = 'EQ' value = gv_module ) TO gt_sel_condition.
PERFORM call_sm34 USING 'ZVCUS_CONF'.


PERFORM get_data.
PERFORM display_data.


WHEN OTHERS.
CHECK strlen( p_node_key ) > 3.
SPLIT p_node_key AT '-' INTO DATA(lv_dir_no) DATA(lv_item_no).
READ TABLE gt_item WITH KEY dir_no = lv_dir_no item_no = lv_item_no ASSIGNING FIELD-SYMBOL(<ls_item>).
IF <ls_item>-ztype = 'SM30'.
PERFORM call_sm30 USING <ls_item>-tabname.
ELSEIF <ls_item>-ztype = 'SM34'.
PERFORM call_sm34 USING <ls_item>-tabname.
ELSE.
PERFORM (<ls_item>-formname) IN PROGRAM (<ls_item>-progname) IF FOUND.
ENDIF.


ENDCASE.
ENDFORM.
FORM call_sm30 USING p_tablename TYPE dd02v-tabname.
DATA: l_action TYPE c VALUE 'U'.


IF gt_sel_condition[] IS INITIAL.
CALL FUNCTION 'VIEW_MAINTENANCE_CALL'
EXPORTING
action = l_action
view_name = p_tablename.
ELSE.
CALL FUNCTION 'VIEW_MAINTENANCE_CALL'
EXPORTING
action = l_action
view_name = p_tablename
TABLES
dba_sellist = gt_sel_condition[].
REFRESH: gt_sel_condition.
ENDIF.
ENDFORM.
FORM call_sm34 USING p_clustername TYPE vcldir-vclname.
DATA: l_action TYPE c VALUE 'U'.


IF gt_sel_condition[] IS INITIAL.
CALL FUNCTION 'VIEWCLUSTER_MAINTENANCE_CALL'
EXPORTING
maintenance_action = 'U'
viewcluster_name = p_clustername.
ELSE.
CALL FUNCTION 'VIEWCLUSTER_MAINTENANCE_CALL'
EXPORTING
maintenance_action = 'U'
viewcluster_name = p_clustername
TABLES
dba_sellist = gt_sel_condition[].
REFRESH: gt_sel_condition.
ENDIF.
ENDFORM.


最后祝大家中秋快乐!


对了,补充一个自己的想法,以后我写的文章,会分为付费和免费两类。

免费的:

思路性的文章,比如-发票预制增强(链接);

工具类的文章,比如您在读的这篇文章。

付费的:

涉及具体开发工作的代码,比如-分页取数的逻辑(链接);

技巧性部分代码,比如-管理自定义的配置项(链接)


我们程序员这个群体,特别是具体到我们ABAP群体,其实是非常没有付费习惯的,很多人可能会花钱去买个游戏皮肤,都不会来付费读一篇文章。


思路性的文章、工具类的文章及代码,我愿意分享出来给大家提供一些可能的帮助,所以设置为免费的。


但是对于非工具类的代码,我不想被人白嫖(比如发票预制的文章,我只提供思路),然后拿走去完成自己的工作。

技巧性的代码,我也希望我的读者愿意自己去动脑思考完成这个技巧,或者花个三五块钱学习这个技巧。

而且,管理自定义的配置项这个技巧,只是个小技巧而已。我上面提供的代码,比管理自定义的配置项的代码,是升级后的版本,比之前的要复杂的多,好用的多。所以我判定一篇文章是否付费,也不是以代码的复杂度来作为依据的。


总结:

免费的思路性的文章,是想帮助一些人拓宽一下思路。

免费的工具类的文章,是想给大家提供一些实用工具。

付费的涉及具体工作的代码,是拒绝白嫖。

付费的技巧性的文章,是想让大家动动脑。


想变的牛逼,你就得学会如何捕鱼,而不是问别人要鱼吃。

如果喜欢,谢谢转发。