Technology Blog Posts by Members
cancel
Showing results for 
Search instead for 
Did you mean: 
Pradeep555
Explorer
0 Kudos
587

Scenario: In standard SAP, SM30 is used to maintain data through a Table Maintenance Generator (TMG). However, it only allows us to assign access to a limited number of users—typically one at a time using authorization objects. This becomes a bottleneck when multiple users need to maintain data but should not be given broad access to SM30. 

To overcome this limitation, I’ve developed a custom report program that acts as a gateway to perform CRUD (Create, Read, Update, Delete) operations on TMG, but only for authorized users of the report. 

 

Business Requirement: 

Allow only authorized users to maintain table data via TMG. 

Eliminate the need to provide direct SM30 access. 

Improve security, flexibility, and usability in maintaining table data. 

 

Security and authorization handling. 

Instead of giving access to SM30: 

Assign a custom role to the report program 

Let users maintain only the specific table via this report 

This restricts access to other tables or SM30 entirely 

*&---------------------------------------------------------------------* 
 *& Report ZPD_RP_TMG_CUSTOM 
 *&---------------------------------------------------------------------* 
 *& 
 *&---------------------------------------------------------------------* 
 REPORT ZPD_RP_TMG_CUSTOM. 
  
 
INCLUDE zpd_tmg_top_dec. 
 include zpd_tmg_logic_forms. 
  
START-OF-SELECTION . 
 PERFORM get_data. 
 PERFORM prepare_field_catalog. 
 PERFORM disp_alv. 

 

 

FORM prepare_field_catalog . 
   CLEAR : gt_fieldcat . 
   gt_fieldcat = VALUE #( ( fieldname = 'MANDT'             col_pos = 1    seltext_l = 'MANDT'        checkbox = '' outputlen = 15 ) 
                          ( fieldname = 'SERIAL_NO'         col_pos = 2    seltext_l = 'SERIAL_NO'    checkbox = '' outputlen = 20 ) 
                          ( fieldname = 'NAME'              col_pos = 3    seltext_l = 'NAME'         checkbox = '' outputlen = 30 ) 
                          ( fieldname = 'USER_ID'           col_pos = 4    seltext_l = 'USER_ID'      checkbox = '' outputlen = 15 ) 
                          ( fieldname = 'PASSWORD'          col_pos = 5    seltext_l = 'PASSWORD'     checkbox = '' outputlen = 30 ) 
                          ( fieldname = 'COMPANY_CODE'      col_pos = 6    seltext_l = 'COMPANY_CODE' checkbox = '' outputlen = 15 ) ). 
  
ENDFORM. 
 

 

FORM disp_alv . 
  
 
  CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY' 
     EXPORTING 
       i_callback_program       = sy-repid 
       i_callback_pf_status_set = 'MY_SCREEN' 
       i_callback_user_command  = 'USER_COMMAND' 
       it_fieldcat              = gt_fieldcat 
     TABLES 
       t_outtab                 = gt_alv 
     EXCEPTIONS 
       program_error            = 1 
       OTHERS                   = 2. 
   IF sy-subrc <> 0. 
 * Implement suitable error handling here 
   ENDIF. 
  
 
 
 
ENDFORM. 

 

*&---------------------------------------------------------------------* 
 *& Form get_data 
 *&---------------------------------------------------------------------* 
 *& text 
 *&---------------------------------------------------------------------* 
 *& -->  p1        text 
 *& <--  p2        text 
 *&---------------------------------------------------------------------* 
 FORM get_data . 
  
  SELECT mandt 
           serial_no 
           name 
           user_id 
           password 
           company_code 
           FROM zpd_t_tmg INTO TABLE gt_zpd_t_tmg. 
  
 
 
  IF sy-subrc = 0. 
     PERFORM zpd_t_tmg_to_gt_alv . 
  
 
ENDIF. 
  
ENDFORM. 
  
 
FORM my_screen USING rx_extab TYPE slis_t_extab . 
   SET PF-STATUS 'MY_SCREEN'. 
 ENDFORM. 
  
 
 
"form  user command" 
  
FORM user_command USING ucomm LIKE sy-ucomm 
                         p_selfld TYPE slis_selfield. 
  
  DATA: gd_repid LIKE sy-repid, 
         ref_grid TYPE REF TO cl_gui_alv_grid. 
   DATA: l_valid TYPE c. 
  
  " to get modified  alv op 
  
 
  CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR' 
     IMPORTING 
       e_grid = ref_grid. 
  
 
  IF NOT ref_grid IS INITIAL. 
  
    CALL METHOD ref_grid->check_changed_data 
       IMPORTING 
         e_valid = l_valid.           " Entries are Consistent 
  
  ENDIF. 
  
 
  CASE ucomm. 
     WHEN  '&DISP'. 
       CLEAR : gv_flag . 
       gv_flag = abap_true . 
       PERFORM change_fieldcat1. 
  
    WHEN '&SAVE' . 
       IF gv_flag = abap_true . 
         PERFORM modify_changes . 
         PERFORM prepare_field_catalog . 
       ELSE. 
         MESSAGE 'No change was made' TYPE 'E'. 
       ENDIF . 
  
    WHEN '&CR_NEW'. 
       REFRESH gt_alv1 . 
       PERFORM get_data1 . 
       PERFORM prepare_field_catalog1 . 
       PERFORM display_alv1 . 
  
    WHEN  '&DATA_SAVE'. 
       PERFORM save_data. 
     WHEN '&GT_DT'. 
       PERFORM dynamic_output . 
     WHEN '&F03' OR  '&F15'. 
       RETURN. 
     WHEN '&F12 '. 
       LEAVE PROGRAM . 
  
  ENDCASE . 
  
 
  p_selfld-refresh = 'X'. 
   IF ucomm = '&DISP'. 
     p_selfld-exit = 'X'. 
     PERFORM disp_alv. 
   ENDIF. 
  
  IF ucomm = '&SAVE'. 
     p_selfld-exit = 'X'. 
     PERFORM prepare_field_catalog. 
     PERFORM disp_alv. 
   ENDIF. 
  
 
 
 
ENDFORM. 

 

*&---------------------------------------------------------------------* 
 *& Form prepare_field_catalog1 
 *&---------------------------------------------------------------------* 
 *& text 
 *&---------------------------------------------------------------------* 
 *& -->  p1        text 
 *& <--  p2        text 
 *&---------------------------------------------------------------------* 
 FORM prepare_field_catalog1 . 
  
 
  CLEAR : gt_fieldcat1 . 
   gt_fieldcat = VALUE #( ( fieldname = 'MANDT'      edit = 'X'   col_pos = 1  seltext_l = 'MANDT'     ref_fieldname = 'MANDT'      checkbox = '' outputlen = 15 ) 
                          ( fieldname = 'SERIAL_NO'  edit = 'X'   col_pos = 2  seltext_l = 'SERIAL_NO' ref_fieldname = 'SERIAL_NO'  checkbox = '' outputlen = 20 ) 
                          ( fieldname = 'NAME'       edit = 'X'   col_pos = 3  seltext_l = 'NAME'      ref_fieldname = 'NAME'       checkbox = '' outputlen = 30 ) 
                          ( fieldname = 'USER_ID'    edit = 'X'   col_pos = 4  seltext_l = 'USER_ID'   ref_fieldname = 'USER_ID'    checkbox = '' outputlen = 15 ) 
                          ( fieldname = 'PASSWORD'   edit = 'X'   col_pos = 5  seltext_l = 'PASSWORD'  ref_fieldname = 'PASSWORD'   checkbox = '' outputlen = 30 ) 
                          ( fieldname = 'COMPANY_CODE' edit = 'X' col_pos = 6  seltext_l = 'COMPANY_CODE'  ref_fieldname = 'PASSWORD'  checkbox = '' outputlen = 15 ) ). 
  
ENDFORM. 

 

*&---------------------------------------------------------------------* 
 *& Form display_alv1 
 *&---------------------------------------------------------------------* 
 *& text 
 *&---------------------------------------------------------------------* 
 *& -->  p1        text 
 *& <--  p2        text 
 *&---------------------------------------------------------------------* 
 FORM display_alv1 . 
  
  CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY' 
     EXPORTING 
       i_callback_program       = sy-repid 
       i_callback_pf_status_set = 'MY_SCREEN1 ' 
       i_callback_user_command  = 'USER_COMMAND' 
       it_fieldcat              = gt_fieldcat 
     TABLES 
       t_outtab                 = gt_alv1 
     EXCEPTIONS 
       program_error            = 1 
       OTHERS                   = 2. 
   IF sy-subrc <> 0. 
 * Implement suitable error handling here 
   ENDIF. 
  
ENDFORM. 

 

FORM my_screen1 USING rx_extab TYPE slis_t_extab . 
   SET PF-STATUS 'MY_SCREEN1'. 
  
ENDFORM. 

 

*&---------------------------------------------------------------------* 
 *& Form get_data1 
 *&---------------------------------------------------------------------* 
 *& text 
 *&---------------------------------------------------------------------* 
 *& -->  p1        text 
 *& <--  p2        text 
 *&---------------------------------------------------------------------* 
 FORM get_data1 . 
  
  DO 150 TIMES. 
     APPEND gs_alv1 TO gt_alv1. 
   ENDDO. 
  
ENDFORM. 

 

*&---------------------------------------------------------------------* 
 *& Form save_data 
 *&---------------------------------------------------------------------* 
 *& text 
 *&---------------------------------------------------------------------* 
 *& -->  p1        text 
 *& <--  p2        text 
 *&---------------------------------------------------------------------* 
 FORM save_data . 
  
  REFRESH  : gt_alv2 , gt_alv3. 
   gt_alv3[] = gt_alv1. 
   IF  gt_alv3 IS NOT INITIAL . 
     DELETE  gt_alv3 WHERE serial_no IS INITIAL. 
   ENDIF. 
  
  IF  gt_alv3 IS NOT INITIAL . 
  
    LOOP AT gt_alv3 INTO gs_alv3. 
       IF gs_alv3 IS NOT INITIAL . 
         IF gs_alv3-company_code IS NOT INITIAL. 
           CLEAR gs_alv. 
           READ TABLE gt_alv INTO gs_alv WITH KEY serial_no = gs_alv3-serial_no . 
  
          IF sy-subrc NE 0. 
             gs_alv2-serial_no = gs_alv3-serial_no. 
             gs_alv2-name = gs_alv3-name. 
             gs_alv2-password = gs_alv3-password. 
             gs_alv2-user_id = gs_alv3-user_id. 
             gs_alv2-company_code = gs_alv3-company_code. 
  
 
            APPEND gs_alv2 TO gt_alv2. 
             CLEAR : gs_alv3, gs_alv2 , gs_alv. 
           ELSE . 
             MESSAGE : ' Data already exists' TYPE 'E'. 
             CLEAR gs_alv. 
           ENDIF. 
  
        ELSE. 
           MESSAGE : 'Fill the required fied(Company code)' TYPE 'E'. 
         ENDIF. 
       ENDIF. 
     ENDLOOP. 
  
    MODIFY zpd_t_tmg FROM TABLE gt_alv2. 
     IF sy-subrc = 0. 
       MESSAGE 'Data transacted successfully' TYPE 'S'. 
  
      LOOP AT  gt_alv2 INTO gs_alv2. 
         gs_alv-name =  gs_alv2-name. 
         gs_alv-user_id = gs_alv2-user_id. 
         gs_alv-password = gs_alv2-password. 
         gs_alv-serial_no = gs_alv2-serial_no. 
         gs_alv-company_code = gs_alv2-company_code. 
  
        APPEND gs_alv TO gt_alv. 
  
        CLEAR : gs_alv , gt_alv2. 
  
      ENDLOOP. 
  
      REFRESH: gt_alv2 , gt_alv1. 
       PERFORM get_data1. 
     ENDIF. 
  
  ELSE. 
     MESSAGE 'Please enter the data' TYPE 'E'. 
  
  ENDIF. 
  
ENDFORM. 

 

FORM dynamic_output . 
  
  CLEAR : gs_serno , gs_ccode. 
  
  REFRESH  gt_alv4. 
   gt_alv4[] = gt_alv1[]. 
  
  IF gt_alv4 IS NOT INITIAL . 
  
    DELETE gt_alv4 WHERE serial_no IS INITIAL. 
     DELETE  gt_alv4 WHERE company_code IS INITIAL . 
  
  ENDIF. 
  
  PERFORM get_data2. 
   CLEAR : gs_serno , gs_ccode. 
  
  IF gt_alv4 IS   NOT INITIAL. 
  
    LOOP AT gt_alv1 ASSIGNING FIELD-SYMBOL(<lfs_data>) 
                     WHERE serial_no IS INITIAL AND 
                      company_code IS INITIAL. 
  
  
      READ TABLE gt_serno INTO gs_serno WITH KEY serial_no = <lfs_data>-serial_no. 
       IF sy-subrc = 0. 
         <lfs_data>-user_id = gs_serno-user_id. 
         <lfs_data>-company_code = gs_serno-company_code. 
  
      ENDIF. 
  
    ENDLOOP. 
  
 
  ELSE. 
  
    MESSAGE 'fill out required entry fields' TYPE 'E'. 
  
  ENDIF. 
  
ENDFORM. 

 

*&---------------------------------------------------------------------* 
 *& Form get_data2 
 *&---------------------------------------------------------------------* 
 *& text 
 *&---------------------------------------------------------------------* 
 *& -->  p1        text 
 *& <--  p2        text 
 *&---------------------------------------------------------------------* 
 FORM get_data2 . 
  
  REFRESH : gt_serno , gt_ccode. 
  ENDFORM. 

 

*&---------------------------------------------------------------------* 
 *& Form change_fieldcat1 
 *&---------------------------------------------------------------------* 
 *& text 
 *&---------------------------------------------------------------------* 
 *& -->  p1        text 
 *& <--  p2        text 
 *&---------------------------------------------------------------------* 
 FORM change_fieldcat1 . 
   LOOP AT gt_fieldcat ASSIGNING FIELD-SYMBOL(<lfs_fieldcat>) 
         WHERE ( fieldname = 'serial_no' OR fieldname = 'company_code' OR fieldname = 'user_id' OR fieldname = 'password' 
                  OR fieldname = 'name'). 
  
    <lfs_fieldcat>-edit = 'X'. 
  
 
 ENDLOOP. 
 ENDFORM. 


FORM modify_changes . 
  
  REFRESH gt_alv5 . 
  
  IF gt_alv IS NOT INITIAL. 
     PERFORM alv_to_alv5. 
     PERFORM get_changed_data. 
  
  ENDIF. 
  
ENDFORM. 

 

*&---------------------------------------------------------------------* 
 *& Form get_changed_data 
 *&---------------------------------------------------------------------* 
 *& text 
 *&---------------------------------------------------------------------* 
 *& -->  p1        text 
 *& <--  p2        text 
 *&---------------------------------------------------------------------* 
 FORM get_changed_data . 
  
  IF gt_alv5[] IS NOT INITIAL . 
     LOOP AT gt_alv5 INTO DATA(gs_alv5) . 
       READ TABLE gt_zpd_t_tmg INTO DATA(gs_zpd_t_tmg) WITH KEY serial_no = gs_alv5-serial_no. 
  
      IF sy-subrc = 0 AND ( gs_alv5 EQ gs_zpd_t_tmg ) . 
  
        DELETE gt_alv5 FROM gs_alv5. 
  
        CLEAR : gs_alv5 ,  gs_zpd_t_tmg . 
       ENDIF. 
       CLEAR :   gs_alv5 ,  gs_zpd_t_tmg . 
     ENDLOOP. 
  
    IF gt_alv5 IS INITIAL . 
       MESSAGE 'No changes was made' TYPE 'E'. 
  
    ELSE. 
       DELETE gt_alv5[] WHERE company_code IS INITIAL. 
       IF sy-subrc NE 0 AND gt_alv5 IS NOT INITIAL . 
         MODIFY zpd_t_tmg  FROM gt_alv5. 
         IF sy-subrc = 0. 
           MESSAGE 'Data saved successfully' TYPE 'S'. 
  
          gv_flag = abap_false. 
  
        ENDIF. 
       ELSE. 
  
        MESSAGE 'Fill the required field(Company code)' TYPE 'E'. 
       ENDIF . 
     ENDIF. 
   ENDIF. 
  
ENDFORM. 

 

*&---------------------------------------------------------------------* 
 *& Form alv_to_alv5 
 *&---------------------------------------------------------------------* 
 *& text 
 *&---------------------------------------------------------------------* 
 *& -->  p1        text 
 *& <--  p2        text 
 *&---------------------------------------------------------------------* 
 FORM alv_to_alv5 . 
  
  LOOP AT  gt_alv INTO gs_alv. 
     gs_alv5-mandt = sy-mandt. 
     gs_alv5-company_code = gs_alv-company_code. 
     gs_alv5-name = gs_alv-name. 
     gs_alv5-password = gs_alv-password. 
     gs_alv5-serial_no = gs_alv-serial_no. 
     gs_alv5-user_id = gs_alv-user_id. 
     APPEND gs_alv5 TO gt_alv. 
     CLEAR : gs_alv ,  gs_alv5. 
  
  ENDLOOP. 
  
ENDFORM. 

 

*&---------------------------------------------------------------------* 
 *& Form zpd_t_tmg_to_gt_alv 
 *&---------------------------------------------------------------------* 
 *& text 
 *&---------------------------------------------------------------------* 
 *& -->  p1        text 
 *& <--  p2        text 
 *&---------------------------------------------------------------------* 
 FORM zpd_t_tmg_to_gt_alv . 
  
AUTHORITY-CHECK OBJECT 'ZAUTHTMG' 
 ID 'ACTVT' FIELD '01'. 
  
  IF sy-subrc <> 0 . 
     MESSAGE e006(zmsg_auth). 
     ENDIF. 
 
  LOOP AT  gt_zpd_t_tmg INTO gs_zpd_t_tmg. 
     gs_alv-serial_no = gs_zpd_t_tmg-serial_no. 
     gs_alv-company_code = gs_zpd_t_tmg-company_code. 
     gs_alv-name = gs_zpd_t_tmg-name. 
     gs_alv-password = gs_zpd_t_tmg-password. 
     gs_alv-user_id = gs_zpd_t_tmg-user_id. 
  APPEND  gs_alv TO gt_alv. 
    CLEAR : gs_alv , gs_zpd_t_tmg . 
  ENDLOOP. 
  ENDFORM. 

 

OUTPUT:

Pradeep555_1-1747050747016.png

When we click on new entries button and click on save :

Pradeep555_3-1747050889779.png

Data will be saved to Database :

Pradeep555_4-1747051011748.png

 

Thanks and regards:

Pradeep Ishwar Devadiga

 

1 Comment
Sandra_Rossi
Active Contributor

One big problem is that people can maintain the same lines at the same time. There's no lock object at line level. How do you handle this case?

I don't understand why your table editor is compared to the Table Maintenance Dialogs, as it doesn't match the classic features like inserting modifications in transport requests, customizing comparison and so on.

You might propose code which can be easily reused by people. What could be interesting is to find and replace the table name by another name, and that would make the program work immediately, then do cosmetic changes. Also, you are proposing code which is missing the includes, GUI status and so on.

Concerning the code, I would recommend to:

  • Use meaningful name (e.g. GT_ALV4, GT_ALV5 are meaningless)
  • Use directly CL_GUI_ALV_GRID instead of using REUSE_ALV_GRID_DISPLAY and GET_GLOBALS_FROM_SLVC_FULLSCR.
  • Replace subroutines (obsolete since 2009) with methods.
  • Handle function module exceptions.
  • Don't clear variables before ENDLOOP (just noise)