Resposta rápida
- Arquivo
.prwpara AdvPL,.tlpppara 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 prefixoU_;Static Functionsó existe dentro do próprio fonte.- Nome
- em
.prw, só os dez primeiros caracteres contam — e oU_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:
- 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.
- 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:
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
#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 cRetIncludes
#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:
| Tag | Para quê |
|---|---|
@type | user function, function, static function, method, class |
@author | quem escreveu |
@since | data de criação |
@version | versão do fonte |
@param | um por parâmetro: nome, tipo, descrição |
@return | tipo e significado do retorno |
@sample | a chamada, como alguém copiaria |
@example | a mesma ideia de @sample, menos usada |
@description | descrição alternativa a Descrição, mais longa |
@see / @obs | referê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
TDNtdn.totvs.com
TDN — AdvPLTDNtdn.totvs.com
TDN — User FunctionTDNtdn.totvs.com
TDN — Static Function
Continue por aqui
- Vídeo
DesenvolvimentoTutorial
Tipos de dados em AdvPL: as oito letras do ValType e como converter entre elas
Tipos de dados em AdvPL: as oito letras do ValType, como o AdvPL identifica cada tipo, como converter sem surpresa e o que a declaração com As garante.
- Vídeo
DesenvolvimentoTutorial
Arrays em AdvPL: índice em 1, aDel que não encolhe e cópia por referência
Como criar, crescer, procurar, ordenar e remover itens de um array em AdvPL, por que aDel precisa de aSize e por que atribuir um array não copia nada.