Help us improve
Share bugs, ideas, or general feedback.
From plugadvpl
Covers ADVPL beyond basic CRUD/MVC: ACID transactions (Begin Transaction/DisarmTransaction), TReport reports, bulk file I/O for SPED/EDI, threads (MsRunInThread), classic OOP, legacy migration, and financial rounding.
npx claudepluginhub jonipraia/plugadvpl --plugin plugadvplHow this skill is triggered — by the user, by Claude, or both
Slash command
/plugadvpl:advpl-advancedThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
ADVPL avançado cobre o que vai além do CRUD MVC básico: transações ACID, relatórios estruturados (TReport), manipulação de arquivos em massa (SPED, EDI), captura de informações multi-linha (grids dinâmicas fora de MVC), threads paralelas, OOP clássico, e técnicas de programação eficiente.
Reviews ADVPL/TLPP source code against 35 automated rules (22 single-file + 13 cross-file). Run after editing ADVPL files or when asked to review code.
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Share bugs, ideas, or general feedback.
ADVPL avançado cobre o que vai além do CRUD MVC básico: transações ACID, relatórios estruturados (TReport), manipulação de arquivos em massa (SPED, EDI), captura de informações multi-linha (grids dinâmicas fora de MVC), threads paralelas, OOP clássico, e técnicas de programação eficiente.
Begin Transaction / BeginTran (gravações atômicas multi-tabela)..csv, .txt, layouts SPED/EDI/SEFAZ) com FCreate/FOpen/FRead/FWrite ou FT_FUse/FT_FReadLn (família 2).MsNewGetDados, TCBrowse) fora do contexto MVC.NoRound), I/O bulk.MsRunInThread (cuidado com WorkArea por thread).Class ... Method ... EndClass).Modelo1/Modelo2/Modelo3/AxCadastro (legacy) → MVC moderno.Begin Transaction
RecLock("SC5", .T.)
SC5->C5_NUM := cNumPed
SC5->C5_FILIAL := xFilial("SC5")
SC5->C5_CLIENTE := cCli
SC5->(MsUnlock())
RecLock("SC6", .T.)
SC6->C6_FILIAL := xFilial("SC6")
SC6->C6_NUM := cNumPed
SC6->C6_ITEM := "01"
SC6->(MsUnlock())
If lErroNaValidacao
DisarmTransaction() // forca rollback explicito
Break // sai do bloco
EndIf
End Transaction
Begin Transaction é multi-tabela ACID — garante atomicidade.BeginTran/EndTran é forma alternativa (mais rara).DisarmTransaction() antes do Break força rollback explícito.MsgInfo, Pergunte, MsgBox) dentro de transação — trava o banco com lock segurando.Veja [[advpl-code-review]] regra BP-002 (impl: BEGIN TRANSACTION sem END) e [[advpl-refactoring]] padrão 6.
#include "TOTVS.CH"
#include "TBICONN.CH"
#include "TOPCONN.CH"
#include "RPTDEF.CH"
User Function ZRel01()
Local oReport := Nil
oReport := TReport():New( ;
"ZREL01",; // ID
"Relatorio de Clientes",; // titulo
"ZREL01",; // pergunta SX1 (opcional)
{|oReport| ReportPrint(oReport)},; // callback
"Lista clientes por grupo") // descricao
DefineSecoes(oReport)
oReport:PrintDialog() // abre dialog de impressao
Return Nil
Static Function DefineSecoes(oReport)
Local oSec1 := TRSection():New(oReport, "Clientes", {"SA1"})
TRCell():New(oSec1, "A1_COD", "SA1", "Codigo", "@!", 6)
TRCell():New(oSec1, "A1_NOME", "SA1", "Nome", "@!", 30)
TRCell():New(oSec1, "A1_GRUPO", "SA1", "Grupo", "@!", 6)
Return Nil
Static Function ReportPrint(oReport)
Local oSec1 := oReport:Section(1)
oSec1:Init()
DbSelectArea("SA1")
DbGoTop()
While !Eof()
oSec1:PrintLine()
SA1->(DbSkip())
EndDo
oSec1:Finish()
Return Nil
Saída: PDF / Excel / CSV / TXT / HTML nativo (usuário escolhe no dialog).
TReport (singular) é a API canônica. TReports (plural) era uma classe anterior, hoje em desuso. Veja diferença TReports vs TReport.
Para queries complexas que precisam de tabela intermediária sem poluir SQL nativo (e sem CREATE TABLE direto que viola padrão):
Local oTmp := FWTemporaryTable():New("TMP")
oTmp:SetFields({ ;
{"COD", "C", 6, 0}, ;
{"DESC", "C", 30, 0}, ;
{"VLR", "N", 14, 2}, ;
{"DT", "D", 8, 0} ;
})
oTmp:AddIndex("01", {"COD"})
oTmp:Create()
// usa "TMP" como alias normal
DbSelectArea("TMP")
RecLock("TMP", .T.)
TMP->COD := "001"
TMP->VLR := 100.50
TMP->(MsUnlock())
// no fim, sempre limpar
oTmp:Delete()
Tabela existe só durante a sessão. Garbage collection automático no :Delete().
Duas famílias:
FCreate/FOpen/FRead/FWrite/FClose (baixo nível)Local nHnd := FCreate("\system\out.txt")
If nHnd < 0
ConOut("Erro FCreate: " + cValToChar(FError()))
Return
EndIf
FWrite(nHnd, "Linha 1" + Chr(13) + Chr(10))
FWrite(nHnd, "Linha 2" + Chr(13) + Chr(10))
FClose(nHnd)
Use Família 1 para arquivos binários ou pequenos. Sempre cheque retorno < 0 (erro).
FT_FUse/FT_FReadLn/FT_FGoTop/FT_FEof (eficiente, GB-scale)Local nHnd := FT_FUse("\system\sped.txt")
If nHnd < 0
ConOut("Erro abrir arquivo")
Return
EndIf
FT_FGoTop()
While !FT_FEof()
cLinha := FT_FReadLn()
ProcessaLinha(cLinha)
FT_FSkip()
EndDo
FT_FUse() // libera handle (FT_FUse sem args = close)
Use Família 2 para SPED, EDI, layouts SEFAZ, arquivos > 100MB — buffered, não carrega tudo em memória. Família 1 carrega o arquivo inteiro pra RAM.
MemoWrite / MemoRead — atalho pra escrita rápida// Grava string em arquivo (sobrescreve)
MemoWrite("\system\debug.txt", cStr)
// Grava com append
MemoWrite("\system\debug.txt", cStr, .T.)
// Le inteiro
cContent := MemoRead("\system\debug.txt")
Útil para debug em JOB ou serialização rápida. Não use pra arquivos > 10MB (carrega tudo em memória).
Local aHeader := {}
Local aCols := {}
Local oGrid := Nil
// aHeader: {titulo, campo, picture, tamanho, decimal, valid, usado, tipo, F3, when}
aAdd(aHeader, {"Codigo", "COD", "@!", 6, 0, "", "", "C", "", ""})
aAdd(aHeader, {"Nome", "NOME", "@!", 30, 0, "", "", "C", "", ""})
aAdd(aHeader, {"Valor", "VLR", "@E 999.999.999,99", 14, 2, "", "", "N", "", ""})
// aCols: array de arrays — cada linha é um array com 1 valor por coluna + flag deletado
aAdd(aCols, {"001", "Cliente A", 100.50, .F.})
aAdd(aCols, {"002", "Cliente B", 200.00, .F.})
oGrid := MsNewGetDados():New( ;
0, 0, 200, 400,; // nTop, nLeft, nBottom, nRight
GD_INSERT + GD_UPDATE + GD_DELETE,; // operacoes permitidas
"AllwaysTrue",; // bLineValid
"AllwaysTrue",; // bTudoOk
"",; // cIniCpos
,;
999,; // max linhas
, , ,;
oDlg,;
aHeader,;
aCols)
Padrão para dialogs ad-hoc onde MVC seria over-engineering (ex: rotina de importação custom, popup de selecionar registros).
#include "TOTVS.CH"
Class TCliente
Data cCodigo
Data cNome
Data nLimite
Data lAtivo
Method New(cCodigo) Constructor
Method GetNome() // public
Method SetLimite(nValor) // public
Method Save() // public
EndClass
Method New(cCodigo) Class TCliente
::cCodigo := cCodigo
::cNome := ""
::nLimite := 0
::lAtivo := .T.
If !Empty(cCodigo) .And. DbSelectArea("SA1") .And. DbSeek(xFilial("SA1") + cCodigo)
::cNome := AllTrim(SA1->A1_NOME)
::nLimite := SA1->A1_LC
EndIf
Return Self
Method GetNome() Class TCliente
Return ::cNome
Method SetLimite(nValor) Class TCliente
If nValor < 0
Return .F.
EndIf
::nLimite := nValor
Return .T.
Method Save() Class TCliente
DbSelectArea("SA1")
DbSetOrder(1)
If DbSeek(xFilial("SA1") + ::cCodigo)
RecLock("SA1", .F.)
Else
RecLock("SA1", .T.)
SA1->A1_FILIAL := xFilial("SA1")
SA1->A1_COD := ::cCodigo
EndIf
SA1->A1_NOME := ::cNome
SA1->A1_LC := ::nLimite
SA1->(MsUnlock())
Return .T.
Uso:
Local oCli := TCliente():New("000001")
oCli:SetLimite(5000)
oCli:Save()
ConOut(oCli:GetNome())
ADVPL clássico não tem
private/protected— todos os Data/Method são essencialmente públicos. TLPP corrige isso (veja[[advpl-tlpp]]).
MsRunInThread// Dispara funcao XYZProcParalelo numa nova thread Protheus
Local lOk := MsRunInThread(0, "XYZProcParalelo", aDados)
// nSlot = 0 (qualquer); 2o param = nome da funcao; 3o em diante = args
Static Function XYZProcParalelo(aDados)
Local aArea := GetArea() // sempre saveArea por thread!
Local nI
For nI := 1 To Len(aDados)
// processa item aDados[nI]
Next nI
RestArea(aArea) // sempre restArea no fim
Return Nil
Crítico em thread:
- Cada thread herda o ambiente do pai (cFilAnt, cEmpAnt, RpcSetEnv state).
- Cada thread DEVE fazer seu próprio
GetArea/RestArea— sem isso, threads disputam o ponteiro do alias e geram bug "qual registro tô vendo?".- Cada thread DEVE ter seu próprio
DbSelectArea/DbSetOrder— não confie no estado do pai.- Limite prático: pool default do AppServer aceita ~10-50 threads simultâneas. Acima disso, lentidão.
Veja [[advpl-jobs-rpc]] para mais detalhes em contexto JOB.
Type, ValType, Eval// Type retorna tipo de uma EXPRESSAO como string
cTipo := Type("nValor") // "N", "C", "D", "L", "A", "O", "U" (undefined)
// ValType retorna tipo de um VALOR
cTipo := ValType(uValor) // mesmo retorno
// Eval avalia codeblock em runtime
bBloco := {|x| x * 2}
nRet := Eval(bBloco, 10) // 20
// __ClassName retorna nome da classe (ADVPL OO)
cClasse := __ClassName(oObj) // "TCliente"
// __ClassMethArr lista métodos da classe
aMtds := __ClassMethArr(oObj)
Em TLPP, use
FwReflectionque é mais estruturado (veja[[advpl-tlpp]]).
NoRound(nValor, nCasas) para arredondamento financeiro determinístico (evita banker's rounding implícito).For Each em vez de For/Next com índice quando possível (mais legível).+ em loop para construir strings — use array + FwArrayJoin (em TOTVS R26+) ou Array2String (legacy).Local, If, EndIf em PascalCase (convenção TOTVS).Static no fonte ou Local na função em vez de Public global — escopo limpo.AScan em vez de loop manual para busca em array.aSize(arr, n) para redimensionar em vez de criar novo.FwTimeStamp() em vez de concat Date() + Time() (mais rápido + formato ISO).AxCadastro/Modelo2/Modelo3 em código novo → catalogado MOD-004 (não detectado pelo lint atual, mas é warning conceitual). Veja [[advpl-refactoring]] padrão 4.MsgInfo/Alert em JOB → bloqueia execução. Veja [[advpl-jobs-rpc]].Begin Transaction com UI dentro → deadlock garantido. BP-002 (impl).+ em vez de array + FwArrayJoin (PERF-004 catalog).FOpen/FRead/FT_FUse (retornam -1 ou < 0) → exception silenciosa.GetArea/RestArea → corrompe contexto do pai.MemoRead em arquivo > 10MB → carrega tudo em RAM, pode estourar memória.:Finish() na seção → última linha não imprime.[[advpl-tlpp]] — OOP moderno com private/protected/static + namespaces.[[advpl-refactoring]] — AxCadastro → MVC, concat em loop → array.[[advpl-fundamentals]] — Static/Local/Private/Public scopes; reservadas.[[advpl-code-review]] — BP-002 (Transaction), BP-005 (>6 params), PERF-001/002/003.[[advpl-embedded-sql]] — SQL em transação, MPSysOpenQuery.[[advpl-mvc]] — quando preferir MVC vs MsNewGetDados ad-hoc.[[advpl-jobs-rpc]] — threads e RpcSetEnv em background.[[advpl-encoding]] — arquivos cp1252 vs UTF-8 quando ler/escrever texto.[[advpl-debugging]] — debug de transação travada, lock órfão.[[plugadvpl-index-usage]] — /plugadvpl:find function TReport, etc./plugadvpl:find function TReport — relatórios indexados no projeto./plugadvpl:grep "Begin Transaction\|BeginTran" — transações./plugadvpl:grep "MsRunInThread\|StartJob" — threading./plugadvpl:grep "FT_FUse\|FCreate" — manipulação de arquivos./plugadvpl:grep "Class.*Method\|EndClass" — OOP clássico./plugadvpl:lint <arq> — checa BP-001 (RecLock+MsUnlock), BP-002 (Transaction), BP-005 (>6 params), BP-006 (RecLock raw mix), MOD-001/002 (ConOut, Public).operacoes_escrita cataloga todos os RecLock+MsUnlock patterns do projeto.Para detalhes completos (~2.3k linhas), consulte reference.md ao lado deste arquivo: