ABAP Enhancement:第二部分

ABAP Enhancement:第二部分_第1张图片
声明: 原创作品转载时请注明文章来自 SAP师太 技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun并以超链接形式标明文章原始出处否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4291296.html

第三代:基于类的增强(BADI... 25

BADI新方式实现... 42

1-构建BADI. 42

2-实现BADI. 44

3-使用BADI过滤器... 49

4-多个实现时究竟调谁... 51

查找系统中的BADI53

BADI详细说明文档... 54

示例:通过BADI实现采购订单屏幕增强... 54

需求说明... 55

Step 1: 标准表EKKOEKPO结构扩展... 56

Step 2: 创建自定义表... 57

Step 3: Create Function Group. 57

功能函数... 58

标准表扩展所涉及的函数... 59

Z_PO_SUBSCREEN_GRP_POP_HEAD.. 59

Z_PO_SUBSCREEN_GRP_POP_ITEM.. 59

Z_PO_SUBSCREEN_GRP_PUSH_HEAD.. 59

Z_PO_SUBSCREEN_GRP_PUSH_ITEM.. 59

自建表扩展所涉及的函数... 60

Z_PO_SUBSCREEN_GRP_POP_ITEM_2. 60

Z_PO_SUBSCREEN_GRP_PUSH_ITEM_2. 60

Z_PO_SUBSCREEN_GRP_INIT.. 60

Z_PO_SUBSCREEN_GRP_OPEN.. 60

Z_PO_SUBSCREEN_GRP_GET_DATA.. 61

Z_PO_SUBSCREEN_GRP_SET_DATA.. 61

Z_PO_SUBSCREEN_GRP_POST.. 62

Z_PO_SUBSCREEN_GRP_COMMIT.. 62

全局数据定义... 64

LMEVIEWSF01. 64

子屏幕设计... 70

0100. 71

0101. 72

0102. 72

0103. 72

Step 4: BADI ME_GUI_PO_CUST的实现,子屏幕数据传递处理... 72

ZCL_IM__JZJ_BADI_IMPL_PO类的属性设计... 74

IF_EX_ME_GUI_PO_CUST~SUBSCRIBE,引用自定义子屏幕... 74

IF_EX_ME_GUI_PO_CUST~MAP_DYNPRO_FIELDS屏幕字段编号... 75

IF_EX_ME_GUI_PO_CUST~TRANSPORT_FROM_MODEL,从业务模型中读取数据到BADI属性... 76

IF_EX_ME_GUI_PO_CUST~TRANSPORT_TO_DYNP,将BADI属性中的数据传到屏幕中显示... 77

IF_EX_ME_GUI_PO_CUST~TRANSPORT_FROM_DYNP,将屏幕字段值传到BADI属性中... 77

IF_EX_ME_GUI_PO_CUST~TRANSPORT_TO_MODEL,将BADI属性中的数据传到业务数据模型中... 77

Step 4: BADI ME_PROCESS_PO_CUST的实现,数据处理... 79

IF_EX_ME_PROCESS_PO_CUST~FIELDSELECTION_HEADER,(头)字段可见性、可输入状态设置... 80

IF_EX_ME_PROCESS_PO_CUST~FIELDSELECTION_ITEM,(Item)字段可见性、可输入状态设置... 80

IF_EX_ME_PROCESS_PO_CUST~PROCESS_HEADER,头数据处理,如校验... 81

IF_EX_ME_PROCESS_PO_CUST~PROCESS_ITEM,头数据处理,如校验... 82

IF_EX_ME_PROCESS_PO_CUST~INITIALIZE,程序运行时初始化... 84

IF_EX_ME_PROCESS_PO_CUST~OPEN,读取自建表中的数据... 84

IF_EX_ME_PROCESS_PO_CUST~POST,保存数据到自建表中... 84

通过程序查找出口对象或BADI84

Enhancement Framework 基本概念... 87

为自己程序创建显示增强Explicit Enhancement spot. 88

隐式与显示增强... 89

系统标准表结构增强... 91

Include Struture. 91

Append Strutures. 92

SE14调整表

第三代:基于的增强(BADI

BADI新方式实现

1-构建BADI

1SAP BADI的由来

大家都知道SAPERP行业中,应用最广的是财务领域。由于各个国家财务制度以及税务制度的差异,SAP希望在自己的程序开发平台中引入BADI,能够让开发人员自己编写业务插件,系统会自动调用这些插件程序来完成某种业务运算。本文中的举例是计算不同国家的税率。

2,创建一个Enhancement Spot

Enhancement Spot是作为一个BADI的容器在这个容器里面,我们可以定义自己的多个BADI

  • TCode SE18

ABAP Enhancement:第二部分_第2张图片

ABAP Enhancement:第二部分_第3张图片

3,定义一个BADI

ABAP Enhancement:第二部分_第4张图片

4,定义BADI接口

接下来我们需要一个接口来定义这个BADI所需要用的方法

  • 双击接口,此时可以选择或者输入一个新的接口名

ABAP Enhancement:第二部分_第5张图片

  • 为接口Z_IF_CALC_VAT创建一个方法get_vat,并设置参数

ABAP Enhancement:第二部分_第6张图片

ABAP Enhancement:第二部分_第7张图片

至此,我们已经建立了一个enhancement spot而且带有一个BADI和一个接口。仅仅如此是不能使用这个BADI的,我们需要一个BADI实例来在程序中被调用。

 

5,现在我们写一小段程序来调用这个BADI方法get_vat,系统有两个关键字用来得到BADI实例和调用BADI,分别是GET BADICALL BADI也可直接调用接口与实现类,请参考前面实例最后部分:直接调用BAPI接口与类

 

DATA: handle TYPE REF TO z_badi_calc_vat,"z_badi_calc_vatBADI定义,不是接口也不是类,但又好像能代表接口
      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.

ABAP Enhancement:第二部分_第8张图片

由于还没有实现,所以编译出错

2-实现BADI

一个Enhancement Spot可以定义多个BADI,每个BADI又是由一个接口与多个实例类组成的Enhancement Spot相当于容器概念,用来存储多个BADI,而每一个BADI必须定义一个接口,该接口可以有一个或多个实现类,BADI实质上就是将接口与实现类组织(打包、捆绑)在一起了,而BADI本身又可以代表接口的概念(因为一个BADI只有一个接口)。

ABAP Enhancement:第二部分_第9张图片

1,建立BADI增强实现容器

由于一个BADI的实现可以有多个类,这些多个实现类需要组织(打包、捆绑)在一起(与多个BADI放在一个Enhancement Spot容器中是一个概念),所以需要先创建一个新的BADI增强实现容器,如图:

ABAP Enhancement:第二部分_第10张图片

2,BADI类实现

紧接着要求输入BADI实现名及实现类名:

ABAP Enhancement:第二部分_第11张图片

当保存后,会自动跳转到 BADI的增强实现界面(因为一个BADI的实现类可以有多个,所以新开一个界面来专门来进行BADI的实现过程):

ABAP Enhancement:第二部分_第12张图片

ABAP Enhancement:第二部分_第13张图片

 

一个增强实现(Enhancement Implementation)可以有多个BADI Implementations(相当于多个版本),但起作用的同时只能有一个,有多个版本时需要进行设置:

ABAP Enhancement:第二部分_第14张图片

ABAP Enhancement:第二部分_第15张图片

 

两个实现版本类所现实接口GET_VAT方法如下:

ABAP Enhancement:第二部分_第16张图片

ABAP Enhancement:第二部分_第17张图片

 

上面虽然创建了两个BADI ImplementationZ_BADI_CALC_IMPLZ_BADI_CALC_IMPL2),或者说两个实现类(Z_CL_CALC_IMPLZ_CL_CALC_IMPL2),但这些都是属于同一个Enhancement Implementation增强实现(Z_BADI_CALC_IMPL_C)的,到目前此,对于BAdI DefinitionBADI 定义)Z_BADI_CALC_VAT来说,只有一个Enhancement Implementation(增强实现)Z_BADI_CALC_IMPL_C,而一个Enhancement Implementation(增强实现)里虽然创建了两个两个实现类(Z_CL_CALC_IMPLZ_CL_CALC_IMPL2),但同时只有一个起作用,所以目前最终只有一个BadI Implementation,如果想要达到像Java中多态的话,需要创建多个不同的Enhancement Implementation增强实现,BADI中的多态就是通过不同的Enhancement Implementation增强实现来实现的。

现在我们还可以创建第二个增强实现,如下面:

ABAP Enhancement:第二部分_第18张图片

ABAP Enhancement:第二部分_第19张图片

紧接着创建BADI 实例及对应的实例类:

ABAP Enhancement:第二部分_第20张图片

再实现GET_VAT方法:

ABAP Enhancement:第二部分_第21张图片

此时如果激活方法时,会出错,原因就是目前面有两个BADI的实现Z_BADI_CALC_IMPL_CZ_BADI_CALC_IMPL_C2,所以需要把其中一个的Implementation is active前的钩去掉才能被激活:

ABAP Enhancement:第二部分_第22张图片

 

当有多个BADI实现时,需要增加过滤器来选择使用哪个实现(类)

3-使用BADI过滤器

比如Z_CL_CALC_VAT_GB,但是当运行程序时,系统会dump,这是因为我们定义BADI时,是采用了默认的单一使用(single-use),没有选中复合使用选项(Multiple Use Option),单一使用的限制是只能有一个实现类。如何解决这个问题,请看本系列的最后一篇文章,如何使用过滤器。

ABAP Enhancement:第二部分_第23张图片

 

ABAP Enhancement:第二部分_第24张图片

ABAP Enhancement:第二部分_第25张图片

ABAP Enhancement:第二部分_第26张图片

 

注意:上面过滤值一定要大写,否则运行时匹配不到。

 

使用下面测试程序进行测试:

parameters: filter(2) type c.
DATA: handle TYPE REF TO z_badi_calc_vat,"z_badi_calc_vatBADI定义,不是接口也不是类
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.

ABAP Enhancement:第二部分_第27张图片

 

ABAP Enhancement:第二部分_第28张图片

 

 

4-多个实现时究竟调谁

在同一Enhancement Implementation中(如下图中的Z_BADI_CALC_IMPL_C),不同的BADI ImplementationsZ_BADI_CALC_IMPLZ_BADI_CALC_IMPL2)之间究竟选谁的问题,是由 Default ImplementationImplementation is active选项共同来决定的,且在同一时间内只能有一个BADI Implementations能被激活调用,所以要通过这两个选项来控制究竟谁被用来当作当前实现被使用,是否被使用也可通过图中的 Runtime Behavior说明文字来查看:

ABAP Enhancement:第二部分_第29张图片

 

不同的Enhancement Implementation之间(Z_BADI_CALC_IMPLZ_BADI_CALC_IMPL2)调用谁,则是由过滤器来决定的:

ABAP Enhancement:第二部分_第30张图片

ABAP Enhancement:第二部分_第31张图片

但前提是该实现要被激活:

ABAP Enhancement:第二部分_第32张图片

 

 

 

 

查找系统中的BADI

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详细说明文档

ABAP Enhancement:第二部分_第33张图片

示例:通过BADI实现采购订单屏幕增强

主要用到两个BADI: ME_GUI_PO_CUSTME_PROCESS_PO_CUST

这两个BADI都是有例子的, 可以在se18那里输入BADI名进入后,按GoTo->Sample code->Display来查看, 也可以直接在SE24查看实例类CL_EXM_IM_ME_GUI_PO_CUSTCL_EXM_IM_ME_PROCESS_PO_CUST,实例类代码中有很详细的注释:

ABAP Enhancement:第二部分_第34张图片

ABAP Enhancement:第二部分_第35张图片

现在我们对PO header加上自己的subscreen, SAP的例子提供的是对item增加subscreen

需求说明

最后做出的效果图:

 

ABAP Enhancement:第二部分_第36张图片

ABAP Enhancement:第二部分_第37张图片

ABAP Enhancement:第二部分_第38张图片

本示例对标准表的扩展方法使用的是 SMOD中对其预留的扩展结构CI_EKKODB CI_EKPODB来做的,该方法使用的是Include对标准表进行扩展,但用户自己不能直接对标准表采用Include方式来对其扩展(而IncludeCI_EKKODB CI_EKPODB又可以是因为这两个结构是系统预留好的扩展结构),本来想通过Append Stucture来对EKKOEKPO进行扩展的,但最后经过测试,经过Append Stucture方式扩展EKPO 后,数据读取存储都正常,但EKKO死也不行,无奈之下,放弃了Append Stucture方式扩展标准表,而是采用了对系统预留的标准扩展结构CI_EKKODB CI_EKPODB修改来完成,这两个预留结构可以通过SMOD来查看MM06E005增强点得到,具体请参考前面示例

 

而另一种扩展方式就是自创建一张表,此种方式的数据在屏幕与数据库之间的传递比起直接对标准表字段进行扩充,实现起来困难许多,但因不影响标准表,所以不失为好的扩展方法。这里只为EKPO创建了自定义表,我想EKKO是一样的,这里就不再对EKKO进行自定义扩展了

Step 1: 标准表EKKOEKPO结构扩展

本示例的表扩展分为两种,一种就是直接扩展标准表,第二种就是自已创建一个自定义数据库。这里就是介绍怎样直接扩展标准表。

 

ABAP Enhancement:第二部分_第39张图片

ABAP Enhancement:第二部分_第40张图片

ABAP Enhancement:第二部分_第41张图片

激活后,发现EKKOEKPO标准表都Include这两个结构了:

ABAP Enhancement:第二部分_第42张图片

ABAP Enhancement:第二部分_第43张图片

 

这里使用到的CI_EKKODB以及CI_EKPODB可能刚开始不存在,它们分别为SAP提供的用来扩展标准表EKKOEKPO结构的增强结构,为SAP所预留,这两个预留结构的创建需通过SMOD来操作(直接通过SE11双击表结构里以 CI_ 打头的 .INCLUDE 也可创建或修改),具体还可以参考SMOD采购订单屏幕增强章节示例

Step 2: 创建自定义表

上一步就已说明,本示例中的另一种表扩展就是创建自己的表,而不是直接对EKKOEKPO标准表进结构修改。创建的自己定义表如下:

 

ABAP Enhancement:第二部分_第44张图片

Step 3: Create Function Group

MEPOBADIEX函数拷贝出新的函数组Z_PO_SUBSCREEN_GRPMEPOBADIEXBADI  ME_GUI_PO_CUST的实现示例所用到的函数组,这可以从ME_GUI_PO_CUST实现类CL_EXM_IM_ME_GUI_PO_CUSTSUBSCRIBE方法示例代码中查找出所使用到的示例函数组为MEPOBADIEX(该函数组已搭好了架子,包含了subroutineFunction等,所以需要从此拷贝,拷贝后修改修改即可使用):

ABAP Enhancement:第二部分_第45张图片

ABAP Enhancement:第二部分_第46张图片

功能函数

ABAP Enhancement:第二部分_第47张图片

标准表扩展所涉及的函数

下面这些函数都是用在标准扩展方式(即通过CI_EKKODBCI_EKPODB结构对表EKKOEKPO进行的扩展)下

Z_PO_SUBSCREEN_GRP_POP_HEAD

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.

Z_PO_SUBSCREEN_GRP_POP_ITEM

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.

Z_PO_SUBSCREEN_GRP_PUSH_HEAD

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.

Z_PO_SUBSCREEN_GRP_PUSH_ITEM

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.

自建表扩展所涉及的函数
Z_PO_SUBSCREEN_GRP_POP_ITEM_2

FUNCTION Z_PO_SUBSCREEN_GRP_POP_ITEM_2 .
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  EXPORTING
*"     REFERENCE(EX_DYNP_DATA) TYPE  ZEKPO_DB
*"----------------------------------------------------------------------
  ex_dynp_data
= ZEKPO_DB.
ENDFUNCTION.

Z_PO_SUBSCREEN_GRP_PUSH_ITEM_2

FUNCTION Z_PO_SUBSCREEN_GRP_PUSH_ITEM_2 .
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     REFERENCE(IM_DYNP_DATA) TYPE  ZEKPO_DB
*"----------------------------------------------------------------------
* set dynpro dataBADI内存中的数据读取到屏幕上
  ZEKPO_DB
= im_dynp_data.
ENDFUNCTION.

Z_PO_SUBSCREEN_GRP_INIT

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.

Z_PO_SUBSCREEN_GRP_OPEN

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.

Z_PO_SUBSCREEN_GRP_GET_DATA

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.

Z_PO_SUBSCREEN_GRP_SET_DATA

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.

Z_PO_SUBSCREEN_GRP_POST

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.

Z_PO_SUBSCREEN_GRP_COMMIT

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.

全局数据定义

ABAP Enhancement:第二部分_第48张图片

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.

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 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

子屏幕设计

ABAP Enhancement:第二部分_第49张图片

注:这此屏幕的属性都要设置成子屏幕

ABAP Enhancement:第二部分_第50张图片

0100

ABAP Enhancement:第二部分_第51张图片

ABAP Enhancement:第二部分_第52张图片ABAP Enhancement:第二部分_第53张图片

注:所有的屏幕都需要调用event_pbo event_pai两个Module: 如果不调用这两个module, BADI ME_GUI_PO_CUST下面的4个方法都不会触发:

TRANSPORT_FROM_MODEL

TRANSPORT_TO_DYNP

TRANSPORT_FROM_DYNP

TRANSPORT_TO_MODEL

0101

ABAP Enhancement:第二部分_第54张图片ABAP Enhancement:第二部分_第55张图片

0102

ABAP Enhancement:第二部分_第56张图片ABAP Enhancement:第二部分_第57张图片

0103

ABAP Enhancement:第二部分_第58张图片ABAP Enhancement:第二部分_第59张图片

Step 4: BADI ME_GUI_PO_CUST的实现,子屏幕数据传递处理

ABAP Enhancement:第二部分_第60张图片

ABAP Enhancement:第二部分_第61张图片

ABAP Enhancement:第二部分_第62张图片

ABAP Enhancement:第二部分_第63张图片

点击“Source Code-Base”进入到整个类代码编辑界面(如果不是自定义类,是SAP系统提供的标准类时,是没有这个按钮的,即不能进入类整体代码编辑器的):

ABAP Enhancement:第二部分_第64张图片

ZCL_IM__JZJ_BADI_IMPL_PO类的属性设计

ABAP Enhancement:第二部分_第65张图片

IF_EX_ME_GUI_PO_CUST~SUBSCRIBE,引用自定义子屏幕

ABAP Enhancement:第二部分_第66张图片

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.
   
"首次加载采购订界面时(打开ME21NME22NME23N)会调用两次:第一次为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.

IF_EX_ME_GUI_PO_CUST~MAP_DYNPRO_FIELDS屏幕字段编号

  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来看,Customfield好像最多只能10个,但可以自己编号(如程序中的9900000199000002):

ABAP Enhancement:第二部分_第67张图片

 

经过上面步骤, 我们可以在ME23N看到custom subscreen, 但在ME21NME22N依然是看不到的,这个是为什么,还搞不清楚,只知道在实现BADI  ME_PROCESS_PO_CUST中的IF_EX_ME_PROCESS_PO_CUST~FIELDSELECTION_HEADER/ITEM方法后,三个界面中的子屏幕都才显示出来

IF_EX_ME_GUI_PO_CUST~TRANSPORT_FROM_MODEL,从业务模型中读取数据到BADI属性

image181

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.

IF_EX_ME_GUI_PO_CUST~TRANSPORT_TO_DYNP,将BADI属性中的数据传到屏幕中显示

image182

  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.

IF_EX_ME_GUI_PO_CUST~TRANSPORT_FROM_DYNP,将屏幕字段值传到BADI属性中

ABAP Enhancement:第二部分_第68张图片

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_changedX时,F_EX_ME_PROCESS_PO_CUST~PROCESS_HEADER/ITEM方法才会触发
      re_changed
= mmpur_yes.
   
ENDIF.
ENDMETHOD.

IF_EX_ME_GUI_PO_CUST~TRANSPORT_TO_MODEL,将BADI属性中的数据传到业务数据模型中

image184

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.

 

Step 4: BADI ME_PROCESS_PO_CUST的实现,数据处理

ABAP Enhancement:第二部分_第69张图片

ABAP Enhancement:第二部分_第70张图片

ABAP Enhancement:第二部分_第71张图片

IF_EX_ME_PROCESS_PO_CUST~FIELDSELECTION_HEADER,(头)字段可见性、可输入状态设置

ABAP Enhancement:第二部分_第72张图片

'-'代表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.

IF_EX_ME_PROCESS_PO_CUST~FIELDSELECTION_ITEM,(Item)字段可见性、可输入状态设置

ABAP Enhancement:第二部分_第73张图片

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.

IF_EX_ME_PROCESS_PO_CUST~PROCESS_HEADER,头数据处理,如校验

image190

  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,一次改变后按一次回车有反应,第二次就没反应:

ABAP Enhancement:第二部分_第74张图片

 

这里有一个缺陷,当我们按save里,程序中设置的错误message是不会显示到下面message框中的(研究了很久, 没研究出来怎么搞),幸好这里有个功能十分不错, 选中PO header data still faulty这个message,按Edit, 就可以定位到出错的字段:.

ABAP Enhancement:第二部分_第75张图片

Item中的字段出错后,会显示到提示框中:

ABAP Enhancement:第二部分_第76张图片

 

IF_EX_ME_PROCESS_PO_CUST~PROCESS_ITEM,头数据处理,如校验

image194

  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-

你可能感兴趣的:(abap)