ABAP Enhancement:第二部分


第三代:基於類的增強(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

image115

image116

3,定義一個BADI

image117

4,定義BADI接口

接下來我們需要一個接口來定義這個BADI所需要用的方法

  • 雙擊接口,此時可以選擇或者輸入一個新的接口名

image118

  • 為接口Z_IF_CALC_VAT創建一個方法get_vat,並設置參數

image119

image120

至此,我們已經建立了一個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.

image121

由於還沒有實現,所以編譯出錯

2-實現BADI

一個Enhancement Spot可以定義多個BADI,每個BADI又是由一個接口與多個實例類組成的Enhancement Spot相當於容器概念,用來存儲多個BADI,而每一個BADI必須定義一個接口,該接口可以有一個或多個實現類,BADI實質上就是將接口與實現類組織(打包、捆綁)在一起了,而BADI本身又可以代表接口的概念(因為一個BADI只有一個接口)。

image122

1,建立BADI增強實現容器

由於一個BADI的實現可以有多個類,這些多個實現類需要組織(打包、捆綁)在一起(與多個BADI放在一個Enhancement Spot容器中是一個概念),所以需要先創建一個新的BADI增強實現容器,如圖:

image123

2,BADI類實現

緊接着要求輸入BADI實現名及實現類名:

image124

當保存后,會自動跳轉到 BADI的增強實現界面(因為一個BADI的實現類可以有多個,所以新開一個界面來專門來進行BADI的實現過程):

image125

image126

 

一個增強實現(Enhancement Implementation)可以有多個BADI Implementations(相當於多個版本),但起作用的同時只能有一個,有多個版本時需要進行設置:

image127

image128

 

兩個實現版本類所現實接口GET_VAT方法如下:

image129

image130

 

上面雖然創建了兩個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增強實現來實現的。

現在我們還可以創建第二個增強實現,如下面:

image131

image132

緊接着創建BADI 實例及對應的實例類:

image133

再實現GET_VAT方法:

image134

此時如果激活方法時,會出錯,原因就是目前面有兩個BADI的實現Z_BADI_CALC_IMPL_CZ_BADI_CALC_IMPL_C2,所以需要把其中一個的Implementation is active前的鈎去掉才能被激活:

image135

 

當有多個BADI實現時,需要增加過濾器來選擇使用哪個實現(類)

3-使用BADI過濾器

比如Z_CL_CALC_VAT_GB,但是當運行程序時,系統會dump,這是因為我們定義BADI時,是采用了默認的單一使用(single-use),沒有選中復合使用選項(Multiple Use Option),單一使用的限制是只能有一個實現類。如何解決這個問題,請看本系列的最后一篇文章,如何使用過濾器。

image136

 

image137

image138

image139

 

注意:上面過濾值一定要大寫,否則運行時匹配不到。

 

使用下面測試程序進行測試:

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.

image140

 

image141

 

 

4-多個實現時究竟調誰

在同一Enhancement Implementation中(如下圖中的Z_BADI_CALC_IMPL_C),不同的BADI ImplementationsZ_BADI_CALC_IMPLZ_BADI_CALC_IMPL2)之間究竟選誰的問題,是由 Default ImplementationImplementation is active選項共同來決定的,且在同一時間內只能有一個BADI Implementations能被激活調用,所以要通過這兩個選項來控制究竟誰被用來當作當前實現被使用,是否被使用也可通過圖中的 Runtime Behavior說明文字來查看:

image142

 

不同的Enhancement Implementation之間(Z_BADI_CALC_IMPLZ_BADI_CALC_IMPL2)調用誰,則是由過濾器來決定的:

image143

image144

但前提是該實現要被激活:

image145

 

 

 

 

查找系統中的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詳細說明文檔

image146

示例:通過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,實例類代碼中有很詳細的注釋:

image147

image148

現在我們對PO header加上自己的subscreen, SAP的例子提供的是對item增加subscreen

需求說明

最后做出的效果圖:

 

image149

image150

image151

本示例對標准表的擴展方法使用的是 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結構擴展

本示例的表擴展分為兩種,一種就是直接擴展標准表,第二種就是自已創建一個自定義數據庫。這里就是介紹怎樣直接擴展標准表。

 

image152

image153

image154

激活后,發現EKKOEKPO標准表都Include這兩個結構了:

image155

image156

 

這里使用到的CI_EKKODB以及CI_EKPODB可能剛開始不存在,它們分別為SAP提供的用來擴展標准表EKKOEKPO結構的增強結構,為SAP所預留,這兩個預留結構的創建需通過SMOD來操作(直接通過SE11雙擊表結構里以 CI_ 打頭的 .INCLUDE 也可創建或修改),具體還可以參考SMOD采購訂單屏幕增強章節示例

Step 2: 創建自定義表

上一步就已說明,本示例中的另一種表擴展就是創建自己的表,而不是直接對EKKOEKPO標准表進結構修改。創建的自己定義表如下:

 

image157

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等,所以需要從此拷貝,拷貝后修改修改即可使用):

image158

image159

功能函數

image160

標准表擴展所涉及的函數

下面這些函數都是用在標准擴展方式(即通過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.

全局數據定義

image161

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

子屏幕設計

image162

注:這此屏幕的屬性都要設置成子屏幕

image163

0100

image164

image165image166

注:所有的屏幕都需要調用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

image167image168

0102

image169image170

0103

image171image172

Step 4: BADI ME_GUI_PO_CUST的實現,子屏幕數據傳遞處理

image173

image174

image175

image176

點擊“Source Code-Base”進入到整個類代碼編輯界面(如果不是自定義類,是SAP系統提供的標准類時,是沒有這個按鈕的,即不能進入類整體代碼編輯器的):

image177

ZCL_IM__JZJ_BADI_IMPL_PO類的屬性設計

image178

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

image179

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

image180

 

經過上面步驟, 我們可以在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屬性中

image183

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的實現,數據處理

image185

image186

image187

IF_EX_ME_PROCESS_PO_CUST~FIELDSELECTION_HEADER,(頭)字段可見性、可輸入狀態設置

image188

'-'代表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)字段可見性、可輸入狀態設置

image189

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,一次改變后按一次回車有反應,第二次就沒反應:

image191

 

這里有一個缺陷,當我們按save里,程序中設置的錯誤message是不會顯示到下面message框中的(研究了很久, 沒研究出來怎么搞),幸好這里有個功能十分不錯, 選中PO header data still faulty這個message,按Edit, 就可以定位到出錯的字段:.

image192

Item中的字段出錯后,會顯示到提示框中:

image193

 

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-field1< 100.
             mmpur_metafield
99000001."出錯后定位到出錯字段
           
MESSAGE e083(me) WITH 'ZEKPO_DB-FIELD1不能小於100' space INTO lv_dummy.
            mmpur_message_forced sy
-msgty sy-msgid sy-msgno
                                 sy
-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.

* invalidate the object
           
CALL METHOD im_item->invalidate( ).
         
ENDIF.
       
ENDIF.
     
ENDIF.

* check field field2 擴展字段Field2不管是已持久化過都需要檢測
     
IF ls_customer-field2IS INITIAL.
       
MESSAGE e083(me) WITH 'ZEKPO_DB-FIELD2不能為空!' space 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( ).
        mmpur_metafield
99000002.
     
ENDIF.
   
ENDIF.
 
ENDMETHOD.

IF_EX_ME_PROCESS_PO_CUST~INITIALIZE,程序運行時初始化

image195

METHOD if_ex_me_process_po_cust~initialize.
* initializations 在第一次打開采購單主界面(ME21N/ME22N/ME23N)時,會調用,但
* 在通過主界面上的編輯按鈕在顯示與編輯模式之間切換時,不會再調用,即在只在程序
* 啟動(如打開一個Tcode)時才調用
   
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_INIT'.
ENDMETHOD.

IF_EX_ME_PROCESS_PO_CUST~OPEN,讀取自建表中的數據

image196

METHOD if_ex_me_process_po_cust~open.
   
DATA: ls_mepoheader TYPE mepoheader.
*---------------------------------------------------------------------*
* read customer data
*---------------------------------------------------------------------*
*****在第一次打開采購單主界面時 或在通過主界面(ME22N/ME23N)上的編輯
* 按鈕在顯示與編輯模式之間切換時,或者在不同的PO之間進行切換時,就會調用一次
* this has to be done when we open a persistent object只有為修改或顯示模式下才需要讀取數據庫表
   
CHECK im_trtyp EQ 'V' OR im_trtyp EQ 'A'.
    ls_mepoheader
= im_header->get_data( ).
* read customer data from database
   
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_OPEN'
     
EXPORTING
        im_ebeln
= ls_mepoheader-ebeln.
ENDMETHOD.

IF_EX_ME_PROCESS_PO_CUST~POST,保存數據到自建表中

image197

METHOD if_ex_me_process_po_cust~post.
   
"將數據更新到數據庫表中
   
CALL FUNCTION 'Z_PO_SUBSCREEN_GRP_POST'
     
EXPORTING
        im_ebeln
= im_ebeln.
ENDMETHOD.

通過程序查找出口對象或BADI

可以通過數據庫表中的信息來查找某事務所有相關的出口信息

SAP中,所有程序名及事務碼,以及程序中所包括的對象信息都會被保存在表TADIR 中:

image198

OBJECT:對象類型,如PROG(程序)、TRAN(事務碼)、SMODSMOD增加)等

OBJ_NAME:對象名稱

DEVCLASS:開發包

 

SAP提示的標准程序中,所有的程序、事務及增強都使用了同一開發類,所以可以先根據程序或事務先找到它定義的開發類,再根據開發類來查找其對就的 SMOD 增強出口對象,至於出口對象的描述,則可以從數據表MODSAPT中來獲取。

 

本例將根據指定的事務碼,來查出所有相關的增強出口對象(要開發時,可以通過該實例快速查找事務相關的SMOD出口對象,但要注意,這個程序只能找出相應事務碼絕大多數的出口對象,其他找不着的可以調試MODX_FUNCTION_ACTIVE_CHECK函數來獲取):

image199


TABLES : tstc,tadir,modsapt,modact,trdir,tfdir,enlfdir,sxs_attrt ,tstct.
DATA : jtab LIKE tadir OCCURS 0 WITH HEADER LINE.
DATA : field130.
DATA : v_devclass LIKE tadir-devclass.
PARAMETERS : p_tcode LIKE tstc-tcode,"根據Tcode來查
             p_pgmna
LIKE tstc-pgmna ."或直接指定主程序
DATA wa_tadir TYPE tadir.

START-OF-SELECTION.
 
IF NOT p_tcode IS INITIAL.
   
"根據Tcode查找對應的主程序
   
SELECT SINGLE * FROM tstc WHERE tcode EQ p_tcode.
 
ELSEIF NOT p_pgmna IS INITIAL.
    tstc
-pgmna = p_pgmna.
 
ENDIF.
 
IF sy-subrc EQ 0.
   
"查找程序所對應的開發類(包)
   
SELECT SINGLE * FROM tadir WHERE pgmid = 'R3TR' AND object = 'PROG' AND obj_name = tstc-pgmna.
    v_devclass
= tadir-devclass.
   
IF sy-subrc NE 0.
     
SELECT SINGLE * FROM trdir
     
WHERE name = tstc-pgmna.

     
IF trdir-subc EQ 'F'.
       
SELECT SINGLE * FROM tfdir WHERE pname = tstc-pgmna.
       
SELECT SINGLE * FROM enlfdir WHERE funcname = tfdir-funcname.
       
SELECT SINGLE * FROM tadir WHERE pgmid = 'R3TR' AND object = 'FUGR' AND obj_name EQ enlfdir-area.
       
MOVE : tadir-devclass TO v_devclass.
     
ENDIF.
   
ENDIF.
   
SELECT * FROM tadir INTO TABLE jtab WHERE pgmid = 'R3TR' AND object IN 'SMOD', 'SXSD' AND devclass = v_devclass.
   
SELECT SINGLE * FROM tstct WHERE sprsl EQ sy-langu AND tcode EQ p_tcode.
   
FORMAT COLOR COL_POSITIVE INTENSIFIED OFF.
   
WRITE:/19 'Transaction Code - ', 2020 p_tcode, 4550 tstct-ttext.
   
SKIP.
   
IF NOT jtab[] IS INITIAL.
     
WRITE:/105 sy-uline.
     
FORMAT COLOR COL_HEADING INTENSIFIED ON.
* Sorting the internal Table
     
SORT jtab BY object.
     
DATA : wf_txt60 TYPE c, wf_smod TYPE i , wf_badi TYPE i , wf_object230 TYPE c.
     
CLEAR : wf_smod, wf_badi , wf_object2.
* Get the total SMOD.
     
LOOP AT jtab INTO wa_tadir.
       
AT FIRST.
         
FORMAT COLOR COL_HEADING INTENSIFIED ON.
         
WRITE:/1 sy-vline,
         
2 'Enhancement/ Business Add-in',
         
41 sy-vline ,
         
42 'Description',
         
105 sy-vline.
         
WRITE:/105 sy-uline.
       
ENDAT.
       
CLEAR wf_txt.
       
AT NEW object.
         
IF wa_tadir-object = 'SMOD'.
            wf_object2
= 'Enhancement' .
         
ELSEIF wa_tadir-object = 'SXSD'.
            wf_object2
= ' Business Add-in'.
         
ENDIF.
         
FORMAT COLOR COL_GROUP INTENSIFIED ON.
         
WRITE:/1 sy-vline,
         
2 wf_object2,
         
105 sy-vline.
       
ENDAT.
       
CASE wa_tadir-object.
         
WHEN 'SMOD'.
            wf_smod
= wf_smod + 1.
           
SELECT SINGLE modtext INTO wf_txt
           
FROM
modsapt
           
WHERE sprsl = sy-
langu
           
AND name = wa_tadir-obj_name.

           
FORMAT COLOR COL_NORMAL INTENSIFIED OFF.
         
WHEN 'SXSD'.
* For BADis
            wf_badi
= wf_badi + 1 .
           
SELECT SINGLE text INTO wf_txt
           
FROM
sxs_attrt
           
WHERE sprsl = sy-
langu
           
AND exit_name = wa_tadir-obj_name.

           
FORMAT COLOR COL_NORMAL INTENSIFIED ON.
       
ENDCASE.
       
WRITE:/1 sy-vline,
       
2 wa_tadir-obj_name HOTSPOT ON,
       
41 sy-vline ,
       
42 wf_txt,
       
105 sy-vline.
       
AT END OF object.
         
WRITE : /105 sy-uline.
       
ENDAT.
     
ENDLOOP.
     
WRITE:/105 sy-uline.
     
SKIP.
     
FORMAT COLOR COL_TOTAL INTENSIFIED ON.
     
WRITE:/ 'No.of Exits:' , wf_smod.
     
WRITE:/ 'No.of BADis:' , wf_badi.
   
ELSE.
     
FORMAT COLOR COL_NEGATIVE INTENSIFIED ON.
     
WRITE:/105 'No userexits or BADis exist'.
   
ENDIF.
 
ELSE.
   
FORMAT COLOR COL_NEGATIVE INTENSIFIED ON.
   
WRITE:/105 'Transaction does not exist'.
 
ENDIF.

AT LINE-SELECTION.
 
DATA : wf_object TYPE tadir-object.
 
CLEAR wf_object.
 
GET CURSOR FIELD field1.
 
CHECK field18 EQ 'WA_TADIR'.
 
READ TABLE jtab WITH KEY obj_name = sy-lisel+120.
 
MOVE jtab-object TO wf_object.
 
CASE wf_object.
   
WHEN 'SMOD'.
     
SET PARAMETER ID 'MON' FIELD sy-lisel+110.
     
CALL TRANSACTION 'SMOD' AND SKIP FIRST SCREEN.
   
WHEN 'SXSD'.
     
SET PARAMETER ID 'EXN' FIELD sy-lisel+120.
     
CALL TRANSACTION 'SE18' AND SKIP FIRST SCREEN.
 
ENDCASE.

Enhancement Framework 基本概念

Enhancement Framework的目的:在不改變(或盡量少改變)SAP標准程序的情況下滿足客戶的定制開發需求。Keep less Modification.

Enhancement Framework的基本概念:

Ehancement Spot: 用來組織Enhancement optionsit's a container of Enhancement options.

Enhancement Implementation:用來組織Enhancement options的實現代碼。

 

ENHANCEMENT-POINT是在程序中直接插入代碼,其概念與BADIUSER_EXIT類似,標准程序預留了部分已定義好的增強點可以讓ABAP做插入代碼來實現這個增強(也可以自定義增強點,但不能自定義增強選項,增強選項一定是系統預留下來的,如果沒有增強選項則該處不可做增強),但是不能做屏幕和菜單增強。

 

其最大的優勢在於方便,可以使用程序中已定義的變量,不像BTEUSER_EXIT中只能使用函數接口傳過來看參數。

 

一般增強步驟:

1.         DEBUG標准程序找到需要增強的位置,點EDIT->SHOW IMPLICIT ENHANCEMENT OPTIONS查看是否有預留增強選項。(標准程序不能自己創建enhancement option ,只能使用系統預留的)

2.         創建增強點實現

 

為自己程序創建顯示增強Explicit Enhancement spot

image200

 

進入創建增強選項界面,輸入增強點名及增強容器名(以Z開頭),確認回車。

注:Enhancement Spot 就是SE18中的Enhancement Spot

image201

隨后Editor上會多出一條語句,然后轉到增強模式

image202

 

image203

 

image204

注:

Enhancement Spot相當於一個容器,創建一個增強點的必要條件是要有一個容器。每個增強點(如ZENH_POINT_01)都可以創建到這個容器當中,也可以再創建一個容器。刪除這個容器的方法:在本地對象或它的包中刪除或在SE18中刪除,激活程序,退出再進。

 

對於ENHANCEMENT-SECTION, 定義和實現的方法與ENHANCEMENT-POINT一樣。

兩者的區別是:enhancement-point沒有代碼,只有一個預留點,允許在這個位置插入新代碼(implementation.nhancement-sectionend-enhancement-section.之間有代碼,implementation之后,替換舊代碼,只執行新代碼,原來的代碼不再執行。

 

隱式與顯示增強

隱式增強就是系統內置的Enhancement options,有一點AOP的味道,但只能針對單個對象。Implicit enhancements comprise class enhancements, function group enhancements and predefined enhancement points at particular predefined positions such as the end of a report, a function module, an include or a structure and the beginning and the end of a method.

 

顯式增強就是我們人工加入到程序中的Enhancement options,有兩種顯式增強:

ENHANCEMENT-POINT ,用來插入新的功能代碼,沒有代碼,只有一個預留點

Defines a position in an ABAP program as an enhancement option, at which one or more source code plug-ins can be inserted.

 

ENHANCEMENT-POINT Syntax:

 

ENHANCEMENT-POINT enh_id SPOTS spot1 spot2 ...
                   [STATIC]
                   [INCLUDE BOUND].

 

ENHANCEMENT-SECTION ,用例替換原有的功能代碼,ENHANCEMENT-SECTION  END-ENHANCEMENT-SECTION. 之間有代碼, implementation 之后,替換舊代碼,只執行新代碼,原來的代碼不再執行.

 

Defines a section of an ABAP program as an enhancement option, which can can be replaced by one or more source code plug-ins.

 

ENHANCEMENT-SECTION Syntax:

 

ENHANCEMENT-SECTION enh_id SPOTS spot1 spot2 ...
                     [STATIC]
                     [INCLUDE BOUND].
   ...
END-ENHANCEMENT-SECTION.

 

 

隱式增強是系統本身就預留的,如在:執行程序,包含程序,函數組,對話模塊的結尾;Form例程,函數模塊,方法等的開始和結尾;結構的結尾這些地方都會有

顯示增強:需要在編輯器中創建,可參考上面

image205

 

image206

系統標准表結構增強

一般對於用戶自己創建的表就沒有必要采用增加了,直接修改即可。但對於SAP系統提供的表,如果需要擴展字段的話,則需要采用增加的方式來擴展。

SAP中一般是不允許直接修改系統標准表的,但是SAP提供了標准表的增加功能,允許用戶在原有表字段的基礎上增加一些自定義的字段,通常稱為表結構的增加。

表結構的增加並不是直接修改系統表,而在表中預留一個可以修改的結構體,該結構體再被系統表直接引用作為擴充的表字段。

 

表增強可以解決部分業務開展問題,但是同進也會增加對系統資源的消耗。SAP已經預留置了很多字段給用戶作業務擴展用,在追加表字段前,應該先盡量了解系統中已有功能是否能滿足目前業務的需要,避免造成一些不必要的資源浪費

 

SAP R/3系統提供了兩種方式對表或結構體進行增強:

l  Customizing includesCL includes):使用Include Struture對表結構進行增強

l  使用Append Strutures對表結構進行增強

通過這兩種方式可以使我們在不真正修改SAP的標准透明表結構的基礎上,對表的字段進行添加。

Include Struture

只有扁平的結構體才能被包含

包含可以被嵌套,最多九層

只有結構體才可以被包含在透明表的定義中。但透明表、視圖、結構體可以被包含到結構體中。

當多個表有相同的幾個字段時,這時可以將這些相同的字段抽出來形成一個結構,然后再將這個結構Include到表結構中

 

注:Include嚴格來講,不屬於表增強,因為在使用此功能時,需要切換到編輯模式,這樣就直接使用表結構了,標准表是不允許的,但Append就不需要切換到編輯模式就可以實現擴展字段

 

image207

image208

Append Strutures

AppendInclude兩種方式的區別:

l  Include方式時,會在透明表中增加一行名為“.INCLUDE”的列,而Append時,會在末尾增加一列名為“.APPEND”的列

l  Include可以插入到任何位置,但Append每次只能附加到當前表結構的末尾(可以附加多個)(但經過多次的修改,最后Append進來的結構也可能位於當前表結構的中間)

l  Include時,結構要事先創建好,但Append時,不能引用事先創建好的結構,只能在Append過程中創建

l  Include不同的是,Append可以在不修改SAP系統表(編輯狀態)的情況下,可以給系統表新增一字段,對現有使用該表的程序影響很小。

l  不能夠為PooledCluster表進行Append

l  如果某個表中有長文本字段(類型為LCHRLRAW)的表,不能夠在使用Append對它進行擴展,因為長文本字段通常也是只能放在表結構的最后面。

l  Append的結構名需要以ZY打頭,結構中的字段名要使用YYZZ打頭。

l  Append時,SE11不需要切換到編輯模式,但Include需要切換到編輯模式下才能使用

l  Append后,Append的結構中所字段會全部緊跟着顯示在“.APPEND”行后面,但Include時是不會將被Include里的字段顯示出來 不是沒顯示出來,是沒有展開(AppendInclude其實都是可以展開的):

image209

l  在復制表時,“.Append”會丟失,但Append中的字段會被拷貝過來,但Include與之不同,包括 .INCLUDE 與其字段都會被拷貝過來,這進一步證實了 .INCLUDE 結構是可以重復使用的,但Append結構不能

image210

點新增按鈕,出現新建Append結構對話框:

image211

image212

CURR類型的字段還需要參照表與字段:

image213

image214

SE14調整表

如果表的結構修改后,不能激活或激活失敗,此時可以使用SE14重新對表進行調整即可:

 

image215

若選擇“刪除數據”,會運載整個數據庫表進行清空,包括其他Client端的數據,請謹慎選擇


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM