sexta-feira, 29 de novembro de 2013

Search Help com valores do texto do dominio e não o valor do código - Utilizando EXIT

Situação: Criado um search help no qual um dos campos é um dominio com valores, nesse caso aparece o valor do chave do valor e não a descrição.

Objetivo: Ao invés de mostrar a chave mostrar também a descrição da chave.

Comentário: é um negócio bem chato de fazer. Tem que usar a função de exit no search help, e dentro da função vem uma estrutura de linha com os campos do search help concatenados, o que deve ser feito é ver a posição da coluna onde vai ser inserido o texto e colocar o texto no meio, mas aquilo que vc vê nem sempre é o que parece, tentei fazer os cálculos para colocar uma coluna de texto no meio, mas nunca dava certo, até que debuguei e reparei que internamente ele faz uma leitura binária/hexa que distorce a posição que vc acha que é, e tem outro detalhe na primeira vez que vc executa o matchcode a posição vem de um jeito, se nessa mesma tela vc faz uma segunda busca, a posição se altera, nesse caso tem que fazer uma validação para quando for a primeira visualização e outra para as sequências de busca sem fechar a tela.

Enfim.. eu fiz um exemplo e montei um cálculo para colocar o campo no lugar certo.

Criei uma tabela ZTSZK3 com o campo ZSZK_F1 com tipo do elemento de tados ZSZK1_F1 do dominio ZSZK_F1

- Coloque 3 valores para o dominio conforme abaixo
- Criei a ajuda de pesquisa para o campo CODIGO da tabela
- E a função ZFSZK_SH para exit de ajuda de pesquisa que vou explicar mais abaixo.

- E criei um programa para testar o search help que criei. Nesse caso está mostrando o resultado da ajuda de forma standard com o valor do dominio mas sem a descrição.


- A idéia é que eu tenho q modificar a estrutura que o standard monta, modificando o tamanho do campo do dominio e a posição do próximo campo somando o tamanho desse campo que vou modificar.
- Para isso tem q debugar e pegar primeiro os valores standard montado na ajuda de pesquisa.
- Nessa tela abaixo as propriedades de cada campo da estrutura da ajuda

- Abaixo os valores da tabela.





- Então.. que valores pegar ???
- copie os seguinte valores da tabela SHLP-FIEDDESCR, o nome do campo, a posição, o offset e o tamanho da saída (outputlen).

No meu caso ficou da seguinte forma.
- O Valor original é o valor que o standard trouxe na entrada da função,  um detalhe importante é ordenar os campos pela posição senão não dá certo.
- Como tinha dito, tenho 2 momentos no search help,  a primeira é quando clica no matchcode do campo e o segundo depois que a tela da ajuda já está aberta e se deseja fazer um filtro em cima dos valores apresentados. Eu separei abaixo como "VALOR NA PRIMEIRA EXECUÇÃO" e "VALOR COM BUSCA NA MESMA TELA".
- Coloquei a coluna OFFSET modificado para mostra o valor que tem q ser modiificado no OFFSET do campo seguinte a minha modificação. No meu caso vou modificar o tamanho de saída do meu camp ZSZK_F1 de 1 para 40, nesse caso eu tenho q modificar o OFFSET do próximo campo para depois do tamanho que quero modificar.
- O cálculo para saber o OFFSET do próximo campo fica assim conforme mostrei na fórmula. Seria o OUTPUTLEN do campo modificado no meu caso 40 + o OFFSET do campo seguinte 14 que é igual a 54 nisso soma-se + 2, que vai dar os 56.
- No caso da segunda busca o mesmo cálculo só que com valores diferentes que vai dar 88.



- E abaixo o código da função. Como é um código na visualização, coloquei um CHECK CALLCONTROL-STEP = 'DISP'.
- Depois eu dou um LOOP nas características de cada campo da estrutura do search help, modificando para o campo ZSZK_F1 o outputlen e o inlen = 40, e para o campo seguinte ERDAT eu coloco o novo OFFSET para 56, observem que tem um IF se o OFFSET for menor que 15, eu considerei que quando é menor que 15 é a primeira chamada e o ELSE a segunda chamada que está com OFFSET 88.
- Tenho também uma variável L_OFFSET_AUX que determina a posição do valor do dominio ZSZK_F1.
- Depois tem o LOOP dos valores da RECORD_TAB que vou alterar.
- Utilizo a função DOMAIN_VALUE_GET para pegar a descrição do dominio.
- Basicamente essa lógica pega a parte da linha do começo até o valor do dominio e inclui o texto e coloca o resto do texto na frente.

FUNCTION ZFSZK_SH.
*"----------------------------------------------------------------------
*"*"Interface local:
*"  TABLES
*"      SHLP_TAB TYPE  SHLP_DESCT
*"      RECORD_TAB STRUCTURE  SEAHLPRES
*"  CHANGING
*"     VALUE(SHLP) TYPE  SHLP_DESCR
*"     VALUE(CALLCONTROL) LIKE  DDSHF4CTRL STRUCTURE  DDSHF4CTRL
*"----------------------------------------------------------------------

  DATA: ls_fielddescr TYPE dfies, "loc str for shlp-fielddescr
        ls_selopt TYPE ddshselopt.  "loc str for shlp-selopt
*Local structure for itab record_tab
  DATA: BEGIN OF ls_record.
          INCLUDE STRUCTURE seahlpres.
  DATA: END OF ls_record.

  DATA: ls_start TYPE string,
        ls_end TYPE string.

  DATA: l_ddtext TYPE dd07v-ddtext,
        l_domvalue TYPE dd07v-domvalue_l,
        l_offset TYPE i,
        l_offset_aux TYPE i.

  CHECK callcontrol-step = 'DISP'.

  CLEAR l_offset.

  LOOP AT shlp-fielddescr INTO ls_fielddescr.

    CASE ls_fielddescr-fieldname.
      WHEN 'ZSZK_F1'.
        ls_fielddescr-intlen = ls_fielddescr-outputlen = 40.
        MODIFY shlp-fielddescr FROM ls_fielddescr INDEX sy-tabix.
      WHEN 'ERDAT'.
        IF ls_fielddescr-offset < 15.

                        l_offset_aux = 6.
          ls_fielddescr-offset = 56.
        ELSE.

          l_offset_aux = 3.
          ls_fielddescr-offset = 88.
        ENDIF.

        MODIFY shlp-fielddescr FROM ls_fielddescr INDEX sy-tabix.
    ENDCASE.

  ENDLOOP.

 

  LOOP AT record_tab INTO ls_record.
    l_offset = l_offset_aux.

    l_domvalue = ls_record-string+l_offset(1).

    CALL FUNCTION 'DOMAIN_VALUE_GET'
      EXPORTING
        i_domname  = 'ZSZK_F1'
        i_domvalue = l_domvalue
      IMPORTING
        e_ddtext   = l_ddtext
      EXCEPTIONS
        not_exist  = 1
        OTHERS     = 2.

    IF sy-subrc <> 0.
      CLEAR l_ddtext.
    ENDIF.

    ADD 1 TO l_offset.
    ls_start = ls_record-string+0(l_offset).
    ls_end = ls_record-string+l_offset(*).
    CLEAR: ls_record-string.
    ls_record-string+0(l_offset) = ls_start.
    IF l_ddtext IS NOT INITIAL.
      ls_record-string+l_offset(1) = '-'.
    ENDIF.
    ADD 1 TO l_offset.
    ls_record-string+l_offset(20) = l_ddtext(20).
    ADD 20 TO l_offset.
    ls_record-string+l_offset(*) = ls_end.
    MODIFY record_tab FROM ls_record.
    CLEAR: ls_record,ls_start,ls_end.

  ENDLOOP.

ENDFUNCTION.


Resultado das 2 telas.
 - Primeira chamada da ajuda


- Tela de filtro da ajuda de pesquisa 

quinta-feira, 28 de novembro de 2013

Magic /H - para debug em tela tipo popup

Essa é uma dica muito boa para DEBUG. Em geral quando estamos no programa de tela e queremos dar um /H para debug, simplesmente colocamos um /H onde se coloca a transação e damos um <enter> para ativarmos o DEBUG.


Só que quando temos uma janela tipo popup tipo mensagem ou qualquer outra janela tipo modal. Essa janela não tem um campo para digital o /H

Nesse caso crie um arquivo tipo texto (.txt) e coloque o código abaixo e salve com um nome tipo magic_h.txt

[function]
command=/h
type=systemcommand


E quando aparecer uma janela tipo POPUP, simplesmente arraste essa arquivo texto do Gerenciador de arquivos para a janela do popup, quando vc soltar em cima da janela já vai dar a mensagem embaixo de DEBUG ativado.



Criação de tabela dinâmica 2 formas

- Criação de tabela dinâmica normal.
- Declara um objeto tipo DATA e depois cria o objeto referenciado a estrutura do tipo de tabela que se deseja.



DATA: t_out_tab     TYPE REF TO data.
DATA: wa_out_tab  TYPE REF TO data.

 FIELD-SYMBOLS: <fs_out_tab> TYPE STANDARD TABLE,
               <fsw_out_tab> TYPE any,


Data: l_tabname TYPE tabname.

    CASE 'X'.
      WHEN p_r1.
               l_tabname = 'Z_STRUC1'.
            WHEN p_r2.
       l_tabname = 'Z_STRUC2'.
    ENDCASE.

   "Tabela dinâmica
    CREATE DATA t_out_tab TYPE TABLE OF (l_tabname).
    ASSIGN t_out_tab->* TO <fs_out_tab>.


    "Estrutura da tabela dinâmica
    CREATE DATA wa_out_tab TYPE (l_tabname).    ASSIGN wa_out_tab->* TO <fsw_out_tab>.



- Essa forma criando com o TYPE TABLE OF como demonstrei acima funciona bem, mas eu tive um problema criando dessa forma uma vez que tive que montar uma função para fazer cálculo de condição para SD, nesse caso minha função acessava uma tabela que tinha relacionado uma lista de tabelas de valores de acordo com a condição selecionada, nisso o programa fazia um LOOP e fazia um teste para cada tabela criando tabela dinâmica. Se o número de tabelas a se acessada era muito grande um DUMP ocorria e na busca de uma solução achei a correção usando em vez do TYPE TABLE OF criar com TYPE HANDLE, conforme exemplo abaixo.

FIELD-SYMBOLS: <ft_dyntable> TYPE STANDARD TABLE,
               <fw_dyntable>,
               <fw_fldval> TYPE ANY,
               <fw_struct>.

DATA: t_newtable TYPE REF TO data,
      t_newline  TYPE REF TO data,
      t_fldcat   TYPE lvc_t_fcat.


*&---------------------------------------------------------------------*
*&      Form  CRIAR_TAB_DINAMICA
*&---------------------------------------------------------------------*
FORM criar_tab_dinamica USING p_tabname.

  DATA: lwcl_struc       TYPE REF TO cl_abap_structdescr,
        lwcl_struc_table TYPE REF TO cl_abap_tabledescr.

  DATA: lw_fldcat TYPE lvc_s_fcat.

  UNASSIGN <ft_dyntable>.
  UNASSIGN <fw_dyntable>.

  REFRESH t_fldcat.
  CLEAR: t_fldcat, t_newtable.

  LOOP AT t_dd03l WHERE tabname eq p_tabname.
    MOVE-CORRESPONDING t_dd03l TO lw_fldcat.
    APPEND lw_fldcat TO t_fldcat.
  ENDLOOP.

  CHECK sy-subrc = 0.

  PERFORM create_internal_structure TABLES t_fldcat
                                  CHANGING lwcl_struc
                                           lwcl_struc_table.

*Create references to dyn internal table and work area
  CREATE DATA t_newline TYPE HANDLE lwcl_struc_table.
  ASSIGN t_newline->* TO <ft_dyntable>.
  CREATE DATA t_newline TYPE HANDLE lwcl_struc.
  ASSIGN t_newline->* TO <fw_dyntable>.


ENDFORM.                    " CRIAR_TAB_DINAMICA

 
*&--------------------------------------------------------------*
*&      Form  CREATE_INTERNAL_STRUCTURE
*&--------------------------------------------------------------*
*       Create internal structure
*---------------------------------------------------------------*
FORM create_internal_structure TABLES p_fcattab TYPE lvc_t_fcat
                  CHANGING p_struc TYPE REF TO cl_abap_structdescr
                           p_struc_table TYPE REF TO cl_abap_tabledescr.

  DATA: ls_comp TYPE abap_componentdescr,
        lw_fcat TYPE lvc_s_fcat,
        lt_comp TYPE cl_abap_structdescr=>component_table,
        lv_integer TYPE i.

  LOOP AT p_fcattab INTO lw_fcat.
    ls_comp-name = lw_fcat-fieldname.
    lv_integer = lw_fcat-intlen.
    ls_comp-type = cl_abap_elemdescr=>get_c( lv_integer ).
    APPEND ls_comp TO lt_comp.
  ENDLOOP.

  TRY.
      p_struc =
              cl_abap_structdescr=>create( p_components = lt_comp ).
    CATCH cx_sy_struct_creation .
  ENDTRY.

  TRY.
      p_struc_table = cl_abap_tabledescr=>create( p_struc ).
    CATCH cx_sy_table_creation .
  ENDTRY.

ENDFORM.                    " CREATE_INTERNAL_STRUCTURE



================  10/11/2022
CALL METHOD cl_alv_table_create=>create_dynamic_table
EXPORTING
i_style_table             = 
‘X’
it_fieldcatalog           = gt_dyn_fcat
IMPORTING
ep_table                  = gt_dyn_table
EXCEPTIONS
generate_subpool_dir_full = 
1
OTHERS                    = 2.

IF sy-subrc EQ 0.
* Assign the new table to field symbol
ASSIGN gt_dyn_table->* TO <gfs_dyn_table>.
* Create dynamic work area for the dynamic table
CREATE DATA gw_line LIKE LINE OF <gfs_dyn_table>.
CREATE DATA gw_line1 LIKE LINE OF <gfs_dyn_table>.
ASSIGN gw_line->* TO <gfs_line>.
ASSIGN gw_line1->* TO <gfs_line1>.
ENDIF

===========================

DATA lr_table TYPE REF TO data.
DATA lr_structdescr TYPE REF TO cl_abap_structdescr.
lr_structdescr ?= cl_abap_typedescr=>describe_by_data_ref( ref #( ls_structure ) ).
DATA lr_tabledescr TYPE REF TO cl_abap_tabledescr.
lr_tabledescr ?= cl_abap_tabledescr=>create( p_line_type = lr_structdescr ).

CREATE DATA lr_table TYPE HANDLE lr_tabledescr.
ASSIGN lr_table->* TO FIELD-SYMBOL(<ft_table>).


============================================================

* Create a new Line with the same structure of the table.
  ASSIGN lo_table_mon->* TO <lt_table_mon>.

  LOOP AT <l_table4> ASSIGNING FIELD-SYMBOL(<ls_table4>).

    APPEND INITIAL LINE TO <lt_table_mon> ASSIGNING FIELD-SYMBOL(<ls_table_mon>).

    DATA(lt_comp) = CAST cl_abap_structdescr(
                cl_abap_typedescr=>describe_by_data( <ls_table4> ) )->components.

    LOOP AT lt_comp ASSIGNING FIELD-SYMBOL(<ls_comp>).

      CHECK <ls_comp>-name NS 'STYLE'.

      IF <ls_comp>-name(5) <> |W{ v_month1(4) }| AND
         <ls_comp>-name(5) <> |W{ v_monthn(4) }| AND
         <ls_comp>-name(5) <> |W{ v_week1(4) }|.

        ASSIGN COMPONENT <ls_comp>-name OF STRUCTURE <ls_table4> TO <ls_value_w>.
        ASSIGN COMPONENT <ls_comp>-name OF STRUCTURE <ls_table_mon> TO <ls_value_m>.
        <ls_value_m> = <ls_value_w>.

      ELSE.

        ls_mon_str = <ls_comp>-name.
        DATA(l_month) = |{ ls_mon_str-m }{ ls_mon_str-year }{ ls_mon_str-month }|.
        ASSIGN COMPONENT <ls_comp>-name OF STRUCTURE <ls_table4> TO <ls_value_w>.
        ASSIGN COMPONENT l_month OF STRUCTURE <ls_table_mon> TO <ls_value_m>.
        IF sy-subrc = 0.
          ADD <ls_value_w> TO <ls_value_m>.
        ENDIF.

      ENDIF.

    ENDLOOP.

  ENDLOOP.




quarta-feira, 27 de novembro de 2013

Executando BAPI pela SE37 - Gravando ordem de venda, pedido de compra, etc....

Executar uma função na SE37, basta colocar a função e testar.
Em alguns casos como nas bapi's de criação de ordem de venda (BAPI_SALESORDER_CREATEFROMDAT1) , pedido de compra (BAPI_PO_CREATE), etc. quando vc executa em modo de teste normal, a BAPI é executada inclusive gera o número da BAPI executada, só que se vc entrar na transação como ME23N para visualizar um pedido de compra, um erro é gerado falando que o pedido não foi criado.

Para solucionar esse problema, vc deve montar uma sequência de teste com o nome da BAPI + a BAPI - BAPI_TRANSACTION_COMMIT

Como no exemplo abaixo.
Depois de executar a transação SE37 - ir no menu "Módulo de função"-> Testar->Sequencia de teste
- Vai aparecer a tela abaixo para colocar a sequência do teste
- No meu caso a BAPI_PO_CREATE e depois coloquei a BAPI_TRANSACTION_COMMIT
- Clica em executar
- Depois vai entrar na tela para preencher os parâmetros da BAPI, ou vc pega os dados do diretório de testes e executa a bapi. Depois de gerado quando vc clicar no botão voltar(BACK-F3) automaticamente a bapi em sequência (no caso a BAPI_TRANSACTION_COMMIT) será executado, e é só executar novamente que vai salvar no caso o número do pedido de compra, se consultar na ME23N vai aparecer o pedido com os parâmetros criados.

- OBSERVAÇÃO: Executar dessa forma em sequência quando tiver testado somente a bapi com os parâmetros com sucesso, pois quando vc executa em sequência independente se der erro ou não quando vc voltar a tela (F3) a segunda função será executada e nesse modo não dá para salvar no diretório de teste. Então a dica é como nesse exemplo, execute somente a BAPI_PO_CREATE, e teste-a até que gere o número do pedido sem erro, e depois execute em sequência para salvar no banco de dados.

Ponto Enhancement Point para alteração de valores da Nota Fiscal (BR) na MIRO

Como bem sabemos a SAP disponibiliza poucos pontos para alterações do standard na geração de Nota Fiscal, tudo bem que com a entrada do ECC 6.0 a disponibilização do Enhancements Points foi a salvação da lavoura para muitos abaps, pq geralmente as BADIs, EXITS, BTE, nunca estavam onde queríamos alterar. Como sou das antigas eu sou do principio que esses Enhancements devem ser usados com moderação e com uma lógica precisa do ponto onde deve ser incluído, pois vejo muita gente colocando essas ampliações em duplicidade em pontos diferentes para um mesmo processo. Então sejamos mais especialistas e antes de usar um ponto de ampliação explicito que seja com muito bom senso, e antes de mais nada utilizem os métodos standard para tal, e em último caso usar essa ampliação.

No caso em questão vou dar a dica de um ponto de ampliação muito interessante para alteração de valores da Nota Fiscal, nesse caso na entrada fiscal MIRO. Eu utilizei muitas vezes pq em muitas situações as empresas por alguma situação especifica querem que na NF tenha um valor diferenciado para os impostos nos campos de base, valor de imposto, outras bases e bases excluídas.

O enhancement spot é standard então já está localizado no meio da rotina da geração da NF da MIRO.

No include LJ1BIF01
Dentro do Form NF_OBJECT_ADD
É só criar a a implementação da ampliação sobre spot ES_SAPLJ1BI


ALV - Mostrar campo como Listview

- Objetivo: em um campo com valores de dominio, ao invés do usuário clicar no matchcode mostrar um listview.
- No caso um ALV editável.

No estrutura LVC_S_FCAT preencher os campos:
DRDN_HNDL =   <colocar um identificador para o handle do listview, caso vc queira colocar mais de um listview tem que colocar um outro identificador, pode ser um contador tipo handle = 1 para o primeiro listview handle = 2 para o segundo e assim por diante. >

DRDN_ALIAS = 'X'

Como é editável não esquecer de preencher o campo EDIT = 'X'

- Depois de criar o objeto ALV ( método SET_TABLE_FOR_FIRST_DISPLAY), utilizar o método SET_DROP_DOWN_TABLE para preencher o campo listview. Conforme código exemplo.


  DATA: lt_dral  TYPE lvc_t_dral,
        lw_dral  TYPE lvc_s_dral,
        lw_dd07t TYPE dd07t,
        lw_z404t TYPE z404t.

  "Valores do status
  SELECT *
    INTO lw_dd07t
    FROM dd07t
   WHERE domname    EQ 'ZXX_STATUS_LOTE'
     AND ddlanguage EQ sy-langu.

    CLEAR lw_dral.
    lw_dral-handle = '1'.
    IF lw_dd07t-domvalue_l = space.
      CONCATENATE '''''' '-' lw_dd07t-ddtext
            INTO lw_dral-value SEPARATED BY space.
    ELSE.
      CONCATENATE lw_dd07t-domvalue_l '-' lw_dd07t-ddtext
             INTO lw_dral-value SEPARATED BY space.
    ENDIF.

    lw_dral-int_value =  lw_dd07t-domvalue_l.
    APPEND lw_dral TO lt_dral.

  ENDSELECT.

**Setting the Drop down table for Reason Code
  CALL METHOD gcl_grid->set_drop_down_table
    EXPORTING
      it_drop_down_alias = lt_dral.

BADI - MM - Para habilitar/desabilitar campos na tela da ordem de compra ME21(N), ME22(N)

- Situação, desabilitar campos nas transações de ordem de compra que devem ser habilitadas ou não para preenchimento por alguma condição especifica.

- Criar implementação para BADI ME_PROCESS_PO_CUST 
- No meu exemplo vou desabilitar o campo Utilização do Material (J_1BMATUSE) referente ao item do pedido da tabela EKPO

- Depois de criado a implementação, utilizar o método
FIELDSELECTION_ITEM

- Exemplo de código.


METHOD if_ex_me_process_po_cust~fieldselection_item .

 TYPE-POOLS:
    mmmfd.


  DATA: ls_item  TYPE mepoitem.

  DATA: l_campo1 TYPE char40 VALUE '(SAPLMEPO)AKTYP'.

  FIELD-SYMBOLS:
    <fs> LIKE LINE OF ch_fieldselection,
    <ak> TYPE c.

  ASSIGN (l_campo1) TO <ak>.
  IF sy-subrc = 0.
    CHECK <ak> <> 'A'.  "Verifica se não é exibição
  ELSE.
    EXIT.
  ENDIF.

  READ TABLE ch_fieldselection ASSIGNING <fs>
                          WITH TABLE KEY metafield = mmmfd_mat_usage.
  IF sy-subrc IS INITIAL.
    <fs>-fieldstatus = '.'. " Display
  ENDIF.


ENDMETHOD.    


- O TYPE-POOLS    MMMFD, é importante pois cada campo tem um código constante que é definido na tabela de retorno CH_FIELDSELECTION
- No caso o campo de utilização do material é o MMMFD_MAT_USAGE
O campo FIELDSTATUS que define como o campo deve ser mostrado. Podem ser utilizados os seguintes status.
+ (sinal de soma) = Campo obrigatório
 . (ponto)             = Campo de entrada

* (Asterisco)      = Campo somente para visualização
 - (sinal de menos) = Campo não é visualizado

ALV OO - Editável valores modificados

- No ALV existe um parâmetro no FIELDCAT que indica se um determinado campo é editável ou
não.
SLIS_FIELDCAT_ALV-EDIT  ou
LVC_S_FCAT-EDIT

Se marcados esse campos o campo fica editável, mas no caso de uma coluna de valor editável e uma outra coluna que faz o cálculo com esse campo, no momento q vc alterar o valor e dar <ENTER> o campo não se atualiza automaticamente, somente se vc colocar um botão para recalcular os valores eles serão atualizados.

Utilizando o evento do ALV objeto DATA_CHANGED os valores podem ser atualizados ao dar <ENTER> sem a necessidade de uma segunda ação do usuário.

- Para isso vc tem que declarar uma classe para o evento.

*---------------------------------------------------------------------*
*       CLASS CL_ALV_EVENT DEFINITION
*---------------------------------------------------------------------*
CLASS cl_alv_event DEFINITION.

  PUBLIC SECTION.

    METHODS handle_data_changed
      FOR EVENT data_changed OF cl_gui_alv_grid
      IMPORTING er_data_changed e_ucomm.

ENDCLASS.                    "cl_tree_event_rec DEFINITION



- No meu caso em vez de implentar a rotina dentro da chamada do método, eu criei um perfom para ficar mais bonito.

*----------------------------------------------------------------------*
*       CLASS cl_alv_event IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS cl_alv_event IMPLEMENTATION.

  METHOD handle_data_changed.
    PERFORM alv_data_changed USING er_data_changed e_ucomm.
  ENDMETHOD.                    "handle_data_changed
ENDCLASS.                    "cl_alv_event IMPLEMENTATION


Depois declaro o meu objeto para o evento
DATA gcl_alv_event    TYPE REF TO cl_alv_event.

Na criação do ALV eu defino o evento

  CALL METHOD gcl_grid->set_table_for_first_display
    EXPORTING
      i_save               = 'A'
      is_layout            = lw_layout
      it_toolbar_excluding = lt_exc_toolb
    CHANGING
      it_outtab            = gt_pin
      it_fieldcatalog      = gt_fcat_alv
      it_filter            = lt_filter.

  CREATE OBJECT gcl_alv_event.

  SET HANDLER gcl_alv_event->handle_data_changed  FOR gcl_grid.


  CALL METHOD gcl_grid->register_edit_event
    EXPORTING
      i_event_id = cl_gui_alv_grid=>mc_evt_enter.


O método REGISTER_EDIT_EVENT é importante, pois determina que o evento que vc colocar no parâmetro I_EVENT_ID será o trigger para o evento DATA_CHANGED ser executado. no meu caso o ENTER - coloquei um atributo da classe para não colocar constante no programa. Se quiser podem colocar outro ID de evento.

- Não esquecer de marcar o campo EDIT do seu FIELDCAT.
  LOOP AT gt_fcat_alv ASSIGNING <lfsw_fcat>.

    CASE <lfsw_fcat>-fieldname.
      WHEN 'TFRNR'.
        <lfsw_fcat>-ref_table = 'LFA1'.
        <lfsw_fcat>-ref_field = 'LIFNR'.
        <lfsw_fcat>-edit = 'X'.
    ENDCASE.

  ENDLOOP.


- Codificando o evento DATA_CHANGED
- O método GET_CELL_VALUE retorna o valor digitado do campo.
- O metodo ADD_PROTOCOL_ENTRY gera a mensagem de erro caso o campo digitado esteja errado por alguma validação
- O método MODIFY_CELL modifica qualquer célula que se queira caso exista um outro campo dependente do digitado. Esse seria o caso de um valor calculado de acordo com um valor digitado pelo usuário.
- Uma coisa muito importante é que essa modificações são feitas em tela, não modifica o valor da tabela interna passada no método SET_TABLE_FOR_FIRST_DISPLAY. Então seria correto nesse código ao ser alterado um campo, não esquecer de alterar a tabela interna também.


*&---------------------------------------------------------------------*
*&      Form  ALV_DATA_CHANGED
*&---------------------------------------------------------------------*
FORM alv_data_changed USING fcl_data TYPE REF TO cl_alv_changed_data_protocol
                            f_ucomm TYPE sy-ucomm. "#EC NEEDED

  DATA: lw_mod_cells TYPE lvc_s_modi.

  DATA: l_tfrnr TYPE zxxtpin_lote-tfrnr.

  LOOP AT fcl_data->mt_good_cells INTO lw_mod_cells.

    CASE lw_mod_cells-fieldname.
      WHEN  'TFRNR'.

        CALL METHOD fcl_data->get_cell_value
          EXPORTING
            i_row_id    = lw_mod_cells-row_id
            i_fieldname = lw_mod_cells-fieldname
          IMPORTING
            e_value     = l_tfrnr.

        CHECK l_tfrnr IS NOT INITIAL.

        SELECT COUNT( * )
          FROM lfa1
         WHERE lifnr EQ l_tfrnr.

        IF sy-subrc <> 0.

          CALL METHOD fcl_data->add_protocol_entry
            EXPORTING
              i_msgid     = 'ZXXPIN'
              i_msgno     = '003'
              i_msgty     = 'E'
              i_msgv1     = l_tfrnr
              i_fieldname = lw_mod_cells-fieldname
              i_row_id    = lw_mod_cells-row_id.

        ELSE.
          CALL METHOD fcl_data->modify_cell
            EXPORTING
              i_row_id    = lw_mod_cells-row_id
              i_fieldname = lw_mod_cells-fieldname
              i_value     = lw_sflight-carrname.

        ENDIF.


    ENDCASE.

  ENDLOOP.

ENDFORM.                    " ALV_DATA_CHANGED

Evento para SM34 - Cluster de visão


- Situação: validar campos na inclusão de valores em visão de atualização adicionada há um cluster de visão na SE54.

- Entrar na transação SE54 - Cluster de visão.
 - colocar a visão.
- Clicar em Hora Momento







- Selecionar o evento desejado (no meu caso usei o CK - na entrada de valores ou modificação) , esse evento seria o apropriado para checagem de valores.
- No meu caso coloquei um nome para o FORM CHECK_TESTE

 - Ao dar o <ENTER> aparecerá a tela abaixo.
- O sistema pede para criar um programa onde a rotina (no meu caso CHECK_TESTE) será inserida.
- Coloque um nome 
- A verificação abaixo é bem importante pois indica qual o tipo de programa q vc deve criar. É mais informativo, apenas continue.


- Como vai ser um programa só de rotinas simples eu descartei o include TOP.
- Aparecerá a janela com as características do programa e nesse momento vc tem q escolher o tipo de programa conforme a mensagem anterior. Na minha opinião o mais correto seria um POOL de subrotinas.
- Continuando ele entra no programa, conforme abaixo.
==============================================================
 Abaixo um código que fiz para validação no evento da SE34, não é para mesma tabela q criei as telas acima, que apenas criei para demonstrar o passo a passo.

- O include  lsvcmcod, é bom manter para obter alguns valores globais do programa standard.
- No meu código abaixo as 2 visões do cluster são a ZXX_V_LTG_CAB e a ZCC_V_LTG_ITEM
- A variável VCL_ACTION - indica se é uma inclusão ou alteração

*&---------------------------------------------------------------------*
*& Pool subrotinas   ZXX_R_LTG_VIEW_EVENT
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*

PROGRAM  ZXX_r_ltg_view_event.

INCLUDE lsvcmcod.

*&---------------------------------------------------------------------*
*&      Form  check_view_ltg_ck
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM check_view_ltg_ck.

  DATA: l_viewname TYPE vclstruc-object,
        l_error_flag TYPE vcl_flag_type,
        lw_header TYPE vimdesc,
        lw_total(1000) TYPE c,
        lw_extract(1000) TYPE c,
        lw_ltg_cab TYPE ZXX_v_ltg_cab,
        lw_ltg_item TYPE ZXX_v_ltg_item.

  FIELD-SYMBOLS: <lfs_view>    TYPE ANY,
                 <lfs_view_x>  TYPE x,
                 <lfs_ent_x>   TYPE x,
                 <lfs_viewkey> TYPE x.

  " assign data of current view to global field symbols (<vcl_total>, <vcl_extract> etc.)
  l_viewname = vcl_akt_view.

  PERFORM vcl_set_table_access_for_obj USING  l_viewname
                                       CHANGING l_error_flag.

  " get view name (if you`ll use several views to make it dynamic)
  READ TABLE <vcl_header> INTO lw_header INDEX 1.


  " assign field symbols
*  ASSIGN: lw_total                    TO <lfs_view>    CASTING TYPE (lw_header-maintview),
  ASSIGN: vcl_object_area         TO <lfs_view>    CASTING TYPE (lw_header-maintview),
          <lfs_view>                  TO <lfs_view_x>  CASTING,
          <lfs_view_x>(lw_header-keylen) TO <lfs_viewkey>.

  CASE vcl_akt_view.
    WHEN 'ZXX_V_LTG_CAB'.

      lw_ltg_cab = <lfs_view>.

      IF vcl_action = 'I'.

        SELECT COUNT( * )
          FROM ZXX_tltg_cab
         WHERE matnr   EQ lw_ltg_cab-matnr
           AND werks   EQ lw_ltg_cab-werks
           AND ltgvers EQ lw_ltg_cab-ltgvers
           AND bdatu   EQ lw_ltg_cab-bdatu.

        IF sy-subrc = 0.

          MESSAGE i010(ZXX_ltg)
             WITH lw_ltg_cab-matnr lw_ltg_cab-werks
                  lw_ltg_cab-ltgvers lw_ltg_cab-bdatu
                  RAISING invalid_key.
        ENDIF.

      ENDIF.

    WHEN 'ZXX_V_LTG_ITEM'.
      lw_ltg_item = <lfs_view>.

      IF vcl_action = 'I'.

        SELECT COUNT( * )
          FROM ZXX_tltg_item
         WHERE stlnr EQ lw_ltg_item-stlnr
           AND idnrk EQ lw_ltg_item-idnrk.

        IF sy-subrc = 0.
          MESSAGE i011(ZXX_ltg)
             WITH lw_ltg_item-idnrk
                  RAISING invalid_key.
        ENDIF.

      ENDIF.

  ENDCASE.

  IF vcl_action = 'U'.
  ENDIF.

ENDFORM.                    "check_view_ltg_ck









terça-feira, 26 de novembro de 2013

Criando novo botão Status GUI na visão de atualização SM30

- Criar um botão no status GUI é muito fácil, mas tive muitos problemas na implementação do botão para visualização de um LOG  de modificação criado para uma tabela Z, o grande problema é que quando só copiava o status GUI original do standard para visão de atualização sempre dava um erro pois tinha STATUS GUI com nomes com 2 sílabas separados por espaço, coisa q geralmente não é permitido quando vc vai criar um Z. Daí a gente depois de um tempo lembra que existe a transação de status SE41.

- O que foi feito.... entrei na transação SE41 e coloquei o nome do programa da SM30 SAPLSVIM, e cliquei em copiar interface do usuário. Depois disso é só entrar no seu programa criado na visão de atualização e colocar o botão desejado no STATUS GUI da SM30.

Não esquecer do passo de criar no evento horas para a função de ST, a relação com o programa da visão.






Criando LOG de modificação para tabela transação SCDO

- Clicar em criar


- Apenas colocar a tabela e salvar
 

 - Colocar o cursor na tabela desejada e clicar em “Gerar prog.atualiz”.

- Colocar um grupo de função Z que não exista, (o grupo de função de acordo com o padrão da empresa).
- E clicar em “Gerar”..  vai aparecer um log apenas como simulação, para finalizar tem q apertar no botão salvar.

- Lembrar que em elementos de dados novos para gravar a modificação do campo tem que flegar o campo doc. Modificação conforme abaixo.
No meu caso eu quis exibir o log de modificação da visão de atualização na própria tela da SM30, nesse caso tem q implementar uma rotina nos eventos da visão de atualização. Além de implementar a inclusão do log no momento de salvar a alteração na tabela.
 
 - Abaixo o código para atualização do log antes de salvar.




FORM before_post_log.

  DATA: ls_cdhdr TYPE cdhdr.

  FIELD-SYMBOLS: <lfs_value>,
                 <lfs_field>.

  DATA: l_tabix TYPE sy-tabix,
        l_field TYPE c LENGTH 50.

  DATA: lt_comp TYPE TABLE OF rstrucinfo.

  DATA: ls_zmmt0002 TYPE zmmt0002,
        ls_zmmt0002_old TYPE zmmt0002,
        ls_comp TYPE rstrucinfo.

  SELECT fieldname
    INTO ls_comp-compname
    FROM dd03l
   WHERE tabname EQ 'ZMMT0002'.
    APPEND ls_comp TO lt_comp.
  ENDSELECT.

  ls_cdhdr-objectclas = 'ZMMT0002'.
  ls_cdhdr-username   = sy-uname.
  ls_cdhdr-udate      = sy-datum.
  ls_cdhdr-utime      = sy-uzeit.
  ls_cdhdr-tcode      = sy-tcode.

  LOOP AT total.

    l_tabix = sy-tabix.

    CHECK <action> <> space.

    IF <action> = 'N' OR <action> = 'U'.

      ASSIGN COMPONENT 'AEDAT' OF STRUCTURE <vim_total_struc> TO <lfs_value>.
      IF sy-subrc = 0.
        <lfs_value> = sy-datum.
      ENDIF.

      ASSIGN COMPONENT 'AENAM' OF STRUCTURE <vim_total_struc> TO <lfs_value>.
      IF sy-subrc = 0.
        <lfs_value> = sy-uname.
      ENDIF.

      MODIFY total INDEX l_tabix.

      LOOP AT lt_comp INTO ls_comp.

        CONCATENATE 'LS_ZMMT0002-' ls_comp-compname INTO l_field.
        ASSIGN (l_field) TO <lfs_field>.
        CHECK sy-subrc = 0.

        ASSIGN COMPONENT ls_comp-compname OF STRUCTURE <vim_total_struc> TO <lfs_value>.
        IF sy-subrc = 0.
          <lfs_field> = <lfs_value>.
        ENDIF.

      ENDLOOP.

      CONCATENATE sy-mandt ls_zmmt0002-mtart ls_zmmt0002-matnr ls_zmmt0002-werks
             INTO ls_cdhdr-objectid.

      IF <action> = 'U'.

        SELECT SINGLE *
          INTO ls_zmmt0002_old
          FROM zmmt0002
         WHERE mtart EQ ls_zmmt0002-mtart
           AND matnr EQ ls_zmmt0002-matnr
           AND werks EQ ls_zmmt0002-werks.

      ENDIF.

      PERFORM grava_modificacao USING ls_cdhdr
                                      'ZMMT0002'
                                      ls_zmmt0002_old
                                      ls_zmmt0002
                                      <action>.

    ENDIF.

  ENDLOOP.

ENDFORM.                    "before_post_log

*&---------------------------------------------------------------------*
*&      Form  GRAVA_MODIFICACAO
*&---------------------------------------------------------------------*
FORM grava_modificacao USING fs_cdhdr TYPE cdhdr
                             f_tabname
                             fs_data_old TYPE zmmt0002
                             fs_data_new TYPE zmmt0002
                             f_chg_ind.

  DATA: lt_cdtxt TYPE TABLE OF cdtxt.
  DATA: ls_cdtxt TYPE cdtxt.

  ls_cdtxt-teilobjid = fs_cdhdr-objectid.
  ls_cdtxt-textspr = sy-langu.
  ls_cdtxt-updkz   = f_chg_ind.
  APPEND  ls_cdtxt TO lt_cdtxt.

  CALL FUNCTION 'ZMMT0002_WRITE_DOCUMENT'
    EXPORTING
      objectid                      = fs_cdhdr-objectid
      tcode                         = fs_cdhdr-tcode
      utime                         = fs_cdhdr-utime
      udate                         = fs_cdhdr-udate
      username                      = fs_cdhdr-username
      n_zmmt0002                    = fs_data_new
      o_zmmt0002                    = fs_data_old
     UPD_ZMMT0002                  = f_chg_ind
    TABLES
      icdtxt_zmmt0002               = lt_cdtxt.


ENDFORM.                    " GRAVA_MODIFICACAO

*&---------------------------------------------------------------------*
*&      Module  Z_MOSTRA_LOG  INPUT
*&---------------------------------------------------------------------*
*       Mostra log
*----------------------------------------------------------------------*
MODULE z_mostra_log INPUT.

  IF function = 'ZLOG'.
    SUBMIT rsscd100 WITH objekt = 'ZMMT0002'
                    WITH objectid = ' '
                    WITH dat_bis = ' '
                    AND RETURN.
  ENDIF.

ENDMODULE.                 " Z_MOSTRA_LOG  INPUT