SSkilltecabyclaudinhocode
Enviar skill
← Voltar para o catálogo

advpl-dev

Dados e Análise

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, trata

1estrelas
Ver no GitHub ↗Autor: jaylsonLicença: MIT

ADVPL / TLPP — Skill de Desenvolvimento Protheus

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

TipoPrefixoExemplosNotas
CaracterccNome, cCodigoStrings com aspas duplas
NumériconnTotal, nQtdInt e float unificados
DataddEmissao, dVencCriada via CToD(), Date()
LógicollAtivo, lOk.T. / .F. (.Y./.N. também ok)
ArrayaaItens, aParamÍndice começa em 1; máx 100.000 elem.
ObjetoooModel, oGridInstâncias de classes
BlocobbValid, bWhenCódigo armazenado para execução futura
NILx (genérico)xRetVariá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

ComandoEscopoBoas Práticas
LOCALFunção atual apenasPreferir sempre — não vaza
STATICArquivo fonte; persiste entre chamadas⚠️ Usar para contadores/caches locais
PRIVATEFunção atual + filhas (herança)⚠️ Apenas quando necessário
PUBLICTodo o ambiente (sessão)🚫 Evitar; causa efeitos colaterais

Regra de ouro: declare sempre LOCAL 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

3. BANCO DE DADOS — PADRÕES E ARMADILHAS

3.1 Acesso via ISAM (modo ERP)

// ✅ PADRÃO ERP: usar RecLock + MsUnlock + BeginTran/EndTran
BeginTran()
  If RecLock("SB1", .F.)   // .F. = alteração, .T. = inclusão
    SB1->B1_DESC := "Novo Produto"
    MsUnlock()
  EndIf
EndTran()

// Para inclusão de novo registro:
BeginTran()
  RecLock("SB1", .T.)      // .T. = append/novo
    SB1->B1_COD   := "000001"
    SB1->B1_DESC  := "Produto Teste"
  MsUnlock()
EndTran()

🚫 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  := FWFormV

Como adicionar

/plugin marketplace add jaylson/claude-skill-advpl

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Comentários · Nenhum comentário

Entre para comentar. Entrar

  • Ainda não há comentários. Seja o primeiro.