ADVPL / TLPP — Skill de Desenvolvimento Protheus | Skills Pool
技能档案
ADVPL / TLPP — Skill de Desenvolvimento Protheus
Especialista em desenvolvimento ADVPL/TLPP para o ecossistema TOTVS Protheus. Use esta skill para auxiliar com: escrita e revisão de código ADVPL e TLPP, criação de pontos de entrada (convencional e MVC), desenvolvimento de APIs REST, consultas SQL/Embedded SQL, acesso a tabelas do Protheus, criação de classes OO, estrutura MVC (Model-View-Controller), reports com SetPrintServer/FWMSPrinter, tratamento de erros, boas práticas de nomenclatura, performance e debugging. Ativar sempre que o usuário mencionar: ADVPL, TLPP, Protheus, TDS, ponto de entrada, RecLock, MsUnlock, BeginTran, MVC Protheus, SX (SX3, SX5, SB1 etc.), customização ERP, User Function, rotina MATA/SIGACOM/SIGAFAT/SIGAEST, ou qualquer desenvolvimento para o ecossistema TOTVS. Inclui referências a boas práticas do Guia Oficial TDN TOTVS.
jaylson0 星标2026年3月31日
职业
分类
框架内部
技能内容
Documentação de referência: TDN TOTVS (tdn.totvs.com) | Central de Atendimento TOTVS | TOTVS Developers
1. FUNDAMENTOS DA LINGUAGEM
1.1 Tipos de Dados
Tipo
Prefixo
Exemplos
Notas
Caracter
c
cNome, cCodigo
Strings com aspas duplas
Numérico
n
nTotal, nQtd
Int e float unificados
Data
d
dEmissao, dVenc
Criada via CToD(), Date()
相关技能
Lógico
l
lAtivo, lOk
.T. / .F. (.Y./.N. também ok)
Array
a
aItens, aParam
Índice começa em 1; máx 100.000 elem.
Objeto
o
oModel, oGrid
Instâncias de classes
Bloco
b
bValid, bWhen
Código armazenado para execução futura
NIL
x (genérico)
xRet
Variável não inicializada
⚠️ ADVPL não é strongly typed — variáveis mudam de tipo dinamicamente. TLPP introduz tipagem forte.
1.2 Notação Húngara (convenção obrigatória)
Local cNome := "João" // c = Caracter
Local nSalario := 2500.00 // n = Numérico
Local dAdmiss := CToD("01/03/25") // d = Data
Local lAtivo := .T. // l = Lógico
Local aItens := {} // a = Array
Local oObj := NIL // o = Objeto
1.3 Escopo de Variáveis
Comando
Escopo
Boas Práticas
LOCAL
Função atual apenas
✅ Preferir sempre — não vaza
STATIC
Arquivo fonte; persiste entre chamadas
⚠️ Usar para contadores/caches locais
PRIVATE
Função atual + filhas (herança)
⚠️ Apenas quando necessário
PUBLIC
Todo o ambiente (sessão)
🚫 Evitar; causa efeitos colaterais
Regra de ouro: declare sempreLOCAL no topo da função; nunca use variáveis não declaradas.
1.4 Limitação de 10 Caracteres (ADVPL clássico)
// ❌ ERRADO — os dois nomes colapsam (mesmos 10 primeiros chars)
Local nTotalGeralAnual := 300
Local nTotalGeralMensal := 100 // MESMO que a anterior!
// ✅ CORRETO — diferenciar nos primeiros caracteres
Local nAnualTotal := 300
Local nMensalTotal := 100
TLPP resolve isso com suporte a nomes longos nativamente.
2. ESTRUTURA DE PROGRAMAS
2.1 Cabeçalho Padrão com Protheus.doc
#INCLUDE "PROTHEUS.CH"
#INCLUDE "TOTVS.CH"
/*/{Protheus.doc} MinhaFuncao
Breve descrição do que a função faz.
@type Function
@author Seu Nome
@since 2025-01-01
@version 1.0
@param cCodigo, Caracter, Código do produto
@param nQtd, Numérico, Quantidade a processar
@return lOk, Lógico, .T. se processou com sucesso
@see MATA010, SB1
/*/
User Function MinhaFuncao( cCodigo, nQtd )
Local lOk := .T.
// ... implementação
Return lOk
2.2 Tipos de Função
// Ponto de entrada — chamada pelo ERP (nome curto, máx 10 chars)
User Function MT010INC() // PE após inclusão em MATA010
// PARAMIXB traz os parâmetros do sistema
Local aParam := PARAMIXB
Return .T.
// Função auxiliar local ao arquivo
Static Function ValidaDados( cCod, nVal )
Return ( !Empty(cCod) .AND. nVal > 0 )
// Função pública (acessível via ExecBlock/CallFunc)
Function U_HelperFn()
Return NIL
🚫 NUNCA misturar funções de Framework (RecLock, MsUnlock, BeginTran) com funções de baixo nível (DBAppend, DBRUnlock, TCCommit) no mesmo processo.
3.2 Acesso via SQL (TCQuery / Embedded SQL)
Local cQuery := ""
Local cAliasQ := GetNextAlias()
cQuery := "SELECT B1_COD, B1_DESC "
cQuery += " FROM " + RetSqlName("SB1") + " SB1 "
cQuery += " WHERE B1_FILIAL = '" + xFilial("SB1") + "' "
cQuery += " AND SB1.D_E_L_E_T_ = ' ' " // Filtro de exclusão lógica
cQuery += " ORDER BY B1_COD"
TCQuery( cQuery, .T., cAliasQ )
If !(cAliasQ)->(EOF())
While !(cAliasQ)->(EOF())
ConOut("Produto: " + (cAliasQ)->B1_COD + " - " + (cAliasQ)->B1_DESC)
(cAliasQ)->(DBSkip())
EndDo
EndIf
(cAliasQ)->(DBCloseArea())
✅ Sempre filtrar D_E_L_E_T_ = ' ' (registros não excluídos logicamente).
✅ Sempre usar xFilial("ALIAS") no filtro de filial em consultas SQL.
✅ Usar RetSqlName() para obter o nome real da tabela no banco.
3.3 Posicionamento em Tabelas
// Busca com índice (preferível para performance)
If MsSeek( xFilial("SA1") + cCodCli, "SA1", "1" )
// Registro encontrado no índice 1 da SA1
cNomeCli := SA1->A1_NOME
EndIf
// Busca sem índice (evitar em volumes grandes)
SA1->(DBSetOrder(1))
If SA1->(DbSeek( xFilial("SA1") + cCodCli ))
cNomeCli := SA1->A1_NOME
EndIf
4. PONTOS DE ENTRADA (PE)
4.1 PE Convencional
#INCLUDE "PROTHEUS.CH"
/*/{Protheus.doc} MT010INC
Ponto de entrada executado após inclusão de produto em MATA010.
@type User Function
@author Dev
@since 2025-01-01
@param Nenhum (usa PARAMIXB)
@return NIL
/*/
User Function MT010INC()
Local aParam := PARAMIXB
// aParam[1] = código do produto incluído (conforme doc TDN)
ConOut("Produto incluído: " + SB1->B1_COD)
Return NIL
4.2 PE em MVC (padrão moderno — único PE por Model)
#INCLUDE "PROTHEUS.CH"
#INCLUDE "FWMVCDEF.CH"
/*/{Protheus.doc} GPEA010
Ponto de entrada único para fonte MVC GPEA010.
O ID do PE deve ser igual ao ID do Model de dados.
@type User Function
@param aParam, Array, Parâmetros via PARAMIXB
@return xRet, Variado, Depende do IDPonto executado
/*/
User Function GPEA010()
Local aParam := PARAMIXB
Local xRet := NIL
Local cIDPonto := ""
If ValType(aParam) == "A" .AND. Len(aParam) >= 1
cIDPonto := aParam[1] // Identifica qual momento do MVC chamou o PE
EndIf
Do Case
Case cIDPonto == "MODELINIT"
// Inicialização do model
xRet := .T.
Case cIDPonto == "MODELCOMMITTTS"
// Após gravação — dentro da transação
xRet := .T.
Case cIDPonto == "MODELCANCEL"
// Usuário cancelou
xRet := .F.
Case cIDPonto == "VIEWINIT"
// View foi inicializada
EndCase
Return xRet
⚠️ Em MVC: o nome do User Function (PE) deve ser igual ao ID do Model, mas nunca igual ao nome do arquivo fonte.
5. DESENVOLVIMENTO MVC
5.1 Estrutura de um Fonte MVC Completo
#INCLUDE "PROTHEUS.CH"
#INCLUDE "FWMVCDEF.CH"
#INCLUDE "TOTVS.CH"
// ---- CONTROLLER ----
User Function MYFONT01()
Local aArea := GetArea()
Local oBrowse
oBrowse := FwMBrowse():New()
oBrowse:SetAlias("SB1")
oBrowse:SetDescription("Cadastro de Produtos")
oBrowse:Activate()
RestArea(aArea)
Return NIL
// ---- MODEL ----
Static Function ModelDef()
Local oModel := MPFormModel():New("MYFONT01MDL", /*bPre*/, /*bPost*/, /*bCommit*/, /*bCancel*/)
Local oStruct := FWFormStruct(1, "SB1")
oModel:AddFields("SB1MASTER", /*parent*/, oStruct)
oModel:SetPrimaryKey({"B1_COD"})
Return oModel
// ---- VIEW ----
Static Function ViewDef()
Local oView := FWFormView():New()
Local oModel := FWLoadModel("MYFONT01")
oView:SetModel(oModel)
oView:AddField("VIEW01", FWFormStruct(2, "SB1"), "SB1MASTER")
oView:CreateHorizontalBox("TOP", 100)
oView:SetOwnerView("VIEW01", "TOP")
Return oView
6. REST API (ADVPL / TLPP)
6.1 Endpoint REST com TLPP (recomendado)
// Arquivo: MYAPI.tlpp
#INCLUDE "TLPP-CORE.TH"
#INCLUDE "TLPP-MVC.TH"
Namespace MyApp.Api
@Get("/produtos")
Function u_GetProdutos() As Object
Local jResp As JsonObject
Local cQuery As Character
Local cAliasQ As Character
jResp := JsonObject():New()
cAliasQ := GetNextAlias()
cQuery := "SELECT B1_COD, B1_DESC FROM " + RetSqlName("SB1")
cQuery += " WHERE B1_FILIAL = '" + xFilial("SB1") + "'"
cQuery += " AND D_E_L_E_T_ = ' '"
TCQuery(cQuery, .T., cAliasQ)
Local aItens := {}
While !(cAliasQ)->(EOF())
Local jItem := JsonObject():New()
jItem["codigo"] := AllTrim((cAliasQ)->B1_COD)
jItem["descricao"] := AllTrim((cAliasQ)->B1_DESC)
AAdd(aItens, jItem)
(cAliasQ)->(DBSkip())
EndDo
(cAliasQ)->(DBCloseArea())
jResp["items"] := aItens
oRest:setResponse(jResp:toJson())
Return jResp
6.2 REST com ADVPL clássico (WSRestful)
#INCLUDE "PROTHEUS.CH"
#INCLUDE "APWEBSRV.CH"
// Necessário registrar no appserver.ini: [HTTPServer] / [REST]
WSRestful oMeuServico Description "Serviço de Produtos"
WsData oRest As Object
WSMethod GET Description "Lista produtos" wsSyntax "/produtos"
End WSRestful
WSMethod GET WsReceive oMeuServico
Local jResp := JsonObject():New()
jResp["status"] := "ok"
::SetContentType("application/json")
::SetResponse(jResp:toJson())
Return .T.
7. TRATAMENTO DE ERROS
// Try/Catch nativo do ADVPL
Begin Sequence With {|oErr| BreakOnError(oErr)}
// Código que pode falhar
RecLock("SB1", .F.)
SB1->B1_DESC := "Teste"
MsUnlock()
Recover With oErr
// oErr:Description, oErr:Operation, oErr:SubCode
MsgStop("Erro: " + oErr:Description, "Atenção")
ConOut("[ERRO] " + oErr:Description + " - " + oErr:Operation)
End Sequence
// Alternativa: função ErrorBlock
Local bErr := ErrorBlock({|e| MyErrHandler(e)})
// ... código ...
ErrorBlock(bErr) // Restaura handler anterior
Static Function MyErrHandler(oErr)
ConOut("Erro capturado: " + oErr:Description)
Return Break(oErr) // Interrompe o sequence
8. PERFORMANCE — REGRAS CRÍTICAS
Evitar macroexecução desnecessária (&cVar) — tem overhead elevado. Prefira Eval(bBloco).
Fechar áreas abertas — sempre fechar TCQuery com DBCloseArea() após uso.
Transações curtas — não deixar BeginTran() aberto por longos períodos; aumenta locks.
Arrays grandes — evitar arrays com > 10.000 elementos; usar queries paginadas.
TCQuery vs ISAM — para relatórios e leituras, SQL é mais rápido; para gravação no ERP, use RecLock/MsUnlock.
ConOut para log — usar ConOut() em JOBs (sem interface). Nunca usar Alert() em processos servidor.
Filtros SQL — sempre indexar filtros de WHERE com campos indexados; evitar LIKE '%valor%'.
Não misturar funções de Framework com funções de baixo nível (DBAppend, DBRUnlock, TCCommit) no mesmo processo do ERP.
9. CONVENÇÕES DE NOMENCLATURA
Elemento
Padrão
Exemplo
Arquivo fonte
7 chars maiúsculos + extensão
MATA010.PRW, GPEA010.PRW
User Function
U_ + nome (para chamada externa)
U_ValidaCliente()
Static Function
PascalCase
ValidaDados()
Variável Local
Prefixo tipo + CamelCase
cNomeCli, nValTotal
Parâmetros
Mesmo padrão de variáveis
cCodigo, lAtivo
Constantes
UPPER_SNAKE_CASE
MAX_TENTATIVAS
Classes
PascalCase (sem limite de chars)
FwFormModel
Métodos
PascalCase
oModel:GetValue()
Includes
UPPER.CH
PROTHEUS.CH, TOTVS.CH
10. INCLUDES ESSENCIAIS
#INCLUDE "PROTHEUS.CH" // Base do Protheus — sempre incluir
#INCLUDE "TOTVS.CH" // Framework geral
#INCLUDE "FWMVCDEF.CH" // Constantes MVC (MODEL_FIELD_WHEN, etc.)
#INCLUDE "PARMTYPE.CH" // Validação de tipos de parâmetros
#INCLUDE "APWEBSRV.CH" // WebServices REST/SOAP (clássico)
#INCLUDE "TLPP-CORE.TH" // Núcleo TLPP (somente em fontes .tlpp)
#INCLUDE "TLPP-MVC.TH" // MVC em TLPP
11. TLPP — DIFERENÇAS E MODERNIZAÇÃO
Recurso
ADVPL clássico
TLPP
Tipagem
Dinâmica
Forte (As Character, As Numeric)
Nomes longos
Máx 10 chars
Ilimitado
Namespaces
Não
✅ Namespace MinhaEmpresa.Modulo
Parâmetros nomeados
Não
✅ fn(cNome := "João")
Try/Catch
Begin Sequence
✅ Try / Catch / Finally
Annotations REST
Não
✅ @Get, @Post, @Put
Extensão
.prw / .prg / .prx
.tlpp
Compatibilidade
—
Chama ADVPL e vice-versa
12. CHECKLIST DE QUALIDADE
Antes de entregar um fonte ADVPL/TLPP, verificar:
Todas as variáveis declaradas com LOCAL (ou escopo correto) no topo da função