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

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第1张图片


现在我们对PO header加上自己的subscreen, SAP的例子提供的是对item增加subscreen.


Step 1: Create Function Group

仿照Function Group MEPOBADIEX建一个Function Group

我建的Function Group如下图所示

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第2张图片


Step 2: 在Function Group里建Screen, screen number随便

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第3张图片

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第4张图片

在TOP里面加入对应屏幕的变量, 这里我是直接用tables.

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第5张图片


Step 3: 给BADI ME_GUI_PO_CUST建一个implementation

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第6张图片

在Public Section里加上TYPE-POOLS mmmfd .

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第7张图片


ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第8张图片


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.

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.
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

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第9张图片

在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.
这里有两点注意下:

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

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第10张图片

这两个module存放在下图的程序里

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第11张图片

注意: 如果不调用这两个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.

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.

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第12张图片


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.



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.

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.

如果按回车来check的话, 它会显示error message,一次改变后按一次回车有反应, 第二次就没反应.

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第13张图片
这里有一个缺陷, 当我们按save里, 这条message是不会显示到message表中的(研究了很久, 没研究出来怎么搞), 幸好这里有个功能十分不错, 选中PO header data still faulty这个message, 按Edit, 就可以定位到error message的field.

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第14张图片

至此, 增强完成.


补充:

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.
ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第15张图片

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要勾上.

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第16张图片

ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST_第17张图片


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.

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.













你可能感兴趣的:(po,exit,screen,badi,ME_GUI_PO_CUST)