最近想更深入學習下classic BADI, 以前沒玩過BADI屏幕增強, 所以決定玩一下.
這次的屏幕增強主要用到兩個BADI: ME_GUI_PO_CUST和ME_PROCESS_PO_CUST
這兩個BADI都是有例子的, 可以在se18那里按GoTo->Sample code->Display來查看, 也可以直接在SE24查看類CL_EXM_IM_ME_GUI_PO_CUST和CL_EXM_IM_ME_PROCESS_PO_CUST
現在我們對PO header加上自己的subscreen, SAP的例子提供的是對item增加subscreen.
Step 1: Create Function Group
仿照Function Group MEPOBADIEX建一個Function Group
我建的Function Group如下圖所示
Step 2: 在Function Group里建Screen, screen number隨便
在TOP里面加入對應屏幕的變量, 這里我是直接用tables.
Step 3: 給BADI ME_GUI_PO_CUST建一個implementation
在Public Section里加上TYPE-POOLS mmmfd .
Step 4: 在Method IF_EX_ME_GUI_PO_CUST~SUBSCRIBE, 加入custom subscreen的設置代碼
- METHOD if_ex_me_gui_po_cust~subscribe.
- DATA: lw_subscribers TYPE mepo_subscribers.
- * we want to add a customer subscreen on the Header tab
- CHECK im_application = 'PO'.
- CHECK im_element = 'HEADER'.
- CLEAR lw_subscribers.
- lw_subscribers-name = subscreen1.
- lw_subscribers-dynpro = '0100'.
- lw_subscribers-program = 'SAPLZCI_EKKODB'.
- lw_subscribers-struct_name = 'CI_EKKODB'.
- lw_subscribers-label = 'Zero test2'.
- lw_subscribers-position = 11.
- lw_subscribers-height = 8.
- APPEND lw_subscribers TO re_subscribers.
- ENDMETHOD.
METHOD if_ex_me_gui_po_cust~subscribe. DATA: lw_subscribers TYPE mepo_subscribers. * we want to add a customer subscreen on the Header tab CHECK im_application = 'PO'. CHECK im_element = 'HEADER'. CLEAR lw_subscribers. lw_subscribers-name = subscreen1. lw_subscribers-dynpro = '0100'. lw_subscribers-program = 'SAPLZCI_EKKODB'. lw_subscribers-struct_name = 'CI_EKKODB'. lw_subscribers-label = 'Zero test2'. lw_subscribers-position = 11. lw_subscribers-height = 8. APPEND lw_subscribers TO re_subscribers. ENDMETHOD.
Step 5: 在IF_EX_ME_GUI_PO_CUST~MAP_DYNPRO_FIELDS, 加入代碼, 使field name和它的數字編號關聯起來
- FIELD-SYMBOLS: <mapping> LIKE LINE OF ch_mapping.
- LOOP AT ch_mapping ASSIGNING <mapping>.
- CASE <mapping>-fieldname.
- WHEN 'LV_TEST1'. <mapping>-metafield = mmmfd_cust_01.
- WHEN 'LV_TEST2'. <mapping>-metafield = mmmfd_cust_02.
- WHEN 'LV_TEST3'. <mapping>-metafield = mmmfd_cust_03.
- ENDCASE.
- ENDLOOP.
FIELD-SYMBOLS: <mapping> LIKE LINE OF ch_mapping. LOOP AT ch_mapping ASSIGNING <mapping>. CASE <mapping>-fieldname. WHEN 'LV_TEST1'. <mapping>-metafield = mmmfd_cust_01. WHEN 'LV_TEST2'. <mapping>-metafield = mmmfd_cust_02. WHEN 'LV_TEST3'. <mapping>-metafield = mmmfd_cust_03. ENDCASE. ENDLOOP.
Custom的field估計只能加9個, 因為我在TYPE-POOLS mmmfd看到最大的是mmfd_cust_09,
這里也可以把一些standard field弄到custom subscreen上.
經過以上五步, 我們可以在ME23N看到custom subscreen, 但在ME21N和ME22N依然是看不到的...這個是為什么, 我還搞不清楚.
Step 6: 給BADI ME_PROCESS_PO_CUST建一個implementation
在Public Section加入TYPE-POOLS mmmfd .
Step 7: 在Method IF_EX_ME_PROCESS_PO_CUST~FIELDSELECTION_HEADER加入代碼, 改變表ch_fieldselection的field status
'-'代表hidden, '+'或'.'表示editable, '*'代表display
- METHOD if_ex_me_process_po_cust~fieldselection_header.
- DATA: lv_persistent TYPE mmpur_bool.
- FIELD-SYMBOLS: <fs> LIKE LINE OF ch_fieldselection.
- 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.
- * if the item is already on the database, we disallow to change field badi_bsgru
- lv_persistent = im_header->is_persistent( ).
- set_input mmmfd_cust_01.
- set_input mmmfd_cust_02.
- set_input mmmfd_cust_03.
- ENDMETHOD.
METHOD if_ex_me_process_po_cust~fieldselection_header. DATA: lv_persistent TYPE mmpur_bool. FIELD-SYMBOLS: <fs> LIKE LINE OF ch_fieldselection. 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. * if the item is already on the database, we disallow to change field badi_bsgru lv_persistent = im_header->is_persistent( ). set_input mmmfd_cust_01. set_input mmmfd_cust_02. set_input mmmfd_cust_03. ENDMETHOD.
這里有兩點注意下:
1. im_header->is_changeable()可以判斷當前這些field是不是處於可編輯狀態, 如果是則為'X'. 比如剛進TCODE ME21N, ME22N它們是可編輯的, 但當按save保存成功后, 它們暫時是不可以編輯的, 要再按把它改回編輯狀態才可以編輯,這里就實現了這個功能.
2. im_header->is_persistent()可以判斷當這些field在數據庫里有值了, 它就是'X', 當我們把數據寫進表里就不想它被修改時, 可以用這個來判斷, 比如把某個field設置成ME21N可編輯, ME22N不可編輯.
經過這七步, ME21N/ME22N/ME23N都可以看到這個custom subscreen, 但所有自建field都是可編輯狀態, 必需做完第八步, 才能正常顯示
Step 8: 在screen中調用2個module
這兩個module存放在下圖的程序里
注意: 如果不調用這兩個module, BADI ME_GUI_PO_CUST下面的4個method都不會觸發
TRANSPORT_FROM_MODEL
TRANSPORT_TO_DYNP
TRANSPORT_FROM_DYNP
TRANSPORT_TO_MODEL
Step 9: 在Method IF_EX_ME_GUI_PO_CUST~TRANSPORT_FROM_MODEL加入代碼
把header的三個自建field傳到attribute dynp_data_pho里
- METHOD if_ex_me_gui_po_cust~transport_from_model.
- DATA: lw_header TYPE REF TO if_purchase_order_mm,
- lw_mepoheader TYPE mepoheader,
- lw_customer TYPE ci_ekkodb.
- *--------------------------------------------------------------------*
- * system asks to transport data from the business logic into the view
- *--------------------------------------------------------------------*
- IF im_name = subscreen1.
- * 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
- lw_mepoheader = lw_header->get_data( ).
- * store info for later use
- MOVE-CORRESPONDING lw_mepoheader TO dynp_data_pbo.
- ENDIF.
- ENDMETHOD.
METHOD if_ex_me_gui_po_cust~transport_from_model. DATA: lw_header TYPE REF TO if_purchase_order_mm, lw_mepoheader TYPE mepoheader, lw_customer TYPE ci_ekkodb. *--------------------------------------------------------------------* * system asks to transport data from the business logic into the view *--------------------------------------------------------------------* IF im_name = subscreen1. * 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 lw_mepoheader = lw_header->get_data( ). * store info for later use MOVE-CORRESPONDING lw_mepoheader TO dynp_data_pbo. ENDIF. ENDMETHOD.
Step 10: 在Method IF_EX_ME_GUI_PO_CUST~TRANSPORT_TO_DYNP加入代碼
通過調用FM:ZCI_EKKODB_PUSH 把attribute
- METHOD if_ex_me_gui_po_cust~transport_to_dynp.
- IF im_name = subscreen1.
- CALL FUNCTION 'ZCI_EKKODB_PUSH'
- EXPORTING
- im_dynp_data = dynp_data_pbo.
- ENDIF.
- ENDMETHOD.
METHOD if_ex_me_gui_po_cust~transport_to_dynp. IF im_name = subscreen1. CALL FUNCTION 'ZCI_EKKODB_PUSH' EXPORTING im_dynp_data = dynp_data_pbo. ENDIF. ENDMETHOD.
Step 11: 在IF_EX_ME_GUI_PO_CUST~TRANSPORT_FROM_DYNP加入代碼
PAI事件時, 把屏幕的值傳到attribute dynp_data_pai, 並且比較dynp_data_pbo和dynp_data_pai, 確定自建field的值有所改變, 把re_changed置為'X', 這樣IF_EX_ME_PROCESS_PO_CUST~PROCESS_HEADER就會觸發.
- METHOD if_ex_me_gui_po_cust~transport_from_dynp.
- IF im_name = subscreen1.
- CALL FUNCTION 'ZCI_EKKODB_POP'
- IMPORTING
- ex_dynp_data = dynp_data_pai.
- ENDIF.
- IF dynp_data_pai <> dynp_data_pbo.
- * something has changed therefor we have to notify the framework
- * to transport data to the model
- re_changed = mmpur_yes.
- ENDIF.
- ENDMETHOD.
METHOD if_ex_me_gui_po_cust~transport_from_dynp. IF im_name = subscreen1. CALL FUNCTION 'ZCI_EKKODB_POP' IMPORTING ex_dynp_data = dynp_data_pai. ENDIF. IF dynp_data_pai <> dynp_data_pbo. * something has changed therefor we have to notify the framework * to transport data to the model re_changed = mmpur_yes. ENDIF. ENDMETHOD.
Step 12: 在IF_EX_ME_GUI_PO_CUST~TRANSPORT_TO_MODEL加入代碼, 把修改后的數據傳到bussiness object里
- METHOD if_ex_me_gui_po_cust~transport_to_model.
- DATA: lw_header TYPE REF TO if_purchase_order_mm,
- lw_mepoheader TYPE mepoheader,
- lw_customer TYPE ci_ekkodb,
- lw_po_header_handle TYPE REF TO cl_po_header_handle_mm.
- *--------------------------------------------------------------------*
- * data have to be transported to business logic
- *--------------------------------------------------------------------*
- IF im_name = subscreen1.
- * 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?
- IF dynp_data_pbo-lv_test1 <> dynp_data_pai-lv_test1
- OR dynp_data_pbo-lv_test2 <> dynp_data_pai-lv_test2
- OR dynp_data_pbo-lv_test3 <> dynp_data_pai-lv_test3.
- * update standard fields
- lw_mepoheader-lv_test1 = dynp_data_pai-lv_test1.
- lw_mepoheader-lv_test2 = dynp_data_pai-lv_test2.
- lw_mepoheader-lv_test3 = dynp_data_pai-lv_test3.
- CALL METHOD lw_header->set_data
- EXPORTING
- im_data = lw_mepoheader.
- ENDIF.
- ENDIF.
- ENDMETHOD.
METHOD if_ex_me_gui_po_cust~transport_to_model. DATA: lw_header TYPE REF TO if_purchase_order_mm, lw_mepoheader TYPE mepoheader, lw_customer TYPE ci_ekkodb, lw_po_header_handle TYPE REF TO cl_po_header_handle_mm. *--------------------------------------------------------------------* * data have to be transported to business logic *--------------------------------------------------------------------* IF im_name = subscreen1. * 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? IF dynp_data_pbo-lv_test1 <> dynp_data_pai-lv_test1 OR dynp_data_pbo-lv_test2 <> dynp_data_pai-lv_test2 OR dynp_data_pbo-lv_test3 <> dynp_data_pai-lv_test3. * update standard fields lw_mepoheader-lv_test1 = dynp_data_pai-lv_test1. lw_mepoheader-lv_test2 = dynp_data_pai-lv_test2. lw_mepoheader-lv_test3 = dynp_data_pai-lv_test3. CALL METHOD lw_header->set_data EXPORTING im_data = lw_mepoheader. ENDIF. ENDIF. ENDMETHOD.
Step 13: 如果要check這些field的值的話, 可以寫在IF_EX_ME_PROCESS_PO_CUST~PROCESS_HEADER, 這里我要check field1必須是PALM.
- METHOD if_ex_me_process_po_cust~process_header.
- DATA: lw_mepoheader TYPE mepoheader,
- lw_customer TYPE ci_ekkodb.
- DATA: lv_testflag TYPE c.
- INCLUDE mm_messages_mac. "useful macros for message handling
- lw_mepoheader = im_header->get_data( ).
- IF lw_mepoheader-lv_test1 <> 'PALM'.
- * Place the cursor onto field lv_test1. The metafield was defined in BAdI ME_GUI_PO_CUST,
- * Method MAP_DYNPRO_FIELDS.
- mmpur_metafield mmmfd_cust_01.
- mmpur_message_forced 'E' 'ZDEV001' '999' '' '' '' ''.
- * MESSAGE 'This field should be filled ''PALM''' TYPE 'W'.
- * CLEAR lv_testflag.
- * lv_testflag = 'X'.
- * EXPORT lv_testflag FROM lv_testflag TO MEMORY ID 'Z_ZERO_ME22N'.
- * invalidate the object
- CALL METHOD im_header->invalidate( ).
- ENDIF.
- ENDMETHOD.
METHOD if_ex_me_process_po_cust~process_header. DATA: lw_mepoheader TYPE mepoheader, lw_customer TYPE ci_ekkodb. DATA: lv_testflag TYPE c. INCLUDE mm_messages_mac. "useful macros for message handling lw_mepoheader = im_header->get_data( ). IF lw_mepoheader-lv_test1 <> 'PALM'. * Place the cursor onto field lv_test1. The metafield was defined in BAdI ME_GUI_PO_CUST, * Method MAP_DYNPRO_FIELDS. mmpur_metafield mmmfd_cust_01. mmpur_message_forced 'E' 'ZDEV001' '999' '' '' '' ''. * MESSAGE 'This field should be filled ''PALM''' TYPE 'W'. * CLEAR lv_testflag. * lv_testflag = 'X'. * EXPORT lv_testflag FROM lv_testflag TO MEMORY ID 'Z_ZERO_ME22N'. * invalidate the object CALL METHOD im_header->invalidate( ). ENDIF. ENDMETHOD.
如果按回車來check的話, 它會顯示error message,一次改變后按一次回車有反應, 第二次就沒反應.
這里有一個缺陷, 當我們按save里, 這條message是不會顯示到message表中的(研究了很久, 沒研究出來怎么搞), 幸好這里有個功能十分不錯, 選中PO header data still faulty這個message, 按Edit, 就可以定位到error message的field.
至此, 增強完成.
補充:
1. IF_EX_ME_PROCESS_PO_CUST~INITIALIZE: initialize function groupZCI_EKKODB的變量語句可以寫在這.
- METHOD if_ex_me_process_po_cust~initialize.
- CALL FUNCTION 'ZCI_EKKODB_INIT'
- .
- ENDMETHOD.
METHOD if_ex_me_process_po_cust~initialize. CALL FUNCTION 'ZCI_EKKODB_INIT' . ENDMETHOD.
2. IF_EX_ME_PROCESS_PO_CUST~OPEN: 如果要select自建表的東西可以寫在這里, 這里有個field IM_TRTYP有一定的控制作用, 具體可以查看它的domain值.
3. IF_EX_ME_PROCESS_PO_CUST~POST: update自建表的語句可以寫在這里.
4. Custom subscreen的field必須引用method IF_EX_ME_GUI_PO_CUST~SUBSCRIBE里的定義的那個structure, from dict要勾上.
5. 如果有自建表里面的field, 必須在method IF_EX_ME_GUI_PO_CUST~TRANSPORT_TO_MODEL判斷自建表的field改變時, 加上語句CALL METHOD lw_header->set_changed( ), 不這樣的話, 你的自建表的field怎么變在保存時都會提示No data changed. 下面代碼是例子.
- METHOD if_ex_me_gui_po_cust~transport_to_model.
- DATA: lw_header TYPE REF TO if_purchase_order_mm,
- lw_mepoheader TYPE mepoheader,
- lw_customer TYPE ci_ekkodb,
- lw_po_header_handle TYPE REF TO cl_po_header_handle_mm.
- *--------------------------------------------------------------------*
- * data have to be transported to business logic
- *--------------------------------------------------------------------*
- IF im_name = subscreen1.
- * 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?
- IF dynp_data_pbo-lv_test1 <> dynp_data_pai-lv_test1
- OR dynp_data_pbo-lv_test2 <> dynp_data_pai-lv_test2
- OR dynp_data_pbo-lv_test3 <> dynp_data_pai-lv_test3.
- * update standard fields
- lw_mepoheader-lv_test1 = dynp_data_pai-lv_test1.
- lw_mepoheader-lv_test2 = dynp_data_pai-lv_test2.
- lw_mepoheader-lv_test3 = dynp_data_pai-lv_test3.
- CALL METHOD lw_header->set_data
- EXPORTING
- im_data = lw_mepoheader.
- ENDIF.
- IF dynp_data_pbo-zdamon <> dynp_data_pai-zdamon.
- CALL FUNCTION 'ZCI_EKKODB_SET_DATA'
- EXPORTING
- im_damon = dynp_data_pai-zdamon.
- CALL METHOD lw_header->set_changed( ).
- ENDIF.
- ENDIF.
- ENDMETHOD.
METHOD if_ex_me_gui_po_cust~transport_to_model. DATA: lw_header TYPE REF TO if_purchase_order_mm, lw_mepoheader TYPE mepoheader, lw_customer TYPE ci_ekkodb, lw_po_header_handle TYPE REF TO cl_po_header_handle_mm. *--------------------------------------------------------------------* * data have to be transported to business logic *--------------------------------------------------------------------* IF im_name = subscreen1. * 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? IF dynp_data_pbo-lv_test1 <> dynp_data_pai-lv_test1 OR dynp_data_pbo-lv_test2 <> dynp_data_pai-lv_test2 OR dynp_data_pbo-lv_test3 <> dynp_data_pai-lv_test3. * update standard fields lw_mepoheader-lv_test1 = dynp_data_pai-lv_test1. lw_mepoheader-lv_test2 = dynp_data_pai-lv_test2. lw_mepoheader-lv_test3 = dynp_data_pai-lv_test3. CALL METHOD lw_header->set_data EXPORTING im_data = lw_mepoheader. ENDIF. IF dynp_data_pbo-zdamon <> dynp_data_pai-zdamon. CALL FUNCTION 'ZCI_EKKODB_SET_DATA' EXPORTING im_damon = dynp_data_pai-zdamon. CALL METHOD lw_header->set_changed( ). ENDIF. ENDIF. ENDMETHOD.
6.Method IF_EX_ME_GUI_PO_CUST~EXECUTE可以處理function code.
- METHOD if_ex_me_gui_po_cust~execute.
- IF im_fcode = 'ZZZZ'.
- MESSAGE 'Zero test' TYPE 'I'.
- ENDIF.
- ENDMETHOD.
METHOD if_ex_me_gui_po_cust~execute. IF im_fcode = 'ZZZZ'. MESSAGE 'Zero test' TYPE 'I'. ENDIF. ENDMETHOD.