17. 增強
17.1. 第一代:基于源碼增強(子過程subroutine)
17.2. 第二代:基于函數出口增強(Function)
17.2.1. 示例:采購訂單螢幕增強
17.2.1.1. 定義全局變量
17.2.1.2. 子螢幕
17.2.1.3. 螢幕與業務表資料間傳遞
17.2.1.4. 相關函數說明
17.2.2. 如何快速找到增強
17.3. 第三代:基于類的增強(BADI)
17.3.1. 新式BADI建立
17.3.1.1. 定義
17.3.1.2. 實作
17.3.1.3. 過濾器
17.3.1.3.1. 調用
17.3.1.4. 多個BADI/ Enhancement實作時究竟調誰
17.3.2. 經典BADI建立
17.3.2.1. Filter-Depend.過濾器
17.3.2.1.1. 調用
17.3.2.2. 通過經典BADI擴充自定義程式(菜單、螢幕、功能)
17.3.3. 示例:通過BADI實作采購訂單螢幕增強
17.4. 第四代:Enhancement-Point
17.4.1. 為自己程式建立顯示增強
17.4.2. 隐式與顯示增強
17. 增強
标準教材:BC425、BC427
17.1. 第一代:基于源碼增強(子過程subroutine)
這些Form集中存儲在一些檔案名倒數第二個字元為Z的包含程式中(如後面銷售憑證主程式SAPMV45A中的MV45ATZZ、MV45AOZZ等Include檔案)
這些Form的名稱一般是以UserExit_打頭的子子產品,是以一般找到所要增強的主程式,再查找UserExit_ 關鍵字即可找到相關的出口
Form源代碼增強事先要到 service marketplace 申請對象鍵(ACCESS KEY),然後才能修改這些子程式
另外,可以在SPRO中搜尋 USER EXIT關鍵字來查找
17.2. 第二代:基于函數出口增強(Function)
用SMOD(激活增強,隻需一次激活)和CMOD(實作增強)維護;在SAP釋出的版本中,使用CALL CUSTOMER-FUNCTION <3位數字>調用函數子產品的,是以你可以通過在程式中查找cusomer-function來查找增強,出口函數名稱由三部分組成:EXIT_<程式名>_<3位數字>(注:這裡的<程式名>即指調用此出口函數的程式名),這樣你就可以找到對應的增強函數了
針對資料表的增強出口是 “CI_ ”打頭的結構,這些結構将.INCLUDE 結構的形式包含到時相應的資料表中,使用者可以通過向這些結構中添加字段進而達到對資料表字段的增加
第二代增強中主要有4類:
1)E. Function exits:函數增強(最常用,在SAP上線很多年後都會使用,如:銷售單VA02中,對PO長度限制在10-15位之間,且不能為中文與其他特殊字元,還有如對PO采購日期不能晚于交貨日期的檢驗等,這些都會用來函數增強)
2)C.GUI codes:GUI增強
3)S. Screens:螢幕增強 增強螢幕的調用是使用CALL CUSTOMER-SUBSCREEN(不常用,一般在上線之初才會做,上線後不常用)
4)T. Tabes:表結構增強
查找Enhancement的方法:
1、 在程式中搜尋CUSTOMER-FUNCTION找到後面的3位數字編号,出口函數名的規則為EXIT_<程式名>_<3位數字>,然後通過找到的出口函數名到MODSAP表裡查找所對應的出口對象(即增強點)
2、 通過調試系統相關函數:MODX_FUNCTION_ACTIVE_CHECK
3、 代碼找增強

以VA01對應的主程式SAPMV45A為例,在源碼中可以查找包含CALL CUSTOMER-FUNCTION的字元串,可以找到這樣的代碼:
根據出口所對應的函數名規則,這個函數名為EXIT_SAPMV45A_003
MODSAP表:增強點(出口對象)與函數關系對應表
再根據出口函數,到MODSAP表中查找對應的增強點(出口對象):
注:一個出口函數隻對應一個出口對象,而一個出口對象可以對應到多個出口函數
Enhancement比較重要的表MODSAP,這個表裡重要的字段有增強名(Name,即出口對象名),元件類型(TYP: E C S T),元件功能子產品名(Member):裡面記錄了所有enhancement的增強。TFDIR所有的函數表,重要字段有FUNCName(函數名),MAND(功能子產品激活狀态如果是C代表此函數子產品激活)
17.2.1. 示例:采購訂單螢幕增強
通過調試MODX_FUNCTION_ACTIVE_CHECK系統函數,運作ME23N,找到名為EXIT_SAPMM06E_006的出口函數,再根據這個出口函數到MODSAP表中找到對應的出口對象(增強點)MM06E005,再通過SMOD檢視這個出口對象(增強點):
MM06E005包含功能出口、螢幕出口、表出口三種增強
在上面MM06E005增強的SMOD界面上輕按兩下表出口“CI_EKKODB”,可以對EKKO表結構進行擴充
在上面MM06E005增強的SMOD界面上輕按兩下出口函數“EXIT_SAPMM06E_006”,則會打開函數編輯器SE37,再點選工具欄中的“Display Object List”按鈕,則切換到SE80編輯器模式中顯示,這樣就可以找到出口函數所在的函數組為XM06,主程式為SAPLXM06:
INCLUDE LXM06TOP(Global Data在此為增強定義global data)
INCLUDE LXM06UXX.(Function Modules實際上包含所有可用的user exit出口函數)
INCLUDE LXM06F00. (SAP-Formpool for Customer-Use可在此建立Form pool)
INCLUDE ZXM06ZZZ. (Subprograms and Modules,在此建立增強子螢幕)
17.2.1.1. 定義全局變量
螢幕字段名的字首必須要設定為系統預先定義好的全局 EKKO_CI 内表類型名,這樣螢幕字段的就可以自動與該内表結構進行互動,EKKO_CI即為系統預先就定義好的增強螢幕所需的結構類型:
當向結構預留結構CI_EKKODB中擴充字段時,EKKO_CI也會自動的得到擴充,還有EKKO表結構也會被擴充
17.2.1.2. 子螢幕
在MM06E005增強點的SMOD界面上輕按兩下出口函數“SAPMM06E 0101 CUSTSCR1 SAPLXM06 0101”螢幕出口行,則會新建立螢幕0101(螢幕屬性需設定為子螢幕):
17.2.1.3. 螢幕與業務表資料間傳遞
17.2.1.4. 相關函數說明
MM06E005增強出口中各個出口函數功能說明:
006:Export Data to Customer Subscreen for Purchasing Document Header (PBO) Header,顯示子螢幕前調用,即在子螢幕的PBO事件塊執行前就會先調用此函數,在該函數中可以:将資料表中擴充字段所存業務資料導出到采購憑證頭中的客戶增強子螢幕中
007:Export Data to Customer Subscreen for Purchasing Document Header (PAI) Header,輸入後校驗
在該函數中:可以對輸入的資料進行檢驗
008:Import Data from Customer Subscreen for Purchasing Document Header Header,将通過驗證後的最終螢幕資料轉存到業務資料内表中,将作為最終業務資料插入到資料庫中
012:Check Customer-Specific Data Before Saving 按儲存按鈕後執行,儲存前調用
016: Export Data to Customer Subscreen for Purchasing Document Item (PBO) Item,與006相同
017:Export Data to Customer Subscreen for Purchasing Document Item (PAI) Item,與007相同
018:Import Data from Customer Subscreen for Purchasing Document Item Item,與008相同
17.2.2. 如何快速找到增強
盡管可以快速根據Tcode找到其對應的增強,可是往往因為這樣找到的是所有的增強,而且有些增強可能是随着系統啟動了某子產品才可能會用到的,這樣你可能會面臨究竟使用哪個增強的困惑, 是以在此介紹一種方法不用任何程式可以快速定位每個事務碼對應的增強,一刀緻命.
第一步:在檢查出口增強函數設定斷點(Tcode:SE37).
SE37輸入出口檢查函數MODX_FUNCTION_ACTIVE_CHECK.
系統有3種類增強,一是FUNCTION增強,這個最常用,我們一般所用的增強就是它,一是MENUENTRY菜單增強,還有一個就是SUBSCREEN增強,比如采購訂單(Tcode:ME21N),工單等很多主資料上都允許螢幕增強,就是如果你有非常極其BT的需求,允許自定義一個使用者螢幕,在螢幕上搞些自定義的字段,這些東西當然最後被儲存在自定義的表格中,這種思路代表了ERP設計的先進方向,如果你有興趣可以學習學習.
第二步:執行你想執行的任何Tcode
現在假設我執行MB1B我需要做一些檢查增強,系統自然執行到MODX_FUNCTION_ACTIVE_CHECK ,輸入變量l_funcname看看它是啥值,比如是EXIT_SAPLF048_001,這個增強的輸入參數有doc header and Item(如圖3),憑證頭和身子在這倆内表都有了,應該可做任何檢查.
根據屠宰經驗,是這樣的,函數包括增強函數都躺在表TFDIR,如果強函數TFDIR-MAND = ‘C’則表示該增強是激活的,于是系統賦予一個标志active = ‘X’,測試一下,現在有人将TFDIR-MAND改成’C’或直接将Active改成’X’, 系統馬上會到增強哪去逛一下,如果增強有諸如某個條件不match就錯誤的邏輯,系統就報告錯誤知道你糾正為止. 不過,象我這樣一看就非常老實厚道的人一般不會做這種欺騙系統的事情.
第三步:快速找到增強名稱(SE16|SMOD|CMOD).
确定增強函數EXIT_SAPLF048_001可用後,SE16:MODSAP,這表儲存了函數和增強名稱的對應關系,在MEMBER輸入EXIT_SAPLF048_001,如圖4,找到增強F180A001 .
SMOD|CMOD激活增強F180A001,激活函數EXIT_SAPLF048_001,建立程式ZXF48U01,在該程式中寫入增強邏輯并激活,注意一個增強生效時必須同時激活這3個東東.
有個弟兄說跟我在項目中學到了不少”歪門邪道“,什麼世道?祖傳的殺豬獨門功夫都讓他學去了
17.3. 第三代:基于類的增強(BADI)
BADI維護是通過SE18、SE19事務來來維護的。SE18用于建立及維護BADI對象;SE19用于維護BADI的執行個體
BADI的查找方法:
1、主程式都會調用cl_exitHandler=>get_instance(這隻是經典BADI是這樣來調用的,如果是新式的BADI,則調用為GET BADI handle-BADI定義名、CALL BADI handle->method)來判斷對象是否存在,并傳回執行個體。我們可以在se24中對類cl_exitHandler=>get_instance方法進行調試,運作一個tcode,看一下exit_name的值,這就是要找的BADI
2、在主程式中搜尋cl_exitHandler,檢視它所引用(TYPE REF TO)的接口名,根據接口命名規則 IF_EX_<badi>,得到<badi>命稱
3、通過程式查找
命名規則:
Badi definition: Z<badi>
Interface: ZIF_EX_<badi>
BADI implementation:Z<impl>
Implementing class:ZCL_IM_<impl>
17.3.1. 新式BADI建立
新式BADI中的增強容器Enhancement Spot、BADI定義 BADI Definitions、接口Interface、增強實作Enhancement Implementation、BADI實作BADI Implementation、實作類之間的關系:
一個增強容器下可以建立多個BADI定義,每個BADI定義由一個接口與多個增強實作組成,而每個增強實作裡又可以建立多個BADI實作,而每個BADI實作裡可以建立一個現實類
17.3.1.1. 定義
首先需要建立BADI增強點(Enhancement Spot), Enhancement Spot是作為一個BADI的容器, 在容器裡面,我們可以定義自己的多個BADI:
在建立立的enhancement spot中建立BADI:
定義BADI時,預設采用的是單一使用(single-use),如果沒有選中複合使用選項(Multiple Use),單一使用的限制是隻能有一個實作
一個Enhancement Spot可以定義多個BADI,每個BADI又是由一個接口與多個執行個體類組成的。Enhancement Spot相當于容器概念,用來存儲多個BADI,而每一個BADI必須定義一個接口,該接口可以有一個或多個實作(增強實作 Enhancement Implementation,每個增強實作裡面才能定義實作類),BADI實質上就是将接口與實作類組織(打包、捆綁)在一起了:
BADI對象是由接口與實作組成的,下面建立BADI接口:
輕按兩下接口名,可以建立接口,以及定義接口中的方法
17.3.1.2. 實作
由于一個BADI的實作可以有多個類,這些多個實作類需要組織(打包、捆綁)在一起(與多個BADI放在一個Enhancement Spot容器中是一個概念),是以需要建立一個新的BADI增強實作容器ZBADI_DEM001_IMP:
一個增強實作(Enhancement Implementation)可以有多個BADI Implementations(相當于多個版本,每個BADI Implementations即與一個且僅一個實作類對應),但起作用的同時隻能有一個,有多個版本時需要進行設定:
如果想要達到像Java中多态的話,需要建立多個不同的Enhancement Implementation增強實作,BADI中的多态就是通過不同的Enhancement Implementation增強實作來實作的:
當有兩個增強實作Z_BADI_CALC_IMPL_C、Z_BADI_CALC_IMPL_C2,需要把其中一個的Implementation is active前的鈎去掉才能被激活:
17.3.1.3. 過濾器
注意:上面過濾值一定要大寫,否則運作時比對不到
17.3.1.3.1. 調用
parameters: filter(2) type c.
DATA: handle TYPE REF TO z_badi_calc_vat,"z_badi_calc_vat為BADI定義名,不是接口也不是類
sum TYPE p,
vat TYPE p,
percent TYPE p.
sum = 50.
GET BADI handle
FILTERS "SE18中定義的過濾器名作為這裡的參數名
filter1 = 'C'.
CALL BADI handle->get_vat
EXPORTING
im_amount = sum
IMPORTING
ex_amount_vat = vat
ex_percent_vat = percent.
17.3.1.4. 多個BADI/ Enhancement實作時究竟調誰
在同一Enhancement Implementation中(如下圖中的Z_BADI_CALC_IMPL_C),不同的BADI Implementations(Z_BADI_CALC_IMPL、Z_BADI_CALC_IMPL2)之間究竟選誰的問題,是由 Default Implementation、Implementation is active選項共同來決定的,且在同一時間内隻能有一個BADI Implementations能被激活調用,是以要通過這兩個選項來控制究竟誰被用來當作目前實作被使用,是否被使用也可通過圖中的 Runtime Behavior說明文字來檢視:
不同的Enhancement Implementation之間(Z_BADI_CALC_IMPL、Z_BADI_CALC_IMPL2)調用由過濾器來決定:
17.3.2. 經典BADI建立
通過SE18->Utilities->Create Classic BAdi建立經典BADI
17.3.2.1. Filter-Depend.過濾器
當BADI的某個實作版本有多個實作類時,這時在調用時如果想要調用指定的類,則需添加過濾器參數,該參數實質上由其代理類來使用,在運作時代理類會去執行個體化所對應的類。
加上該選項後,接口與實作類中的所有方法都會自動的加上一個必輸參數:FLT_VAL
鈎選Filter-Depend選項後,我們再為實作增加過濾值:
17.3.2.1.1. 調用
DATA: out TYPE string.
DATA: l_badi_instance TYPE REF TO zif_ex__badidef_baditest2. zif_ex__badidef_baditest2是BAdi Definition的Interface name接口名
CALL METHOD cl_exithandler=>get_instance
CHANGING instance = l_badi_instance.
IF l_badi_instance IS NOT INITIAL.
CALL METHOD l_badi_instance->test
EXPORTING
"flt_val參數是由l_badi_instance執行個體來使用的,從這裡可以推斷l_badi_instance應該屬于代理對象,由它在運作時根據過濾器值來選擇性的調用相應實作類的方法
flt_val = '800'
in = 'hello'
CHANGING
out = out.
WRITE: / out.
ENDIF.
17.3.2.2. 通過經典BADI擴充自定義程式(菜單、螢幕、功能)
下面是實作:
DATA: ok_code LIKE sy-ucomm.
DATA: program TYPE program,
dynpro TYPE dynnr.
DATA: ref_badi_interface TYPE REF TO zif_ex_badi_defined.
CALL SCREEN 100.
MODULE status_0100 OUTPUT.
SET PF-STATUS '100'.
IF ref_badi_interface IS INITIAL.
DATA: act_imp_existing .
"擷取 BADI 的實作 Generated Exit Class
CALL METHOD cl_exithandler=>get_instance
EXPORTING
exit_name = 'ZBADI_DEFINED'
"如果未找到BADI實作或有實作但未激活時,ref_badi_interface是否可以接受NULL(即 INITIAL)
"一般設定為空,在為空時,如果未實作或未激活時,還是會傳回一個代理實作,這樣後面程式運作不
"會出錯,否則設定為X時,在未實作或未激活時,ref_badi_interface不會有值,則如果通過它調用
"方法時,會抛異常
null_instance_accepted = ' '
IMPORTING
act_imp_existing = act_imp_existing "實作是否已激活
CHANGING
instance = ref_badi_interface.
IF act_imp_existing <> 'X'.
MESSAGE 'BADI實作沒有被激活' TYPE 'I'.
"EXIT.
ENDIF.
CALL METHOD cl_exithandler=>set_instance_for_subscreens
EXPORTING
instance = ref_badi_interface.
"擷取BADI實作中所配置的增強子螢幕資訊
CALL METHOD cl_exithandler=>get_prog_and_dynp_for_subscr
EXPORTING
exit_name = 'ZBADI_DEFINED'"BADI 出口名,即BADI定義名
calling_dynpro = '0100'"主調螢幕号
calling_program = 'ZRP_BADITEST'"主調螢幕所屬程式
subscreen_area = 'SUB_AREA'"主調螢幕中的增強子螢幕區域名
IMPORTING
called_dynpro = dynpro "增強子螢幕号
called_program = program."增強子螢幕所屬程式
ENDIF.
ENDMODULE. " STATUS_0100 OUTPUT
MODULE user_command_0100 INPUT.
CASE ok_code.
WHEN 'FC1'.
MESSAGE '普通菜單' TYPE 'I'.
"隻要BADI實作激活後,才會出現菜單,即可以點選,才可能走這裡的邏輯
WHEN '+BADI'.
MESSAGE '增強菜單' TYPE 'I'.
WHEN 'BUT1'.
"如果BADI未實作或實作但未激活時,隻要 cl_exithandler=>get_instance
"時,設定輸入參數 null_instance_accepted = ' ',ref_badi_interface
"就會指向一個代理實作類,調用不會抛異常,但隻是個空的方法,什麼作用
"也不會有
CALL METHOD ref_badi_interface->hello.
ENDCASE.
ENDMODULE.
17.3.3. 示例:通過BADI實作采購訂單螢幕增強
主要用到兩個BADI:ME_GUI_PO_CUST(螢幕處理)和ME_PROCESS_PO_CUST(業務資料處理)
詳細請參考增強相關文檔
17.4. 第四代:Enhancement-Point
此種不建議使用,隻有無法通過 User Exit與BADI都無法實作時,才考慮這個
第四代其實是第三代上的加強
Ehancement Spot: 用來組織Enhancement options,it's a container of Enhancement options
Enhancement Implementation:用來組織Enhancement options的實作代碼
Enhancement Spot是對Enhancement的一個管理平台,Enhancement-Point技術與BADI是有差別的,首先BADI是SAP預留的類的接口,而Enhancement-Point則是允許使用者對現有的SAP代碼進行修改,例如插入、替換,隻要符合一定的規則即可,不需要SAP預先定義好
ENHANCEMENT-POINT是在程式中直接插入代碼,其概念與BADI的USER_EXIT類似,标準程式預留了部分已定義好的增強點可以讓ABAP做插入代碼來實作這個增強(也可以自定義增強點(ENHANCEMENT-POINT),但不能自定義增強選項(ENHANCEMENT-OPTION),增強選項一定是系統預留下來的,如果沒有增強選項則該處不可做增強),但是不能做螢幕和菜單增強。
其最大的優勢在于友善,可以直接使用程式中所有已定義的變量,不像BADI和USER EXIT中隻能使用方法或函數接口傳過來看參數
一般增強步驟:
1. DEBUG标準程式找到需要增強的位置,點EDIT->SHOW IMPLICIT ENHANCEMENT OPTIONS檢視是否有預留增強選項。(标準程式不能自己建立enhancement option ,隻能使用系統預留的)
2. 建立增強點實作
17.4.1. 為自己程式建立顯示增強
進入建立增強選項界面,輸入增強點名及增強容器名(以Z開頭),确認回車。
注:Enhancement Spot 就是SE18中的Enhancement Spot
随後Editor上會多出一條語句,然後轉到增強模式
注:Enhancement Spot相當于一個容器,建立一個增強點的必要條件是要有一個容器。每個增強點(如ZENH_POINT_01)都可以建立到這個容器當中,也可以再建立一個容器。删除這個容器的方法:在本地對象或它的包中删除或在SE18中删除
對于ENHANCEMENT-SECTION,定義和實作的方法與ENHANCEMENT-POINT一樣。兩者的差別是:enhancement-point沒有代碼,隻有一個預留點,允許在這個位置插入新代碼(implementation),而nhancement-section和end-enhancement-section.之間有代碼,implementation之後,替換舊代碼,隻執行新代碼,原來的代碼不再執行
17.4.2. 隐式與顯示增強
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 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 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 enh_id SPOTS spot1 spot2 ...
[STATIC]
[INCLUDE BOUND].
...
END-ENHANCEMENT-SECTION.
隐式增強:在 執行程式,包含程式,函數組,對話子產品的結尾;Form例程,函數子產品,方法等的開始和結尾;結構的結尾這些地方都會有
顯示增強:需要在編輯器中建立,可參考上面