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