quarta-feira, 27 de abril de 2011

Design de Aplicações - Parte 11

Encadeamento de múltiplas transacções

Tipicamente, a execução de uma aplicação desenvolvida à medida regista mais que um único documento em SAP. Tipicamente isso implica mais que uma chamada transaccional.
O programador deve evitar que a execução tenha múltiplos commit’s mas nem sempre é possível ou porque a execução é por batch input ou porque a segunda acção precisa do commit da primeira acção. Nestes casos é necessário garantir o controlo e rastreabilidade dos erros ocorridos durante a gravação, quer seja recorrendo a funcionalidades de reprocessamento ou através de mecanismos que dêm ao utilizador a capacidade de intervir na ocorrência de um erro ou simplesmente um registo de log dos erros para posterior análise e correcção pela equipa de manutenção.
No caso do grupo PT este tipo de funcionalidades está já disponibilizado num desenvolvimento à medida, a máquina de estados.
Mas caso não exista, outros mecanismos podem ser preparados:
  • A execução fica a cargo de uma cadeia de processos do XI;
  • A execução fica a cargo de um ou mais IDOC’s;
  • A execução fica a cargo de um workflow;

Cada um destes três mecanismos já tem funcionalidades para análise de erro e reprocessamento que podem ser aproveitadas. As seguintes opções são menos aconselhadas mas também possíveis em alguns cenários:
  • Se execução é por batch-input
    • Interromper a execução na ocorrência de um erro para utilizador tomar medidas correctivas;
  • A execução regista em tabelas de log os dados da execução e os erros para que a equipa de manutenção resolva manualmente o problema e reprocesse.

Quando existe mais de um commit é necessário ter uma preocupação adicional. Quando o commit transaccional ocorre inicia-se a fase de atualização da transacção[1]. Até à conclusão da actualização V1 os objectos de bloqueio não são libertados, ou seja, após o commit é possível que o objecto esteja bloqueado no momento em que se pretende iniciar o segundo bloco transaccional. Em alguns casos isto significa que o programador deve controlar o fim do bloqueio antes de iniciar o bloco seguinte.
Ficam aqui duas sugestões para resolver os problemas de bloqueio. Se for uma chamada de BAPI use a instrução SET UPDATE TASK LOCAL[2] antes da chamada ao BAPI_COMMIT. Isso faz com que a execução apenas continue após a fase de actualização V1. Para chamadas de batch input use o parâmetro update da instrução CALL TRANSACTION … UPDATE 'L' com o mesmo objectivo.
Estes cenários devem prever sempre no programa uma funcionalidade de estorno que permita cancelar de forma simples todo o processo do inicio ao fim.

Output
O output para impressão ou para e-mail tem mecanismos próprios fora do âmbito deste documento. Se a execução for uma chamada standard então não é preciso preparar nada. Senão, sempre que possível usar um dos seguintes (por ordem de prioridade, ignorando alguns outputs mais específicos ou menos relevantes):
  • Adobe Interactive Forms
  • Smartforms
  • SAPscript



[1] Ver help da SAP sobre LUW’s
[2] Ver documentação da SAP sobre a instrução. Basicamente significa que as tarefas na secção de actualização V1, onde se libertam os bloqueios, executam localmente e portanto o processamento só continua após completar o código desta secção.

segunda-feira, 21 de março de 2011

Design de Aplicações - Parte 10

Classes vs Funções

A execução pode ser disponibilizada numa classe ou num grupo de funções. Disponibilizar por classe é preferencial excepto se venha a ser usada por RFC. Nesse caso o programador deve optar por seguir a nomenclatura dos BAPI’s bem como disponibilizar toda a informação dos RFC’s nas próprias funções. Idealmente deve seguir também as convenções dos BAPI’s e ainda associar um objecto mas isso nem sempre se justifica.

Intervalos de numeração

Tipos de documento totalmente novos devem ter associado um intervalo de numeração. A obtenção do número de documento deve respeitar as funções standard para garantir a parametrização do intervalo de numeração, como por exemplo o uso de buffers.

Objectos de Bloqueio

Tal como para o intervalo de numeração, os tipos de documento novos devem obrigatoriamente controlar o documento com um objecto de bloqueio. 

segunda-feira, 14 de março de 2011

Design de Aplicações - Parte 9

Execução

O exemplo usado neste documento tem por base movimentos de mercadoria standard, sendo essencialmente um interface simplificado para introdução de dados de forma simples e de acordo com os requisitos de negócio. A execução está a cargo de uma classe que se divide em duas subclasses, uma para fabrico e outra para desfabrico. A criação do movimento está a cargo do BAPI BAPI_GOODSMVT_CREATE.
Caso a execução tenha por base processos standard então o programador deve seleccionar o método mais adequado de entre os existentes, aqui grosseiramente ordenados de aconselhado a desaconselhado e, para o leitor atento, até repetidos:
  1. Funções ou métodos Z desenvolvidos para disponibilizar a acção
  2. BAPI
  3. Processamento de IDOC
  4. Processamento de LSMW
  5. Chamadas a Webservices
  6. Chamadas a PI/XI
  7. Objectos ou processos de Workflow
  8. Batch Input
  9. Chamada directa a função ou método standard

Os métodos devem se possível contemplar um modo teste que devolva os erros de execução sem commit do BAPI.
O código seguinte apresenta o método de gravar do exemplo. O método de teste é chamado antes da execução para validar erros. Os erros são devolvidos numa tabela interna. A parte de apresentação do programa é que decide se e como apresenta os erros ao utilizador.

method save .
  
field-symbols<fs_return> type bapiret2.

  
call method me->test
    
importing
      return_o 
return_o
      equi_o   
equi_o.
*  verifica se ocorreram erros
  
loop at return_o assigning <fs_return> 
                   where type 'E' or type 'A'.
    
exit.
  
endloop.
  
if sy-subrc <> 0.
    
call method me->call_bapi
      
exporting
        testrun_i 
''
      
importing
        return_o  
return_o
        mkpf_o    
mkpf_o.
  
endif
.

sexta-feira, 11 de março de 2011

Design de Aplicações - Parte 8

Variáveis

Na definição de variáveis é preciso ter o cuidado de evitar definir variáveis que podem ser locais como variáveis globais e reduzir ao máximo o número de variáveis. Algumas variáveis não podem ser evitadas como o ok_code, tableview’s e tabstrip’s.
Muitas vezes levo esta redução ao extremo e para além das que me são impostas apenas crio duas variáveis globais, uma para identificar o próprio documento, tipicamente uma instância da classe de execução e outra com os dados das telas. No nosso exemplo tenho apenas uma variável para a os dados de tela e execução:
dataw_data type t_data.
Esta variável está organizada de forma a conter toda a informação do programa com a seguinte estrutura (alguns campos foram removidos):
Variavel
1º nivel
2º nível
3º nível

w_data
title
action
Dados do título
refdoc
fullname
screens
All
nº de subtela activo
top
header
refdoc
items
Item
top
action
Dados da subtela topo
refdoc
werks
lgort
ref
ebeln
vbeln
mblnr
mjahr
header
budat
Dados da subtela de cabeçalho
bldat
xblnr
items[]
zeile
Dados da subtela de items
expand
matnr
maktx
menge
meins
werks
Lgort
charg
bwart
item
tabix
Dados da subtela de item
Item
equi[]
sernr
instance
Instância de execução
Portanto, a variável usa uma estrutura complexa para guardar a informação que é apresentada no ecran em determinado momento e ainda a referência para a instância da classe de execução. Esta instância há-de conter os dados reais.
Portanto, durante a execução do programa o evento PBO passa os dados da instância para os dados da tela e no evento PAI passa os dados modificados da tela para a instância.
Alguns campos como a descrição do material podem não existir na instância e são calculados para os dados da tela sempre que a chave, neste caso o código de material, é alterada.
É muito importante manter presente que as conversões de dados ocorrem logo após a introdução do dado pelo utilizador. Os dados têm de estar sempre em formato interno depois do input. Os campos da tela permitem associar a regra de conversão, logo não é preciso usá-la mais tarde.
O mesmo é válido para input por ficheiro, ou seja, após ler o ficheiro deve-se converter imediatamente os dados para formato interno.
Também não menos importante, o output deve ser sempre em formato externo e de acordo com as regras do utilizador.
Todos os campos de quantidade ou valor devem ter unidade de medida ou moeda. Essa unidade tem de ser apresentada claramente ao utilizador.

Elementos ALV

Os elementos ALV vieram trazer uma nova dimensão à forma de visualização de dados a à capacidade do utilizador intervir no que vê. No entanto nem sempre não são os mais indicados para a introdução de dados. Por exemplo, os campos editáveis dos ALV TreeView são de texto, não permitem o uso directo de rotinas de conversão nem o uso associado de unidades de medida ou moeda e o copy&paste para múltiplas células não funciona.
O ALV mais útil para aplicações é provavelmente o ALV Grid, sendo que se aproxima bastante do Tablecontrol, permitindo até em alguns casos um interface mais amigável. O ALV TreeView é útil por exemplo para listar documentos ou fluxos a processar.
É preciso um cuidado adicional na aplicação para estes elementos. Se a transacção for disponibilizada também por ITS então os componentes gráficos da tela não podem ser complexos. Entre os que não podem ser usados estão os ALV.

sexta-feira, 4 de março de 2011

Design de Aplicações - Parte 7

Listboxes e ajudas de pesquisa

Uma listbox é óptima para colocar pequenas listas de valores. De forma a encontrar facilmente o código para associação desses valores, posiciono-o na subtela onde o campo está, num módulo PBO específico criado para o efeito. O seguinte código é chamado no evento PBO da subtela do topo:
form f_action_set_values .
  
staticsst_action type vrm_values.
  
datal_id type vrm_id,
        ls_action 
type vrm_value.
  
if st_action[] is initial.
    l_id 
'W_DATA-TOP-ACTION'.
    ls_action
-key 'A01'.
    ls_action
-text 'Fabrico'.
    
append ls_action to st_action.
    ls_action
-key 'A07'.
    ls_action
-text 'Desfabrico'.
    
append ls_action to st_action.
    
call function 'VRM_SET_VALUES'
      
exporting
        
id              l_id
        values          
st_action
      
exceptions
        id_illegal_name 
1
        
others          2.
  
endif.
endform.                    
" F_ACTION_SET_VALUES
A variável estática permite que o código seja executado apenas uma vez.
Por sua vez as ajudas de pesquisa que não existam no dicionário de dados podem ser determinadas recorrendo à função 'HELP_VALUES_GET_WITH_TABLE'.
process on value-request.
  
field w_data-item-item-charg module m_help_charg.

form f_help_charg .
*…
  call function 'HELP_VALUES_GET_WITH_TABLE'
    
exporting
      fieldname    
'MCHA'
      tabname      
'CHARG'
    
importing
      select_value 
<fs_item>-charg
    
tables
      
fields       lt_fields
      valuetab     
lt_values.
  w_items
-charg w_data-item-item-charg <fs_item>-charg.
endform.                    " F_HELP_CHARG

Validações de input

As validações de input devem ser associadas ao evento PAI ON REQUEST. É importante que os campos que estejam relacionados estejam associados num chain por dois motivos:
  1. Não repetir a execução do código
  2. Se despoletar uma mensagem de erro dentro do módulo então os campos do chain ficam em aberto para correcção

  chain.
    
field w_data-top-ref-mblnr.
    
field w_data-top-ref-mjahr.
    
module m_get_mblnr on chain-request.
  endchain
.

A validação deve ser reduzida ao mínimo nesta etapa. A verdadeira validação ocorre depois de se carregar no botão de gravar ou no botão de simulação caso exista. Não deve haver validações muito específicas mas caso existam o seu código deve ser disponibilizado por um método da classe de execução. Em caso de erro o método devolve uma excepção que pode ser tratada com uma mensagem de erro. Os métodos da classe de execução nunca devem despoletar mensagens de erro nem outros eventos de output.
Nem sempre são necessárias validações ao campo. Por exemplo, talvez não seja necessário validar se a data do documento está no passado se essa validação existe na gravação.

quarta-feira, 2 de março de 2011

Design de Aplicações - Parte 6

Activar e desactivar edição de campo



Alguns campos devem ser encerrados ou abertos consoante as opções do utilizador. É preferível ocultar toda a subtela, se possível, senão pode-se colocar um módulo no PBO (primeiro módulo do PBO) para fazer esse tratamento. Por regra defino que todos os campos editáveis estão por defeito como abertos.
process before output.
  
module m_set_screen.
 
Diferentes condições podem gerar o encerramento do campo. No exemplo seguinte, os campos do topo são encerrados quando o objecto de execução está instanciado (ou seja a acção já está definida) e as transacções específicas de fabrico e desfabrico estão impedidas de alterar o tipo de acção.
module m_set_screen output.
  
if w_data-instance is bound.
    
loop at screen.
      
if screen-group1 'TOP'.
        
clear screen-input.
        
modify screen.
      
endif.
    
endloop.
  
endif.
* Bloqueia campos via transaction
  
if sy-tcode 'ZLOGMM_FAB'.
    
loop at screen.
      
if screen-name 'W_DATA-TOP-ACTION' 
         
or screen-name 'W_DATA-TOP-REFDOC'.
        
clear screen-input.
        
modify screen.
      
endif.
    
endloop.
  
elseif sy-tcode 'ZLOGMM_DESFAB'.
    
loop at screen.
      
if screen-name 'W_DATA-TOP-ACTION'.
        
clear screen-input.
        
modify screen.
      
endif.
    
endloop.
  
endif.
endmodule.                 " M_SET_SCREEN  OUTPUT