[ABAP] Editierbares SALV-Grid (IF_SALV_GUI_OM_EXTEND_GRID_API, IF_SALV_GUI_OM_EDIT_RESTRICTED)

* Quelle: https://blogs.sap.com/2022/08/01/editable-cl_salv_table-after-release-756/
* ab SAP Release 756
* Achtung: das Ganze funktioniert nur für kleine Tabellen mit max. 5000 Zellen, siehe:
*
* Methode: CL_SALV_GUI_OM_ADAPTER_TABLE->CAN_RUN_RESTRICTED_EDIT_MODE( )
* Konstante: CV_MAX_CELLS_FOR_EDITABLE (Wert: 5000).

* Eventhandler
CLASS lcl_events DEFINITION.
  PUBLIC SECTION.
* Bezeichner der Buttons
    CONSTANTS: co_btn_edit TYPE string VALUE 'BTN_EDIT'.
    CONSTANTS: co_btn_save TYPE string VALUE 'BTN_SAVE'.

* Platzhalter für Referenz auf SALV-Grid
    CLASS-DATA: o_salv TYPE REF TO cl_salv_table.

* Eventhandler-Methode für Button-Klicks in der Toolbar des SALV-Grids
    CLASS-METHODS : on_toolbar_click FOR EVENT added_function OF cl_salv_events_table
      IMPORTING
        e_salv_function
        sender.
  PRIVATE SECTION.
* Edit-Status des SALV-Grids
    CLASS-DATA: gv_edit TYPE abap_bool VALUE abap_false.
ENDCLASS.

CLASS lcl_events IMPLEMENTATION.
  METHOD on_toolbar_click.
    IF o_salv IS BOUND.

      DATA(o_api) = o_salv->extended_grid_api( ).
      DATA(o_edit) = o_api->editable_restricted( ).

      CASE e_salv_function.

        WHEN co_btn_edit.
* Edit-Modus umschalten
          IF gv_edit = abap_false.
            gv_edit = abap_true.
          ELSE.
            gv_edit = abap_false.
          ENDIF.

          TRY.
* Spalte(n) (nicht) editierbar setzen
              o_edit->set_attributes_for_columnname( EXPORTING columnname              = 'EKGRP'
                                                               all_cells_input_enabled = gv_edit ).

              o_edit->set_attributes_for_columnname( EXPORTING columnname              = 'SMTP_ADDR'
                                                               all_cells_input_enabled = gv_edit ).
            CATCH cx_salv_not_found.
          ENDTRY.

          o_edit->validate_changed_data( ).
          o_salv->refresh( ).

        WHEN co_btn_save.
* Daten auf Validität prüfen
          DATA(lv_data_is_valid) = abap_false.

          TRY.
              o_edit->validate_changed_data( IMPORTING is_input_data_valid = lv_data_is_valid ).
              o_salv->refresh( ).
            CATCH cx_salv_not_found.
          ENDTRY.

          IF lv_data_is_valid = abap_true.
* Daten hier speichern / weiterverarbeiten
            MESSAGE co_btn_save TYPE 'I'.
          ENDIF.
      ENDCASE.
    ENDIF.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.

  TRY.
* Beispieldaten (Einkäufergruppen) holen
      SELECT FROM t024
        FIELDS *
        INTO TABLE @DATA(it_t024).

      IF sy-subrc = 0.
        cl_salv_table=>factory( EXPORTING r_container  = cl_gui_container=>default_screen
                                IMPORTING r_salv_table = lcl_events=>o_salv
                                CHANGING  t_table      = it_t024 ).

* Standardbuttons der SALV-Table ausblenden
        lcl_events=>o_salv->get_functions( )->set_all( abap_false ).

* Eigenen SALV-Button hinzufügen
* das Hinzufügen des Buttons funktioniert nur, wenn die SALV-Table innerhalb eines Containers (z.B. cl_gui_container=>default_screen) eingebettet ist
        lcl_events=>o_salv->get_functions( )->add_function( name = |{ lcl_events=>co_btn_edit }|
                                                            icon = |{ icon_edit_file }|
                                                            text = 'Edit'
                                                            tooltip = 'Daten editieren'
                                                            position = if_salv_c_function_position=>right_of_salv_functions ).

        lcl_events=>o_salv->get_functions( )->add_function( name = |{ lcl_events=>co_btn_save }|
                                                            icon = |{ icon_save_as_template }|
                                                            text = 'Save'
                                                            tooltip = 'Daten speichern'
                                                            position = if_salv_c_function_position=>right_of_salv_functions ).

* Eventhandler für Klicks in die Toolbar des SALV-Grids setzen
        SET HANDLER lcl_events=>on_toolbar_click FOR lcl_events=>o_salv->get_event( ).

* SALV anzeigen
        lcl_events=>o_salv->display( ).

* Toolbar der Listausgabe unterdrücken
        cl_abap_list_layout=>suppress_toolbar( ).

* Listausgabe erzwingen für Erzeugung von cl_gui_container=>default_screen
        WRITE: space.

      ENDIF.
    CATCH cx_root INTO DATA(e_txt).
      WRITE: / e_txt->get_text( ).
  ENDTRY.

[ABAP] SALV-Tree: Beispiel für Verwendung von cl_salv_tree

* Demoprogramme
*
* SALV_DEMO_TREE_DATA_UPDATE
* SALV_DEMO_TREE_EVENTS
* SALV_DEMO_TREE_FUNCTIONS
* SALV_DEMO_TREE_METADATA
* SALV_DEMO_TREE_SELECTIONS
* SALV_DEMO_TREE_SETTINGS
* SALV_DEMO_TREE_SIMPLE

TYPES: ty_it_spfli TYPE STANDARD TABLE OF spfli WITH DEFAULT KEY.

DATA: o_tree TYPE REF TO cl_salv_tree.
DATA: it_spfli TYPE ty_it_spfli.
DATA: it_spfli_empty TYPE ty_it_spfli.

* Eventhandlerklasse
CLASS lcl_events DEFINITION.
  PUBLIC SECTION.

    CLASS-METHODS:
      on_button_click FOR EVENT link_click OF cl_salv_events_tree
        IMPORTING
            columnname
            node_key
            sender.
    CLASS-METHODS:
      on_user_command FOR EVENT added_function OF cl_salv_events
        IMPORTING
            e_salv_function
            sender.
ENDCLASS.

CLASS lcl_events IMPLEMENTATION.

  METHOD on_button_click.

    TRY.
        IF o_tree IS BOUND.
* Unternode (Child) zur akt. geklickten Node holen
          DATA(o_child_node) = o_tree->get_nodes( )->get_node( node_key )->get_first_child( ).

* alle Unternodes durchlaufen und Checkbox der Spalte Carrier setzen
          WHILE o_child_node IS BOUND.

            DATA(o_item) = o_child_node->get_item( 'CARRID' ).

            IF o_item->is_checked( ) = abap_true.
              o_item->set_checked( abap_false ).
            ELSE.
              o_item->set_checked( abap_true ).
            ENDIF.

            o_child_node = o_child_node->get_next_sibling( ).
          ENDWHILE.

        ENDIF.
      CATCH cx_salv_msg.
    ENDTRY.

  ENDMETHOD.

  METHOD on_user_command.
    MESSAGE e_salv_function TYPE 'I'.
  ENDMETHOD.

ENDCLASS.

START-OF-SELECTION.
* Daten holen
  SELECT * FROM spfli INTO CORRESPONDING FIELDS OF TABLE it_spfli.

  SORT: it_spfli BY carrid.

  TRY.
* Tree-Objekt mit leerer Tabelle (Dummy) erzeugen
      cl_salv_tree=>factory( IMPORTING
                               r_salv_tree = o_tree
                             CHANGING
                               t_table     = it_spfli_empty ).

* Header setzen
      o_tree->get_tree_settings( )->set_header( CONV #( sy-title ) ).
      o_tree->get_tree_settings( )->set_hierarchy_header( 'Carrier' ).
      o_tree->get_tree_settings( )->set_hierarchy_tooltip( 'Carrier' ).
      o_tree->get_tree_settings( )->set_hierarchy_size( 40 ).
      o_tree->get_tree_settings( )->set_hierarchy_icon( CONV #( icon_tree ) ).

* Treenodes einfügen
      LOOP AT it_spfli ASSIGNING FIELD-SYMBOL(<c>) GROUP BY <c>-carrid.

        DATA(it_cp) = VALUE ty_it_spfli( FOR <cp> IN GROUP <c> ( <cp> ) ).

        DATA(o_parent) = o_tree->get_nodes( )->add_node( related_node   = ''
                                                         relationship   = cl_gui_column_tree=>relat_last_child
                                                         collapsed_icon = CONV #( icon_closed_folder )
                                                         expanded_icon  = CONV #( icon_open_folder )
                                                         row_style      = if_salv_c_tree_style=>intensified
                                                         text           = CONV #( <c>-carrid ) ).

        IF lines( it_cp ) > 1.
          o_parent->get_item( 'CARRID' )->set_type( if_salv_c_item_type=>button ).
          o_parent->get_item( 'CARRID' )->set_value( 'all' ).
        ENDIF.

        LOOP AT GROUP <c> ASSIGNING FIELD-SYMBOL(<f>).
          DATA(o_carrid) = o_tree->get_nodes( )->add_node( related_node = o_parent->get_key( )
                                                           relationship = cl_gui_column_tree=>relat_last_child
                                                           data_row     = <f>
                                                           row_style    = if_salv_c_tree_style=>intensified
                                                           text         = CONV #( <f>-connid ) ).

          o_carrid->get_item( 'CARRID' )->set_type( if_salv_c_item_type=>checkbox ).
          o_carrid->get_item( 'CARRID' )->set_editable( abap_true ).
        ENDLOOP.

      ENDLOOP.

* Mandant ausblenden
      o_tree->get_columns( )->get_column( 'MANDT' )->set_technical( abap_true ).
* Carrid zentriert
      o_tree->get_columns( )->get_column( 'CARRID' )->set_alignment( if_salv_c_alignment=>right ).

* Spaltenbreiten optimieren
      o_tree->get_columns( )->set_optimize( abap_true ).

* Flugzeiten Aggregieren
      o_tree->get_aggregations( )->add_aggregation( columnname  = 'FLTIME'
                                                    aggregation = if_salv_c_aggregation=>total ).

* Alle Funktionsbuttons einschalten
      o_tree->get_functions( )->set_all( abap_true ).

* eigenen Button hinzufügen
* das Hinzufügen des Buttons funktioniert nur, wenn die SALV-Table innerhalb eines Containers (z.B. cl_gui_container=>default_screen) eingebettet ist
*      o_tree->get_functions( )->add_function( name     = 'BTN_USR'
*                                              icon     = CONV #( icon_abap )
*                                              text     = 'Testbutton'
*                                              tooltip  = 'Testbutton'
*                                              position = if_salv_c_function_position=>right_of_salv_functions ).

* Handler für Button-Click setzen
      SET HANDLER lcl_events=>on_button_click FOR o_tree->get_event( ).
*      SET HANDLER lcl_events=>on_user_command FOR o_tree->get_event( ).

      o_tree->display( ).
    CATCH cx_root INTO DATA(e_txt).
      WRITE: / e_txt->get_text( ).
  ENDTRY.

[ABAP] SALV-Table: Eigenen Button einfügen und Ereignis abfangen

* http://ajanzen.com/pdf_dokumente/sap_abap/alv/alv_sel_mode_own_funct.pdf

* Eventhandler für neuen Button
CLASS lcl_events DEFINITION.
  PUBLIC SECTION.
* Bezeichner des Buttons
    CONSTANTS: co_btn_xl_export TYPE string VALUE 'BTN_XL_EXPORT'.

    CLASS-METHODS : on_toolbar_click FOR EVENT added_function OF cl_salv_events_table
      IMPORTING
          e_salv_function
          sender.
ENDCLASS.

CLASS lcl_events IMPLEMENTATION.
  METHOD on_toolbar_click.
    CASE e_salv_function.
      WHEN co_btn_xl_export.
        MESSAGE co_btn_xl_export TYPE 'S'.
    ENDCASE.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.

* Toolbar der Listausgabe unterdrücken
  cl_abap_list_layout=>suppress_toolbar( ).

  DATA: o_alv TYPE REF TO cl_salv_table.
  DATA: it_tab TYPE ...

  cl_salv_table=>factory( EXPORTING
                            r_container = cl_gui_container=>default_screen
                          IMPORTING
                            r_salv_table = o_alv
                          CHANGING
                            t_table = it_tab ).

* Standardbuttons der SALV-Table ausblenden
  o_alv->get_functions( )->set_all( abap_false ).

* Eigenen SALV-Button hinzufügen
* das Hinzufügen des Buttons funktioniert nur, wenn die SALV-Table innerhalb eines Containers (z.B. cl_gui_container=>default_screen) eingebettet ist
  o_alv->get_functions( )->add_function( name = |{ lcl_events=>co_btn_xl_export }|
                                         icon = |{ icon_export }|
                                         text = 'Export'
                                         tooltip = 'Daten exportieren'
                                         position = if_salv_c_function_position=>right_of_salv_functions ).

* Eventhandler für Klicks in die Toolbar des SALV-Grids setzen
  SET HANDLER lcl_events=>on_toolbar_click FOR o_alv->get_event( ).

* SALV anzeigen
  o_alv->display( ).

* Listausgabe erzwingen für Erzeugung von cl_gui_container=>default_screen
  WRITE: space.

[ABAP] SALV-Table: Funktionsbuttons hinzufügen

Variante 1 (nur wenn in eigenem Dynpro + Container)

SELECT * FROM sflights INTO TABLE @DATA(it_data).

DATA: o_salv TYPE REF TO cl_salv_table.

cl_salv_table=>factory( EXPORTING
                          r_container  = cl_gui_container=>default_screen " Standard-Container der Listausgabe nutzen
                        IMPORTING
                          r_salv_table = o_salv
                        CHANGING
                          t_table      = it_data ).

o_salv->get_functions( )->set_all( ).

* Für diesen Button muss zus. noch ein Event-Handler ausgeprägt werden
o_salv->get_functions( )->add_function( name     = 'MYFUNC'
                                        icon     = |{ icon_complete }|
                                        text     = 'Funktionstext'
                                        tooltip  = 'ToolTipText'
                                        position = if_salv_c_function_position=>right_of_salv_functions ).

o_salv->display( ).

* Erzwingen von cl_gui_container=>default_screen (Listausgabe)
WRITE space.

Variante 2 (PF_STATUS)

* ohne Angabe eines Containers in der factory-Methode:
SELECT * FROM sflights INTO TABLE @DATA(it_data).

DATA: o_salv TYPE REF TO cl_salv_table.

cl_salv_table=>factory( IMPORTING
                          r_salv_table = o_salv
                        CHANGING
                          t_table      = it_data ).

* PF-Status aus Programm SAPLSLVC_FULLSCREEN benutzen
o_salv->set_screen_status( pfstatus      = 'STANDARD_FULLSCREEN'
                           report        = 'SAPLSLVC_FULLSCREEN'
                           set_functions = cl_salv_model_base=>c_functions_all ).

o_salv->display( ).

oder

SELECT * FROM sflights INTO TABLE @DATA(it_data).

DATA: o_salv TYPE REF TO cl_salv_table.

cl_salv_table=>factory( IMPORTING
                          r_salv_table = o_salv
                        CHANGING
                          t_table      = it_data ).

* PF-Status manuelle aus Funktionsgruppe SALV_METADATA_STATUS kopieren
* im lokalen Programm (sy-repid) aufrufen
o_salv->set_screen_status( pfstatus      = 'SALV_TABLE_STANDARD'
                           report        = sy-repid
                           set_functions = cl_salv_model_base=>c_functions_all ).

o_salv->display( ).