第一代:基于源碼增強(子過程subroutine:Form)
通過SPRO->IMG查找使用者出口
通過開發類查找使用者出口
使用者出口子程式所能使用的資料變量
VA01增強示例
第二代:基于函數出口增強(Function)
SMOD與CMOD的差別
查找出口函數
根據出口函數查找對應的出口對象
出口對象激活(SMOD)
增強詳細說明文檔
示例:通過出口實作采購訂單螢幕增強
示例:VA01增強(CMOD建立項目)
利用系統函數尋找增強
二代增強幾個重要的表
第三代:基于類的增強(BADI)
BADI命名限制
建立自定義BADI(SE18)
BADI兩種建立(新式、經典)與兩種調用方式
Filter-Depend.過濾器
BADI總結
screenenhancement
通過經典BADI擴充自定義程式(菜單、螢幕、功能)
定義
現實
程式
第一代:基于源碼增強(子過程subroutine:Form)
基于源碼增加就是對SAP所預留的空的子過程(subroutine:Form)進行編碼,即對标準程式預留的空的Form進行編碼
源代碼增強以子程式形式釋出
第一代(基于源代碼的增強)是sap提供一個空代碼的子過程,在這個子過程中使用者可以添加自己的代碼。這類增強都需要修改sap的标準代碼,它們在釋出的時候都是空的,這些Form集中存儲在一些檔案名倒數第二個字元為Z的包含程式中(如後面銷售憑證出口文檔載圖中的MV45ATZZ、MV45AOZZ等Include檔案)。由于這些Form與主程式在一起,是以所有程式的全局資料都可以使用,但同時系統更新時會被新版本覆寫;這種Form源代碼增強和螢幕增強的說明可以從事務碼spro背景配置中相關子產品的路徑裡面找到;這些Form的名稱一般是以UserExit_ 打頭的子子產品,是以一般找到所要增強的主程式,再查找UserExit_關鍵字即可找到相關的出口。
螢幕增強以客戶螢幕形式釋出,它們包含在标準程式中,沒有什麼特别規律。
Form源代碼增強事先要到 service marketplace 申請對象鍵(ACCESS KEY),然後才能修改這些子程式,不過可以隐藏增強來實作,這樣就不需要直接修改源代碼了
查找此類出口的方法:
1、在增強主程式中找第二個字元為Z的包含程式;
2、通過SPRO在背景找對應子產品增強;
3、在程式中搜尋USEREXIT_打頭的關鍵字子程式;
4、打開想增強的程式,點選工具欄上的“Display Object List”按鈕,選擇Subroutines,查找以“UserExit”開頭的子程式,根據子程式前面的注釋文檔來查找使用者出口:

下面這些以USEEXIT_...開頭的都是空的子程式,可以添加自己的代碼:
通過SPRO->IMG查找使用者出口
一般相近功能的子程式(Form)都會放到一個統一的Include檔案中。可通過SAP的IMG(系統配置工具)擷取相關出口程式的資訊。
現以銷售模式為例來看看:
點選前面的
就會打開該節點功能描述文檔,會對所提供的使用者出口程式類型、名稱及功能做簡要的說明。通過說明文檔可以擷取SAP預留的出口名稱,如下圖中可以看到出口Include檔案,以及每個出口Include檔案中具體有哪些使用者出口子程式(Form):
通過開發類查找使用者出口
大部分銷售與分銷(還有MM子產品)的程式都是基于Form的源碼來實作增強的,這一類的增強都放在開發類VMOD中。
相應的出口子程式頭注釋中有相應描述及示例行代碼。
做出口開發需要注意,僅能在指定的子程式中間穿插代碼,不能對子程式的結構或名稱做任何變動,而且必須保證所編譯代碼正常激活,否則可能導緻整個業務系統異常。
使用者出口子程式所能使用的資料變量
以MV45AFZZ為例,其主要功能包括銷售訂單的建立、檢查及儲存,VBAP、VBAK表的資料在記憶體中分别是放在内表XVBAP、XVBAK中
查找出口中能使用到哪些内表變量是開發的關鍵,可以通過SE38查找對應的出口Include檔案在哪裡使用過:
然後選中某行後,點選
,就可以看到該程式的源:
SAP程式規範是一般會把不同功能代碼封閉在不同的Include檔案,如是變量定義一般存放于以 TOP 結尾的Include檔案中,如上面為 MV45ATOP,在這裡面可以找到想要的資料變量。
VA01增強示例
在以TOP結尾的Include定義的變量及内表資料都可以在使用者出口子程式中直接引用,系統執行中的一些業務資料會被暫存在某些内表中供出口程式判斷和使用。
根據出口From名稱,可以初步判斷其調用過程,如子程式USEREXIT_SAVE_DOCUMENT_PREPARE則會在訂單維護程式(VA01、VA02)儲存前調用。
這裡增加一個儲存之前驗證的邏輯:儲存銷售訂單時,若訂單類型為 ZDZ1(使用者自定義電子商務訂單類型),要求必須填入訂單編号,否則不能儲存。
通過F1鍵查找螢幕字段技術資訊,訂單類型為 VBAK-AUART,采購訂單編号在螢幕中的字段名為 VBKD-BSTKD,這兩個技術字段直接可以在使用者出口中引用:
第二代:基于函數出口增強(Function)
源代碼增強以函數子產品形式釋出
此種方式是基礎函數的,即所有增加代碼是增加到指定的函數裡,具有特定的輸入/輸出參數。每個出口函數都會與一個出口對象相對應,要使用這個函數,則需要找到對應的出口對象,并激活它才能生效
第二代增強(基于函數子產品的增強),用SMOD和CMOD維護;在SAP釋出的版本中,使用Call customer-function 'xxx'調用函數子產品的,是以你可以通過在程式中查找cusomer-function來查找第二代增強,第二代增強函數名構成:Exit_程式名_ ' xxx(3 digital number) ',這樣你就可以找到對應的增強函數了,這些出口函數在釋出的時候其源碼中隻有一句代碼include 'xxxxxxx ',修改時無需像第一代增強一樣需要ACCESS key,直接輕按兩下回車就可以了,但同時這種增強以及後面幾代的增強都不能像第一代一樣随便使用程式的全局資料,隻能使用接口中傳遞進來的參數。
螢幕增強也包含在函數子產品所屬的函數組中。
針對資料表的增強出口是 “CI_ ”打頭的結構,這些結構将.INCLUDE 結構的形式包含到時相應的資料表中,使用者可以通過向這些結構中添加字段進而達到對資料表字段的增加 (但标準表是不讓使用者直接通過Include方式來擴充表結構的,除了這預留的擴充結構)
上述這類增強通過事務碼 SMOD 進行維護,CMOD 進行實作。SMOD 中的一個增強可以包含上述的源代碼、螢幕和表結構增強
第二代增強中主要有4類:
1)E類. Ehancement exits:函數增強,就是常說User_exit (使用者出口函數)。這些出口函數以 Exit_打頭,你可以到SE37中檢視,也可以在資料字典中TFDIR(函數表)中查詢Exit_打頭的函數。
2)C類.GUI codes, (GUI增強)
3)S類. Subscreens(螢幕增強):比如,在建立采購訂單、工單等主資料時,系統都預留了螢幕增強,也就是說允許使用者自定義使用者輸入界面并編寫相應的輸入輸出處理程式。
4)T類. include structure增強:比如增強 MM06E005 允許使用者建立兩個結構 CI_EKKODB 和 CI_EKPODB
Enhancement在MODSAP表(增強點與函數關系對應表)中可查到,而TFDIR表中字段 MAND的值為C時才表示此出口函數被激活了。當然可使用SMOD(CMOD)來激活exit function,但有時候一時難以查詢到相關Enhancement時就無法使用SMOD(CMOD)來激活,則可使用下面程式将出口函數激活:
REPORT zactexitfun .
DATA ztfdir LIKE tfdir .
* select single * from tfdir into ztfdir * where FUNCNAME = * 'EXIT_SAPMM06E_013'. * ztfdir-MAND = 'C' .
* update tfdir from ztfdir.
* 将EXIT_SAPMM06E_013換成實際所需exit函數名
UPDATE tfdir SET mand = 'C'.
where funcname = 'EXIT_SAPMM06E_013'.
當然也可通過SE16:MODSAP表enhancement輸入EXIT_SAPMM06E_013然後得到enhancement name MM06E005後使用SMOD測試激活exit函數。
注意,為了確定一個出口被真正應用,必須同時激活相關程式和出口函數(SMOD、CMOD),反正就是要保證tfdir-mand='C',用上面的程式代碼也可
Enhancement比較重要的表MODSAP,這個表裡重要的字段有增強名(Name,即出口對象名),元件類型(TYP: E C S T),元件功能子產品名(Member):裡面記錄了所有enhancement的增強。TFDIR所有的函數表,重要字段有FUNCName(函數名),MAND(功能子產品激活狀态如果是C代表此函數子產品激活)。
查找Enhancement的方法:
1、 在程式中搜尋Customer-function找到後面的3為digit suffix,然後出口函數組成就是Exit_程式名_3 digit suffix.
2、 代碼找增強。利用後面的代碼找enhancement(有些exit使用它并不能找到)
3、 通過調試系統相關函數
SMOD與CMOD的差別
SMOD可以直接用來檢視某個出口(如果某個出口的相應出口函數各自的作用文檔),而CMOD是與項目有關的,如果要通過CMOD檢視某個出口情況,則是需要先知道這個出口在被哪個項目使用了才能通過項目名進去檢視。一個出口隻能被一個項目所使用,如果某個出口還沒有被使用過,則需要先通過CMOD建立項目,在這個項目中引用這個出口者可以
===============================================================
===============================================================
如果某個出口被使用後,不能再被另外的項目使用了,如上面的RSAP0001出口已被BW項目所使用,是以在建立ZZBW引用時,會報錯:
查找出口函數
可先通過源碼找到出口函數,再通過出口函數找到出口對象
出口函數名稱由三部分組成,命名規則為:EXIT_<程式名>_<3位數字>首4個字母EXIT是固定的,表示是使用者出口;第二部分為程式名;第三部分為3位序号
出口函數在程式中是通過CALL CUSTOMER-FUNCTION <3位數字>來調用的,以VA01對應的主程式SAPMV45A為例,在源碼中可以查找包含CALL CUSTOMER-FUNCTION的字元串,可以找到這樣的代碼:
根據出口所對應的函數名規則,這個函數名為EXIT_SAPMV45A_003,再通過SE37打開函數,可看到這個函數中指定了一個預留的程式,名稱為 ZXVVAU05:
由于程式名為ZXVVAU05,以Z打頭,可以直接修改或建立,但是在輕按兩下時,會提示
,這是由于 ZX打頭的Include程式名用作增強Include檔案名了,是以出現了警告,此時隻要按Enter鍵,即可繼續建立該Include檔案
根據出口函數查找對應的出口對象
前面隻是通過程式源碼來查找出了出口函數,但程式執行時并不會直接調用這些函數,必須先確定該函數所對應的出口對象是否已被激活,再通過該出口對象來引用這些出口函數
出口函數與出口對象的對應關系是儲存在 MODSAP 表中,Name字段為出口對象名稱,Type為出口對象的類型(E:功能退出;S:螢幕;T:表;C:GUI代碼;本例中的函數為E類型),Member為出口函數名,通過出口函數 EXIT_SAPMV45A_003 可以查找對應的出口對象,如下:
注:由于一個出口函數隻對應一個出口對象,而一個出口對象可以對應到多個出口函數;是以上面根據出口函數來查找出口對象時,一定隻能找到一個出口對象。但如果是根據出口對象來查找出口函數,則會查找出多個出口函數:
出口對象的描述則儲存在表 MODSAPT 中,如出口對象V45A0003的描述如下:
出口對象激活(SMOD)
通過事務碼SMOD,可以檢視并修改該出口對象。單擊工具欄中的執行按鈕,可以看到出口對象所包含的函數清單,并可檢視該出口對象目前的狀态,若該出口對象未被激活,其輸出清單将會由紅色圖示來顯示,否則為綠色。另外可以通過工具欄按鈕來激活該對象,隻有被激活的對象才能在程式執行時被調用:
增強詳細說明文檔
一個增強一般有都有相應的文檔,文檔可以用來指導開發,文檔可以顯示如下:
示例:通過出口實作采購訂單螢幕增強
通過調試MODX_FUNCTION_ACTIVE_CHECK系統函數,運作ME23N(或ME21N、ME22N)時,會進入到調試界面,并能找到名為EXIT_SAPMM06E_006的出口函數:
再根據這個出口函數到MODSAP表中找到對應的增強為MM06E005:
再使用Tcode:SMOD查找增強有哪些:
從上面圖中可以看到增強MM06E005包含功能出口、螢幕出口、表出口三種增強。在SMOD界面輕按兩下後面對應的增強名可以進行增強功能的實作。
本示例為采購訂單的表頭增加一個子螢幕,子螢幕中的字段内容可以一并存儲到資料庫表中,并在ME21N,ME22N或ME23N界面中都可使用,最終的效果如下:
具體開發過程如下
在上面MM06E005增強的SMOD界面上輕按兩下表出口“CI_EKKODB”,可以為EKKO表Include自定義結構,該結構中的字段即為子螢幕中使用的字段:
表結構增強并激活後,EKKO表中就會出現增強的字段:
在上面MM06E005增強的SMOD界面上輕按兩下出口函數“EXIT_SAPMM06E_006”,則會打開函數編輯器SE37:
再點選工具欄中的“Display Object List”按鈕,則切換到SE80編輯器模式中顯示,并且左邊顯示出了該出口函數所屬性的函數組:
在該函數組中,可以現實螢幕與函數的增強代碼
輕按兩下左樹中的“XM06”節點,即可打開函數所所對應的主程式“SAPLXM06”:
SAPLXM06函數組主程式實際上包含四個include:
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,在此建立增強子螢幕)
在目前為止,還隻找到增強點MM06E005所需實作的地方,但還不知道該函數組中的出口函數及子螢幕到底被哪個程式所使用了,具實很簡單,可根據出口函數命名的規則:EXIT_<程式名>_<3位數字>,從出口函數名EXIT_SAPMM06E_006得知,其對應的主調程式為SAPMM06E:
在主調程式中我們搜尋“EXIT_SAPMM06E_006”出口函數(注:搜尋時一定要輕按兩下SAPMM06E主程式,打開主程式後才能搜尋到),即可找到調該函數的地方:
有的出口函數是通過CALL CUSTOMER-FUNCTION <3位數字>的形式來調用的,是以也可以在主調程式中搜尋關鍵字“CALL CUSTOMER-FUNCTION”,即可找到一其他些調用出口函數的地方:
上面找到了出口函數的地方了,那出口子螢幕又是在哪裡被調用的呢?還是一樣,在主調程式中查找,因為增強螢幕的調用是使用“CALL CUSTOMER-SUBSCREEN CUSTSCR1 INCLUDING 'SAPLXM06' '0101'.”形式來調用的,是以搜尋“CALL CUSTOMER-SUBSCREEN”關鍵字即可找到螢幕調用地方:
(注:搜尋前需要打開某個螢幕才能搜尋得到,因為這些螢幕調用語句是在螢幕邏輯流中進行調用的,是以不能像上面那樣打開主調程式進行搜尋)
(從上面搜尋結果看,SAPMM06E可以增強的Subscreen No有0101、0111、0201、0211、0301、0311,增強的子屏作用可從後面描述中就知)
輕按兩下上圖中的了螢幕号“0101”時,就會跳轉到SAPLXM06主程式中的0101号螢幕,但是左邊的Object List對象清單沒有跟着變化,因為0101号子螢幕現應屬于XM06函數組,但可點選工具欄中的
Display Object List 按鈕即可同步顯示(這也是用來定位一個程式或螢幕所對在的主程式或函數組的常用方法):
下面開始實作子螢幕及相關代碼
定義全局變量:
在MM06E005增強的SMOD界面上輕按兩下出口函數“SAPMM06E 0101 CUSTSCR1 SAPLXM06 0101”螢幕出口行,則會新建立螢幕0101:
(螢幕屬性需設定為子螢幕)
(對子螢幕的PBO進行編輯,目的是對螢幕字段顯示前設定是否可編輯)
再對0101螢幕進行設定:
(注意:螢幕字段名的字首設定為全局 EKKO_CI 内表結構變量名,這樣螢幕字段的内容就自動與該内表結構進行綁定了,即螢幕字段就可以與記憶體變量進行互動了)
上面隻是完成螢幕的設計,下面還要對螢幕所綁定的内表EKKO_CI 與最終内表(資料從資料庫表中讀取出來或資料存儲到資料庫表中時,資料都是存儲在與資料庫表結構一樣的内表中的,這種内表就是最終内表)進行資料的互動操作(讀取與指派):
Item子螢幕的增加過程基本上是一樣的,隻是實作的地方不一樣,具體實作入口如下:
如果要去掉增強子螢幕,則取消增強激活即可:
MM06E005中各個出口函數功能說明:
006:Export Data to Customer Subscreen for Purchasing Document Header (PBO)
在運作事務ME31K(建立合同),輸入了初始螢幕中的資料後及ME21N輸入了供應商、機構資料後回車時将調用。其中IMPORT參數I_CI_EKKO是EKKO的定制包含資料,I_LFM1是供應商的采購組織資料,I_KEKKO是合同資料,TEKPO是訂單項目資料,I_EKPO為訂單項目目前正在輸入的這一項資料(結構),TEKKN為訂單賬戶設定資料,TEKET為确認交貨的計劃行資料,TKOMV為條件資料。可用于組織結構資料及供應商資料的控制。其英文描述表示采購檔案擡頭資料顯示到界面前(PBO)調用。擡頭或行項目資料隻要發生更改就會調用(輸入後不更改不會調用)。
007:Export Data to Customer Subscreen for Purchasing Document Header (PAI) 采購訂單建立時輸入資料後或修改時輸入資料後将調用,可用于輸入資料後的各種校
驗及控制。
008:Import Data from Customer Subscreen for Purchasing Document Header 修改或建立采購訂單擡頭的客戶自定義螢幕中的資料或行項目資料時調用。此出口用
于控制采購訂單的定制包含中的資料,即最終需要儲存什麼樣的定制資料到資料庫。
012:Check Customer-Specific Data Before Saving 采購訂單儲存前調用。在螢幕上錄入修改資料回車等不會調用,隻有按儲存鈕時調用。
013:Update Customer's Own Data in Purchasing Document
014: Read Customer-Specific Data when Importing Purchasing Document
016: Export Data to Customer Subscreen for Purchasing Document Item (PBO) 行項目資料顯示到界面前(PBO)調用
017:Export Data to Customer Subscreen for Purchasing Document Item (PAI) 采購訂單建立時輸入資料後或修改時輸入資料後将調用,可用于輸入資料後的各種校
驗及控制。I_EKPO_OLD為訂單修改前的舊資料(如從合同帶過來的資料等,可用于與修改後的資料做比較)。
018:Import Data from Customer Subscreen for Purchasing Document Item 修改或建立采購訂單行項目的資料或擡頭資料時調用。此出口用于控制采購訂單的定
制包含中的資料,即最終需要儲存什麼樣的定制資料到資料庫。
示例:VA01增強(CMOD建立項目)
SMOD包含具體的增強,而CMOD是包含一組SMOD編寫的增強.
前面幾節隻是介紹了如何查找程式中的出口,但找到出口之後該如何使用?
首先需要通過 CMOD 來建立項目:
單擊增加配置設定按鈕,可以為這個項目配置設定多個出口對象。需要注意的是,一個出口對象隻能被引用一次,若在本CMOD項目中被配置設定了,那麼這個出口對象就不能再在其他CMOD項目中再配置設定:
再點選元件按鈕,系統将進入元件清單對象頁面,該頁面列出了是以出口對象所對應的出口函數清單。從該頁面可以看出項目以及出口對象是否都已經激活:
輕按兩下EXIT_SAPMV45A_003函數,進行函數編輯器,然後再輕按兩下預留Include程式ZXVVAU05,此時會彈出警告,回車即可建立ZXVVAU05:
在ZXVVAU05檔案裡可以增加如下代碼:
最後要激活 CMOD項目,以及SMOD出口對象,這樣在通過VA01建立銷售訂單時,就會執行這一段代碼
利用系統函數尋找增強
Function:
DYNP_VALUES_READ
MODX_ALL_ACTIVE_MENUENTRIES(菜單增強)
MODX_FUNCTION_ACTIVE_CHECK(檢查E類型使用者出口是否被激活)
在函數MODX_FUNCTION_ACTIVE_CHECK中的判斷語句If tfdir-mand = aktiv_flag處設定斷點,然後運作需查找出口的事務碼,如果有增強,就會自動跳入DEBUG界面,如果出口函數active标志=’X’,表示該使用者出口被激活,處理邏輯将從标準程式轉入出口函數。在DEBUG界面,檢視L_FUNCNAME、f_tab等字段,這裡面所顯示的Smod就是關于這個TCODE所有的增強項目的清單(出口函數清單),這些增強都是屬于EXIT_XXXXXX_XXX這種形式。至于如何檢視這個增強是屬于哪個SMOD,可以自己查閱 MODSAP這個表(SAP Enhancements),如下面是運作MIGO時的一個截圖,查到其出口函數EXIT_SAPLEINR_001:
MODX_MENUENTRY_ACTIVE_CHECK(檢查C類型增強激活狀況)
MODX_SUBSCREEN_ACTIVE_CHECK(檢查S類型增強激活狀況)
二代增強幾個重要的表
MODSAP sap enhancement table ,出口函數(與增強點關系表,重要的字段有出口函數、增強點、增強類型(E C S T)
TFDIR function module table,存儲了出口函數名、是否激活(E類)
TFTIT 函數功能的短文本
TSDIR Dynpro Areas CALL CUSTOMER SUBSCREEN(螢幕增強,S類)
CUATEXTS GUI Interface: Menu Texts Changed(GUI 菜單文本增強,C類)
MODSAPA sap 增強的屬性
MODATTR sap增強項目屬性
TADIR 資源庫對象的目錄
第三代:基于類的增強(BADI)
BADI:Business Add-In
主要技術是基于ABAP對象來實作增強。
BADI維護是通過SE18、SE19事務來來維護的。SE18用于建立及維護BADI對象;SE19用于維護BADI的執行個體,即實作
SAP的BADI不但可以實作對标準系統的增強,也可以直接在自定義程式中進行調用
源代碼增強以接口形式釋出
第三代增強(基于面向對象概念的增強BADI(business add-in)),源代碼釋出以接口的方式,通過接口的方法調用來實作使用的。使用者增強實際上是實作一個或多個基于這個接口的實作類,因為接口類實際上是一個抽象類,是以對同一個增強會出現不同的源代碼,這些不同的源代碼是通過過濾器(adapter)來差別用于不同的業務場景的。這種增強是用SE18 SE19來實作的。
BADI的查找方法:
1、 BADI對象的資訊存儲在SXS_INTER,SXC_EXIT,SXC_CLASS和SXC_ATTR這四個表中。
2、 主程式都會調用cl_exithandler=>get_instance來判斷對象是否存在,并傳回執行個體。我們可以打開類編輯器se24中,輸入類CL_EXITHANDLER,并進入get_instance方法,設定斷點,運作一個tcode,看一下exit_name的值,這就是要找的BADI。
3、 se37 檢視SXV_GET_CLIF_BY_NAME,設定斷點,檢視name的值。
4、 它的調用方式是call method(instance), 可以通過exit_handler關鍵詞來查找。
5、 ST05:
1) 選擇SQL trace、buffer trace,然後activate trace ,運作TCODE ,deactivate trace
2) display trace,顯示display trace的對話框,在表的欄位上加上? V_EXT_IMP和 V_EXT_ACT
3) 檢視以IF_EX_開頭的字元串,這是interface class 的名字,IF_EX_後面的就是BADI。例如IF_EX_EQUI_UPDATE
6、 se18 查找接口,se19 實作接口就可以實作使用者增強
第四代其實是第三代的加強switch Framework
當sap進入newweaver 7.0以後推出的新增強體系,将BADI進行了改進,叫新BADI了。還新增Enhancement Spot 和 Enhancement Section 以及隐式增強點的概念,基本可以在面向對象的程式裡實作處處皆可增強的最高境界。
BADI命名限制
BADI DEFINITION中的name conventions
l Badi definition: <badi> or z<badi> or /../<badi>
l Interface:IF_EX_<badi> or ZIF_EX_<badi> or /../IF_EX_<badi>
l Methods:any name
l Generated badi class(adapter class) cl_ex_<badi> or zcl_ex_<badi> or /../cl_ex_<badi>
BADI IMPLEMENTATION中的name conventions
l BADI implementation <impl> or z<impl> or /../<impl>
l Interface: IF_EX_<badi> or ZIF_EX_<badi> or /../IF_EX_<badi>
l Methods:在badi definition中定義
l Implementing class:CL_IM_<impl> ZCL_IM_<impl> /../CL_IM_<impl>
建立自定義BADI(SE18)
首先需要建立一個自定義的BADI增強點(Enhancement Spot)對象
建立BAID對象:
BADI對象是由接口與實作組成的,下面建立BADI接口以及接口編輯:
編輯接口:
下面開始建立BADI實作以及實作類的編輯:
确定後進入 Enhancement Implementation 頁面:
輕按兩下“Implementing Class”進入實作類編輯界面:
點選“Yes”,進入類編輯器對實作類進行編輯:
所建立的BADI可以直接在ABAP程式中進行調用:
BADI兩種建立(新式、經典)與兩種調用方式
發現調用方法有兩種,第一種是傳統BADI調用的方式,第二種是新BADI的調用方式:
1、直接調用方法 cl_exithandler=>get_instance
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
in = 'hello'
CHANGING
out = out.
WRITE: / out.
ENDIF.
2. 使用GET BADI語句
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.
以上兩種調用方法中的BADI有以下幾個差別:
1. 兩種BADI建立的方法有所差別
方法1中的BADI Definition是通過SE18->Utilities->Create Classic BAdi,實作BAdi是通過SE19;下面是經典BADI建立過程:
一個badi包含着enhancement components,其主要包含下面幾種components:
l Program enhancements:program enhancements是通過interface methods來實作的,SAP程式來調取生成的badi class的interface methods。
l Menu enhancements:同customer exit一樣,可以在badi中維護function code。這些menu entry在GUI definition中已經定義就可以在implemented badi看見。
l Screen enhancements:同customer exit一樣你可以在badi中維護screen enhancements。
或者通過SE19來建立實作
方法2中的BADI Definition是在SE18中,選擇Enhancement Spot,直接建立,而實作BAdi可以通過SE19也可以直接在建立的Enhancement Spot中直接建立
注:如果在SE18中選中BAdI Name建立BAdi,系統會報錯""Create" operation is possible only for enhancement spots"。方式2請參照這裡
2. 在程式中,方法1中的zif_ex__badidef_baditest2是BAdi Definition的Interface name
方法2中的Z_BADI_CALC_VAT 直接是BAdi Definition name
對于方法1中建立的BAdi,如果用方法2的程式來調用,激活時報錯,提示"The type **** is unknown", 而如果Type REF TO 改成 Interface name,同樣是激活時報錯,會提示"'HANDLE' ist kein gultiges BAdI-Handle"
如果方法2中建立的BAdi,使用方法1中的程式來調用,程式可以激活,但是運作會dump,Runtime Error "Exception condition "DATA_INCONS_IN_EXIT_MANAGEM" raised."
其他注意的地方:
1. Multiple Use
使用方法2建立BAdi時,如果'Multiple Use'沒有選上,在建立多餘一個Implementation時,會提示出錯,提示兩個Implementation同時被激活。
解決方法是先把'Multiple Use'選上,之後再去掉也沒關系
2. Call fallback if no implementation is executed
該選項是用于在BAdi沒有做任何實作時,該Implementation example會生效.
3. Filter功能
該功能需要先建立一個Filter,然後對選中某個需要Filter的Implementation,輕按兩下Implementation進去進行設定。
Filter-Depend.過濾器
當BADI的某個實作版本有多個實作類時,這時在調用時如果想要調用指定的類,則需添加過濾器參數,該參數實質上由其代理類來使用,在運作時代理類會去執行個體化所對應的類。
Filter type擴充性局限于以下幾個方面:
l Filter type參考的domain必須符合下面的幾個條件:
? Domain要指向cross client value table。這個value table隻有一個使用的data element的domain是這個domain的key field。
? Domain有一個兩個key field的text table,一個key field使用的是這個domain本身,另一個key field是language field。其必須還得有一個text field,這個text field的類型為TEXT或TXT。在ABAP DICTIONARY中必須給text field指定一個value table。
? 這兩個table的delivery class必須是E或S
再加上該選項後,接口與實作類中的所有方法都會自動的加上一個必輸參數:FLT_VAL
鈎選Filter-Depend選項後,我們再為實作增加過濾值:
DATA: out TYPE string.
DATA: l_badi_instance TYPE REF TO zif_ex__badidef_baditest2.
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.
BADI總結
如果badi被激活,當調用程式執行時,相應的implementation methods也會被執行。一旦diactivate這個implementation,相應的方法就不會被調用。然而應用程式中的相關調用仍然會被執行。不同的是adapter class的instance不會找到激活的implementations。并不像CALL CUSTOMER-FUNCTION,CALL METHOD CL_EXITHANDLER=>GET_INSTANCE即使沒用implementations,仍然會被調用。隻有在original system(開發系統)中才可以activate或deactivate implementations,而subsequent systems則隻能transport。如果badi隻能有一個implementation,那麼系統中仍然可以存在這個badi的多個implementation,隻不過隻能有一個激活版本。
同customer exit一樣badi中也有menu enhancements。但是必須滿足以下兩個條件:1,必須預留了menu enhancement。2,必須在Badi的implementation中實作。Menu enhancements的function codes必須以+開始。如果相應的enhancement的badi implementation被激活,那麼menu就會顯示出來。你隻能為single use badi建立function code,而且badi不能是filter dependent。這樣就保證了一個或多個的badi不出現沖突。如果使用者在程式中選擇了相應的以+開始的function code,那麼系統就會調用相應的method。Method call和Menu enhancement是不可分離的,它們隻能屬于同一個badi。
通過badi builder SE18來建立badi。Badi有兩個重要的屬性,Reusable和filter-dependent。如果想讓你的badi支援多個激活的implementation那麼就要選擇reusable。不過implementations的執行順序不能被定義。即使badi本身不支援mulitiple use也可以同時存在多個implementations,隻不過隻能有一個激活的implementation。Badi如果filter dependent的,這樣就可以設定調用的條件。Filter type必須是data element或者ABAP dictionary structure。Data element使用的domain的value table包含了implementation所需要的valid values。如果filter type使用的是structure,那麼這适用于structure的每個字段。當調用enhancement method時,filter value必須傳給interface。Badi中可以包含function code,需要輸入program name,function code以及short description。局限性是不能夠建立隻包含function code的badi,menu enhancement既不能filter dependent也不能reusable。
系統提供了badi的interface和生成class的name。理論上可以将這些name改成任何你喜歡的name,但是保留系統推薦的name會使badi更加容易了解。生成的class的name遵循以下規則:namespace prefix(Z/Y) + CL_ + EX_(代表 exit) + badi name。輕按兩下interface name就會進入class builder,就可以定義interface method。一個badi indterface可以有多個interface method。Class bulder的所有功能都可以使用,如定義interface method,定義method的interface parameters以及聲明interface的attributes等。如果badi是filter dependent的,必須給他的每個method定義一個import parameter flt_val(具體怎麼做請參着這裡)。一旦完成了interface的定義就要激活它。一旦修改了interface,badi class也會重新生成。也可以在badi維護事務中通過utilities->Regenerate來重新生成adapter class。Badi在程式中的調用過程:首先聲明一個badi interface的reference variable。調用service class CL_EXITHANDLER的static method GET_INSTANCE。這個method傳回required object的instance。這裡屬于narrow cast(窄化),是以通過這個variable隻能調用interface methods。然後你就可以調用badi的方法了(具體代碼請參着這裡)。
幾種增強方法的比較:
Customer Exit | BTE | BADI | |
Source code | + | + | + |
Menus | + | - | + |
Screens | + | - | + |
Table | + | - | [+] |
Administration levels | + | - | + |
reusable | - | + | + |
Filter specific | - | + | + |
screenenhancement
對于ABAP virtual machine,screens和class是不能綁定到一塊的。是以隻用classical programs(type I,For M)才能作為screen的容器。Badi的screen enhancement也必須考慮到這點。當建立badi screen enhancement時,在應用程式的screen上要保留一塊subscreen area。然後implementing program就會填充它。應用程式和subscreen的container program并不直接交換資訊,而是通過生成的badi class。如果badi包含screen enhancements那麼它就不能是reusable的。在subscreen tab你需要輸入calling program,screen number和subscreen area。Implementing program和subscreen number是在implementation過程中指定的。
如果想通過badi實作screen enhancement,在應用程式中要實作以下步驟:
l 生成badi class
l 将badi class instance存入到refernce variable中
l 将資料傳遞給badi class
l 得到enhancement screen的program name和screen number
l 調用screen
在main screen的PAI,需要調用另外一個method把修改後的資料傳回給application program。
實作screen enhancement的幾個步驟:
l 首先通過adapter class CL_EXITHANDLER的factory method get_instance來得到生成的badi class的一個instance。并将其存儲到一個reference variable中。DATA:EXIT TYPE REF TO <badi-interface>. CALL METHOD cl_exithandler=>get_instance CHANGING instance = exit.
l 然後implementation必須能夠access badi instance。也就是說必須把reference variable賦給handler class的一個attribute。然後這個reference variable就會賦給badi class。通過下面的語句來實作這個步驟:CALL MEHTOD cl_exithandler=>set_instance_for_subsdcreens
l 為了使implementation獲得資料需要通過兩個步驟來實作。首先将data傳給badi class,這些資料存儲在這個方法的implementation的global attributes中,強烈建議在這個步驟提供sample code。通過badi中定義的mehtod來傳遞資料。然後存儲在badi global attributes中的data就會被自動傳到implementation的global attributes中,當然,implementation必須是active狀态。使用的語句如下:CALL METHOD exit->put_data EXPORTING <data>.
l 在執行CALL SUBSCREEN語句之前,必須得到subscreen的screen number和program name,這個通過方法cl_exithandler=>get_prog_and_dynp_for_subsc來實作。如果沒有active implementation,就會得到dummy function group(SAPLSEXM)的dummy subscreen (screen number 200)。如果存在active implementation,在implementation中指定的subscreen就會被使用。語句如下:CALL METHOD cl_exithandler=>get_prog_and_dynp_for_subscr EXPORTING … IMPORTING called_program = … called_dynpro = ..
l 調用相應的subscreen,如果沒有active implementation,就會顯示default empty subscreen。
l 如果在離開screen之後想得到Implementation中的data,則需要使用method:CALL METHOD exit->get_data IMPORTING <data>。
Implement badi screen enhancement的步驟如下:
l 建立badi的implementation指定包含subscreen的程式名和screen number
l 建立指定的program
l Layout指定的subscreen
l Implementing program必須得到badi的instance
l 從應用程式傳遞給badi的資料可以被取得。
l 如果想将修改後的資料傳回給application program,需要調用相應的方法。
得到badi class instance的方法是CALL METHOD cl_exithandler=>get_instance_for_subscreens,這個reference是通過方法cl_exithandler=>set_instance_for_subscreen來傳遞的。這允許你在implementation中通路badi的attributes以及interface methods。Implementing program通過方法:exit->get_data IMPORTING <data>來擷取badi中的data。通過方法exit1->put_data EXPORTING <data>将修改後的data傳回給badi class的instance。
通過經典BADI擴充自定義程式(菜單、螢幕、功能)
定義
現實
程式
REPORT zrp_baditest.
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.