I had saw some threads/posts in SAP forums, they were asking for the solutions on how to adding a custom screen/tab on the purchase requisition. But just as my knowledge, SAP has not provided any user-exit/Badi/SMOD for enhancing the screen of header part of purchase requisition.

 

And as we also know, purchase requisition database designing is different from other businesses object documents. A general designing of database table for business documents should commonly have Header and Items, some has Schedule Lines. For example, EKKO and EKPO for purchase order, VBAK and VBAP for sales order, BKPF and BSEG for FI documents……

 

For purchase requisition, it seems that the data is stored on item level, we only have EBAN for purchase requisition (other database table is not taken into account in this blog, for example account assignment table EBKN).

 

No standard enhancement and even no database design for purchase requisition header. Well, it seems that it is not possible to adding a custom screen for purchase requisition header level.

 

Some years ago we really cannot do it unless we put lots of effort on changing standard program. Of course this approach is hardly acceptable.

So, in case of receiving a requirement on making a custom header for purchase requisition or adding a new screen on header level, generally we will take an alternative solution, for example, add a custom screen tab on purchase item level, users can input header data on item level and the data should be item-dependent or item-independent, or even design a new application program to achieve the requirements.

 

But now, thanks for the new generation of enhancement technique, we can achieve the requirement by the implicit enhancement, and plus some SMOD/CMOD enhancement and Badi.

 

In this document, I would like to share two solutions on how to make a custom header for purchase requisition. Actually, I like the first one(And I think most of you too)

The result of the idea is we have an enhancement function for purchase requisition, but I hope the idea can also provide us a new thinking for other similar enhancement requirements.

1.    A glance and investigation on purchase requisition application

As everyone knows, the transactions for purchase requisition generally include ME51N, ME52N, ME53N, ME54N. For these transactions, the triggered program is RM_MEREQ_GUI (See SE93).

In source code of triggered program RM_MEREQ_GUI, the function module MEGUI_MAINTAIN is invoked. This function module is a component of function group MEGUI (User interface for purchasing document). As the description of MEGUI, all the components related to user interface of purchasing documents are integrated into/by this function group, for example, screens, GUI statuses (In my opinion,  MEGUI is just like the View of MVC pattern).

For the purchase requisition application, the main screen is 0014 of program SAPLMEGUI (or function group MEGUI), and GUI Status is REQ of program SAPLMEGUI (or function group MEGUI).

Generally, there only a Texts subscreen on the tabstrip. If you press F1 help on the screen field, you will find the program of screen editor is different from the program of the main screen.
By debugging, I found the function module MEGUI_BUILD_REQ_HEADER_PLUGIN is used for integrated to components of tabstrip, and the text editor is plus to tabstrip in this function module.

When I investigate the program at this point, I believe we can add a new subscreen (or more than one) onto the tabstrip by adopting the similar approach. (Yes, this is my idea on first solution)

Also by debugging, I found the application GUI Status is set in method PBO of class CL_WINDOW_MM (Inheriting from class CL_COMPOSITE_SCREEN_VIEW_MM), and the GUI Status is determined by method EXCLUDING_FUNCTIONS. The attributes DYNPRO and PROG indicate the current screen and program.


OK, I believe some people have got another idea on the solution now.

Yes, we can develop a new custom program and insert a GUI Status as the program component, and we also add a new button, saying Custom Header, into the Application Toolbar of the GUI Status. Then we can replace the original standard GUI status (l_pfstatus) and its program (l_pfprogram) by our custom objects. The best replacing position, of cause, should be located at the end of method EXCLUDING_FUNCTIONS, and everyone knows we can do it by implicit enhancement.

It is better to copy the standard GUI Status REQ to our new custom GUI Status, so that we can preserve the original buttons and menus (Actually, this is very important. Otherwise we will lose lots of functionalities when processing a purchase requisition).

But don’t forget that application program needs to response the new button.

By debugging, I found there have a method IF_COMMAND_MM~EXECUTE of local class LCL_DOCUMENT_CMD in function group MEGUI, which is the responder of users’ command.


Thanks the new generation enhancement technique again, we can place an implicit enhancement at the end of this method, so that the program can response the user command.

Beside the function group MEGUI, there has a function group MEREQ inside the whole application. This function group plays the role of processing purchase requisition document; it just looks like the Model of MVC pattern. There have lots of local classes in this function group; I would like to mention two local classes, which are involved in my document.

The local class LCL_REQ_HEADER is the model of purchase requisition header; it contains the processing logic on purchase requisition header. And another local class LCL_INSTANCE_FACTORY, a factory class for purchase requisition, is used for handling the core functionalities of purchase requisition, for example, post.

Of cause, we also have SMOD/Badi enhancements for processing purchase requisition; some of them will be involved in my document.

2.    An enhancement demo step-by-step

My demo business requirement is: Users want to add two subscreens on the tabstrip of purchase requisition header, one is used for storing the related sales data of PR, another is used for storing procurement planning data of the PR.

2.1  Step 1 – Create a Database table for custom header of purchase requisition:

Create a new database table in SE11 ABAP dictionary. In my case, the database table is ZEBAN_HEADER.

2.2  Step 2 – Add a public method ZZGET_NEXT_SUBVIEW_COUNT into class CL_TABSTRIP_VIEW_MM.


METHOD ZZGET_NEXT_SUBVIEW_COUNT .
  RE_COUNT = LINES( SUBVIEWS ) + 1.
ENDMETHOD.

This method is used for counting the subview (subscreen) of the tabstrip

2.3  Step 3 – Create a function group as the core processing logic of the enhancement, and at the meantime, it also provides subscreens.

In my case, the function group is ZME51N_HEADER_ENHM. Below is the code of the TOP INCLUDE.

*Start ************common components for two solution************
TABLES: zeban_header.
DATA: g_original_changed_flag TYPE flag.
DATA: ok-code TYPE syucomm.
DATA: g_trtyp TYPE trtyp. "H = Create, V = Change, A = Display
*End ************common components for two solution************

*Start ************only for first solution************
TYPE-POOLS mmmfd.
DATA: processed_model TYPE REF TO if_model_mm.
INCLUDE lmeviewsf01.
INCLUDE lzme51n_header_enhmcls.
*End ************only for first solution************

There have two INCLUDE programs in the TOP.  I believe lots of ABAP programmers are familiar with LMEVIEWSF01, which is used in enhancements on purchase order subscreens.

LZME51N_HEADER_ENHMCLS is a program file to store the class of our enhancement.

*&---------------------------------------------------------------------*
*&  Include           LZME51N_HEADER_ENHMCLS
*&---------------------------------------------------------------------*
CLASS lcl_custom_header DEFINITION
      INHERITING FROM cl_foreign_application_view_mm.
  PUBLIC SECTION.

    METHODS: constructor
                  IMPORTING im_dynpro         TYPE sy-dynnr
                            im_prog           TYPE sy-repid
                            im_priority       TYPE i OPTIONAL
                            im_foreign_prog   TYPE sy-repid
                            im_foreign_dynpro TYPE sy-dynnr
                            im_metafield      TYPE mmpur_metafield.
  PROTECTED SECTION.
    METHODS: build_dynpro_fields  REDEFINITION.
    METHODS: transport_from_model REDEFINITION.

  PRIVATE SECTION.
    DATA: custom_metafield TYPE mmpur_metafield.
ENDCLASS.                    "lcl_custom_header DEFINITION
*----------------------------------------------------------------------*
*       CLASS lcl_custom_header IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_custom_header IMPLEMENTATION.
  METHOD constructor.

    CALL METHOD super->constructor
      EXPORTING
        im_dynpro         = im_dynpro
        im_prog           = im_prog
        im_priority       = im_priority
        im_foreign_prog   = im_foreign_prog
        im_foreign_dynpro = im_foreign_dynpro.
    custom_metafield = im_metafield.
  ENDMETHOD.                    "CONSTRUCTOR
  METHOD build_dynpro_fields.
    DATA: l_dynpro_entry LIKE LINE OF my_dynpro_fields.
    l_dynpro_entry-metafield = custom_metafield.
    INSERT l_dynpro_entry INTO TABLE my_dynpro_fields.
  ENDMETHOD.                    "BUILD_DYNPRO_FIELDS
  METHOD transport_from_model.
    foreign_application_ok = mmpur_yes.

*    set model to function group
    processed_model = my_model.
  ENDMETHOD.                    "transport_from_model
ENDCLASS.                    "lcl_custom_header IMPLEMENTATION

2.4  Step 4 – Create 3 subscreens for tabstrip subscreen of PR header.

Screen Number Description Purpose
9899 Subscreen Container Used for the screen container of other two subscreens
9900 Subscreen – Sales Used for displaying/inputting the related sales data of PR
9901 Subscreen – Procument Used for displaying/inputting the related procurement data of PR

Subscreen 9899:

PROCESS BEFORE OUTPUT.

  MODULE EVENT_PBO.
  MODULE EVENT_PBO_PREPARE.
  CALL SUBSCREEN CUSTOMSUBSCREEN INCLUDING CALL_PROG CALL_SUBSCREEN.

PROCESS AFTER INPUT.

  MODULE EVENT_PAI_PREPARE.
  CALL SUBSCREEN CUSTOMSUBSCREEN.
  MODULE EVENT_PAI.

Above PBO and PAI modules are located at the INCLUDE program LMEVIEWSF01.

Subscreen 9900:

The fields on 9900 are getting from custom table ZEBAN_HEADER (We had declared it in the TOP INCLUDE program by TABLES statement).

Sales Document – ZEBAN_HEADER-VBELN

Customer PO – ZEBAN_HEADER-BSTKD

Sales budget Number – ZEBAN_HEADER-SBUDGET

PROCESS BEFORE OUTPUT.
  MODULE modify_custom_screen.

*
PROCESS AFTER INPUT.

*----------------------------------------------------------------------*
***INCLUDE LZME51N_HEADER_ENHMO01.
*----------------------------------------------------------------------*
*&---------------------------------------------------------------------*
*&      Module  MODIFY_CUSTOM_SCREEN  OUTPUT
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
MODULE modify_custom_screen OUTPUT.

  IF processed_model IS BOUND.
    CLEAR g_trtyp.
    PERFORM get_transaction_type
              IN PROGRAM saplmereq
              IF FOUND
              USING processed_model
           CHANGING g_trtyp.
  ELSE.
    g_trtyp = 'A'.
  ENDIF.

  LOOP AT SCREEN.
    IF g_trtyp = 'A'.
      screen-input = '0'.
    ELSE.
      screen-input = '1'.
    ENDIF.
    MODIFY SCREEN.
  ENDLOOP.
ENDMODULE.                 " MODIFY_CUSTOM_SCREEN  OUTPUT

Subscreen 9901:

The fields on 9901 are getting from custom table ZEBAN_HEADER:

Procurement Plan – ZEBAN_HEADER-PPLAN

Country – ZEBAN_HEADER-LAND1

Region – ZEBAN_HEADER-REGIO

The PBO module of 9901 is same as 9900, I don’t demonstrate at here again.

2.5   Step 5 – Insert an INCLUDE program LZME51N_HEADER_ENHMF01 into function group ZME51N_HEADER_ENHM. This INCLUDE program used for storing the subroutines of the function group.

*&---------------------------------------------------------------------*
*&  Include           LZME51N_HEADER_ENHMF01
*&---------------------------------------------------------------------*
*&---------------------------------------------------------------------*
*&      Form  ADD_SUBSCREN
*&---------------------------------------------------------------------*
FORM add_subscren  USING    value(u_tab_subscreen) type sydynnr
                            value(u_tab_label) type clike
                            value(u_tab_header_text) type clike
                            uo_model_holder TYPE REF TO  CL_TABSTRIP_VIEW_MM.
  DATA: lo_custom_view TYPE REF TO lcl_custom_header.
  DATA: l_label_text TYPE string40,
        l_header_text TYPE string40,
        l_structure TYPE tabname VALUE 'ZEBAN_HEADER'.
  DATA: l_repid TYPE syrepid VALUE 'SAPLZME51N_HEADER_ENHM'.

  l_label_text = u_tab_label.
  l_header_text = u_tab_header_text.

  CREATE OBJECT lo_custom_view
    EXPORTING
      im_dynpro         = '9899' "subscreen container
      im_prog           = l_repid
      im_foreign_prog   = l_repid
      im_foreign_dynpro = u_tab_subscreen
      im_metafield      = mmmfd_header_userexit.
  IF lo_custom_view IS BOUND.
    CALL METHOD lo_custom_view->set_label( l_label_text ).
    CALL METHOD lo_custom_view->set_name( l_header_text ).
    CALL METHOD lo_custom_view->set_struct_name(
                       im_struct_name = l_structure
                       im_application = 'REQ' ).
    CALL METHOD uo_model_holder->add
      EXPORTING
        im_screen_view = lo_custom_view
        im_position    = uo_model_holder->zzget_next_subview_count( ).
    SET HANDLER lo_custom_view->if_observer_mm~handle_subject_changed
                FOR uo_model_holder.
    SET HANDLER uo_model_holder->if_observer_mm~handle_subject_changed FOR
                lo_custom_view.
  ENDIF.
ENDFORM.                    " ADD_SUBSCREN

2.6   Step 6 – Insert below function modules into the function group ZME51N_HEADER_ENHM.

And please set ZPR_HEADER_CUSTOM_SAVE_CORE as the update module.

FUNCTION zpr_check_transaction_state .
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     REFERENCE(IM_BANFN) TYPE  BANFN
*"  CHANGING
*"     REFERENCE(CH_CHANGED) TYPE  FLAG OPTIONAL
*"----------------------------------------------------------------------
  DATA: l_count TYPE i.
  DATA: ls_custom_header TYPE zeban_header.
  g_original_changed_flag = ch_changed. "backup the original 'changed' indicator
  CHECK ch_changed IS INITIAL.
  SELECT COUNT( * ) INTO l_count FROM eban
         WHERE banfn = im_banfn.
  CHECK l_count >= 1.
*THE PROCESSED PR IS EXISTED IN DB
  SELECT SINGLE * INTO ls_custom_header
         FROM zeban_header
         WHERE banfn = im_banfn.
  IF ls_custom_header-vbeln <> zeban_header-vbeln OR
     ls_custom_header-bstkd <> zeban_header-bstkd OR
     ls_custom_header-sbudget <> zeban_header-sbudget OR
     ls_custom_header-pplan <> zeban_header-pplan OR
     ls_custom_header-land1 <> zeban_header-land1 OR
     ls_custom_header-regio <> zeban_header-regio.
*  THE CUSTOM FIELDS IN RUNTIME IS DIFFERENT FROM DB
*  THE PR HAS BEEN CHANGED
    ch_changed = 'X'.
  ENDIF.
ENDFUNCTION.
FUNCTION zpr_custom_header_tab_add.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     REFERENCE(IM_O_MODEL_HOLDER) TYPE REF TO  CL_TABSTRIP_VIEW_MM
*"----------------------------------------------------------------------
*add subscreen for sales
  PERFORM add_subscren
              USING '9900'
                    'Custom Header- Sales' "tab label
                    'ADDITIONAN HEADER - Sales' "header text
                    im_o_model_holder.
*add subscreen for procurement
  PERFORM add_subscren
              USING '9901'
                    'Custom Header- Procurement' "tab label
                    'ADDITIONAN HEADER - Procurement' "header text
                    im_o_model_holder.
ENDFUNCTION.
FUNCTION zpr_export_buffer.
*"----------------------------------------------------------------------
*"  EXPORTING
*"     REFERENCE(EX_BANFN) TYPE  BANFN
*"     REFERENCE(EX_ORIGINAL_CHANGED) TYPE  FLAG
*"----------------------------------------------------------------------
  ex_banfn = zeban_header-banfn.
  ex_original_changed = g_original_changed_flag.
  CLEAR g_original_changed_flag.
ENDFUNCTION.
FUNCTION zpr_header_custom_save.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     REFERENCE(IM_BANFN) TYPE  BANFN
*"----------------------------------------------------------------------
  DATA: l_changed TYPE flag.
  DATA: l_count TYPE i.
  SELECT COUNT( * ) INTO l_count
         FROM eban
         WHERE banfn = im_banfn.
  IF l_count >= 1.
*    not create mode
*    we need to check if data has been changed
    CALL FUNCTION 'ZPR_CHECK_TRANSACTION_STATE'
      EXPORTING
        im_banfn   = im_banfn
      CHANGING
        ch_changed = l_changed.
    CHECK l_changed IS NOT INITIAL.
  ENDIF.
  zeban_header-banfn = im_banfn.
  CALL FUNCTION 'ZPR_HEADER_CUSTOM_SAVE_CORE' IN UPDATE TASK
    EXPORTING
      im_s_header = zeban_header.
ENDFUNCTION.
FUNCTION zpr_header_custom_save_core.
*"----------------------------------------------------------------------
*"  IMPORTING
*"     VALUE(IM_S_HEADER) TYPE  ZEBAN_HEADER
*"----------------------------------------------------------------------
*please set this module as update module
MODIFY zeban_header FROM IM_S_HEADER.
ENDFUNCTION.
FUNCTION zpr_open.
*"----------------------------------------------------------------------
*"  IMPORTING
*"     REFERENCE(IO_HEADER) TYPE REF TO  IF_PURCHASE_REQUISITION
*"----------------------------------------------------------------------
  DATA: ls_header TYPE mereq_header.
  DATA: l_count TYPE i.
  CLEAR zeban_header.
  clear g_original_changed_flag.
  ls_header = io_header->get_data( ).
  SELECT COUNT( * ) INTO l_count FROM eban
         WHERE banfn = ls_header-banfn.
  CHECK l_count >= 1.
*THE PROCESSED PR IS EXISTED IN DB
  SELECT SINGLE * FROM zeban_header
        WHERE banfn = ls_header-banfn.
  IF sy-subrc <> 0.
    zeban_header-banfn = ls_header-banfn.
  ENDIF.
ENDFUNCTION.

2.7   Step 7 – Apply implicit enhancement implementation to add the new subscreens onto the tabstrip of PR header

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""$"$\SE:(1) Function Module MEGUI_BUILD_REQ_HEADER_PLUGIN, End                                                                                                A
*$*$-Start: (1)---------------------------------------------------------------------------------$*$*
ENHANCEMENT 1  ZPR_CUSTOM_TAB_MEGUI.    "active version
if sy-uname = 'CHENGANGRONG' AND
   ( SY-TCODE = 'ME51N' OR
     SY-TCODE = 'ME52N' OR
     SY-TCODE = 'ME53N' OR
     SY-TCODE = 'ME54N' ).
CALL FUNCTION 'ZPR_CUSTOM_HEADER_TAB_ADD'
  EXPORTING
    IM_O_MODEL_HOLDER         = header_view.
ENDIF.
ENDENHANCEMENT.
*$*$-End:   (1)---------------------------------------------------------------------------------$*$*

Once the above steps are done, you can see the two subscreens are appearing on the tabstrip of PR header.

2.8   Step 8 – Add an implicit enhancement into method if_purchase_requisition~get_transaction_state
The method is a component of local class LCL_REQ_HEADER in function group MEREQ, and located at INCLUDE program LMEREQF12.

Based on the exporting parameter of the method, we know that this method is used for returning the transaction states of the purchase requisition, for example, whether the purchase requisition has been changed.

Why do we need to add this enhancement?

When users just change the data in the enhanced custom header screen but leave the standard data unchanged (EBAN, EBKN, TEXT, etc), the application will not recognize the changing of the document. So application will not trigger the saving process for the document.

In this case we need to overwrite the changed indicator of LCL_REQ_HEADER, so that the program can recognize and accept the changing of the document.

Because it has a CHECK statement in the method, we need to add the implicit enhancement at the beginning of the method; otherwise the implemented codes will be skipped under certain circumstance (for example, Create Mode, ME51N), see below screenshot.

So the enhancement is below:

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""$"$\SE:(1) Class LCL_REQ_HEADER, Interface IF_PURCHASE_REQUISITION, Method GET_TRANSACTION_STATE, Start                                                      A
*$*$-Start: (1)---------------------------------------------------------------------------------$*$*
ENHANCEMENT 1  ZPR_CUSTOM_TAB_MEREQ.    "active version
if  sy-uname = 'CHENGANGRONG' AND
      SY-DYNNR = '0014' AND
      ( SY-TCODE = 'ME51N' OR
      SY-TCODE = 'ME52N' OR
      SY-TCODE = 'ME53N' OR
      SY-TCODE = 'ME54N' ).
IF my_state->changed IS INITIAL.
CALL FUNCTION 'ZPR_CHECK_TRANSACTION_STATE'
  EXPORTING
    im_banfn         = my_state->header-banfn
 CHANGING
   CH_CHANGED       = my_state->changed.
ENDIF.
endif.

ENDENHANCEMENT.
*$*$-End:   (1)---------------------------------------------------------------------------------$*$*

2.9   Step 9 – Implement the Badi ME_PROCESS_REQ_CUST, and then insert below codes into the method OPEN (It is easy to understand that application need to initialize and read the custom header data when a new purchase requisition is opened).

In my case, the Badi implementation is ZME_PROCESS_REQ_CUST .

METHOD if_ex_me_process_req_cust~open.
  IF sy-uname = 'CHENGANGRONG' AND
     ( sy-tcode = 'ME51N' OR
       sy-tcode = 'ME52N' OR
       sy-tcode = 'ME53N' OR
       sy-tcode = 'ME54N' ).
    CALL FUNCTION 'ZPR_OPEN'
      EXPORTING
        io_header = im_header.
  ENDIF.
ENDMETHOD.

2.10   Step 10 – Saving custom header data for purchase requisition.

I use the EXIT function module EXIT_SAPLMEREQ_008 as saving position of the custom header data (The SMOD enhancement is MEREQ001, it is a common enhancement, so that I would not explain the CMOD project in detail).

Maybe someone will ask me why do not put the saving process at method POST of Badi ME_PROCESS_REQ_CUST.

Actually I had tried but it doesn’t work in some case. For example, if user just changes the custom header data but leave the standard data unchanged (EBAN, EBKN, TEXT, etc), the application would not trigger the implemented POST method of ME_PROCESS_REQ_CUST. So I have to make the enhancement position at EXIT_SAPLMEREQ_008.

  IF sy-uname = 'CHENGANGRONG' AND
     sy-dynnr = '0014' AND
     ( sy-tcode = 'ME51N' OR
       sy-tcode = 'ME52N' OR
       sy-tcode = 'ME53N' OR
       sy-tcode = 'ME54N' ).
    CALL FUNCTION 'ZPR_HEADER_CUSTOM_SAVE'
      EXPORTING
        im_banfn = im_banfn_new.
  ENDIF.

2.11   Step 11 – Add a new public method zzcustom_after_post into local class LCL_REQ_HEADER.

Once we implement the custom header screen on purchase requisition, the system behaviors of change mode (ME52N) will have some difference from the original behaviors after saving the PR document.

The original behavior: the system will alternate the transaction type from change mode to display mode (my_state->trtyp). And changed indicator my_state->changed will be reset to SPACE. (see at the last of method implementation prepare_post of local class lcl_req_header in INCLUDE program LMEREQF08)

New behavior of our implementation: If user only changes custom header data but leave standard data unchanged (EBAN, EBKN, TEXT, etc), the method prepare_post will raise an exception no_changes before reset of the changed indicator and alternation of transaction type. In this case the purchase requisition will leave to changed state and change mode after saving the PR document. This is different from the original system behavior.

To overcome the gap between original behavior and new behavior, we need to have a method to set the transaction type and changed indicator as what it is in standard behavior after the document is saved. So I introduce the new method zzcustom_after_post for local class LCL_REQ_HEADER.

In the definition part of LCL_REQ_HEADER (INCLUDE program LMEREQF07 of function group MEREQ), I make such implicit enhancement:

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""$"$\SE:(1) Class LCL_REQ_HEADER, Vor den PUBLIC-Methoden                                                                                                     S
*$*$-Start: (1)---------------------------------------------------------------------------------$*$*
ENHANCEMENT 2  ZPR_CUSTOM_TAB_MEREQ.    "active version
METHODS: zzcustom_after_post.
ENDENHANCEMENT.
*$*$-End:   (1)---------------------------------------------------------------------------------$*$*

And in the implementation part of LCL_REQ_HEADER (INCLUDE program LMEREQF08 of function group MEREQ), I make such implicit enhancement:

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""$"$\SE:(1) Class LCL_REQ_HEADER, End                                                                                                                         S
*$*$-Start: (1)---------------------------------------------------------------------------------$*$*
ENHANCEMENT 3  ZPR_CUSTOM_TAB_MEREQ.    "active version
METHOD zzcustom_after_post.
    me->my_state->trtyp = me->my_state->aktyp = anz.

    me->my_state->changed = mmpur_no.
ENDMETHOD.
ENDENHANCEMENT.
*$*$-End:   (1)---------------------------------------------------------------------------------$*$*

And please see Step 12 on how we using the new enhancement method.

2.12 Step 12 – Add an implicit enhancement at the POST method of local class LCL_INSTANCE_FACTORY.
The POST method of local class LCL_INSTANCE_FACTORY is located at INCLUDE program LMEREQF06 of function group MEREQ.

Just as I had mentioned at above some steps, if user only change the custom header data but leave standard data unchanged (EBAN, EBKN, TEXT, etc), the application will not recognize the changing of the document.

Although we had added an implicit enhancement at Step 8 for replacing the changed indicator of the document, the application still doesn’t trigger the commit work, if no any standard data is changed.

If you have noticed that the custom function module ZPR_HEADER_CUSTOM_SAVE, which we place at EXIT function EXIT_SAPLMEREQ_008, is just a normal function module. But inside the function module there have another FM ZPR_HEADER_CUSTOM_SAVE_CORE is invoked by update task:

So we have to take a solution for triggering the commit work in case of standard data unchanged.

At last, by debugging, I found the best implementation position is at the end of POST method of LCL_INSTANCE_FACTORY.

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""$"$\SE:(1) Class LCL_INSTANCE_FACTORY, Method POST, End                                                                                                      A
*$*$-Start: (1)---------------------------------------------------------------------------------$*$*
ENHANCEMENT 4  ZPR_CUSTOM_TAB_MEREQ.    "active version
 DATA: l_zzehm_banfn TYPE banfn.
  DATA: ls_zzehm_hashed_entry TYPE hash_table_entry.
  data: l_zzhm_original_changed type flag.
  IF   sy-uname = 'CHENGANGRONG' AND
        sy-dynnr = '0014' AND
        ( sy-tcode = 'ME51N' OR
        sy-tcode = 'ME52N' OR
        sy-tcode = 'ME53N' OR
        sy-tcode = 'ME54N' ).
    CALL FUNCTION 'ZPR_EXPORT_BUFFER'
      IMPORTING
        ex_banfn            = l_zzehm_banfn
        ex_original_changed = l_zzhm_original_changed.
    IF l_zzhm_original_changed IS INITIAL.
*  no changed for PR data(standard DB data)
*  program would not execute commit at previous step
*  in this case, we have to execute commit so that
*  the FM ZPR_HEADER_CUSTOM_SAVE_CORE can be submit to commit
*  (see FM EXIT_SAPLMEREQ_008 and ZPR_HEADER_CUSTOM_SAVE)
      IF im_no_commit EQ mmpur_no
         AND im_wait EQ cl_mmpur_constants=>no .
        COMMIT WORK.
      ELSEIF im_no_commit EQ mmpur_no
         AND im_wait EQ cl_mmpur_constants=>yes .
        COMMIT WORK AND WAIT.
      ENDIF.
    ENDIF.
    READ TABLE my_hash_table INTO ls_zzehm_hashed_entry
         WITH KEY banf = l_zzehm_banfn.
    IF sy-subrc = 0.
*  resume the changed indicator for header state
      ls_zzehm_hashed_entry-header->zzcustom_after_post( ).
    ENDIF.
  ENDIF.

ENDENHANCEMENT.
*$*$-End:   (1)---------------------------------------------------------------------------------$*$*

And now you will see the method zzcustom_after_post is called after the commit, so that the changed indicator and transaction type are reset.

2.13 Step 13 – Add an implicit enhancement at the end of LMEREQFXX. In the enhancement, insert an INCLUDE program ZZMEREQ_ENHANCEMENT.

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""$"$\SE:(1) Include LMEREQFXX, End                                                                                                                            S
*$*$-Start: (1)---------------------------------------------------------------------------------$*$*
ENHANCEMENT 5  ZPR_CUSTOM_TAB_MEREQ.    "active version
include ZZMEREQ_ENHANCEMENT.
ENDENHANCEMENT.
*$*$-End:   (1)---------------------------------------------------------------------------------$*$*

The INCLUDE program ZZMEREQ_ENHANCEMENT has below codes:

*&---------------------------------------------------------------------*
*&  Include           ZZMEREQ_ENHANCEMENT
*&---------------------------------------------------------------------*
FORM get_transaction_type
          USING uo_model TYPE REF TO if_model_mm
          CHANGING c_trtyp TYPE trtyp.
  DATA: lo_req_header TYPE REF TO lcl_req_header.

  mmpur_dynamic_cast lo_req_header uo_model.
  CHECK NOT lo_req_header IS INITIAL.
  c_trtyp = lo_req_header->my_state->trtyp.

ENDFORM.                    "get_transaction_type

The subroutine get_transaction_type is used for returning the transaction type of processed PR. This subroutine will be called at the PBO module MODIFY_CUSTOM_SCREEN of function group ZME51N_HEADER_ENHM.

If we have no this subroutine, the subscreens PBO can’t get the transaction type of PR, that means we can’t modify INPUT attribute of the subscreen fields by different transactions types (please see PBO module MODIFY_CUSTOM_SCREEN).

Well , the first solution is finished. We can see there have tow custom subscreens on the tabstrip of the PR header.


Although I really like the first solution, I would like to show you my second solution.
The demo requirement of second solution is: User want to have a button (In my case is Custom Header) on application toolbar of PR screen. If the button is clicked, a dialog box screen is generated and, the custom header data can be display/input on the screen.

This second solution will have lots of common part with the first solution, I only demonstrate the necessary steps for this solution.

 

2.1       Step 1 – Insert below declaration into TOP INCLUDE  of function group ZME51N_HEADER_ENHM.

In my case, the function group is ZME51N_HEADER_ENHM. Below is the code of the TOP INCLUDE.

*Start************only for second solution************
DATA: gs_header_backup TYPE zeban_header.
DATA: main_okcode TYPE syucomm. "OKCODE FROM PR APPLICATION
*End ************only for second solution************

2.2       Step 2 – Create a GUI Status for replacing the original standard GUI status REQ of SAPLMEGUI. And do not forget to copy/adjust the buttons from standard GUI status REQ of SAPLMEGUI.




After adjusting the new GUI Status REQ from standard GUI Status REQ of SAPLMEGUI, we need to add a new button into the application toolbar, so that we can develop the logic for generating the custom header screen. In my case, the function code of new button is ZCUSTOM, and also input the related icon text and preferred icon name.

2.3       Step 3 – Create a dialog box screen for custom header of purchase requisition.

I would not demonstrate the screen creation in detail, since it is just a common development technique. And do not forget to create a dialog box GUI Status and title for this new screen.



Add the STATUS_0001 module into the INCLUDE file LZME51N_HEADER_ENHMO01 of PBO modules .

*&---------------------------------------------------------------------*
*&      Module  STATUS_0001  OUTPUT
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
MODULE status_0001 OUTPUT.
  SET PF-STATUS 'CUSTOM_DYN'.
  SET TITLEBAR 'CUSTOM_DYN_TITLE'.
  LOOP AT SCREEN.
    IF screen-name = 'ZEBAN_HEADER-VBELN'.
      IF g_trtyp = 'A'.
*  display mode, input not possible
        screen-input = '0'.
      ELSE.
*  change or create mode, input activated
        screen-input = '1'.
      ENDIF.
    ENDIF.
    MODIFY SCREEN.
  ENDLOOP.
ENDMODULE.                 " STATUS_0001  OUTPUT

Add PAI module  USER_COMMAND_0001 to  the INCLUDE file LZME51N_HEADER_ENHMI01

*----------------------------------------------------------------------*
***INCLUDE LZME51N_HEADER_ENHMI01.
*----------------------------------------------------------------------*
*&---------------------------------------------------------------------*
*&      Module  USER_COMMAND_0001  INPUT
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
MODULE user_command_0001 INPUT.
  CASE okcode.
    WHEN 'OK'.
      GS_HEADER_BACKUP = ZEBAN_HEADER.
    WHEN 'EXIT'.
      ZEBAN_HEADER = GS_HEADER_BACKUP.
  ENDCASE.

  SET SCREEN 0.
  LEAVE SCREEN.
ENDMODULE.                 " USER_COMMAND_0001  INPUT

In my case, the dialog box GUI Status for the screen 0001 is CUSTOM_DYN, and the GUI title is CUSTOM_DYN_TITLE.

2.4       Step 4 – Insert a new function module ZPR_HEADER_CUSTOM_SCREEN_CALL into function group ZME51N_HEADER_ENHM.

FUNCTION zpr_header_custom_screen_call.
*"----------------------------------------------------------------------
*"  IMPORTING
*"     REFERENCE(IM_O_MODEL) TYPE REF TO  IF_MODEL_MM
*"     REFERENCE(IM_TRTYP) TYPE  TRTYP
*"----------------------------------------------------------------------
  DATA: l_changed TYPE flag.
  DATA: lo_processing_req TYPE REF TO if_purchase_requisition.
  lo_processing_req ?= im_o_model.
  g_trtyp = im_trtyp.
  CHECK lo_processing_req IS BOUND.
  lo_processing_req->get_transaction_state(
        IMPORTING
          ex_changed = l_changed ).
  IF g_trtyp = 'H' AND
    l_changed = space.
*create mode but no data is inputted
*a blank purchase requisition
    MESSAGE 'Please input the PR data first......' TYPE 'S' DISPLAY LIKE 'E'.
    EXIT.
  ENDIF.
  main_okcode = sy-ucomm.
  gs_header_backup = zeban_header.
  CALL SCREEN '0001' STARTING AT 25 6
                     ENDING AT 88 10.
  sy-ucomm = main_okcode.
ENDFUNCTION.

2.5       Step 5 – Apply implicit enhancement implementation to replace the original standard GUI Status REQ of program SAPLMEGUI

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""$"$\SE:(1) Class CL_COMPOSITE_SCREEN_VIEW_MM, Method EXCLUDING_FUNCTIONS, End                                                                                A
*$*$-Start: (1)---------------------------------------------------------------------------------$*$*
ENHANCEMENT 1  ZPR_CUSTOM_TAB_COMP_SCREEN.    "active version
IF   sy-uname = 'CHENGANGRONG' AND
      sy-dynnr = '0014' AND
      ( sy-tcode = 'ME51N' OR
      sy-tcode = 'ME52N' OR
      sy-tcode = 'ME53N' OR
      sy-tcode = 'ME54N' ).
  IF dynpro = '0014' AND
     prog = 'SAPLMEGUI'.
    ch_pfprogram = 'SAPLZME51N_HEADER_ENHM'.
    ch_pfstatus = 'REQ'.
  ENDIF.
ENDIF.

ENDENHANCEMENT.
*$*$-End:   (1)---------------------------------------------------------------------------------$*$*

Once we have above implementation, a new button will appear on the application tool bar of purchase requisition applications (ME51N/ME52N/ME53N/ME54N).

2.6       Step 6 – make the Custom Header button functioning.
The method if_command_mm~execute of local class lcl_document_cmd of program SAPLMEGUI will response the user actions. We can put an implicit enhancement at the end of this method, so that the program can response the user command on the main screen of purchase requisition.

In my demo case, if user click Custom Header button, then program will call a dialog box screen 0001 in the enhancement function group ZME51N_HEADER_ENHM.

At the implementation section of if_command_mm~execute of local class lcl_document_cmd(locate at INCLUDE program LMEGUICJL), I put below implicit enhancement at the end of the method:

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""$"$\SE:(1) Class LCL_DOCUMENT_CMD, Interface IF_COMMAND_MM, Method EXECUTE, End                                                                              A
*$*$-Start: (1)---------------------------------------------------------------------------------$*$*
ENHANCEMENT 3  ZPR_CUSTOM_TAB_MEGUI.    "active version
 DATA: ls_zzenhm_doc_info TYPE mepo_document,
        lt_zzenhm_doc_info TYPE mepo_documents,
        l_zzenhm_model type ref to if_model_mm.
  CHECK sy-uname = 'CHENGANGRONG' AND
        sy-dynnr = '0014' AND
        ( sy-tcode = 'ME51N' OR
        sy-tcode = 'ME52N' OR
        sy-tcode = 'ME53N' OR
        sy-tcode = 'ME54N' ).
  IF l_transaction IS NOT BOUND.
    l_transaction = l_appl->my_trmgr->get_current( ).
    CHECK l_transaction IS BOUND.
  ENDIF.
  l_transaction->get_info(
           IMPORTING
              ex_documents = lt_zzenhm_doc_info
              ex_aggregate = l_zzenhm_model ).
  READ TABLE lt_zzenhm_doc_info INTO ls_zzenhm_doc_info INDEX 1.
  CHECK sy-subrc = 0.
  CASE im_fcode.
    WHEN 'ZCUSTOM'.
      CALL FUNCTION 'ZPR_HEADER_CUSTOM_SCREEN_CALL'
        EXPORTING
          im_o_model = l_zzenhm_model
          im_trtyp   = ls_zzenhm_doc_info-trtyp.
  ENDCASE.

ENDENHANCEMENT.
*$*$-End:   (1)---------------------------------------------------------------------------------$*$*

When this step had been done, the custom header screen can be generated when the Custom Header button is clicked.

Since most of other processes are same as the first solution (for example, save the custom PR header data), so I do not show it any more.

 


Well, the demo is finished. Thanks for looking my works.

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

Leave a Reply