Pragiva — Tecnologia na prática

Estrutura de um fonte AdvPL: do include ao Return

Estrutura de um fonte AdvPL: o que tem num .prw ou .tlpp, como vira APO no RPO e a diferença real entre User Function e Static Function.

Desenvolvimento Tutorial 4 min de leitura

Assista o vídeo

Resposta rápida

Arquivo
.prw para AdvPL, .tlpp para TLPP; .prx é uma convenção mais antiga do mesmo fonte em texto — não é o binário compilado que um RPO gera.
Compilação
cada fonte vira um APO gravado dentro do RPO — o que não está no RPO não existe para o servidor.
Ordem no arquivo
includes, #define, cabeçalho Protheus.doc, declaração da função, corpo, Return.
Visibilidade
User Function é chamada de qualquer lugar com o prefixo U_; Static Function só existe dentro do próprio fonte.
Nome
em .prw, só os dez primeiros caracteres contam — e o U_ gasta dois deles.
Nesta página3 seções

Variável, tipo e laço têm artigo próprio nesta série. Aqui o assunto é o esqueleto: o que um arquivo precisa ter para compilar, ser encontrado e ser chamado.

Do arquivo ao RPO

O servidor Protheus não lê o seu .prw. Ele lê o RPO, o repositório de objetos. Compilar é pegar o fonte, transformar em um objeto binário — o APO — e gravar esse objeto dentro do RPO do ambiente.

Duas consequências práticas:

  1. Salvar não é publicar. O fonte alterado na sua máquina não muda nada no servidor até ser compilado de novo, no RPO certo.
  2. Chamar função que não está no RPO derruba a execução. O erro diz que a função não existe — o mesmo para nome errado, fonte compilado em outro ambiente e fonte que nunca subiu.

O próprio padrão se protege disso quando a chamada é opcional. Em Frtx272T09(), a rotina testa a existência antes de chamar:

ADVPL / TLPP
Local lFRTAtuDesc := FindFunction("U_FRTATUDESC")

Vale para o seu código também: chamada a User Function de outro fonte, que pode não estar compilado em todo ambiente, passa por FindFunction primeiro. Para compilar fora do TDS, veja Compilar AdvPL e TLPP pela linha de comando e as oito armadilhas ao compilar fora do TDS.

Um fonte completo, parte por parte

PGV001.prwADVPL / TLPP
#Include "totvs.ch"

#Define TITULO_AVISO "Conferência de ambiente"

/*/{Protheus.doc} PGV001
Mostra ao usuário a data base, a hora e a filial logada.
Serve de esqueleto: include, constante, cabeçalho, função e retorno.
@type user function
@author Pragiva
@since 17/09/2026
@version 1.0
@return logical, .T. quando a mensagem foi exibida
@example U_PGV001()
/*/
User Function PGV001()

    Local cTexto := ""
 Local lOk := .F.

    cTexto := MontaTexto()

    If !Empty(cTexto)
        MsgInfo(cTexto, TITULO_AVISO)
 lOk := .T.
    EndIf

Return lOk

/*/{Protheus.doc} MontaTexto
Monta o texto exibido por PGV001. Visível só dentro deste fonte.
@type static function
@author Pragiva
@return character, texto pronto para exibição
/*/
Static Function MontaTexto()

    Local cRet := ""

    cRet := "Data base: " + DToC(dDataBase) + CRLF
    cRet += "Hora: " + Time() + CRLF
    cRet += "Filial: " + cFilAnt

Return cRet

Includes

#Include traz para o fonte um arquivo .ch: comandos e constantes que o pré-processador aplica antes de compilar. Sem o include certo, o comando vira nome desconhecido e o fonte não compila.

Na grande maioria dos fontes AdvPL, um dos dois já resolve: protheus.ch sozinho cobre boa parte dos comandos do dia a dia, e totvs.ch completa o que falta em cadastro e regra de negócio — juntos, os dois abrem o suficiente para a esmagadora maioria das rotinas. O resto recorre a outro .ch específico do módulo ou do recurso: topconn.ch para TCQuery, tbiconn.ch para PREPARE ENVIRONMENT em job.

#define

#Define cria uma constante de pré-processador: antes de compilar, cada TITULO_AVISO do arquivo é trocado pelo texto. Não é variável, não ocupa memória em execução e vale só dentro do fonte em que foi declarada. Use para valor fixo que se repete — título, código de status. Caixa alta é convenção, e é a que todo mundo lê.

Cabeçalho Protheus.doc

O bloco /*/{Protheus.doc} Nome... /*/ é comentário para o compilador e documentação para quem vem depois; o TDS consegue gerar documentação a partir dele. As tags que mais aparecem em código de produção:

TagPara quê
@typeuser function, function, static function, method, class
@authorquem escreveu
@sincedata de criação
@versionversão do fonte
@paramum por parâmetro: nome, tipo, descrição
@returntipo e significado do retorno
@samplea chamada, como alguém copiaria
@examplea mesma ideia de @sample, menos usada
@descriptiondescrição alternativa a Descrição, mais longa
@see / @obsreferência e observação

@history soa como a mais óbvia para “manutenção do fonte”, mas na prática é a mais rara das citadas aqui — bem atrás de @author e @since, presentes na maior parte dos cabeçalhos. @return e @param também são comuns; @sample e @description já aparecem em minoria dos fontes.

Um exemplo real do padrão está em GravaFKY(), documentando a função com @author, @param e @return.

User Function e Static Function

O Function sem prefixo é reservado: só compila com a chave da TOTVS. Para quem customiza, sobram duas formas.

User Function é visível em todo o RPO. O nome declarado é PGV001, mas quem chama usa U_PGV001() — no menu, em ExecBlock, em fórmula, em outro fonte. É também a forma de todo ponto de entrada (veja como o padrão te chama).

Static Function só existe dentro do arquivo em que foi escrita. Outro fonte que chame MontaTexto() recebe erro de função inexistente, mesmo com os dois compilados. É o lugar da rotina auxiliar: não colide com função de mesmo nome em outro fonte nem vira ponto de chamada que alguém passa a usar sem você saber. Chama-se sem prefixo.

O limite de nome é a armadilha. Em .prw, o compilador considera apenas os dez primeiros caracteres do nome da função. Como o U_ entra na conta, uma User Function tem oito caracteres úteis. PGVFATUR01 e PGVFATUR02 disputam o mesmo nome — escolha nomes que se distinguem até o oitavo caractere.

Em .tlpp o nome longo é aceito: o padrão tem uma função chamada tiposTitulosValidosFINA087A(). Mas .tlpp novo costuma vir com namespace (no padrão totvs.protheus.backoffice.<modulo>.<feature>), e aí a chamada de fora precisa do nome qualificado pelo namespace — só U_Nome não encontra a função.

Corpo e Return

Entre a declaração e o Return fica o corpo: primeiro as declarações Local, depois as instruções. A execução é sequencial — quando PGV001 chama MontaTexto(), ela só segue para a linha seguinte depois que MontaTexto passar pelo próprio Return.

Return encerra a função e, opcionalmente, devolve um valor. Return lOk entrega o lógico a quem chamou; Return sozinho devolve Nil. Antes dele é onde se fecha o que a função abriu — alias de consulta, arquivo, área salva. Esse encerramento é assunto de GetArea e RestArea.

O que a série cobre depois

O exemplo usa só Local; as outras declarações estão em escopo de variáveis, os valores em tipos de dados e o fluxo em condições e laços.


Se um fonte seu compila e não faz o que deveria, fale com a gente.

Referências

  1. TDNtdn.totvs.com

    TDN — AdvPL
  2. TDNtdn.totvs.com

    TDN — User Function
  3. TDNtdn.totvs.com

    TDN — Static Function

Continue por aqui