Programação

MenuDef e atalhos para o usuário

Escritor por Vinícius Gregório

 

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.

 

MenuDef funcionando

 

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.

Fique atualizado, É GRÁTIS!
Cadastre o seu endereço de e-mail e fique por dentro de todas as atualizações da AcademiaERP.
Não enviamos spam.

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:

 

artigo-02-corrigido

 

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

 

Sobre o Autor

Vinícius Gregório

Tecnólogo em análise de sistemas da informação pela Faculdade IBTA (SP), empreendedor da área de TI e consultor Protheus especializado em desenvolvimento AdvPL. Trabalhou em diversos projetos com os módulos de Compras, Estoque/Custos, Faturamento, Contabilidade Gerencial, Field Service/Gestão de Serviços, Financeiro, Gestão de Contratos e Gestão de Projetos. Atua também como desenvolvedor de sistemas web e é usuário fanático de sistemas e softwares open source... (vinicius.gregorio@academiaerp.com.br)

8 comentários

  • 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

Deixe um comentário

Dúvida?