terça-feira, 6 de outubro de 2009

ABAP - Gerar LOG de alterações de Tabela Z

1 - Para gerar um Log de modificações de uma tabela, o que precisamos primeiro é que a tabela exista, e que todos os campos desta tabela estejam marcados com a Documentação de Modificações Ativa. Para tal, é necessário acessar a tabela conforme exemplo abaixo (transação SE11):

2 - Clicando com Duplo clique em cada elemento de dados e acessando na seqüência a pasta ‘Características Adicionais’, certifique-se que o campo ‘Doc. Modificação’ esteja sempre MARCADO para todos os elementos de dados da tabela (ou todos que permitirão guardar log).


3- Também é necessário que a tabela tenha gerado seu diálogo de atualização. Não entrarei em detalhes sobre como criar um Diálogo de atualização visto que se trata de um procedimento básico. O menu para criação está detalhado abaixo:


4- Aqui segue as características do programa gerado para atualização desta tabela exemplo:


5 – Agora vamos ao desenvolvimento dos LOGS, considerando que o ID DE OBJETO para ser gerado log nesta tabela será seu CAMPO CHAVE.


O primeiro passo é ter, no programa onde o log vai ser gerado, duas tabelas: uma será a tabela que está sendo modificada (que já existia antes no programa), outra será uma tabela idêntica onde assim que eu carregar os dados na tabela original, esta deverá também receber exatamente o mesmo conteúdo. Ao final do processo estas duas tabela serão utilizadas para que o LOG identifique se houve mudança e quais campos foram alterados. Rotina de alimentação das duas tabelas exemplo (neste caso a tabela interna original é a G_TC_CC_ITAB e a tabela espelho a G_TC_CC_AUX) :

*************************************************************
* Dados bancários da Solicitação de Crédito
SELECT * FROM ZARMT024C
  INTO CORRESPONDING FIELDS OF TABLE G_TC_CC_ITAB
    WHERE SOLCRE EQ WC_SOLCRE.
* Alimenta estrutura de comparação p/ geração de Log de Modificações
if not G_TC_CC_ITAB[] is initial.
  SELECT * FROM ZARMT024C
     INTO CORRESPONDING FIELDS OF TABLE G_TC_CC_AUX
       WHERE SOLCRE EQ WC_SOLCRE.
endif.
*************************************************************
A implementação acima será colocada no momento em que as informações são carregadas para serem levadas à tela.
Agora no momento em que a tabela for sofrer a atualização dos dados (evento de salvar), iremos implementar a chamada da função que será responsável por gravar os LOGS de modificações conforme abaixo:
*************************************************************
Data: LC_OBJECTID LIKE CDHDR-OBJECTID.

LOOP AT G_TC_CC_ITAB INTO G_TC_CC_WA. “Aqui eu dou loop na tabela modificada
 G_TC_CC_WA-MANDT = SY-MANDT.
 G_TC_CC_WA-SOLCRE = ZARMT024-SOLCRE.
 MODIFY G_TC_CC_ITAB FROM G_TC_CC_WA. “Linha onde a tabela é modificada
* Gerar log de modificações
 clear G_TC_CC_WAUX.
 READ TABLE G_TC_CC_AUX into G_TC_CC_WAUX “Lendo tabela espelho p/ comparar
   WITH KEY SOLCRE   = G_TC_CC_WA-SOLCRE “Campos chave da tabela...
                       PARCELA = G_TC_CC_WA-PARCELA. “Este identifica a linha.
 IF SY-SUBRC EQ 0.
   CLEAR LC_OBJECTID. “Campo de ID chave para gerar o log
   LC_OBJECTID = G_TC_CC_WA-SOLCRE.
* Grava log de modificações de campo na tabela
   CALL FUNCTION 'Z_GERA_LOG_TABELAS' “Função para gravar o log
     EXPORTING
     I_OBJECTCLAS = 'ZARMT024C' “Nome do Diálogo de Atualização/tabela
     I_OBJECTID = LC_OBJECTID
     I_TCODE = SY-TCODE
     I_UPD = 'U' “Indica UPDATE = modificação
     I_TABLENAME = 'ZARMT024C' “Nome da tabela
     I_WORKAREA_NEW = G_TC_CC_WA “Estrutura com os dados modificados
     I_WORKAREA_OLD = G_TC_CC_WAUX. “Estrutura com os dados antigos
 ENDIF.
ENDLOOP.
*************************************************************
Agora o LOG pôde ser alimentado... Segue então o código fonte da Função Z que foi responsável pelo armazenamento do LOG... Criar função conforme código abaixo (SE37):


*************************************************************

FUNCTION Z_GERA_LOG_TABELAS.
*"----------------------------------------------------------------------
*"*"Interface local:
*" IMPORTING
*" VALUE(I_OBJECTCLAS) LIKE CDHDR-OBJECTCLAS DEFAULT SPACE
*" VALUE(I_OBJECTID) LIKE CDHDR-OBJECTID DEFAULT SPACE
*" VALUE(I_TCODE) LIKE SY-TCODE DEFAULT SPACE
*" VALUE(I_UPD) DEFAULT 'U'
*" VALUE(I_TABLENAME) LIKE CDPOS-TABNAME DEFAULT SPACE
*" VALUE(I_WORKAREA_NEW) DEFAULT SPACE
*" VALUE(I_WORKAREA_OLD) DEFAULT SPACE
*"----------------------------------------------------------------------

CALL FUNCTION 'CHANGEDOCUMENT_OPEN'
EXPORTING OBJECTCLASS = I_OBJECTCLAS
OBJECTID = I_OBJECTID
EXCEPTIONS SEQUENCE_INVALID = 1
OTHERS = 2.

CASE SY-SUBRC.
WHEN 1. MESSAGE A213(A02) WITH 'Sequência Inválida'.
WHEN 2. MESSAGE A213(A02) WITH 'OPEN-Falhou'.
ENDCASE.
IF I_UPD NE SPACE.

CALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE'
EXPORTING TABLENAME = i_tablename
WORKAREA_OLD = I_WORKAREA_OLD
WORKAREA_NEW = I_WORKAREA_NEW
CHANGE_INDICATOR = I_UPD
DOCU_DELETE = 'X'
EXCEPTIONS NAMETAB_ERROR = 1
OPEN_MISSING = 2
POSITION_INSERT_FAILED = 3
OTHERS = 4.

CASE SY-SUBRC.
WHEN 1. MESSAGE A213(A02) WITH 'Nome da tabela'.
WHEN 2. MESSAGE A213(A02) WITH 'Open não encontrado'.
WHEN 3. MESSAGE A213(A02) WITH 'INSERT-falhou'.
WHEN 4. MESSAGE A213(A02) WITH 'SINGLE Falhou'.
ENDCASE.

ENDIF.

CALL FUNCTION 'CHANGEDOCUMENT_CLOSE'
EXPORTING OBJECTCLASS = I_OBJECTCLAS
OBJECTID = I_OBJECTID
DATE_OF_CHANGE = SY-DATUM
TIME_OF_CHANGE = SY-UZEIT
TCODE = I_TCODE
USERNAME = SY-UNAME
EXCEPTIONS HEADER_INSERT_FAILED = 1
OBJECT_INVALID = 2
OPEN_MISSING = 3
NO_POSITION_INSERTED = 4
OTHERS = 5.
CASE SY-SUBRC.
WHEN 1. MESSAGE A213(A02) WITH 'Insert Header falhou'.
WHEN 2. MESSAGE A213(A02) WITH 'Objeto invalido'.
WHEN 3. MESSAGE A213(A02) WITH 'Open não encontrado'.
WHEN 5. MESSAGE A213(A02) WITH 'CLOSE-Falhou'.
ENDCASE.
ENDFUNCTION.

*************************************************************

Agora temos a função pronta, e o programa já deve estar armazenando os LOGS.. mas ONDE? Ele utiliza uma tabela standard chamada CDHDR (chamada via transação se16n):

Esta tabela contém um cabeçalho das modificações. Os itens estão na CDPOS:


Agora precisamos apenas exibir o conteúdo destas tabelas utilizando um botão, e implementando sua ação do evento USER COMMAND da tela em que ele se encontrar conforme abaixo:

************************************************************************
MODULE USER_COMMAND_0100 INPUT.
WC_OK = SY-UCOMM.
CLEAR WC_OKCODE.
REFRESH MESSLOG.
WS_STATUS = ZARMT024-STATUS.
 CASE WC_OK.
   WHEN 'ELOG2'. “Aqui eu coloco o código do botão
     CLEAR LC_OBJECTID. “Esta é a mesma variável de antes
     LC_OBJECTID = ZARMT024C-SOLCRE.
     SUBMIT RSSCD100 WITH OBJEKT = 'ZARMT024C' “Chamada da tela de Exib. Dos Logs
     WITH OBJEKTID = LC_OBJECTID AND RETURN.
  ENDCASE.
ENDMODULE.
************************************************************************
Resultado Final:

Está pronto o nosso log de atualizações da tabela.