Pragiva — Tecnologia na prática

Ponto de entrada no Protheus: como o padrão te chama

Duas linhas do fonte padrão explicam quase todo ponto de entrada que 'não funciona': se ele dispara, e se o que você retorna vale alguma coisa.

Desenvolvimento Referência técnica 4 min de leitura

Assista o vídeo

Resposta rápida

Mecanismo
o fonte padrão testa com ExistBlock() e dispara com ExecBlock().
PARAMIXB
só existe quando ExecBlock recebe um quarto argumento.
Retorno
só importa quando a chamada é atribuída (xRet := ExecBlock(...)), nunca quando é comando solto.
Prova de execução
uma linha de ConOut() na primeira instrução da função, antes de qualquer Return antecipado.

Quando um PE “não dispara” ou “não bloqueia”, a causa está numa dessas quatro linhas — raramente na lógica de negócio escrita dentro dele.

Consulta rápida6 seções

Ponto de entrada é um gancho que a TOTVS deixa dentro do fonte padrão: em posições escolhidas, a rotina pergunta se existe uma User Function com um nome pré-definido e, se existir, executa.

É o que permite customizar o ERP sem editar o fonte padrão — o seu código vive num fonte separado, no RPO custom, e sobrevive à próxima aplicação de pacote.

Este texto é a base. Ele explica o mecanismo uma vez, para que as páginas de cada ponto de entrada possam ser curtas e falar só do que é específico delas.

As duas linhas que explicam tudo

O padrão faz sempre a mesma coisa:

ADVPL / TLPP
If ExistBlock("NOME_DO_PE")
 xRet := ExecBlock("NOME_DO_PE", .F., .F., aParametros)
EndIf
  • ExistBlock() verifica se a função U_NOME_DO_PE existe no RPO.
  • ExecBlock() executa.
  • O quarto argumento, quando existe, é o que vira PARAMIXB dentro da sua função.
  • O retorno só importa se a rotina padrão atribuir esse retorno a alguma coisa.

Guarde as duas últimas. Elas respondem as duas perguntas que trazem todo mundo ao Google: “por que não dispara?” e “por que retorno .F. e nada acontece?”.

“Retornei.F. e não bloqueou”

Compare as duas formas como o padrão chama um PE:

ADVPL / TLPP
Execblock("MT410INC", .F., .F.)              // comando: o retorno some
lRet := Execblock("MT410TOK", .F., .F.,{nOpc,aRecnoSE1RA})   // expressão: vale

Na primeira, ExecBlock é usado como comando. O que a sua função devolve é descartado ali mesmo. Retornar .F. não cancela nada, e nenhuma quantidade de código dentro do PE muda isso.

Na segunda, o retorno é atribuído. Aí sim .F. interrompe a operação.

Não dá para descobrir isso pela documentação: dá para descobrir abrindo o fonte padrão e olhando se tem um := antes do ExecBlock. É o primeiro lugar para onde ir quando um PE “não bloqueia”.

E o corolário: se o retorno é usado, sempre retorne lógico explícito. Esquecer o Return faz o padrão avaliar If lRet .And.... com Nil, e o comportamento aí depende da versão do engine.

“Compilei e não acontece nada”

Quatro causas, em ordem de frequência:

1. O prefixo U_ escrito à mão. User Function U_MT410INC() compila sem erro e nunca dispara. O compilador já prefixa U_ sozinho; escrever de novo cria U_U_MT410INC, e ExistBlock("MT410INC") retorna .F. para sempre.

ADVPL / TLPP
User Function MT410INC()      // certo
User Function U_MT410INC()    // compila, nunca roda

2. O fonte não está no RPO que o ambiente usa. Compilou contra o RPO errado, ou o ambiente aponta para outro RpoCustom. Confira no appserver.ini do ambiente, não no do seu colega.

3. A sessão é antiga. A atualização do RPO passa a valer para novas conexões. Reconecte antes de concluir que não funcionou.

4. Você está no PE errado. O nome existe, compila e roda — só que naquele momento do fluxo, que não é o que você imaginava. É o caso mais chato, porque tudo parece certo. A seção seguinte resolve.

Como provar que ele executou

Esta é a parte que quase não tem resposta escrita, e é a que economiza mais tempo.

ConOut() é o mais barato e o mais confiável. Escreve no console do Application Server, que vai para o console.log da pasta do serviço. Ponha uma linha na primeira instrução da função, antes de qualquer Return antecipado:

ADVPL / TLPP
ConOut("[MT410INC] " + DtoC(Date()) + " " + Time() + " | Pedido: " + SC5->C5_NUM)

Se essa linha não aparece no console.log depois de executar a operação, o PE não está sendo chamado — e o problema é uma das quatro causas acima, não a sua lógica. Se aparece, o PE roda e o problema é outro.

FWLogMsg() para o que fica em produção. Carrega nível e origem, então dá para filtrar depois:

ADVPL / TLPP
FWLogMsg("INFO",, "MT410INC",,,, "Pedido " + SC5->C5_NUM + " processado")

Regra prática: ConOut para depurar, FWLogMsg para deixar.

O teste negativo, que quase ninguém faz. Provar que executou é metade. A outra metade é provar que não executa onde não deve. Se o PE está numa função de inclusão, altere um registro e confirme que o log fica em silêncio. Cancele uma inclusão pela metade e confirme o mesmo. É assim que você descobre que estava no PE errado antes do cliente descobrir.

O que todo ponto de entrada deveria ter

Independente de qual seja:

Chave de desligamento. Um parâmetro no SX6 que desliga a customização sem recompilar. Sem ele, a alternativa em produção é despublicar o fonte às pressas.

ADVPL / TLPP
If !SuperGetMv("MV_XPVAUD", .F., .F.)
    Return
EndIf

GetArea e RestArea, inclusive nos caminhos de erro. PE que deixa área de trabalho deslocada quebra outra rotina, horas depois, sem nenhuma pista apontando para você.

Local e Static apenas. Public e Private no seu fonte podem sobrescrever silenciosamente uma variável da rotina padrão com o mesmo nome. O bug resultante é impossível de rastrear.

Guarda para contexto sem tela. MsgAlert, Pergunte, qualquer MSDIALOG: sob ExecAuto, job agendado ou integração REST, isso trava a thread até o timeout do server. Não é erro que aparece no log — é conexão pendurada. Use IsBlind() para decidir.

Mensagem antes de bloquear. Em PE cujo retorno é usado, devolver .F. calado faz o botão OK simplesmente não fechar a tela. O usuário abre chamado dizendo “o sistema travou”.

E o PARAMIXB?

É o assunto que mais gera erro silencioso em ponto de entrada, e ele não é específico de nenhum: está em PARAMIXB: o array que às vezes não é seu.

Para ver o mecanismo aplicado a um caso concreto, com o momento exato do disparo: MT410INC.

Referências

  1. TDNtdn.totvs.com

    TDN — Pontos de Entrada

Continue por aqui