最近项目成员碰到一个需求,有一个关于定价批导的需求,期望是能像VK11前台一样,输入条件类型后,自动显示所有的关键字组合,并根据选择的组合来动态下载模板及上传,因为每种组合的Key字段都不一样,模板也会不一样。
其实这个问题难点只在于如何动态生成选择屏幕字段,动态下载模板借助OLE可以轻松实现,动态上传借助指针也可以轻松实现。我之前的帖子中有可以达到类似效果的参考代码:
通过ABAP代码实现批导创建SAP表/结构/数据元素/域(SE11/BAPI)https://blog.csdn.net/DeveloperMrMeng/article/details/128257812?spm=1001.2014.3001.5501MD61计划独立需求导入BAPI【按日维度/动态模板/动态字段】
https://blog.csdn.net/DeveloperMrMeng/article/details/125024469?spm=1001.2014.3001.5501虽然那个需求最终并没有选择进行动态编程,但我还是比较好奇这个需求应该如何实现,其实SAP有一些标准事务也有类似的效果,比如SE16,在输入表名后,回车显示的选择屏幕就是动态生成的。
做完手头工作后,我对这个标准程序进行了DEBUG,最终在函数RS_TABLE_LIST_CREATE中发现其生成原理,在该函数中还可以窥探到动态生成屏幕的原理,这里不多赘述。
实现该功能的核心语法如下:
READ REPORT prog INTO itab [MAXIMUM WIDTH INTO wid].
DELETE REPORT prog.
INSERT REPORT prog FROM itab
[MAXIMUM WIDTH INTO wid]
{ [KEEPING DIRECTORY ENTRY]
| { [PROGRAM TYPE pt]
[FIXED-POINT ARITHMETIC fp]
[VERSION vs] }
| [DIRECTORY ENTRY dir] }.
GENERATE REPORT prog [error_handling].
READ TEXTPOOL prog INTO itab LANGUAGE lang.
DELETE TEXTPOOL prog LANGUAGE lg.
INSERT TEXTPOOL prog FROM itab LANGUAGE lang.
以下为demo效果:
实现原理:
注:通过F1查看其中一些语法说明可以发现,涉及到 DELETE 的语法通常会标注仅用于SAP内部使用,不建议在实际生产中进行使用,应该是担心误删掉一些标准代码,所以本文只是对这种实现方式进行测试及说明,如需实际使用,一定要慎重,并且搭配SYNTAX-CHECK语法或者一些错误检查附加项进行使用。
实现源码:
程序ZGENERATE_SELECSCREEN:
*&---------------------------------------------------------------------*
*& REPORT ZGENERATE_SELECSCREEN
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT zgenerate_selecscreen.
DATA:
gv_error TYPE flag.
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.
PARAMETERS:p_type TYPE t682i-kozgf MODIF ID m3.
SELECTION-SCREEN END OF BLOCK b1.
*&---------------------------------------------------------------------*
*& AT SELECTION-SCREEN OUT
*&---------------------------------------------------------------------*
AT SELECTION-SCREEN.
* Generate selection screen
PERFORM frm_genarate_selscr.
START-OF-SELECTION.
*&---------------------------------------------------------------------*
*& Form frm_genarate_selscr
*&---------------------------------------------------------------------*
*& Generate selection screen
*&---------------------------------------------------------------------*
FORM frm_genarate_selscr .
DATA:
lt_prog TYPE STANDARD TABLE OF string,
lt_prog_new TYPE STANDARD TABLE OF string,
lt_text_pool TYPE STANDARD TABLE OF textpool,
ls_text_pool TYPE textpool,
lv_prog TYPE sy-repid,
lv_bt_name TYPE string,
ls_new_code TYPE string,
lv_ucomm TYPE string,
lv_symbol TYPE char1 VALUE '.',
lv_begin_tabix TYPE sy-tabix,
lv_end_tabix TYPE sy-tabix,
lv_text_symbol TYPE numc3.
lv_prog = 'ZDYNAMICS_SCREEN'.
SELECT a~kvewe,
a~kappl,
a~kozgf,
a~kolnr,
b~gstru,
b~gstxt
FROM t682i AS a
LEFT OUTER JOIN tmc1t AS b
ON concat( a~kvewe,a~kotabnr ) = b~gstru
AND b~spras = @sy-langu
WHERE a~kozgf = @p_type
AND a~kvewe = 'A'
AND a~kappl = 'V'
INTO TABLE @DATA(lt_condtype_info).
SORT lt_condtype_info BY gstru ASCENDING.
DELETE ADJACENT DUPLICATES FROM lt_condtype_info COMPARING gstru.
DATA(lv_lines) = lines( lt_condtype_info ).
IF lt_condtype_info IS INITIAL.
gv_error = abap_on.
MESSAGE 'Error Condition Type' TYPE 'E'.
ELSE.
* Get source code
READ REPORT lv_prog INTO lt_prog.
* Get text pool
READ TEXTPOOL lv_prog INTO lt_text_pool LANGUAGE sy-langu.
LOOP AT lt_prog INTO DATA(ls_prog).
IF ls_prog CS '*DYNAMIC SCREEN BEGIN*'.
lv_begin_tabix = sy-tabix.
ENDIF.
IF ls_prog CS '*DYNAMIC SCREEN END*'.
lv_end_tabix = sy-tabix.
EXIT.
ENDIF.
ENDLOOP.
LOOP AT lt_prog INTO ls_prog.
IF sy-tabix > lv_begin_tabix AND sy-tabix < lv_end_tabix.
CONTINUE.
ENDIF.
APPEND ls_prog TO lt_prog_new.
ENDLOOP.
IF lines( lt_condtype_info ) > 1.
* Add Begin Block
ls_new_code = 'SELECTION-SCREEN BEGIN OF BLOCK b3 WITH FRAME TITLE TEXT-003.'.
lv_begin_tabix = lv_begin_tabix + 1.
INSERT ls_new_code INTO lt_prog_new INDEX lv_begin_tabix.
ls_text_pool-id = 'I'.
ls_text_pool-key = '003'.
ls_text_pool-entry = TEXT-003.
ls_text_pool-length = 50.
DELETE lt_text_pool WHERE id = 'I' AND key = '003'.
APPEND ls_text_pool TO lt_text_pool.
CLEAR ls_text_pool.
lv_text_symbol = 900.
* Add Radiobutton
LOOP AT lt_condtype_info INTO DATA(ls_condtype_info).
IF sy-tabix = 1.
lv_ucomm = ` DEFAULT 'X'`.
ELSE.
lv_ucomm = ''.
ENDIF.
lv_bt_name = 'RB_' && ls_condtype_info-gstru.
ls_new_code = 'SELECTION-SCREEN BEGIN OF LINE.'.
lv_begin_tabix = lv_begin_tabix + 1.
INSERT ls_new_code INTO lt_prog_new INDEX lv_begin_tabix.
ls_new_code = 'PARAMETERS:rb_' && ls_condtype_info-gstru &&
' RADIOBUTTON GROUP grp2' && lv_ucomm && lv_symbol.
lv_begin_tabix = lv_begin_tabix + 1.
INSERT ls_new_code INTO lt_prog_new INDEX lv_begin_tabix.
ADD 1 TO lv_text_symbol.
ls_new_code = 'SELECTION-SCREEN COMMENT 5(79) TEXT-' && lv_text_symbol && ` FOR FIELD ` && lv_bt_name && '.'.
lv_begin_tabix = lv_begin_tabix + 1.
INSERT ls_new_code INTO lt_prog_new INDEX lv_begin_tabix.
ls_new_code = 'SELECTION-SCREEN END OF LINE.'.
lv_begin_tabix = lv_begin_tabix + 1.
INSERT ls_new_code INTO lt_prog_new INDEX lv_begin_tabix.
ls_text_pool-id = 'I'.
ls_text_pool-key = lv_text_symbol.
ls_text_pool-entry = ls_condtype_info-gstxt.
ls_text_pool-length = 100.
DELETE lt_text_pool WHERE id = 'I' AND key = lv_text_symbol.
APPEND ls_text_pool TO lt_text_pool.
************************************************************************
* The standard selection text has a maximum length of 30 characters
************************************************************************
* ls_new_code = 'PARAMETERS:rb_' && ls_condtype_info-gstru &&
* ' TYPE char1 RADIOBUTTON GROUP grp2' && lv_ucomm && lv_symbol.
*
* lv_begin_tabix = lv_begin_tabix + 1.
* INSERT ls_new_code INTO lt_prog_new INDEX lv_begin_tabix.
*
* ls_text_pool-id = 'S'.
* ls_text_pool-key = lv_bt_name.
* ls_text_pool-entry+8 = ls_condtype_info-gstxt.
* ls_text_pool-length = 50.
* DELETE lt_text_pool WHERE id = 'S' AND key = lv_bt_name.
* APPEND ls_text_pool TO lt_text_pool.
************************************************************************
CLEAR:
ls_new_code,
ls_text_pool.
ENDLOOP.
* Add End Block
ls_new_code = 'SELECTION-SCREEN END OF BLOCK b3.'.
lv_begin_tabix = lv_begin_tabix + 1.
INSERT ls_new_code INTO lt_prog_new INDEX lv_begin_tabix.
ENDIF.
DELETE REPORT lv_prog.
DELETE TEXTPOOL lv_prog LANGUAGE sy-langu.
INSERT REPORT lv_prog FROM lt_prog_new.
INSERT TEXTPOOL lv_prog FROM lt_text_pool LANGUAGE sy-langu.
GENERATE REPORT lv_prog MESSAGE DATA(lv_err).
SUBMIT (lv_prog) WITH p_type = p_type VIA SELECTION-SCREEN AND RETURN.
ENDIF.
ENDFORM.
程序ZDYNAMICS_SCREEN:
*&---------------------------------------------------------------------*
*& Report ZDYNAMICS_SCREEN
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT zdynamics_screen.
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.
PARAMETERS:p_type TYPE t682i-kozgf MODIF ID m3.
SELECTION-SCREEN END OF BLOCK b1.
*************************DYNAMIC SCREEN BEGIN***************************
* ...
**************************DYNAMIC SCREEN END****************************
SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE TEXT-002.
PARAMETERS:rb_down TYPE char1 RADIOBUTTON GROUP grp1 USER-COMMAND cre,
rb_upld TYPE char1 RADIOBUTTON GROUP grp1 DEFAULT 'X'.
PARAMETERS:p_path TYPE rlgrap-filename MODIF ID m2 MEMORY ID m1.
SELECTION-SCREEN END OF BLOCK b2.
AT SELECTION-SCREEN OUTPUT.
* Screen modify
PERFORM frm_modify_screen.
START-OF-SELECTION.
* Get which on radiobutton has be choosed.
PERFORM frm_get_sel_bt.
*&---------------------------------------------------------------------*
*& Form FRM_MODIFY_SCREEN
*&---------------------------------------------------------------------*
*& Screen Modify
*&---------------------------------------------------------------------*
FORM frm_modify_screen .
LOOP AT SCREEN.
IF screen-name = 'P_TYPE'.
screen-input = 0.
ENDIF.
IF screen-group1 = 'M2'.
IF rb_upld = abap_on.
screen-active = 1.
ELSE.
screen-active = 0.
ENDIF.
ENDIF.
MODIFY SCREEN.
ENDLOOP.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form FRM_GET_SEL_BT
*&---------------------------------------------------------------------*
*& Get which on radiobutton has be choosed.
*&---------------------------------------------------------------------*
FORM frm_get_sel_bt .
DATA:
lv_bt_name TYPE string,
lv_msg TYPE string.
SELECT a~kvewe,
a~kappl,
a~kozgf,
a~kolnr,
b~gstru,
b~gstxt
FROM t682i AS a
LEFT OUTER JOIN tmc1t AS b
ON concat( a~kvewe,a~kotabnr ) = b~gstru
AND b~spras = @sy-langu
WHERE a~kozgf = @p_type
AND a~kvewe = 'A'
AND a~kappl = 'V'
INTO TABLE @DATA(lt_condtype_info).
SORT lt_condtype_info BY gstru ASCENDING.
DELETE ADJACENT DUPLICATES FROM lt_condtype_info COMPARING gstru.
DATA(lv_lines) = lines( lt_condtype_info ).
IF lines( lt_condtype_info ) > 1.
LOOP AT lt_condtype_info INTO DATA(ls_condtype_info).
lv_bt_name = 'RB_' && ls_condtype_info-gstru.
ASSIGN (lv_bt_name) TO FIELD-SYMBOL(<fs_bt>).
IF <fs_bt> IS ASSIGNED.
IF <fs_bt> = 'X'.
lv_msg = |You choose the button { lv_bt_name }|.
EXIT.
ENDIF.
ENDIF.
ENDLOOP.
ELSE.
READ TABLE lt_condtype_info INTO ls_condtype_info INDEX 1.
lv_bt_name = 'RB_' && ls_condtype_info-gstru.
lv_msg = |You choose the button { lv_bt_name }|.
ENDIF.
WRITE lv_msg.
ENDFORM.
由于标准的Selection Text的长度只有30位,而本例中按钮描述超过了30位,所以可以采用SELECTION-SCREEN BEGIN OF LINE的方式去进行选择屏幕生成。
以下为选择不同条件类型后,实际生成的动态代码:
以上。