ALV OO – Trabalhando com botões e eventos

Sem dúvida, uma das atividades mais realizadas por um programador ABAP é a emissão de relatórios.

Como não estamos na era dos dinossauros, “não conhecemos” o relatório “write” e vamos direto aos “ALV’s”.

Este post tem como objetivo exemplificar algumas funcionalidades de um ALV OO (Orientado a Objetos),  são elas:

  • Exclusão de botões do toolbar de um ALV OO
  • Inclusão de botões no toolbar do ALV
  • Utilização de eventos
  • Como desabilitar um botão (ele aparece, mas não é possível clicar)
  • Geração de um ALV sem necessidade de criação de um container

Veja também:

  • Como encontrar ícones
  • Utilização de interfaces

    A modelagem utilizada, foi construída de forma a permitir a utilização dos pontos acima citados.

Especificação

Na tela de seleção, o usuário poderá determinar uma ou mais cias aéreas.  Deve ser gerado um relatório ALV com os dados desta(s) cia(s), a serem extraídos da tabela SCARR.

Neste relatório, ao selecionar uma determinada cia, deve haver um botão que gere um segundo ALV, desta vez exibindo as informações dos vôos (conexões) referentes à seleção realizada (dados a serem extraídos da tabela SPFLI).

E então, vamos ao que interessa!?

No programa principal, devemos criar a tela de seleção e um include, onde codificaremos nossas classes.

Tela de seleção:

Declara-se o select-options para possibilitar a determinação do range das cias aéreas:

include: z_classes.

tables: scarr,
        spfli.

data: wa_scarr type scarr.

data: r_companies type ref to lcl_companies.

selection-screen begin of block filter with frame title text-001.
select-options s_carrid for wa_scarr-carrid.
selection-screen end of block filter.

 

Selection Screen

Vamos criar também, duas telas (as quais nomeei de “0100” e “0200”, que serão utilizadas na exibição dos ALV’s.

Screens

No include criado, segue declaração da interface:

interface: zif_data_reader.
  types: ty_scarr type range of scarr-carrid.

  methods: read_data
              importing
                im_scarr type ty_scarr,

           generate_alv
              importing
                im_structure  type char30.

endinterface.             "zif_data_reader

 

Observe que temos os métodos read_data e generate_alv, que foram declarados na interface, porque serão utilizados em ambos os relatórios.

Agora vamos à definição da classe lcl_companies, que será responsável pela leitura dos dados das cias aéreas.

*----------------------------------------------------------------------*
*       CLASS lcl_companies DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class lcl_companies definition.
  public section.
    interfaces zif_data_reader.

    methods: constructor,

             set_toolbar
                for event toolbar of cl_gui_alv_grid
                importing e_object,

             get_ucomm
                for event user_command of cl_gui_alv_grid
                importing e_ucomm,

             refresh_table
                for event after_user_command
                of cl_gui_alv_grid.

  private section.
    data: r_grid        type ref to cl_gui_alv_grid,
          r_connections type ref to lcl_connections.

    data: gt_scarr      type table of scarr,
          gt_exc_button type ui_functions.

endclass.                                "lcl_companies DEFINITION

Observe que a classe tem como atributos privados:

  • Objeto r_grid (para criação do grid do alv)
  • Objeto r_connections para referência e criação dos objetos referentes aos vôos (conexões)
  • Tabela gt_scarr, contendo as informações da(s) cia(s) aérea;
  • Tabela de botões a serem excluídos

Através da keyword “INTERFACES”, declaramos a utilização da interface zif_data_reader, com isso, na classe lcl_companies devemos implementar todos os métodos contemplados na interface.

Seguem Implementações:

  • Constructor: será responsável pela criação do grid do alv, registrar os eventos a serem utilizados e determinar os botões a serem excluídos.
method: constructor.
    create object r_grid
      exporting
        i_parent          = cl_gui_container=>screen0
      exceptions
        error_cntl_create = 1
        error_cntl_init   = 2
        error_cntl_link   = 3
        error_dp_create   = 4
        others            = 5.

    if sy-subrc ne 0.
      message id sy-msgid type sy-msgty number sy-msgno
                 with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    endif.

*--------------------------------------------------------------------*
* Registra os eventos a serem utilizados
*--------------------------------------------------------------------*
    set handler get_ucomm     for r_grid.
    set handler set_toolbar   for r_grid.
    set handler refresh_table for r_grid.
*--------------------------------------------------------------------*
* Informa os botões a serem excluídos
*--------------------------------------------------------------------*
    append cl_gui_alv_grid=>mc_fc_graph to gt_exc_button.

  endmethod.                              "constructor

Nota: quando informamos “cl_gui_container=>screen0” no parâmetro “i_parent” não temos a necessidade de declarar um container (recomenda-se gerar o ALV sem container apenas em casos de real necessidade, pois esteticamente o programa será prejudicado, visto que o ALV ocupará a tela inteira).

Para entender melhor, vá até a transação SE24, na classe CL_GUI_ALV_GRID.

Na aba “métodos” você encontrará o “constructor”, clique em “Parâmetros” e verá que o parâmetro “i_parent” é do tipo “CL_GUI_CONTAINER”.

Parameter - "I_PARENT"

Clicando em CL_GUI_CONTAINER, na aba atributos, você encontrará “Screen0”.

Attribute - Screen0

Ou seja, o atributo “Screen0” utiliza a tela como nosso container.

Entendendo a exclusão de botões

Novamente na transação SE24, consulte o método “SET_TABLE_FOR_FIRST_DISPLAY”.

Ao clicar em “parâmetros” você encontrará o “IT_TOOLBAR_EXCLUDING”, de tipo “UI_FUNCTIONS”.

Parameter - toolbar_excluding

Vamos agora para a aba de atributos da classe, onde encontraremos os botões.

CL_GUI_ALV_GRID - Botões

Desta forma, basta inserirmos os botões a serem excluídos na tabela declarada, e informá-la no momento em que chamarmos o método “SET_TABLE_FOR_FIRST_DISPLAY” (neste exercício, excluí apenas o botão utilizado para geração de gráficos).

O próximo passo é selecionarmos os dados da tabela SCARR conforme dados da tela de seleção.

  • zif_data_reader~read_data

Observe que quando implementamos um método de uma interface, este é denominado: nome_da_interface~nome_ do_ método.

method: zif_data_reader~read_data.

      clear gt_scarr.
    refresh gt_scarr.

    select *
      from scarr
      into table gt_scarr
      where carrid in im_scarr[].

    if sy-subrc ne 0.
      message: text-002 type 'E'.

    else.
      call method me->zif_data_reader~generate_alv
        exporting
          im_structure = 'SCARR'.
    endif.
  endmethod.                               "zif_data_reader~read_data

Neste método, selecionamos os dados conforme critérios informados na tela de seleção.

Quando sy-subrc não é “0” (valores não encontrados), exibimos uma mensagem de erro, caso contrário, chamamos o  método “zif_data_reader~generate_alv”, vide implementação abaixo:

method: zif_data_reader~generate_alv.

    call method r_grid->set_table_for_first_display
      exporting
        i_structure_name              = im_structure
        it_toolbar_excluding          = gt_exc_button
      changing
        it_outtab                     = gt_scarr
      exceptions
        invalid_parameter_combination = 1
        program_error                 = 2
        too_many_lines                = 3
        others                        = 4.

    if sy-subrc NE 0.
      message id sy-msgid type sy-msgty number sy-msgno
                 with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    endif.

    call screen 100.
  endmethod.                               "zif_data_reader~generate_alv

Com os dados selecionados e exibidos na tela, precisamos incluir o botão para visualizarmos as conexões das cias aéreas.

  • set_toolbar – será chamado cada vez que o evento “TOOLBAR” (registrado no constructor!) for disparado pela classe CL_GUI_ALV_GRID.

Consultando os parâmetros deste evento, na transação SE24, podemos verificar que ele exporta o parâmetro “E_OBJECT”, de tipo  “CL_ALV_EVENT_TOOLBAR_SET”.

Nos atributos desta classe, encontraremos o “MT_TOOLBAR”, de tipo “TTB_BUTTON”, finalmente tipo “STB_BUTTON”, onde estão as informações que precisamos.

Veja a implementação do método “set_toolbar”:

method: set_toolbar.
    data: wa_toolbar type stb_button.
    case sy-dynnr.
      when '1000'.

        clear wa_toolbar.
        wa_toolbar-butn_type = 3.               "Separador
        append wa_toolbar to e_object->mt_toolbar.

        clear wa_toolbar.
        wa_toolbar-function  = '100_CONN'.
        wa_toolbar-icon      = icon_object_folder.
        wa_toolbar-quickinfo = 'Visualiza Conexões'.
        wa_toolbar-butn_type = 0.              "Botão Normal
        wa_toolbar-text      = 'Visualizar Conexões'.
        append wa_toolbar to e_object->mt_toolbar.

      when '0100'.
        clear wa_toolbar.
        wa_toolbar-butn_type = 3.              "Separador
        append wa_toolbar to e_object->mt_toolbar.

        clear wa_toolbar.
        wa_toolbar-function    = '100_CONN'.
        wa_toolbar-icon        = icon_object_folder.
        wa_toolbar-quickinfo   = 'Visualiza Conexões'.
        wa_toolbar-butn_type   = 0.              "Botão normal
        wa_toolbar-text        = 'Visualizar Conexões'.
        if sy-ucomm            = 'BACK'.
          wa_toolbar-disabled  = ' '.
        else.
          wa_toolbar-disabled  = 'X'.
        endif.
        append wa_toolbar to e_object->mt_toolbar.
      when others.
    endcase.

  endmethod.                                 "set_toolbar

Observe que temos duas tratativa diferentes para o botão: quando estamos na tela 1000 (chamando a tela 0100, que contém os dados das cias aéreas), quando deve estar habilitado, e quando estamos na tela 0100 (chamando a tela 0200, que conterá os dados das conexões), quando o botão deve estar desabilitado.

Para melhor compreensão, faça este exercício, coloque um BREAK-POINT no método e debugue! Assim você entenderá os momentos em que ele é chamado e sua tratativa!

Dica: lembre-se de utilizar text-symbols. Na implementação acima, escrevi as informações diretamente no código para facilitar o entendimento!

Nota: informamos o ícone: icon_object_folder, este nome é encontrado na tabela “ICON”, porém, algumas vezes precisamos do parâmetro “internal” quando trabalhamos com ícones. Este pode ser encontrado no programa “SHOWICON”.

  • get_ucomm – é através deste que nosso botão funcionará:
method: get_ucomm.
    data: lv_row   type lvc_s_row,
          wa_scarr like line of gt_scarr.

    data: r_cias  type range of scarr-carrid,
          wa_cias like line of r_cias.

    if e_ucomm eq '100_CONN'.
      call method r_grid->get_current_cell
        importing
          es_row_id = lv_row.

*--------------------------------------------------------------------*
      if lv_row is initial.
        message: text-003 type 'I'.
*--------------------------------------------------------------------*
      else.
        read table gt_scarr into wa_scarr index lv_row-index.

        clear wa_cias.
        wa_cias-sign    = 'I'.
        wa_cias-option  = 'EQ'.
        wa_cias-low     = wa_scarr-carrid.
*        wa_cias-high   = ' ' .
        append wa_cias to r_cias.

        create object r_connections
          exporting
            im_grid = r_grid.

        call method r_connections->zif_data_reader~read_data
          exporting
            im_scarr = r_cias.
*--------------------------------------------------------------------*
      endif.
    endif.
  endmethod.                             "get_ucomm

Lembrando que este método será chamado cada vez que o evento “user_command” for disparado (conforme sua declaração), devemos verificar se o “sy-ucomm” é “100_CONN”, ou seja, se nosso botão foi “clicado”.

Com isso, chamamos o método get_current_cel para verificarmos qual foi a linha selecionada.

Verificamos em nossa tabela qual é  cia aérea referente a linha selecionada, para então criarmos o objeto referente à classe das conexões e ler seus dados.

Observe que utilizamos o range r_cias para informar a cia aérea. O range é como se fosse o select-options, por isso precisamos informar:

  • sign      = “I” (Include)
  • Option = “EQ” (Equal, porque informaremos uma única cia aérea.
  • Low      = wa_scarr-carrid (cia aérea da linha selecionada).
  • High    = Não precisamos informar, pois utilizamos “option = EQ”.

Para a classe lcl_companies, ainda precisamos implementar o método:

  • refresh_table:
method refresh_table.

    check sy-dynnr = 100 and
          sy-ucomm = 'BACK'.

    call method me->zif_data_reader~generate_alv
      exporting
        im_structure = 'SCARR'.

  endmethod.                              "refresh_table

Este método será chamado após o user_command.

É necessário para atualizar as informações do grid quando voltamos da tela 200 para a tela 100. Sem ele, os dados das conexões permanecem na tela.

Para melhor compreensão, debugue!

Finalmente, vamos à declaração da classe “lcl_connections”.

*----------------------------------------------------------------------*
*       CLASS lcl_connections DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class lcl_connections definition.
  public section.
    interfaces: zif_data_reader.

    methods: constructor
                importing
                  im_grid type ref to cl_gui_alv_grid.

  private section.
    data: gt_spfli       type standard table of spfli,
          gt_exc_button  type ui_functions,
          gv_carrid      type scarr-carrid.

    data: r_grid_conn type ref to cl_gui_alv_grid.

endclass.                              "lcl_connections DEFINITION

Como podem ver, esta é uma classe bem simples. Verifiquem sua implementação:

class lcl_connections implementation.
  method: constructor.
    r_grid_conn = im_grid.
  endmethod.                              "constructor

  method: zif_data_reader~read_data.
    clear   gt_spfli.
    refresh gt_spfli.

    select *
      from spfli
      into table gt_spfli
      where carrid in im_scarr.

    if sy-subrc = 0.
      call method me->zif_data_reader~generate_alv
        exporting
          im_structure = 'SPFLI'.
    else.
      message: text-004 type 'I'.
    endif.

  endmethod.                    "zif_data_reader~read_data

  method: zif_data_reader~generate_alv.
    data: wa_exclude like line of gt_exc_button.

    call method r_grid_conn->set_table_for_first_display
      exporting
        i_structure_name              = im_structure
      changing
        it_outtab                     = gt_spfli
      exceptions
        invalid_parameter_combination = 1
        program_error                 = 2
        too_many_lines                = 3
        others                        = 4.

    call screen 200.

  endmethod.                    "zif_data_reader~generate_alv

endclass.

Observe que o constructor é responsável apenas por “importar” o grid gerado na classe lcl_companies.

Vejam abaixo o resultado do nosso exercício:

Tela 0100 - Cias aéreas

Tela 0100, com os as informações das cias aéreas e botão habilitado.

Observe que não há o botão responsável pela geração de gráficos.

Tela 0200 - Conexões

Tela 0200, com informações das conexões e botão desabilitado.

E então, gostaram? baixe o código (Z_ALV_OO ), estude e nos envie suas dúvidas e sugestões!

Veja neste post como utilizar o arquivo.

Você pode gostar...

11 Resultados

  1. Edson disse:

    Olá, baixei o código mas qnd uso o SAP LINK para importar, não vem o Z_CLASSES. Poderia conferir? Obrigado

  2. Vagnão disse:

    Oi Claudia. Gostei do seu exercício. Muito bem elaborado e bem explicado.
    Eu tiraria a declaração do Tables… não faz sentido.

    Outra coisa: cuidado com a informação de que report com Write é “dinossáurico”… Ok, é ultrapassado, mas muita coisa em um sistema SAP ainda depende dele. Sem esse tipo de report, como gerar Livros Fiscais?

    Abraço
    Vagnão

    • Claudia Andressa disse:

      Olá Vagnão!

      Obrigada pelos elogios e pelas dicas. Realmente “bobeei” declarando o “tables”. Eu deveria ter criado uma estrutura e declarado o Select-Options para esta estrutura, de forma a não precisar utilizar o “tables”.

      Quanto aos relatórios write, trabalhei com uma transação que imprimia relatórios de balancete. Meu Deus….quanto trabalho! :/

      Obrigada,

      Claudia.

  3. Vagnão disse:

    Alguém ainda vai me dizer… livros fiscais? SPED?
    ok!

  4. Mauro Luiz Junior disse:

    Parabéns, Excelênte Post.

  5. Miguel Motta disse:

    Parabéns, tenho certeza que ainda vou procurar esse post várias vezes para consulta!

  6. Humberto disse:

    Estou implementando um ALV orientado a objeto. Gostaria de capturar o evento de um clique na linha, porém não consigo capturar esse evento ( já tentei pelo LEFT_CLICK_DESIGN, LEFT_CLICK_RUN) só que não dispara. Desde já obrigado

  7. Claudia Andressa disse:

    Bom dia Humberto!

    Para registrar um único click em uma célula de um resultado do ALV, geralmente utilizamoso hotspot (no field cat e o evento “HOTSPOT_CLICK”), ou então, utilizamos o duplo clique na linha ou célula do ALV, através do evento “DOUBLE_CLICK”.

    Dê uma olhada nestes eventos, e qualquer dúvida, entre em contato novamente!

    Boa sorte!

    Claudia.