ME21N/ME22N/ME23N屏幕增強BADI ME_GUI_PO_CUST


最近想更深入學習下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的設置代碼

 

  1. METHOD if_ex_me_gui_po_cust~subscribe.  
  2.   DATA: lw_subscribers TYPE mepo_subscribers.  
  3.   
  4. * we want to add a customer subscreen on the Header tab  
  5.   CHECK im_application = 'PO'.  
  6.   CHECK im_element     = 'HEADER'.  
  7.   
  8.   CLEAR lw_subscribers.  
  9.   lw_subscribers-name = subscreen1.  
  10.   lw_subscribers-dynpro = '0100'.  
  11.   lw_subscribers-program = 'SAPLZCI_EKKODB'.  
  12.   lw_subscribers-struct_name = 'CI_EKKODB'.  
  13.   lw_subscribers-label = 'Zero test2'.  
  14.   lw_subscribers-position = 11.  
  15.   lw_subscribers-height = 8.  
  16.   APPEND lw_subscribers TO re_subscribers.  
  17. 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和它的數字編號關聯起來

 

  1. FIELD-SYMBOLS: <mapping> LIKE LINE OF ch_mapping.  
  2.   
  3. LOOP AT ch_mapping ASSIGNING <mapping>.  
  4.   CASE <mapping>-fieldname.  
  5.     WHEN 'LV_TEST1'. <mapping>-metafield = mmmfd_cust_01.  
  6.     WHEN 'LV_TEST2'. <mapping>-metafield = mmmfd_cust_02.  
  7.     WHEN 'LV_TEST3'. <mapping>-metafield = mmmfd_cust_03.  
  8.   ENDCASE.  
  9. 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

 

  1. METHOD if_ex_me_process_po_cust~fieldselection_header.  
  2.   DATA: lv_persistent TYPE mmpur_bool.  
  3.   FIELD-SYMBOLS: <fs> LIKE LINE OF ch_fieldselection.  
  4.   
  5.   DEFINE set_input.  
  6.     read table ch_fieldselection assigning <fs> with table key metafield = &1.  
  7.     if sy-subrc = 0.  
  8.      if im_header->is_changeable( ) = mmpur_yes.  
  9.        <fs>-fieldstatus = '+'.  
  10.      else.  
  11.        <fs>-fieldstatus = '*'.  
  12.      endif.  
  13.     endif.  
  14.   END-OF-DEFINITION.  
  15.   
  16. * if the item is already on the database, we disallow to change field badi_bsgru  
  17.   lv_persistent = im_header->is_persistent( ).  
  18.   
  19.   set_input mmmfd_cust_01.  
  20.   set_input mmmfd_cust_02.  
  21.   set_input mmmfd_cust_03.  
  22.   
  23. 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里

 

  1. METHOD if_ex_me_gui_po_cust~transport_from_model.  
  2.   DATA:     lw_header       TYPE REF TO if_purchase_order_mm,  
  3.             lw_mepoheader   TYPE mepoheader,  
  4.             lw_customer     TYPE ci_ekkodb.  
  5. *--------------------------------------------------------------------*  
  6. * system asks to transport data from the business logic into the view  
  7. *--------------------------------------------------------------------*  
  8.   
  9.   IF im_name = subscreen1.  
  10. * is it an Header? im_model can be header or item.  
  11.     mmpur_dynamic_cast lw_header im_model.  
  12.     CHECK NOT lw_header IS INITIAL.  
  13. * transport standard fields  
  14.     lw_mepoheader = lw_header->get_data( ).  
  15. * store info for later use  
  16.     MOVE-CORRESPONDING lw_mepoheader TO dynp_data_pbo.  
  17.   ENDIF.  
  18.   
  19. 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

 

 

  1. METHOD if_ex_me_gui_po_cust~transport_to_dynp.  
  2.   
  3.   IF im_name = subscreen1.  
  4.     CALL FUNCTION 'ZCI_EKKODB_PUSH'  
  5.       EXPORTING  
  6.         im_dynp_data = dynp_data_pbo.  
  7.   ENDIF.  
  8. 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就會觸發.

 

  1. METHOD if_ex_me_gui_po_cust~transport_from_dynp.  
  2.   IF im_name = subscreen1.  
  3.     CALL FUNCTION 'ZCI_EKKODB_POP'  
  4.       IMPORTING  
  5.         ex_dynp_data = dynp_data_pai.  
  6.   
  7.   ENDIF.  
  8.   
  9.   IF dynp_data_pai <> dynp_data_pbo.  
  10. * something has changed therefor we have to notify the framework  
  11. * to transport data to the model  
  12.     re_changed = mmpur_yes.  
  13.   ENDIF.  
  14. 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里

 

  1. METHOD if_ex_me_gui_po_cust~transport_to_model.  
  2.   
  3.   DATA: lw_header            TYPE REF TO if_purchase_order_mm,  
  4.         lw_mepoheader        TYPE mepoheader,  
  5.         lw_customer          TYPE ci_ekkodb,  
  6.         lw_po_header_handle  TYPE REF TO cl_po_header_handle_mm.  
  7. *--------------------------------------------------------------------*  
  8. * data have to be transported to business logic  
  9. *--------------------------------------------------------------------*  
  10.   IF im_name = subscreen1.  
  11.   
  12. * is it an item? im_model can be header or item.  
  13.     mmpur_dynamic_cast lw_header im_model.  
  14.     CHECK NOT lw_header IS INITIAL.  
  15.     lw_mepoheader = lw_header->get_data( ).  
  16. * standard fields changed?  
  17.     IF dynp_data_pbo-lv_test1 <> dynp_data_pai-lv_test1  
  18.     OR dynp_data_pbo-lv_test2 <> dynp_data_pai-lv_test2  
  19.     OR dynp_data_pbo-lv_test3 <> dynp_data_pai-lv_test3.  
  20. * update standard fields  
  21.       lw_mepoheader-lv_test1 = dynp_data_pai-lv_test1.  
  22.       lw_mepoheader-lv_test2 = dynp_data_pai-lv_test2.  
  23.       lw_mepoheader-lv_test3 = dynp_data_pai-lv_test3.  
  24.   
  25.       CALL METHOD lw_header->set_data  
  26.         EXPORTING  
  27.           im_data = lw_mepoheader.  
  28.   
  29.     ENDIF.  
  30.   ENDIF.  
  31. 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.

 

 

  1. METHOD if_ex_me_process_po_cust~process_header.  
  2.   DATA: lw_mepoheader TYPE mepoheader,  
  3.         lw_customer TYPE ci_ekkodb.  
  4.   
  5.   DATA: lv_testflag TYPE c.  
  6.   
  7.   INCLUDE mm_messages_mac. "useful macros for message handling  
  8.   
  9.   lw_mepoheader = im_header->get_data( ).  
  10.   
  11.   IF lw_mepoheader-lv_test1 <> 'PALM'.  
  12. * Place the cursor onto field lv_test1. The metafield was defined in BAdI ME_GUI_PO_CUST,  
  13. * Method MAP_DYNPRO_FIELDS.  
  14.   
  15.     mmpur_metafield mmmfd_cust_01.  
  16.     mmpur_message_forced 'E' 'ZDEV001' '999' '' '' '' ''.  
  17. *    MESSAGE 'This field should be filled ''PALM''' TYPE 'W'.  
  18.   
  19. *    CLEAR lv_testflag.  
  20. *    lv_testflag = 'X'.  
  21. *    EXPORT lv_testflag FROM lv_testflag TO MEMORY ID 'Z_ZERO_ME22N'.  
  22.   
  23. * invalidate the object  
  24.     CALL METHOD im_header->invalidate( ).  
  25.   ENDIF.  
  26.   
  27. 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的變量語句可以寫在這.

 

  1. METHOD if_ex_me_process_po_cust~initialize.  
  2.   CALL FUNCTION 'ZCI_EKKODB_INIT'  
  3.             .  
  4. 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. 下面代碼是例子.

 

  1. METHOD if_ex_me_gui_po_cust~transport_to_model.  
  2.   
  3.   DATA: lw_header            TYPE REF TO if_purchase_order_mm,  
  4.         lw_mepoheader        TYPE mepoheader,  
  5.         lw_customer          TYPE ci_ekkodb,  
  6.         lw_po_header_handle  TYPE REF TO cl_po_header_handle_mm.  
  7. *--------------------------------------------------------------------*  
  8. * data have to be transported to business logic  
  9. *--------------------------------------------------------------------*  
  10.   IF im_name = subscreen1.  
  11.   
  12. * is it an item? im_model can be header or item.  
  13.     mmpur_dynamic_cast lw_header im_model.  
  14.     CHECK NOT lw_header IS INITIAL.  
  15.     lw_mepoheader = lw_header->get_data( ).  
  16. * standard fields changed?  
  17.     IF dynp_data_pbo-lv_test1 <> dynp_data_pai-lv_test1  
  18.     OR dynp_data_pbo-lv_test2 <> dynp_data_pai-lv_test2  
  19.     OR dynp_data_pbo-lv_test3 <> dynp_data_pai-lv_test3.  
  20. * update standard fields  
  21.       lw_mepoheader-lv_test1 = dynp_data_pai-lv_test1.  
  22.       lw_mepoheader-lv_test2 = dynp_data_pai-lv_test2.  
  23.       lw_mepoheader-lv_test3 = dynp_data_pai-lv_test3.  
  24.   
  25.       CALL METHOD lw_header->set_data  
  26.         EXPORTING  
  27.           im_data = lw_mepoheader.  
  28.   
  29.     ENDIF.  
  30.   
  31.     IF dynp_data_pbo-zdamon <> dynp_data_pai-zdamon.  
  32.       CALL FUNCTION 'ZCI_EKKODB_SET_DATA'  
  33.         EXPORTING  
  34.           im_damon = dynp_data_pai-zdamon.  
  35.   
  36.       CALL METHOD lw_header->set_changed( ).  
  37.     ENDIF.  
  38.   
  39.   ENDIF.  
  40. 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.

 

  1. METHOD if_ex_me_gui_po_cust~execute.  
  2.   IF im_fcode = 'ZZZZ'.  
  3.     MESSAGE 'Zero test' TYPE 'I'.  
  4.   ENDIF.  
  5. ENDMETHOD.  
METHOD if_ex_me_gui_po_cust~execute.
  IF im_fcode = 'ZZZZ'.
    MESSAGE 'Zero test' TYPE 'I'.
  ENDIF.
ENDMETHOD.



 

 

 

 

 

 

 

 


免責聲明!

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



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