Here sample program which shows, how to realize table edit through ALV (based on class CL_GUI_ALV_GRID).
The program has some additional features:
- extends the standard check with check against duplicate keys using event DATA_CHANGED
- has a protocol and shows errors using class CL_ALV_CHANGED_DATA_PROTOCOL
- collects the changes for further delta updates of the database table
- disables the key fields for edit in existing rows and enable in inserted rows
- provides functionality of copy row, which you probably missing in standard sample program BCALV_EDIT_04
If you want to start the program in your system, besides copying the source code, you have to create screen, GUI status, etc. See the comment at the beginning of the program for the detailed instruction.
*&---------------------------------------------------------------------*
*& Program ZKMALVED
*&
*& Sample program for table edit through ALV
*& with check against duplicate keys, protocol and delta update
*&---------------------------------------------------------------------*
* Steps to create this program in your system:
*
* 1. SE38: create executable program (type 1)
*
* 2. SE80: create screen 0100
*
* 3. Screen Painter: on the screen 0100 create 'Custom control'
* CONT1_0100
*
* 4. SE80: for screen 0100 set the variable OK_CODE for the element OK
*
* 5. SE80: create calls to PBO and PAI in the flow logic of screen 0100:
*
* PROCESS BEFORE OUTPUT.
* MODULE pbo_0100.
* PROCESS AFTER INPUT.
* MODULE pai_0100.
*
* 6. SE80: create GUI Status STAT_0100, assign functions EXIT and SAVE
* to standard icons, eventually create your own toolbar button
PROGRAM zkmalved.
TYPES: BEGIN OF ts_data.
INCLUDE STRUCTURE spfli.
TYPES: mod_type TYPE string. " type of modification (for protocol)
TYPES: verified TYPE c. " flag for verification
TYPES: celltab TYPE lvc_t_styl.
TYPES: END OF ts_data.
TYPES:
tt_data TYPE STANDARD TABLE OF ts_data WITH NON-UNIQUE DEFAULT KEY.
CLASS cl_event_handler DEFINITION DEFERRED.
DATA:
gt_data TYPE tt_data,
go_grid TYPE REF TO cl_gui_alv_grid,
go_cont TYPE REF TO cl_gui_custom_container,
gt_fcat TYPE lvc_t_fcat,
ok_code LIKE sy-ucomm,
go_event_handler TYPE REF TO cl_event_handler.
*----------------------------------------------------------------------*
* CLASS cl_event_handler DEFINITION
*----------------------------------------------------------------------*
CLASS cl_event_handler DEFINITION.
PUBLIC SECTION.
CLASS-DATA:
data_changed_error TYPE i, " error flag
protocol_tab TYPE tt_data. " protocol of changes since last save
METHODS handle_toolbar
FOR EVENT toolbar OF cl_gui_alv_grid
IMPORTING
e_object
e_interactive.
METHODS handle_user_command
FOR EVENT user_command OF cl_gui_alv_grid
IMPORTING
e_ucomm.
METHODS handle_data_changed
FOR EVENT data_changed OF cl_gui_alv_grid
IMPORTING
er_data_changed.
METHODS protocol_add
IMPORTING
pi_tabix TYPE i
er_data_changed TYPE REF TO cl_alv_changed_data_protocol.
ENDCLASS. "cl_event_handler DEFINITION
*----------------------------------------------------------------------*
* CLASS cl_event_handler IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS cl_event_handler IMPLEMENTATION.
*&---------------------------------------------------------------------*
*& METHOD handle_toolbar
*&---------------------------------------------------------------------*
METHOD handle_toolbar.
FIELD-SYMBOLS:
<fs_button> TYPE stb_button.
* replace the standard function 'check' with own one
READ TABLE e_object->mt_toolbar ASSIGNING <fs_button>
WITH KEY function = cl_gui_alv_grid=>mc_fc_check. " '&CHECK'.
<fs_button>-function = 'CHECK'.
** deactivate the 'copy row' function
** (not necessary in the logic of this program)
*
* READ TABLE e_object->mt_toolbar ASSIGNING <fs_button>
* WITH KEY function = cl_gui_alv_grid=>mc_fc_loc_copy_row. " '&LOCAL©_ROW'.
* <fs_button>-disabled = 'X'.
ENDMETHOD. "handle_toolbar
*&---------------------------------------------------------------------*
*& METHOD handle_user_command
*&---------------------------------------------------------------------*
METHOD handle_user_command.
* force PAI processing with user function as ok_code
cl_gui_cfw=>set_new_ok_code( e_ucomm ).
ENDMETHOD. "handle_user_command
*&---------------------------------------------------------------------*
*& METHOD handle_data_changed
*&---------------------------------------------------------------------*
*& validate changes against duplicate keys and collect the protocol
*& of the changes for further db update or transport of changes
*&---------------------------------------------------------------------*
METHOD handle_data_changed.
DATA:
ls_row TYPE lvc_s_moce,
ls_cell TYPE lvc_s_modi,
lt_data TYPE tt_data,
ls_data TYPE ts_data,
ls_data2 TYPE ts_data,
lv_tabix TYPE i,
lv_tabix2 TYPE i,
lv_new TYPE i.
FIELD-SYMBOLS:
<ft_data> TYPE tt_data,
<fs_cell> TYPE lvc_s_modi.
ASSIGN er_data_changed->mp_mod_rows->* TO <ft_data>.
* consider present error messages from standard data checks
IF LINES( er_data_changed->mt_protocol ) = 0.
data_changed_error = 0.
ELSE.
data_changed_error = 1.
ENDIF.
* 1. collect the deleted rows (no checks needed)
LOOP AT er_data_changed->mt_deleted_rows INTO ls_row.
READ TABLE gt_data INTO ls_data INDEX ls_row-row_id.
ls_data-mod_type = 'DELETE'.
APPEND ls_data TO protocol_tab.
ENDLOOP.
* 2. collect the 'old' changed rows (no changes on key fields)
LOOP AT <ft_data> INTO ls_data
WHERE verified = 'X'.
ls_data-mod_type = 'UPDATE'.
APPEND ls_data TO protocol_tab.
ENDLOOP.
* 3. check the new rows against the old, verified rows
LOOP AT <ft_data> INTO ls_data
WHERE verified = ''.
lv_tabix = sy-tabix.
ADD 1 TO lv_new.
READ TABLE gt_data TRANSPORTING NO FIELDS
WITH KEY carrid = ls_data-carrid
connid = ls_data-connid.
CHECK sy-subrc = 0.
data_changed_error = 1.
CALL METHOD protocol_add
EXPORTING
pi_tabix = lv_tabix
er_data_changed = er_data_changed.
ENDLOOP.
* 4. check the new rows among themselves
IF lv_new > 1.
LOOP AT <ft_data> INTO ls_data
WHERE verified = ''.
lv_tabix = sy-tabix.
LOOP AT <ft_data> INTO ls_data2
WHERE verified = ''.
lv_tabix2 = sy-tabix.
CHECK lv_tabix2 <> lv_tabix.
CHECK ls_data2-carrid = ls_data-carrid
AND ls_data2-connid = ls_data-connid.
data_changed_error = 1.
CALL METHOD protocol_add
EXPORTING
pi_tabix = lv_tabix
er_data_changed = er_data_changed.
CALL METHOD protocol_add
EXPORTING
pi_tabix = lv_tabix2
er_data_changed = er_data_changed.
ENDLOOP.
ENDLOOP.
ENDIF.
* 5. last aktivities in event DATA_CHANGED
LOOP AT <ft_data> INTO ls_data
WHERE verified = ''.
lv_tabix = sy-tabix.
READ TABLE er_data_changed->mt_mod_cells INTO ls_cell WITH KEY tabix = sy-tabix.
READ TABLE er_data_changed->mt_protocol TRANSPORTING NO FIELDS WITH KEY row_id = ls_cell-row_id.
* if check against duplicate key was ok
CHECK sy-subrc <> 0.
* collect new rows
ls_data-mod_type = 'INSERT'.
APPEND ls_data TO protocol_tab.
* disable key fields
LOOP AT er_data_changed->mt_good_cells ASSIGNING <fs_cell>
WHERE tabix = lv_tabix.
READ TABLE gt_fcat TRANSPORTING NO FIELDS WITH KEY fieldname = <fs_cell>-fieldname
key = 'X'.
CHECK sy-subrc = 0.
<fs_cell>-style = cl_gui_alv_grid=>mc_style_disabled.
ENDLOOP.
ENDLOOP.
** 6. activate the copied rows
** (not necessary in the logic of this program)
*
* DATA:
* ls_cell TYPE lvc_s_modi.
*
* LOOP AT er_data_changed->mt_inserted_rows INTO ls_row.
*
* LOOP AT er_data_changed->mt_mod_cells INTO ls_cell
* WHERE row_id = ls_row-row_id.
*
* CALL METHOD er_data_changed->modify_style
* EXPORTING
* i_row_id = ls_cell-row_id
* i_fieldname = ls_cell-fieldname
* i_style = cl_gui_alv_grid=>mc_style_enabled.
* ENDLOOP.
* ENDLOOP.
ENDMETHOD. "handle_data_changed
*&---------------------------------------------------------------------*
*& METHOD protocol_add
*&---------------------------------------------------------------------*
METHOD protocol_add.
DATA:
ls_cell TYPE lvc_s_modi,
ls_fcat TYPE lvc_s_fcat,
lv_string TYPE string,
lv_msgv1 TYPE symsgv,
lv_msgv2 TYPE symsgv,
ls_roid_front TYPE lvc_s_roid,
lv_row_id TYPE i.
LOOP AT er_data_changed->mt_mod_cells INTO ls_cell
WHERE tabix = pi_tabix.
* report only key fields
READ TABLE gt_fcat INTO ls_fcat WITH KEY fieldname = ls_cell-fieldname.
CHECK ls_fcat-key = 'X'.
* report only once
READ TABLE er_data_changed->mt_protocol TRANSPORTING NO FIELDS
WITH KEY fieldname = ls_cell-fieldname
row_id = ls_cell-row_id.
CHECK sy-subrc <> 0.
* map the row id between frontend and internal tables for the message
READ TABLE er_data_changed->mt_roid_front INTO ls_roid_front
WITH KEY row_id = ls_cell-row_id.
lv_row_id = sy-tabix.
lv_msgv1 = 'Row &1:'.
lv_string = lv_row_id.
REPLACE SUBSTRING '&1' IN lv_msgv1 WITH lv_string.
lv_msgv2 = 'Duplicate key with value ''&1'''.
lv_string = ls_cell-value.
REPLACE SUBSTRING '&1' IN lv_msgv2 WITH lv_string.
CALL METHOD er_data_changed->add_protocol_entry
EXPORTING
i_msgid = '0K'
i_msgno = '000'
i_msgty = 'E'
i_msgv1 = lv_msgv1
i_msgv2 = lv_msgv2
i_fieldname = ls_cell-fieldname
i_row_id = ls_cell-row_id. " pass the row id without mapping here
ENDLOOP.
ENDMETHOD. "protocol_add
ENDCLASS. "cl_event_handler IMPLEMENTATION
*&---------------------------------------------------------------------*
*& START-OF-SELECTION
*&---------------------------------------------------------------------*
START-OF-SELECTION.
PERFORM data_load CHANGING gt_data.
* initially check all rows as verified
PERFORM data_verified_set CHANGING gt_data.
PERFORM fieldcat_create CHANGING gt_fcat.
PERFORM data_key_readonly_set USING gt_fcat CHANGING gt_data.
CALL SCREEN 0100.
*&---------------------------------------------------------------------*
*& Form data_load
*&---------------------------------------------------------------------*
FORM data_load CHANGING pt_data TYPE tt_data.
SELECT *
INTO CORRESPONDING FIELDS OF TABLE pt_data
FROM spfli.
ENDFORM. "data_load
*&---------------------------------------------------------------------*
*& Form fieldcat_create
*&---------------------------------------------------------------------*
FORM fieldcat_create CHANGING pt_fcat TYPE lvc_t_fcat.
FIELD-SYMBOLS:
<fs_fcat> TYPE lvc_s_fcat.
CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
EXPORTING
i_structure_name = 'SPFLI'
CHANGING
ct_fieldcat = gt_fcat.
LOOP AT gt_fcat ASSIGNING <fs_fcat>.
<fs_fcat>-edit = 'X'.
* key fields
IF <fs_fcat>-fieldname = 'CARRID'
OR <fs_fcat>-fieldname = 'CONNID'.
<fs_fcat>-key = 'X'.
ELSE.
<fs_fcat>-key = ''.
ENDIF.
ENDLOOP.
ENDFORM. "fieldcat_create
*&---------------------------------------------------------------------*
*& Form data_key_readonly_set
*&---------------------------------------------------------------------*
FORM data_key_readonly_set USING pt_fcat TYPE lvc_t_fcat
CHANGING pt_data TYPE tt_data.
DATA:
lt_style TYPE lvc_t_styl,
ls_style TYPE lvc_s_styl,
ls_fcat TYPE lvc_s_fcat.
FIELD-SYMBOLS:
<fs_data> TYPE ts_data.
* the following construct works well for function 'copy row' (MC_FC_LOC_COPY_ROW)
* (the style of the whole row is not copied and the key fields are open for change)
* first deactivate complete row for edit
ls_style-style = cl_gui_alv_grid=>mc_style_disabled.
INSERT ls_style INTO TABLE lt_style.
LOOP AT pt_fcat INTO ls_fcat
WHERE NOT key = 'X'.
* then activate non-key fields
ls_style-fieldname = ls_fcat-fieldname.
ls_style-style = cl_gui_alv_grid=>mc_style_enabled.
INSERT ls_style INTO TABLE lt_style.
ENDLOOP.
LOOP AT pt_data ASSIGNING <fs_data>.
<fs_data>-celltab = lt_style.
ENDLOOP.
ENDFORM. "data_key_readonly_set
*&---------------------------------------------------------------------*
*& Form data_verified_set
*&---------------------------------------------------------------------*
FORM data_verified_set CHANGING pt_data TYPE tt_data.
FIELD-SYMBOLS:
<fs_data> TYPE ts_data.
LOOP AT pt_data ASSIGNING <fs_data>
WHERE verified = ''.
<fs_data>-verified = 'X'.
ENDLOOP.
ENDFORM. "data_verified_set
*&---------------------------------------------------------------------*
*& Form data_change_post_processing
*&---------------------------------------------------------------------*
FORM data_change_post_processing CHANGING pt_data TYPE tt_data.
* check all rows as verified
PERFORM data_verified_set CHANGING pt_data.
* deactivate edit mode of key fields
PERFORM data_key_readonly_set USING gt_fcat
CHANGING pt_data.
* refresh ALV grid from internal table
CALL METHOD go_grid->refresh_table_display.
ENDFORM. "data_change_post_processing
*&---------------------------------------------------------------------*
*& Form data_save
*&---------------------------------------------------------------------*
FORM data_save.
DATA:
ls_data TYPE ts_data,
ls_db_data TYPE spfli.
LOOP AT cl_event_handler=>protocol_tab INTO ls_data.
MOVE-CORRESPONDING ls_data TO ls_db_data.
CASE ls_data-mod_type.
WHEN 'INSERT'.
INSERT spfli FROM ls_db_data.
WHEN 'UPDATE'.
UPDATE spfli FROM ls_db_data.
WHEN 'DELETE'.
DELETE spfli FROM ls_db_data.
ENDCASE.
ENDLOOP.
COMMIT WORK.
MESSAGE S999(B1) WITH 'Data saved'.
ENDFORM. "data_save
*&---------------------------------------------------------------------*
*& Form alv_init
*&---------------------------------------------------------------------*
FORM alv_init.
DATA:
ls_layout TYPE lvc_s_layo.
IF go_cont IS INITIAL.
SET PF-STATUS 'STAT_0100'.
CREATE OBJECT go_cont
EXPORTING
container_name = 'CONT1_0100'.
CREATE OBJECT go_grid
EXPORTING
i_parent = go_cont.
CREATE OBJECT go_event_handler.
SET HANDLER go_event_handler->handle_toolbar FOR go_grid.
SET HANDLER go_event_handler->handle_user_command FOR go_grid.
SET HANDLER go_event_handler->handle_data_changed FOR go_grid.
ls_layout-stylefname = 'CELLTAB'.
CALL METHOD go_grid->set_table_for_first_display
EXPORTING
is_layout = ls_layout
CHANGING
it_fieldcatalog = gt_fcat
it_outtab = gt_data.
** if you need event DATA_CHANGED after function 'copy row'
** (not necessary in the logic of this program)
*
* CALL METHOD go_grid->register_edit_event
* EXPORTING
* i_event_id = cl_gui_alv_grid=>mc_evt_modified.
ENDIF.
ENDFORM. "alv_init
*&---------------------------------------------------------------------*
*& Form user_command
*&---------------------------------------------------------------------*
FORM user_command.
CALL METHOD cl_gui_cfw=>dispatch.
CASE ok_code.
WHEN 'EXIT'.
LEAVE PROGRAM.
WHEN 'CHECK'.
* 1. synchronize the internal table and check against duplicate keys
CALL METHOD go_grid->check_changed_data.
* 2. if there are no duplicated keys
CHECK cl_event_handler=>data_changed_error = 0.
* post processing after successful synchronization
PERFORM data_change_post_processing CHANGING gt_data.
WHEN 'SAVE'.
* 1. synchronize the internal table
CALL METHOD go_grid->check_changed_data.
* 2. if there are no duplicated keys
CHECK cl_event_handler=>data_changed_error = 0.
* post processing after successful synchronization
PERFORM data_change_post_processing CHANGING gt_data.
* 3. if the data was changed
CHECK NOT cl_event_handler=>protocol_tab IS INITIAL.
* save the delta changes in database
PERFORM data_save.
* clear the protocol
CLEAR cl_event_handler=>protocol_tab.
ENDCASE.
CLEAR ok_code.
ENDFORM. "user_command
*----------------------------------------------------------------------*
* MODULE pbo_0100 OUTPUT
*----------------------------------------------------------------------*
MODULE pbo_0100 OUTPUT.
PERFORM alv_init.
ENDMODULE. "pbo_0100 OUTPUT
*----------------------------------------------------------------------*
* MODULE pai_0100 INPUT
*----------------------------------------------------------------------*
MODULE pai_0100 INPUT.
PERFORM user_command.
ENDMODULE. "pai_0100 INPUT
See other related notes on my website: