1,SAP BADI的由来
大家都知道SAP在ERP行业中,应用最广的是财务领域。由于各个国家财务制度以及税务制度的差异,SAP希望在自己的程序开发平台中引入BADI,能够让开发人员自己编写业务插件,系统会自动调用这些插件程序来完成某种业务运算。本文中的举例是计算不同国家的税率。
2,创建一个Enhancement Spot
Enhancement Spot是作为一个BADI的容器,在这个容器里面,我们可以定义自己的多个BADI。
3,定义一个BADI
4,定义BADI接口
接下来我们需要一个接口来定义这个BADI所需要用的方法
至此,我们已经建立了一个enhancement spot而且带有一个BADI和一个接口。仅仅如此是不能使用这个BADI的,我们需要一个BADI实例来在程序中被调用。
5,现在我们写一小段程序来调用这个BADI方法get_vat,系统有两个关键字用来得到BADI实例和调用BADI,分别是GET BADI和CALL BADI(也可直接调用接口与实现类,请参考前面实例最后部分:直接调用BAPI接口与类)
DATA: handle TYPE REF TO z_badi_calc_vat,"z_badi_calc_vat为BADI定义,不是接口也不是类,但又好像能代表接口
sum TYPE p,
vat TYPE p,
percent TYPE p.
sum = 50.
GET BADI handle.
CALL BADI handle->get_vat
EXPORTING
im_amount = sum
IMPORTING
ex_amount_vat = vat
ex_percent_vat = percent.
WRITE: 'percentage:', percent, 'VAT:', vat.
由于还没有实现,所以编译出错
一个Enhancement Spot可以定义多个BADI,每个BADI又是由一个接口与多个实例类组成的。Enhancement Spot相当于容器概念,用来存储多个BADI,而每一个BADI必须定义一个接口,该接口可以有一个或多个实现类,BADI实质上就是将接口与实现类组织(打包、捆绑)在一起了,而BADI本身又可以代表接口的概念(因为一个BADI只有一个接口)。
1,建立BADI增强实现容器
由于一个BADI的实现可以有多个类,这些多个实现类需要组织(打包、捆绑)在一起(与多个BADI放在一个Enhancement Spot容器中是一个概念),所以需要先创建一个新的BADI增强实现容器,如图:
2,BADI类实现
紧接着要求输入BADI实现名及实现类名:
当保存后,会自动跳转到 BADI的增强实现界面(因为一个BADI的实现类可以有多个,所以新开一个界面来专门来进行BADI的实现过程):
一个增强实现(Enhancement Implementation)可以有多个BADI Implementations(相当于多个版本),但起作用的同时只能有一个,有多个版本时需要进行设置:
两个实现版本类所现实接口GET_VAT方法如下:
上面虽然创建了两个BADI Implementation(Z_BADI_CALC_IMPL、Z_BADI_CALC_IMPL2),或者说两个实现类(Z_CL_CALC_IMPL、Z_CL_CALC_IMPL2),但这些都是属于同一个Enhancement Implementation增强实现(Z_BADI_CALC_IMPL_C)的,到目前此,对于BAdI Definition(BADI 定义)Z_BADI_CALC_VAT来说,只有一个Enhancement Implementation(增强实现)Z_BADI_CALC_IMPL_C,而一个Enhancement Implementation(增强实现)里虽然创建了两个两个实现类(Z_CL_CALC_IMPL、Z_CL_CALC_IMPL2),但同时只有一个起作用,所以目前最终只有一个BadI Implementation,如果想要达到像Java中多态的话,需要创建多个不同的Enhancement Implementation增强实现,BADI中的多态就是通过不同的Enhancement Implementation增强实现来实现的。
现在我们还可以创建第二个增强实现,如下面:
紧接着创建BADI 实例及对应的实例类:
再实现GET_VAT方法:
此时如果激活方法时,会出错,原因就是目前面有两个BADI的实现Z_BADI_CALC_IMPL_C、Z_BADI_CALC_IMPL_C2,所以需要把其中一个的Implementation is active前的钩去掉才能被激活:
当有多个BADI实现时,需要增加过滤器来选择使用哪个实现(类)
比如Z_CL_CALC_VAT_GB,但是当运行程序时,系统会dump,这是因为我们定义BADI时,是采用了默认的单一使用(single-use),没有选中复合使用选项(Multiple Use Option),单一使用的限制是只能有一个实现类。如何解决这个问题,请看本系列的最后一篇文章,如何使用过滤器。
注意:上面过滤值一定要大写,否则运行时匹配不到。
使用下面测试程序进行测试:
parameters: filter(2) type c.
DATA: handle TYPE REF TO z_badi_calc_vat,"z_badi_calc_vat为BADI定义,不是接口也不是类
sum TYPE p,
vat TYPE p,
percent TYPE p.
sum = 50.
GET BADI handle
FILTERS"SE18中定义的过滤器名作为这里的参数名
filter1 = 'C'.
CALL BADI handle->get_vat
EXPORTING
im_amount = sum
IMPORTING
ex_amount_vat = vat
ex_percent_vat = percent.
WRITE: / 'percentage:', percent, 'VAT:' ,vat.
在同一Enhancement Implementation中(如下图中的Z_BADI_CALC_IMPL_C),不同的BADI Implementations(Z_BADI_CALC_IMPL、Z_BADI_CALC_IMPL2)之间究竟选谁的问题,是由 Default Implementation、Implementation is active选项共同来决定的,且在同一时间内只能有一个BADI Implementations能被激活调用,所以要通过这两个选项来控制究竟谁被用来当作当前实现被使用,是否被使用也可通过图中的 Runtime Behavior说明文字来查看:
不同的Enhancement Implementation之间(Z_BADI_CALC_IMPL、Z_BADI_CALC_IMPL2)调用谁,则是由过滤器来决定的:
但前提是该实现要被激活:
在SAP源码中,BADI增强都是通过方法CL_EXITHANDLER=>GET_INSTANCE来调用的,所以可以在主程序代码中查找“CL_EXITHANDLER=>GET_INSTANCE”这样的字符串,如查找到的:
CALL METHOD CL_EXITHANDLER=>GET_INSTANCE
exporting " \TP 563352
exit_name = 'CUSTOMER_ADD_DATA' " \TP 563352
null_instance_accepted = 'X' " \TP 563352
CHANGING
INSTANCE = G_ADDITIONAL_DATA.
其中exit_name参数指定的值就是 BADI对象名,然后再通过SE18来查看这个BADI对象,则可以看到其接口与实现类
另外,由于SAP在开发时习惯将相关的东西放在同一包中,所以可以根据主程序所在的开发包在SE80中来查找相应的BADI
主要用到两个BADI: ME_GUI_PO_CUST和ME_PROCESS_PO_CUST
这两个BADI都是有例子的, 可以在se18那里输入BADI名进入后,按GoTo->Sample code->Display来查看, 也可以直接在SE24查看实例类CL_EXM_IM_ME_GUI_PO_CUST和CL_EXM_IM_ME_PROCESS_PO_CUST,实例类代码中有很详细的注释:
现在我们对PO header加上自己的subscreen, SAP的例子提供的是对item增加subscreen
最后做出的效果图:
本示例对标准表的扩展方法使用的是 SMOD中对其预留的扩展结构CI_EKKODB 、CI_EKPODB来做的,该方法使用的是Include对标准表进行扩展,但用户自己不能直接对标准表采用Include方式来对其扩展(而IncludeCI_EKKODB 、CI_EKPODB又可以是因为这两个结构是系统预留好的扩展结构),本来想通过Append Stucture来对EKKO或EKPO进行扩展的,但最后经过测试,经过Append Stucture方式扩展EKPO 后,数据读取存储都正常,但EKKO死也不行,无奈之下,放弃了Append Stucture方式扩展标准表,而是采用了对系统预留的标准扩展结构CI_EKKODB 、CI_EKPODB修改来完成,这两个预留结构可以通过SMOD来查看MM06E005增强点得到,具体请参考前面示例
而另一种扩展方式就是自创建一张表,此种方式的数据在屏幕与数据库之间的传递比起直接对标准表字段进行扩充,实现起来困难许多,但因不影响标准表,所以不失为好的扩展方法。这里只为EKPO创建了自定义表,我想EKKO是一样的,这里就不再对EKKO进行自定义扩展了
本示例的表扩展分为两种,一种就是直接扩展标准表,第二种就是自已创建一个自定义数据库。这里就是介绍怎样直接扩展标准表。
激活后,发现EKKO与EKPO标准表都Include这两个结构了:
这里使用到的CI_EKKODB以及CI_EKPODB可能刚开始不存在,它们分别为SAP提供的用来扩展标准表EKKO、EKPO结构的增强结构,为SAP所预留,这两个预留结构的创建需通过SMOD来操作(直接通过SE11双击表结构里以 CI_ 打头的 .INCLUDE 也可创建或修改),具体还可以参考SMOD采购订单屏幕增强章节示例
上一步就已说明,本示例中的另一种表扩展就是创建自己的表,而不是直接对EKKO、EKPO标准表进结构修改。创建的自己定义表如下:
从MEPOBADIEX函数拷贝出新的函数组Z_PO_SUBSCREEN_GRP,MEPOBADIEX为BADI ME_GUI_PO_CUST的实现示例所用到的函数组,这可以从ME_GUI_PO_CUST实现类CL_EXM_IM_ME_GUI_PO_CUST的SUBSCRIBE方法示例代码中查找出所使用到的示例函数组为MEPOBADIEX(该函数组已搭好了架子,包含了subroutine、Function等,所以需要从此拷贝,拷贝后修改修改即可使用):
下面这些函数都是用在标准扩展方式(即通过CI_EKKODB、CI_EKPODB结构对表EKKO、EKPO进行的扩展)下
FUNCTION Z_PO_SUBSCREEN_GRP_POP_HEAD .
*"----------------------------------------------------------------------
*"*"Local Interface:
*" EXPORTING
*" REFERENCE(EX_DYNP_DATA) TYPE CI_EKKODB
*"----------------------------------------------------------------------
* get dynpro data 将屏幕上的数据读取到BADI内存中
"ci_ekKodb已与Head增强子屏幕绑定,所以这里实质上是将屏幕中的
"数据读取到BADI ME_GUI_PO_CUST实现类ZCL_IM__JZJ_BADI_IMPL_PO
"的私有属性dynp_data_pai_head里。该函数在PAI事件后调用
ex_dynp_data = ci_ekkodb.
ENDFUNCTION.
FUNCTION Z_PO_SUBSCREEN_GRP_POP_ITEM .
*"----------------------------------------------------------------------
*"*"Local Interface:
*" EXPORTING
*" REFERENCE(EX_DYNP_DATA) TYPE CI_EKPODB
*"----------------------------------------------------------------------
* get dynpro data 将屏幕上的数据读取到BADI内存中
"ci_ekpodb已与Item增强子屏幕绑定,所以这里实质上是将屏幕中的
"数据读取到BADI ME_GUI_PO_CUST实现类ZCL_IM__JZJ_BADI_IMPL_PO
"的私有属性dynp_data_pai_item里。该函数在PAI事件后调用
ex_dynp_data = ci_ekpodb.
ENDFUNCTION.
FUNCTION Z_PO_SUBSCREEN_GRP_PUSH_HEAD .
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" REFERENCE(IM_DYNP_DATA) TYPE CI_EKKODB
*"----------------------------------------------------------------------
* set dynpro data 将BADI内存中的数据读取到屏幕上,在屏幕PBO前调用
ci_ekkodb = im_dynp_data.
ENDFUNCTION.
FUNCTION Z_PO_SUBSCREEN_GRP_PUSH_ITEM .
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" REFERENCE(IM_DYNP_DATA) TYPE CI_EKPODB
*"----------------------------------------------------------------------
* set dynpro data 将BADI内存中的数据读取到屏幕上,在屏幕PBO前调用
ci_ekpodb = im_dynp_data.
ENDFUNCTION.
FUNCTION Z_PO_SUBSCREEN_GRP_POP_ITEM_2 .
*"----------------------------------------------------------------------
*"*"Local Interface:
*" EXPORTING
*" REFERENCE(EX_DYNP_DATA) TYPE ZEKPO_DB
*"----------------------------------------------------------------------
ex_dynp_data = ZEKPO_DB.
ENDFUNCTION.
FUNCTION Z_PO_SUBSCREEN_GRP_PUSH_ITEM_2 .
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" REFERENCE(IM_DYNP_DATA) TYPE ZEKPO_DB
*"----------------------------------------------------------------------
* set dynpro data将BADI内存中的数据读取到屏幕上
ZEKPO_DB = im_dynp_data.
ENDFUNCTION.
FUNCTION Z_PO_SUBSCREEN_GRP_INIT.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"----------------------------------------------------------------------
"初始化时清除持久数据与界面操作数据。这里好像没有必要,虽然这里的gt_persistent_data、 gt_data
"虽然是全局内表,但每次被调用(如打一个Tcode、运行一个报表等 调用此函数组)时,gt_persistent_data、 gt_data
"是不会共用的,也就是说不会在不同的程序会话中共享,它们只在同一运行程序中共享,直到程序结束时,它们
"所占内存才会被释放
CLEAR: gt_persistent_data[], gt_data[].
ENDFUNCTION.
FUNCTION Z_PO_SUBSCREEN_GRP_OPEN.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" REFERENCE(IM_EBELN) TYPE EBELN
*"----------------------------------------------------------------------
* read customer data from database 根据单号从自定义扩展数据库表读取数据
CHECK NOT im_ebeln IS INITIAL.
SELECT * FROM ZEKPO_DB INTO TABLE gt_persistent_data
WHERE ebeln = im_ebeln.
"刚读出来时,将界面数据与持久数据设置成一样
gt_data = gt_persistent_data.
ENDFUNCTION.
FUNCTION Z_PO_SUBSCREEN_GRP_GET_DATA.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" REFERENCE(IM_EBELN) TYPE EBELN
*" REFERENCE(IM_EBELP) TYPE EBELP
*" EXPORTING
*" VALUE(EX_DATA) TYPE ZEKPO_DB
*"----------------------------------------------------------------------
CLEAR ex_data.
CHECK NOT im_ebelp IS INITIAL.
"从界面操作数据内表中读取
READ TABLE gt_data INTO ex_data WITH TABLE KEY mandt = sy-mandt
ebeln = im_ebeln
ebelp = im_ebelp.
"如果没有查到,则新增一条后返回
IF NOT sy-subrc IS INITIAL.
ex_data-mandt = sy-mandt.
ex_data-ebeln = im_ebeln.
ex_data-ebelp = im_ebelp.
INSERT ex_data INTO TABLE gt_data.
ENDIF.
ENDFUNCTION.
FUNCTION Z_PO_SUBSCREEN_GRP_SET_DATA.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" REFERENCE(IM_DATA) TYPE ZEKPO_DB
*" REFERENCE(IM_PHYSICAL_DELETE_REQUEST) TYPE MMPUR_BOOL OPTIONAL
*"----------------------------------------------------------------------
* update customers data
**********该函数就是用于界面操作数据后,同步更新内表 gt_data**************
DATA: ls_data LIKE LINE OF gt_data.
FIELD-SYMBOLS: <data> LIKE LINE OF gt_data.
CHECK NOT im_data-ebelp IS INITIAL.
"如果是要删除数据操作时
IF NOT im_physical_delete_request IS INITIAL.
* delete a line from gt_data
DELETE TABLE gt_data WITH TABLE KEY mandt = sy-mandt
ebeln = im_data-ebeln
ebelp = im_data-ebelp.
ELSE."否则是更新或新增数据
* update customer data
READ TABLE gt_data ASSIGNING <data> WITH TABLE KEY
mandt = sy-mandt
ebeln = im_data-ebeln
ebelp = im_data-ebelp.
IF sy-subrc IS INITIAL."更新数据
* update existing data
<data>-field1 = im_data-field1.
<data>-field2 = im_data-field2.
ELSE."新增数据
* make a new entry into the data table
ls_data = im_data.
ls_data-mandt = sy-mandt.
INSERT ls_data INTO TABLE gt_data.
ENDIF.
ENDIF.
ENDFUNCTION.
FUNCTION Z_PO_SUBSCREEN_GRP_POST.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" VALUE(IM_EBELN) TYPE EBELN
*"----------------------------------------------------------------------
DATA: ls_data LIKE LINE OF gt_data,
lt_data_new TYPE STANDARD TABLE OF ZEKPO_DB,
lt_data_old TYPE STANDARD TABLE OF ZEKPO_DB.
* prepare customers data for posting 数据存储到数据库中前准备
CHECK NOT im_ebeln IS INITIAL.
lt_data_new[] = gt_data."当前界面操作后的数据
lt_data_old[] = gt_persistent_data."界面操作之前的数据
ls_data-mandt = sy-mandt.
ls_data-ebeln = im_ebeln.
"单号为空时需要设置单号
MODIFY lt_data_new FROM ls_data TRANSPORTING mandt ebeln WHERE ebeln IS initial.
"提交数据库
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_COMMIT' IN UPDATE TASK
TABLES
imt_data_new = lt_data_new
imt_data_old = lt_data_old.
ENDFUNCTION.
FUNCTION Z_PO_SUBSCREEN_GRP_COMMIT.
*"----------------------------------------------------------------------
*"*"Update Function Module:
*"*"Local Interface:
*" TABLES
*" IMT_DATA_NEW STRUCTURE ZEKPO_DB
*" IMT_DATA_OLD STRUCTURE ZEKPO_DB
*"----------------------------------------------------------------------
DATA: ls_data_new LIKE LINE OF gt_data,
ls_data_old LIKE LINE OF gt_data,
data_ins TYPE STANDARD TABLE OF zekpo_db,
data_upd TYPE STANDARD TABLE OF zekpo_db,
data_del TYPE STANDARD TABLE OF zekpo_db.
*********将数据保存到数据库中************************
* new state
LOOP AT imt_data_new INTO ls_data_new.
READ TABLE imt_data_old INTO ls_data_old WITH KEY
mandt = sy-mandt
ebeln = ls_data_new-ebeln
ebelp = ls_data_new-ebelp.
"如果新数据在旧数据中查找得到
IF sy-subrc IS INITIAL.
"对比一条后,就将其删除,这样剩下没有被删除的就代表通过界面删除了
DELETE imt_data_old INDEX sy-tabix.
"如果新旧数据不同,则表示界面修改过数据了
IF ls_data_new NE ls_data_old.
* existing entry was changed
"将需要更新的数据暂存到data_upd表中
APPEND ls_data_new TO data_upd.
ENDIF.
ELSE."如果为新增数据
* a new entry was added
"将需要新增的数据暂存到data_ins表中
APPEND ls_data_new TO data_ins.
ENDIF.
ENDLOOP.
* remaining old state: can be deleted
"剩下的就是需要被删除的数据
APPEND LINES OF imt_data_old TO data_del.
*---------------------------------------------------------------------*
* actual update operations
*---------------------------------------------------------------------*
* insert
IF NOT data_ins[] IS INITIAL.
INSERT zekpo_db FROM TABLE data_ins.
IF sy-subrc NE 0.
MESSAGE a807(me) WITH 'zekpo_db'.
ENDIF.
ENDIF.
* update
IF NOT data_upd[] IS INITIAL.
UPDATE zekpo_db FROM TABLE data_upd.
IF sy-subrc NE 0.
MESSAGE a808(me) WITH 'zekpo_db'.
ENDIF.
ENDIF.
* delete
IF NOT data_del[] IS INITIAL.
DELETE zekpo_db FROM TABLE data_del.
IF sy-subrc NE 0.
MESSAGE a809(me) WITH 'zekpo_db'.
ENDIF.
ENDIF.
ENDFUNCTION.
FUNCTION-POOL z_po_subscreen_grp. "MESSAGE-ID ..
* persistent data 已持久化的数据,即当前数据库中目前所拥有的数据
DATA: gt_persistent_data TYPE SORTED TABLE OF zekpo_db
WITH UNIQUE KEY mandt ebeln ebelp,
* actual data 用于存储当前界面操作之后的数据,与数据库中的实际数据有所不同了
gt_data TYPE SORTED TABLE OF zekpo_db
WITH UNIQUE KEY mandt ebeln ebelp.
* dynpro output structure
TABLES: zekpo_db.
**========上面是自建表所需用到的变量,下面是标准表扩展所需用到的变量========**
* dynpro output structure
TABLES: ci_ekkodb,ci_ekpodb.
* definitions required for dynpro/framework integration
DATA: ok-code TYPE sy-ucomm.
INCLUDE lmeviewsf01.
此Include没有修改过,目前只用到event_pbo 与event_pai两个Module
*----------------------------------------------------------------------*
* INCLUDE LMEVIEWSF01 *
*----------------------------------------------------------------------*
DATA: call_subscreen TYPE sy-dynnr, "#EC NEEDED
call_prog TYPE sy-repid, "#EC NEEDED
call_view TYPE REF TO cl_screen_view_mm, "#EC NEEDED
call_view_stack TYPE REF TO cl_screen_view_mm OCCURS 0,"#EC NEEDED
global_framework TYPE REF TO cl_framework_mm, "#EC NEEDED
global_help_view TYPE REF TO cl_screen_view_mm, "#EC NEEDED
global_help_prog TYPE sy-repid. "#EC NEEDED
*---------------------------------------------------------------------*
* FORM CALL_SCREEN *
*---------------------------------------------------------------------*
* ........ *
*---------------------------------------------------------------------*
* --> P_SCREEN *
*---------------------------------------------------------------------*
FORM call_screen USING p_screen TYPE sy-dynnr.
CALL SCREEN p_screen.
ENDFORM.
*---------------------------------------------------------------------*
* FORM CALL_POPUP *
*---------------------------------------------------------------------*
* ........ *
*---------------------------------------------------------------------*
FORM call_popup USING p_screen TYPE sy-dynnr
p_starting_x TYPE sy-tabix
p_starting_y TYPE sy-tabix
p_ending_x TYPE sy-tabix
p_ending_y TYPE sy-tabix.
CALL SCREEN p_screen STARTING AT p_starting_x p_starting_y
ENDING AT p_ending_x p_ending_y.
ENDFORM.
*---------------------------------------------------------------------*
* FORM SET_SCREEN *
*---------------------------------------------------------------------*
* ........ *
*---------------------------------------------------------------------*
* --> P_SCREEN *
*---------------------------------------------------------------------*
FORM set_screen USING p_screen TYPE sy-dynnr.
SET SCREEN p_screen.
ENDFORM.
*---------------------------------------------------------------------*
* FORM PUSH_CALL_VIEW *
*---------------------------------------------------------------------*
* ........ *
*---------------------------------------------------------------------*
FORM push_call_view.
APPEND call_view TO call_view_stack.
ENDFORM.
*---------------------------------------------------------------------*
* FORM POP_CALL_VIEW *
*---------------------------------------------------------------------*
* ........ *
*---------------------------------------------------------------------*
FORM pop_call_view.
IF NOT call_view_stack[] IS INITIAL.
DATA: last TYPE sy-tabix.
DESCRIBE TABLE call_view_stack LINES last.
READ TABLE call_view_stack INTO call_view INDEX last.
DELETE call_view_stack INDEX last.
ENDIF.
ENDFORM.
*---------------------------------------------------------------------*
* FORM SET_VALUE *
*---------------------------------------------------------------------*
* ........ *
*---------------------------------------------------------------------*
* --> P_NAME *
* --> P_VALUE *
*---------------------------------------------------------------------*
FORM set_value USING p_name p_value.
FIELD-SYMBOLS <field>.
ASSIGN (p_name) TO <field>.
<field> = p_value.
ENDFORM.
*---------------------------------------------------------------------*
* FORM SET_SUBSCREEN_AND_PROG *
*---------------------------------------------------------------------*
* ........ *
*---------------------------------------------------------------------*
* --> DYNNR *
* --> PROG *
* --> VIEW *
* --> TO *
* --> CL_SCREEN_VIEW_MM *
*---------------------------------------------------------------------*
FORM set_subscreen_and_prog USING dynnr TYPE sy-dynnr
prog TYPE sy-repid
view TYPE REF TO cl_screen_view_mm.
call_subscreen = dynnr.
call_prog = prog.
call_view = view.
ENDFORM.
*---------------------------------------------------------------------*
* FORM GET_OK_CODE *
*---------------------------------------------------------------------*
* ........ *
*---------------------------------------------------------------------*
* --> FCODE *
*---------------------------------------------------------------------*
FORM get_ok_code USING fcode TYPE sy-ucomm.
fcode = ok-code.
ENDFORM.
*&---------------------------------------------------------------------*
*& Module EVENT_PBO OUTPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE event_pbo OUTPUT.
CALL METHOD call_view->handle_event( 'PBO' ).
ENDMODULE. " EVENT_PBO OUTPUT
*&---------------------------------------------------------------------*
*& Module EVENT_PBO_TC OUTPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE event_pbo_tc OUTPUT.
CALL METHOD call_view->handle_event( 'PBO_TC_LINE' ).
ENDMODULE. " EVENT_PBO_TC OUTPUT
*&---------------------------------------------------------------------*
*& Module EVENT_PBO_SUBSCREEN OUTPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE event_pbo_subscreen OUTPUT.
PERFORM push_call_view.
CALL METHOD call_view->handle_event( 'PBO_SUBSCREEN' ).
ENDMODULE. " EVENT_PBO_SUBSCREEN OUTPUT
*&---------------------------------------------------------------------*
*& Module EVENT_PBO_POPSUBSCREEN
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE event_pbo_popsubscreen OUTPUT.
PERFORM pop_call_view.
ENDMODULE. " EVENT_PBO_SUBSCREEN OUTPUT
*&---------------------------------------------------------------------*
*& Module EVENT_PBO_FINISHED OUTPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE event_pbo_finished OUTPUT.
CALL METHOD call_view->handle_event( 'PBO_FINISHED' ).
ENDMODULE. " EVENT_PBO_FINISHED OUTPUT
*&---------------------------------------------------------------------*
*& Module EVENT_PAI_POPSUBSCREEN
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE event_pai_popsubscreen INPUT.
PERFORM pop_call_view.
ENDMODULE. " EVENT_PBO_SUBSCREEN OUTPUT
*&---------------------------------------------------------------------*
*& Module EVENT_PAI_SUBSCREEN
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE event_pai_subscreen INPUT.
PERFORM push_call_view.
CALL METHOD call_view->handle_event( 'PAI_SUBSCREEN' ).
ENDMODULE. " EVENT_PBO_SUBSCREEN OUTPUT
*&---------------------------------------------------------------------*
*& Module EVENT_PAI INPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE event_pai INPUT.
CALL METHOD call_view->handle_event( 'PAI' ).
ENDMODULE. " EVENT_PAI INPUT
*&---------------------------------------------------------------------*
*& Module EVENT_PAI_FINISHED INPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE event_pai_finished INPUT.
ENHANCEMENT-POINT EVENT_PAI_FINISHED_01 SPOTS ES_LMEVIEWSF01 INCLUDE BOUND.
*$*$-Start: EVENT_PAI_FINISHED_01---------------------------------------------------------------$*$*
ENHANCEMENT 1 AD_MPN_PUR2_LMEVIEWSF01. "active version
* clear temporary MPN system messages "note 916061
perform mepo_pic_delete_message in program saplmepo.
*
* Addition by Roger <<< DI Note: 426616
if not ok-code is initial.
call method cl_framework_mm=>get_instance
IMPORTING ex_instance = global_framework.
CALL METHOD global_framework->set_fcode
EXPORTING im_fcode = sy-ucomm.
CLEAR ok-code.
endif.
ENDENHANCEMENT.
*$*$-End: EVENT_PAI_FINISHED_01---------------------------------------------------------------$*$*
CALL METHOD call_view->handle_event( 'BEFORE_TRANSPORT' ).
CALL METHOD call_view->handle_event( 'PAI_FINISHED' ).
ENDMODULE. " EVENT_PAI_FINISHED INPUT
*&---------------------------------------------------------------------*
*& Module EVENT_PAI_TC INPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE event_pai_tc INPUT.
CALL METHOD call_view->handle_event( 'PAI_TC_LINE' ).
ENDMODULE. " EVENT_PAI_TC INPUT
*&---------------------------------------------------------------------*
*& Module EVENT_PBO_PREPARE OUTPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE event_pbo_prepare OUTPUT.
CALL METHOD call_view->handle_event( 'PBO_PREPARE' ).
ENDMODULE. " EVENT_PBO_PREPARE OUTPUT
*&---------------------------------------------------------------------*
*& Module EVENT_PAI_PREPARE INPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE event_pai_prepare INPUT.
CALL METHOD call_view->handle_event( 'PAI_PREPARE' ).
ENDMODULE. " EVENT_PAI_PREPARE INPUT
*&---------------------------------------------------------------------*
*& Module EVENT_POV_LIST INPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE event_pov_list INPUT.
CALL METHOD cl_framework_mm=>get_instance
IMPORTING ex_instance = global_framework.
global_help_prog = sy-repid.
CALL METHOD global_framework->get_view
EXPORTING im_prog = global_help_prog
im_dynnr = sy-dynnr
IMPORTING ex_view = global_help_view.
IF NOT global_help_view IS INITIAL.
CALL METHOD global_help_view->handle_event( 'EVENT_POV_LIST' ).
ENDIF.
ENDMODULE.
*&---------------------------------------------------------------------*
*& Module EVENT_POH_LIST INPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE event_poh_list INPUT.
CALL METHOD cl_framework_mm=>get_instance
IMPORTING ex_instance = global_framework.
global_help_prog = sy-repid.
CALL METHOD global_framework->get_view
EXPORTING im_prog = global_help_prog
im_dynnr = sy-dynnr
IMPORTING ex_view = global_help_view.
IF NOT global_help_view IS INITIAL.
CALL METHOD global_help_view->handle_event( 'EVENT_POH_LIST' ).
ENDIF.
ENDMODULE.
*&---------------------------------------------------------------------*
*& Module GET_FCODE INPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE get_fcode INPUT.
CALL METHOD cl_framework_mm=>get_instance
IMPORTING ex_instance = global_framework.
CALL METHOD global_framework->set_fcode
EXPORTING im_fcode = sy-ucomm.
CLEAR ok-code.
ENDMODULE.
*&---------------------------------------------------------------------*
*& Module FCODE_EXIT INPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE fcode_exit INPUT.
CALL METHOD call_view->handle_event( 'FCODE' ).
ENDMODULE. " FCODE_EXIT INPUT
*&---------------------------------------------------------------------*
*& Module BEFORE_TRANSPORT INPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE before_transport INPUT.
CALL METHOD call_view->handle_event( 'BEFORE_TRANSPORT' ).
ENDMODULE. " BEFORE_TRANSPORT INPUT
*&---------------------------------------------------------------------*
*& Module CLEAR_OKCODE INPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE clear_okcode INPUT.
CLEAR sy-ucomm.
ENDMODULE. " CLEAR_OKCODE INPUT
*>>> OLC Project
* Core adaptation for VORNR searchhelp
*&---------------------------------------------------------------------*
*& Module VAL_REQ_VORNR INPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE val_req_vornr INPUT.
ENHANCEMENT-POINT LMEVIEWSF01_OLC_001 SPOTS ES_LMEVIEWSF01 STATIC INCLUDE BOUND .
ENHANCEMENT-SECTION LMEVIEWSF01_OLC_002 SPOTS ES_LMEVIEWSF01 INCLUDE BOUND .
* No OLC order => Ordinary F4 Help
CALL FUNCTION 'F4IF_FIELD_VALUE_REQUEST'
EXPORTING
TABNAME = 'MEACCT1000'
FIELDNAME = 'VORNR'
DYNPPROG = SY-REPID
DYNPNR = SY-DYNNR
DYNPROFIELD = 'MEACCT1000-VORNR'
EXCEPTIONS
OTHERS = 1.
END-ENHANCEMENT-SECTION.
ENDMODULE. " VAL_REQ_VORNR INPUT
*<<< OLC Project
注:这此屏幕的属性都要设置成子屏幕
注:所有的屏幕都需要调用event_pbo 与event_pai两个Module: 如果不调用这两个module, BADI ME_GUI_PO_CUST下面的4个方法都不会触发:
TRANSPORT_FROM_MODEL
TRANSPORT_TO_DYNP
TRANSPORT_FROM_DYNP
TRANSPORT_TO_MODEL
点击“Source Code-Base”进入到整个类代码编辑界面(如果不是自定义类,是SAP系统提供的标准类时,是没有这个按钮的,即不能进入类整体代码编辑器的):
METHOD if_ex_me_gui_po_cust~subscribe.
****构建子屏幕*******
DATA: ls_subscriber LIKE LINE OF re_subscribers.
CHECK im_application = 'PO'.
* re_subscribers内表中的每一行就是一个子屏幕,每个子屏幕会形成一个Tab
* CLEAR re_subscribers[].
* CLEAR ls_subscriber.
"首次加载采购订界面时(打开ME21N、ME22N、ME23N)会调用两次:第一次为HEADER,第二次为ITEM
IF im_element = 'HEADER'."如果当前是加载Header屏幕时
* the name is a unique identifier for the subscreen and defined in this class definition
ls_subscriber-name = 'H_SUBSCREEN_1'."子屏幕标识名,用来唯一区分子屏幕,各子屏幕这个标识不能相同
* the dynpro number to use所需嵌入的子屏幕号
ls_subscriber-dynpro = '0100'.
* the program where the dynpro can be found子屏幕所在的主程序名
ls_subscriber-program = 'SAPLZ_PO_SUBSCREEN_GRP'.
* each subscreen needs his own DDIC-Structure子屏幕中的字段所绑定的结构名,即屏幕字段名的前缀“XXX-XXX”(减号前面)
ls_subscriber-struct_name = 'CI_EKKODB'.
* a label can be defined
ls_subscriber-label = 'CI_EKKODB增强子屏幕1'.
* the position within the tabstrib can be defined 新增Tab标签所在tabstrib中的位置
*如果不指定,则会放在最后
* ls_subscriber-position = 5.
* the height of the screen can be defined here. Currently we suport two screen sizes:
* value <= 7 a sevel line subscreen
* value > 7 a 16 line subscreen
ls_subscriber-height = 2.
APPEND ls_subscriber TO re_subscribers.
**Head第二个子屏幕
ls_subscriber-name = 'H_SUBSCREEN_2'.
ls_subscriber-dynpro = '0101'.
ls_subscriber-program = 'SAPLZ_PO_SUBSCREEN_GRP'.
ls_subscriber-struct_name = 'CI_EKKODB'.
ls_subscriber-label = 'CI_EKKODB增强子屏幕2'.
ls_subscriber-height = 3.
APPEND ls_subscriber TO re_subscribers.
ELSEIF im_element = 'ITEM'."如果当前是加载Item屏幕时
ls_subscriber-name = 'I_SUBSCREEN_1'.
ls_subscriber-dynpro = '0102'.
ls_subscriber-program = 'SAPLZ_PO_SUBSCREEN_GRP'.
ls_subscriber-struct_name = 'CI_EKPODB'.
ls_subscriber-label = 'CI_EKPODB增强子屏幕1'.
ls_subscriber-height = 8.
APPEND ls_subscriber TO re_subscribers.
ls_subscriber-name = 'I_SUBSCREEN_2'.
ls_subscriber-dynpro = '0103'.
ls_subscriber-program = 'SAPLZ_PO_SUBSCREEN_GRP'.
ls_subscriber-struct_name = 'ZEKPO_DB'.
ls_subscriber-label = 'ZEKPO_DB扩展'.
ls_subscriber-height = 8.
APPEND ls_subscriber TO re_subscribers.
ENDIF.
ENDMETHOD.
METHOD if_ex_me_gui_po_cust~map_dynpro_fields.
********屏幕子字段编号*********
* ch_mapping结构如下
* BEGIN OF mmpur_dynpro_entry,
* screenname TYPE scrfname,
* fieldname TYPE fieldname,
* position TYPE sy-index,
* metafield TYPE mmpur_metafield,
* display_only TYPE mmpur_bool,
* initial_no_disp TYPE mmpur_bool,
* initial_is_inactive TYPE mmpur_bool,
* END OF mmpur_dynpro_entry,
FIELD-SYMBOLS: <mapping> LIKE LINE OF ch_mapping.
"给每个屏幕字段一个编号: mmmfd_cust_01...09
LOOP AT ch_mapping ASSIGNING <mapping>.
CASE <mapping>-fieldname.
WHEN 'ZZ_HEAD_F1'. <mapping>-metafield = mmmfd_cust_01.
WHEN 'ZZ_HEAD_F2'. <mapping>-metafield = mmmfd_cust_02.
WHEN 'ZZ_ITEM_F1'.
<mapping>-metafield = mmmfd_cust_03.
"也可使用自己的编号,不一定要使用预留的。此编号会在
"IF_EX_ME_PROCESS_PO_CUST~FIELDSELECTION_ITEM中使用到
WHEN 'ZZ_ITEM_F2'. <mapping>-metafield = 99000000.
WHEN 'FIELD1'. <mapping>-metafield = 99000001.
WHEN 'FIELD2'. <mapping>-metafield = 99000002.
ENDCASE.
ENDLOOP.
ENDMETHOD.
从Type Group MMMFD来看,Custom的field好像最多只能10个,但可以自己编号(如程序中的99000001、99000002):
经过上面步骤, 我们可以在ME23N看到custom subscreen, 但在ME21N和ME22N依然是看不到的,这个是为什么,还搞不清楚,只知道在实现BADI ME_PROCESS_PO_CUST中的IF_EX_ME_PROCESS_PO_CUST~FIELDSELECTION_HEADER/ITEM方法后,三个界面中的子屏幕都才显示出来
METHOD if_ex_me_gui_po_cust~transport_from_model.
DATA: lw_header TYPE REF TO if_purchase_order_mm,
lw_mepoheader TYPE mepoheader.
DATA: l_item TYPE REF TO if_purchase_order_item_mm,
ls_mepoitem TYPE mepoitem.
*--------------------------------------------------------------------*
* system asks to transport data from the business logic into the view
*--------------------------------------------------------------------*
**********将业务数据转存到BADI相应属性里************
IF im_name = 'H_SUBSCREEN_1' OR im_name = 'H_SUBSCREEN_2'.
* is it an Header? im_model can be header or item.
mmpur_dynamic_cast lw_header im_model."强制向下转型
CHECK NOT lw_header IS INITIAL."如果强转不出错
* transport standard fields 与EKKO在同一表中的扩展字段
lw_mepoheader = lw_header->get_data( ).
* store info for later use将初始数据暂存起来过后使用
MOVE-CORRESPONDING lw_mepoheader TO dynp_data_pbo_head.
ELSEIF im_name = 'I_SUBSCREEN_1'.
* is it an item? im_model can be header or item.
mmpur_dynamic_cast l_item im_model.
CHECK NOT l_item IS INITIAL.
* transport standard fields
ls_mepoitem = l_item->get_data( ).
* store info for later use
MOVE-CORRESPONDING ls_mepoitem TO dynp_data_pbo_item.
ELSEIF im_name = 'I_SUBSCREEN_2'.
mmpur_dynamic_cast l_item im_model.
CHECK NOT l_item IS INITIAL.
* transport standard fields
ls_mepoitem = l_item->get_data( ).
* transport customer fields
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_GET_DATA'
EXPORTING
im_ebeln = ls_mepoitem-ebeln
im_ebelp = ls_mepoitem-ebelp
IMPORTING
ex_data = zekpo_db_pbo.
ENDIF.
ENDMETHOD.
METHOD if_ex_me_gui_po_cust~transport_to_dynp.
***********将BADI属性中的数据显示到屏幕上,需要调用前面创建的函数中交互完成****************
IF im_name = 'H_SUBSCREEN_1' OR im_name = 'H_SUBSCREEN_2' .
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_PUSH_HEAD'
EXPORTING
im_dynp_data = dynp_data_pbo_head.
ELSEIF im_name = 'I_SUBSCREEN_1' .
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_PUSH_ITEM'
EXPORTING
im_dynp_data = dynp_data_pbo_item.
ELSEIF im_name = 'I_SUBSCREEN_2' .
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_PUSH_ITEM_2'
EXPORTING
im_dynp_data = zekpo_db_pbo.
ENDIF.
ENDMETHOD.
METHOD if_ex_me_gui_po_cust~transport_from_dynp.
"当PAI事件发生时,将屏幕上的数据转存到BADI属性中
IF im_name = 'H_SUBSCREEN_1' OR im_name = 'H_SUBSCREEN_2' .
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_POP_HEAD'
IMPORTING
ex_dynp_data = dynp_data_pai_head.
ELSEIF im_name = 'I_SUBSCREEN_1' .
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_POP_ITEM'
IMPORTING
ex_dynp_data = dynp_data_pai_item.
ELSEIF im_name = 'I_SUBSCREEN_2' .
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_POP_ITEM_2'
IMPORTING
ex_dynp_data = zekpo_db_pai.
ENDIF.
"发生PAI后,判断数据是否发生变化
IF dynp_data_pai_head <> dynp_data_pbo_head
OR dynp_data_pai_item <> dynp_data_pbo_item
OR zekpo_db_pai <> zekpo_db_pbo.
* something has changed therefor we have to notify the framework
* to transport data to the model
"只有re_changed为X时,F_EX_ME_PROCESS_PO_CUST~PROCESS_HEADER/ITEM方法才会触发
re_changed = mmpur_yes.
ENDIF.
ENDMETHOD.
METHOD if_ex_me_gui_po_cust~transport_to_model.
DATA: lw_header TYPE REF TO if_purchase_order_mm,
lw_mepoheader TYPE mepoheader.
DATA: l_item TYPE REF TO if_purchase_order_item_mm,
ls_mepoitem TYPE mepoitem,
ls_customer TYPE zekpo_db.
*--------------------------------------------------------------------*
* data have to be transported to business logic
*--------------------------------------------------------------------*
*********将屏幕字段保存到业务模型中********************
IF im_name = 'H_SUBSCREEN_1' OR im_name = 'H_SUBSCREEN_2'.
* is it an item? im_model can be header or item.
mmpur_dynamic_cast lw_header im_model.
CHECK NOT lw_header IS INITIAL.
lw_mepoheader = lw_header->get_data( ).
* standard fields changed?标准表EKKO扩展字段(通过Include CI_EKKO增强扩展的字段)数据修改
IF dynp_data_pbo_head-zz_head_f1 <> dynp_data_pai_head-zz_head_f1
OR dynp_data_pbo_head-zz_head_f2 <> dynp_data_pai_head-zz_head_f2.
* update standard fields将屏幕上数据存储到最终业务内表中
"以下mepoheader结构中的两个字段是向其Include CMOD预留表结构CI_EKKO而具有的
lw_mepoheader-zz_head_f1 = dynp_data_pai_head-zz_head_f1.
lw_mepoheader-zz_head_f2 = dynp_data_pai_head-zz_head_f2.
CALL METHOD lw_header->set_data( lw_mepoheader ).
ENDIF.
ELSEIF im_name = 'I_SUBSCREEN_1' .
* is it an item? im_model can be header or item.
mmpur_dynamic_cast l_item im_model.
CHECK NOT l_item IS INITIAL.
ls_mepoitem = l_item->get_data( ).
* standard fields changed? 标准表EKPO扩展字段(通过Include CI_EKPO增强扩展的字段)数据修改
IF dynp_data_pbo_item-zz_item_f1 NE dynp_data_pai_item-zz_item_f1 OR
dynp_data_pbo_item-zz_item_f2 NE dynp_data_pai_item-zz_item_f2.
* update standard fields
ls_mepoitem-zz_item_f1 = dynp_data_pai_item-zz_item_f1.
ls_mepoitem-zz_item_f2 = dynp_data_pai_item-zz_item_f2.
CALL METHOD l_item->set_data( ls_mepoitem ).
ENDIF.
ELSEIF im_name = 'I_SUBSCREEN_2' .
* is it an item? im_model can be header or item.
mmpur_dynamic_cast l_item im_model.
CHECK NOT l_item IS INITIAL.
ls_mepoitem = l_item->get_data( ).
* customer fields changed?扩展表ZEKPO_DB数据修改
IF zekpo_db_pbo-field1 NE zekpo_db_pai-field1 OR
zekpo_db_pbo-field2 NE zekpo_db_pai-field2.
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_GET_DATA'
EXPORTING
im_ebeln = ls_mepoitem-ebeln
im_ebelp = ls_mepoitem-ebelp
IMPORTING
ex_data = ls_customer.
ls_customer-field1 = zekpo_db_pai-field1.
ls_customer-field2 = zekpo_db_pai-field2.
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_SET_DATA'
EXPORTING
im_data = ls_customer.
ENDIF.
ENDIF.
ENDMETHOD.
'-'代表hidden, '+'或'.'表示editable, '*'代表display
METHOD if_ex_me_process_po_cust~fieldselection_header.
**********Header增强子屏幕字段可输入性处理******************
DATA: lv_persistent TYPE mmpur_bool.
FIELD-SYMBOLS: <fs> LIKE LINE OF ch_fieldselection.
LOOP AT ch_fieldselection ASSIGNING <fs>.
CASE <fs>-metafield.
WHEN OTHERS."下面看似无意义,但如果不经过下面处理,有时子屏幕又显示不出来,不知道为什么?
IF <fs>-fieldstatus = '+' .
<fs>-fieldstatus = '.'.
ELSEIF <fs>-fieldstatus = '.'.
<fs>-fieldstatus = '+'.
ELSEIF <fs>-fieldstatus IS INITIAL.
<fs>-fieldstatus = '+'.
ENDIF.
ENDCASE.
ENDLOOP.
ENDMETHOD.
METHOD if_ex_me_process_po_cust~fieldselection_item.
**********Item增强子屏幕字段可输入性设置*****************
DATA: l_persistent TYPE mmpur_bool.
FIELD-SYMBOLS: <fs> LIKE LINE OF ch_fieldselection.
LOOP AT ch_fieldselection ASSIGNING <fs>.
CASE <fs>-metafield.
WHEN 99000001."需特殊处理的字段
l_persistent = im_item->is_persistent( )."当前Item是否已持久化过
* if the item is already on the database, we disallow to change field badi_bsgru
IF l_persistent EQ mmpur_yes."如果该Item已在数据库保存过了,则将Field1扩展字段不能再被修改
<fs>-fieldstatus = '*'. " Display
ENDIF.
WHEN OTHERS."其他无需特殊处理的字段,但如果不经过下面处理,有时子屏幕又显示不出来,不知道为什么?
IF <fs>-fieldstatus = '+' .
<fs>-fieldstatus = '.'.
ELSEIF <fs>-fieldstatus = '.'.
<fs>-fieldstatus = '+'.
ELSEIF <fs>-fieldstatus IS INITIAL.
<fs>-fieldstatus = '+'.
ENDIF.
ENDCASE.
ENDLOOP.
"使用下面方式方式会出问题:在Item打上删除标识后,系统会将Item相关屏幕中的字段都设置为
"不可编辑状态,如是经过下面处理,则会将字段又重设回可编辑,这样与系统所设置的矛盾,原因
"是这里使用的是 im_header->is_changeable( ) 头来判断的,但在编辑状态下头肯定是可编辑的
",所以拿头的可编辑状态来判断Item是否处于可编辑状态是错误的,但发现 im_item又没有
"is_changeable( )方法,所以只能采用上面方式
* DEFINE set_input.
* read table ch_fieldselection assigning <fs> with table key metafield = &1.
* if sy-subrc = 0.
* if im_header->is_changeable( ) = mmpur_yes.
* <fs>-fieldstatus = '+'.
* else.
* <fs>-fieldstatus = '*'.
* endif.
* endif.
* END-OF-DEFINITION.
* set_input mmmfd_cust_01.
* ...
ENDMETHOD.
METHOD if_ex_me_process_po_cust~process_header.
*********Header头数据可以在此校验*****************
DATA: lw_mepoheader TYPE mepoheader.
DATA: lv_testflag TYPE c.
INCLUDE mm_messages_mac. "useful macros for message handling
lw_mepoheader = im_header->get_data( ).
IF lw_mepoheader-zz_head_f1 < 100.
* Place the cursor onto field zz_head_f1. The metafield was defined in BAdI ME_GUI_PO_CUST,
* Method MAP_DYNPRO_FIELDS.
mmpur_metafield mmmfd_cust_01.
mmpur_message_forced 'E' '00' '001' 'CI_EKKODB-ZZ_HEAD_F1 不能小于100!' '' '' ''.
* invalidate the object
CALL METHOD im_header->invalidate( ).
ENDIF.
ENDMETHOD.
这里出错提示好你有点问题:
如果按回车来check的话,它会显示error message,一次改变后按一次回车有反应,第二次就没反应:
这里有一个缺陷,当我们按save里,程序中设置的错误message是不会显示到下面message框中的(研究了很久, 没研究出来怎么搞),幸好这里有个功能十分不错, 选中PO header data still faulty这个message,按Edit, 就可以定位到出错的字段:.
但Item中的字段出错后,会显示到提示框中:
METHOD if_ex_me_process_po_cust~process_item.
DATA: ls_mepoitem TYPE mepoitem,
ls_customer TYPE zekpo_db,
ls_tbsg TYPE tbsg,
lv_dummy TYPE c LENGTH 128.
INCLUDE mm_messages_mac. "useful macros for message handling
*---------------------------------------------------------------------*
* here we check customers data
*---------------------------------------------------------------------*
ls_mepoitem = im_item->get_data( ).
IF ls_mepoitem-loekz EQ 'D'."如果是要删除Item时,删除标识好像是 L ?
* a physical deletion of the item was carried out. therrefor we have to
* delete customer data on the level of the item
ls_customer-ebeln = ls_mepoitem-ebeln.
ls_customer-ebelp = ls_mepoitem-ebelp.
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_SET_DATA'
EXPORTING
im_data = ls_customer
im_physical_delete_request = 'X'.
ELSE."否则为更新与新增操作,则需进行数据有效检测
* update/insert operation
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_GET_DATA'
EXPORTING
im_ebeln = ls_mepoitem-ebeln
im_ebelp = ls_mepoitem-ebelp
IMPORTING
ex_data = ls_customer.
* check customers data
* check field field1. This should be carried out only for new items. Once the PO is posted the
* field should no longer be changeable. This is done in Method FIELDSELECTION_ITEM.
IF im_item->is_persistent( ) EQ mmpur_no."如果Item 未持久化过,才需要进行检测扩展字段 Field1(因为
"在IF_EX_ME_PROCESS_PO_CUST~FIELDSELECTION_ITEM方法中已经对该字段进行了设置:所在Item入库后就不能再修改)
IF ls_customer-field1 IS INITIAL."未持久化过,且扩展字段field1为空时
* Place the cursor onto field field1. The metafield was defined in BAdI ME_GUI_PO_CUST,
* Method MAP_DYNPRO_FIELDS.
mmpur_metafield 99000001."出错后定位到出错字段
MESSAGE e083(me) WITH 'ZEKPO_DB-FIELD1不能为空!' '' INTO lv_dummy.
mmpur_message_forced sy-msgty sy-msgid sy-msgno
sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
CALL METHOD im_item->invalidate( ).
ELSE."如果不为空,且未持久化过时
* check whether the field is valid 检测数据的合法性
"SELECT SINGLE * FROM tbsg INTO ls_tbsg WHERE bsgru EQ ls_customer-badi_bsgru.
IF ls_customer-