Olá!
Neste artigo vamos falar sobre uma das boas práticas de desenvolvimento que podemos adotar em nossos códigos para facilitar o acesso dos usuários às nossas rotinas: a função MenuDef().
É extremamente comum desenvolvermos rotinas que serão chamadas diretamente através do menu de um determinado módulo (cadastros, relatórios, consultas…). Para esses casos, o TOTVS Protheus disponibiliza um padrão de código que permite que cada uma das opções (funções do aRotina) seja disponibilizada no painel de atalhos para o usuário. Normalmente, codificamos e não damos a devida atenção a esse item em particular.
Vamos ver um exemplo criando uma rotina de cadastro simples de clientes (SA1). Essa rotina será do tipo MBrowse, gerada automaticamente pelo Assistente de Código da IDE. Para mais informações sobre o Assistente de Código do DevStudio, leia esse artigo.
#INCLUDE "protheus.ch" /*/ ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ±±ÉÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍ»±± ±±ºPrograma ³ XTSTMDEF º Autor ³ Vinícius Gregório º Data ³ 21/01/14 º±± ±±ÌÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍ͹±± ±±ºDescricao ³ Função teste para demonstrar os atalhos do aRotina segundo º±± ±±º ³ o MenuDef() º±± ±±ÌÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹±± ±±ºUso ³ AcademiaERP º±± ±±ÈÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍŒ±± ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß /*/ User Function XTSTMDEF //ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ //³ Declaracao de Variaveis ³ //ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Private cCadastro := "Cadastro de Clientes" Private aRotina := { {"Pesquisar","AxPesqui",0,1} ,; {"Visualizar","AxVisual",0,2} ,; {"Incluir","AxInclui",0,3} ,; {"Alterar","AxAltera",0,4} ,; {"Excluir","AxDeleta",0,5} } Private cDelFunc := ".T." Private cString := "SA1" dbSelectArea("SA1") dbSetOrder(1) dbSelectArea(cString) mBrowse( 6,1,22,75,cString) Return
Bem simples certo?
Após a criação da rotina e compilação, vamos colocar uma chamada pra ela em um menu. Exatamente como faríamos para disponibilizá-la para os nossos usuários.
No meu caso, utilizei o menu do módulo de Compras, logo abaixo da chamada do cadastro de Produtos. Caso tenha dúvidas como incluir uma rotina em menu, leia esse artigo.
OK, a rotina está no menu e sua chamada está funcionando. Agora, perceba que para os itens de menu Produtos e Unidades de Medida existem atalhos para as funções internas dos respectivos cadastros no painel do Protheus. Já para a nossa função, esses atalhos não estão habilitados e isso ocorre por que não implementamos a função MenuDef().
O que é a função MenuDef() ?
Essa função é uma padronização de código desenvolvida pela TOTVS para habilitar essas facilidades de acesso durante o processo de atualização da interface do sistema (embora as imagens do artigo mostrem a versão 11 do Protheus, também estavam disponíveis na versão 10). “Padrão de Código” significa que essa função é uma orientação que pode ser incorporada em nosso código, não sendo obrigatório o seu uso. Como vimos anteriormente, a única alteração é que, caso não implementada, nossa função não terá os itens do aRotina exibidos para acesso rápido pelo usuário.
Embora o impacto não seja tão significativo (afinal, o usuário consegue acessar normalmente a rotina pelo menu à esquerda), é uma facilidade que você entrega para o seu usuário, mantém o padrão visual do sistema e também o ajuda a encontrar sua função dentro das opções de rotinas disponibilizadas pelo módulo 🙂
Como implementar a alteração
Vamos alterar a nossa “função-exemplo” para adequá-la ao MenuDef():
#INCLUDE "protheus.ch" /*/ ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ±±ÉÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍ»±± ±±ºPrograma ³ XTSTMDEF º Autor ³ Vinícius Gregório º Data ³ 21/01/14 º±± ±±ÌÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍ͹±± ±±ºDescricao ³ Função teste para demonstrar os atalhos do aRotina segundo º±± ±±º ³ o MenuDef() º±± ±±ÌÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹±± ±±ºUso ³ AcademiaERP º±± ±±ÈÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍŒ±± ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß /*/ User Function XTSTMDEF //ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ //³ Declaracao de Variaveis ³ //ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Private cCadastro := "Cadastro de Clientes" Private aRotina := MenuDef() Private cDelFunc := ".T." Private cString := "SA1" dbSelectArea("SA1") dbSetOrder(1) dbSelectArea(cString) mBrowse( 6,1,22,75,cString) Return /* ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ±±ÉÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍ»±± ±±ºPrograma ³MenuDef ºAutor ³Vinícius Gregório º Data ³ 21/01/14 º±± ±±ÌÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍ͹±± ±±ºDesc. ³ Função padronizada pelo Protheus utilizada para o acesso º±± ±±º ³ rápido das funções do aRotina pelos usuários º±± ±±ÌÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹±± ±±ºUso ³ AcademiaERP º±± ±±ÈÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍŒ±± ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß */ Static Function MenuDef() //ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ //³Declaração de variáveis³ //ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Local aArea := GetArea() Local aRetorno := { {"Pesquisar","AxPesqui",0,1} ,; {"Visualizar","AxVisual",0,2} ,; {"Incluir","AxInclui",0,3} ,; {"Alterar","AxAltera",0,4} ,; {"Excluir","AxDeleta",0,5} } RestArea(aArea) Return aRetorno
Após alterar o fonte, compile-o novamente e entre novamente no módulo para testar os acessos:
Conclusão
Se você é um leitor detalhista e atencioso, encontrou uma coisa estranha nessa alteração e, principalmente, deve estar cético sobre o modo como ela funciona. Se não achou nada de estranho, vou dar a dica: como a função MenuDef() pode ser chamada para montar os atalhos no painel do Protheus se ela está como escopo Static dentro de nosso fonte? o.ô
O escopo Static de funções delimita o seu uso somente ao arquivo de código-fonte em que ela está inserida. Logo, uma função externa (como a que monta os atalhos do painel) não deveria ter acesso a executá-la e receber o seu retorno (nesse caso, as funções do aRotina).
Bem… o que acontece, nesse caso, é uma exceção à regra. Conforme mencionei anteriormente, o MenuDef() é um padrão que foi elaborado para adequar todas as rotinas do sistema a uma nova interface. Para realizar essa adequação era necessário utilizar uma chamada única em todos as rotinas que compõe o sistema para recuperar as opções de menu de cada fonte. Dessa forma, ao listar cada item do menu na montagem da interface, o sistema conseguiria listar também as funcionalidades internas de cada rotina.
Segundo a sintaxe da linguagem AdvPL, cada função de acesso externo de um fonte tem que ter um nome exclusivo, fato que inviabilizava a idéia de uma chamada unificada a ser executada para todos os fontes. Para solucionar o problema, foi criada a função StaticCall que “ignora” essa limitação de escopo. A sua sintaxe utiliza o nome do fonte onde está a função, o nome da função com escopo Static e, caso existam, parâmetros que serão enviados para a execução. No nosso caso, ficaria assim:
aRotina := StaticCall(XTSTMDEF,MENUDEF)
Observações importantes: 1- Sim, passamos o nome do fonte e da função sem aspas (não é uma string), 2- O retorno da sua execução é o retorno da função chamada. 3- No nosso caso, não temos mais parâmetros para passar na chamada por que a função MenuDef() não recebe parâmetros 😉
É isso aí. Espero que tenham gostado da dica e, caso tenham alguma dúvida, deixem um comentário.
Abraços e até a próxima
Olá Jean,
Consegue nos enviar o seu código da função MenuDef() para analisarmos?
e se acontecer o erro C2021 Redefinition of function MENUDEF
cara, fiz os testes aqui…
eu dei até risada… de tão boba a solução…
tudo que agente conversou aqui não estava dando certo, ai eu fui em outra função customizada que monta um mBrowse com o SC5 customizado, e ele aparece os atalhos na direita…
reparei que o nome do .PRW é o mesmo que na User Function (PMSC5.PRW e USER FUNCTION PMSC5) e o menu aparecia…
o exemplo que eu te mandei o fonte chamava PM_MATA410.PRW e a user function USER FUNCTION PMMATA410 criei um fonte novo com o nome de PMMATA410.PRW e o menu na direita apareceu ¬¬
a funcao esta do mesmo jeito que eu postei no primeiro post, perguntando se vc sabia a solução
User function PMMATA410()
public aArray_ := {}
MATA410()
Return
desculpe o transtorno, vou compartilhar isso com a galera, erro bobo e que tomou tempinho aqui 🙁
Nossa, na realidade, eu mesmo não sabia que desse pré-requisito!
Acho que nunca peguei esse erro pq normalmente (mas não sempre, rs) eu coloco o nome do PRW com o nome da User Function principal contida nele.
Obrigado pelos comentários, já complementou o artigo com um problema real e a solução para quem precisar posteriormente. Inclusive eu que aprendi mais essa 🙂
Se tiver alguma outra dúvida, só mandar pra gente. Abraços
Bom dia, cara eu fiz isso e coloquei até um for para mostra se ele estava trazendo o arotina do MATA410 certinho, e ele traz, mostrou todas opções certinho mas não esta mostrando o MENUDEF ainda do lado direito
User Function PMMATA410
Public aSzdPM_ := {}
Private aRotina := StaticCall(MATA410, MenuDef)//atribui o arotina implementado no MATA410
for nI:=1 to len(arotina)//mostra as funcoes disponiveis
msginfo(arotina[nI,1])
next
MATA410()
Return
Então, o que importa nesse caso não é se a variável existe, se dá pra pegar o seu conteúdo e por aí vai. O que a rotina do protheus procura é se existe a Static Function MenuDef() dentro do seu PRW.
Tenta colocar o seguinte dentro do seu fonte:
Static Function MenuDef()
Local aRotina2 := {{STR0003,"A410Barra",0,3},; // "Incluir"
{STR0004,"A410Barra",0,4}} // "Alterar"
Local aRotina3 := {{ OemToAnsi(STR0005),"A410Deleta" ,0,5,21,NIL},;
{ OemToAnsi(STR0109),"Ma410Resid",0,2,0,NIL}} //"Residuo"
Private aRotina := { { OemToAnsi(STR0001),"AxPesqui" ,0,1,0 ,.F.},; //"Pesquisar"
{ OemToAnsi(STR0002),"A410Visual" ,0,2,0 ,NIL},; //"Visual"
{ OemToAnsi(STR0003),"A410Inclui" ,0,3,0 ,NIL},; //"Incluir"
{ OemToAnsi(STR0004),"A410Altera" ,0,4,20,NIL},; //"Alterar"
{ OemToAnsi(STR0005),IIf((Type("l410Auto") <> "U" .And. l410Auto),"A410Deleta",aRotina3),0,5,0,NIL},; // Excluir
{ OemToAnsi(STR0006),aRotina2 ,0,3,0 ,NIL},; //"Cod.barra"
{ OemToAnsi(STR0042),"A410PCopia" ,0,6,0 ,NIL},; //"Copia"
{ OemToAnsi(STR0052),"A410Devol" ,0,3,0 ,NIL}} //"Dev. Compras"
If cPaisLoc == "BRA"
Aadd(aRotina,{OemToAnsi(STR0095),"Ma410PvNfs" ,0,2,0 ,NIL}) //"Prep.Doc.Saída"
EndIf
Aadd(aRotina,{ OemToAnsi(STR0032),"A410Legend" ,0,3,0 ,.F.}) //"Legenda"
Aadd(aRotina,{ STR0103 ,"MsDocument" ,0,4,0 ,NIL}) //"Conhecimento"
If ExistBlock("MA410MNU")
ExecBlock("MA410MNU",.F.,.F.)
EndIf
Return(aRotina)
O Static Call é chamado pela rotina que monta o menu, você não precisa chamá-la no seu fonte. Essa rotina irá procurar no seu fonte a definição de MenuDef() e, se encontrar, vai montar o menu lateral de opções.
muito bom, queria saber se voce sabe o que fazer para aparecer as opcoes do menu arotina nesse caso:
eu fiz uma funcao personalizada, que chama a funcao de pedido de venda
User function PMMATA410()
public aArray_ := {}
MATA410()
Return
e no meu eu coloquei:
Pedidos
Pedidos
Pedidos
PMMATA410
03
xxxxxxxxxx
05
0
e nao aparece esse atalho do lado direito de jeito nenhum.
Entendi. Também já encapsulei a chamada de funções do padrão algumas vezes, rs.
O problema nesse caso é que a busca pela Static Function MenuDef() ocorre somente no fonte da função que está no menu. Ou seja, como o fonte que tem a User Function PMMATA410() não tem a definição de MenuDef() (pq ela está dentro da MATA410), o Protheus não consegue encontrar o aRotina e exibir as opções.
Se vc tiver acesso às definições de funções da MATA410, tente criar a função MenuDef() em seu fonte chamando as funções da MATA410, por exemplo. Dessa forma, funcionará normalmente 🙂
Abraços