Você abre um fonte AdvPL de 2008 e vê os mesmos AllTrim(), SubStr(), DBSeek() que escreveu ontem. Não é coincidência. Mais de 80% do que se programa em AdvPL gira em torno de cerca de 20 funções. Esse é o conjunto que vale decorar — depois disso, o resto da linguagem aparece quando precisa.

Esse guia não tenta ser exaustivo. É a lista mínima viável: o que você usa todo dia, mesmo sem perceber. Se você está começando agora ou voltou ao Protheus depois de anos no React, é por aqui.

Como escolhi essas 20

Critério simples: funções que aparecem em quase todo .prw que abre uma tela de cadastro, processa uma rotina ou integra com banco. Algumas são da linguagem xBase original (Clipper, Visual Objects). Outras são do framework Microsiga (prefixos FW, MV_, Ap). As duas turmas convivem e se misturam num projeto típico.

Strings — 6 funções

1. AllTrim() — remove espaços nas duas pontas

cNome := AllTrim(cVar)         // "  joao  " → "joao"
cCpf  := AllTrim(SA1->A1_CGC)  // util pra comparar campos do banco

Use sempre que comparar strings vindas do banco. Campos Character em Protheus vêm preenchidos com espaços até o tamanho declarado.

2. SubStr() — extrai pedaço da string

cAno := SubStr(cData, 1, 4)        // "20260105" → "2026"
cMes := SubStr(cData, 5, 2)        // "20260105" → "01"

Pegadinha: índice começa em 1, não 0. E o terceiro parâmetro é quantidade, não posição final.

3. Upper() / Lower() — converte caixa

cBusca := Upper(AllTrim(cFiltro))  // padroniza pra busca case-insensitive
cEmail := Lower(SA1->A1_EMAIL)

Combinação Upper(AllTrim(...)) é o padrão de comparação de strings em Protheus.

4. PadL() / PadR() / StrZero() — preenchimento

StrZero(15, 6)        // "000015"
PadL("Joao", 10, " ") // "      Joao"
PadR(cCod, 20)        // alinha à esquerda, completa com espaços

StrZero() existe especificamente pra zerar à esquerda. Mais legível que PadL(cVal, n, "0").

5. StrTran() — substitui ocorrências

cTexto := StrTran(cTexto, ".", "")     // remove pontos
cFone  := StrTran(cFone, "-", "")      // limpa CPF/CNPJ/telefone

Equivalente ao String.replace de outras linguagens. Substitui todas as ocorrências, não só a primeira.

6. Empty() — string vazia ou só espaços?

If Empty(cCampo)
    Help(...,..., "Campo obrigatório")
EndIf

Não é só pra string. Empty() também trata data zerada (CTOD(" / / ")), zero numérico, array vazio, e NIL. Vale como validação universal de "tem valor?".

Datas — 3 funções

7. Date() — data atual do sistema

dHoje := Date()                // data atual no formato Date
nAno  := Year(Date())

Retorna a data do servidor, não do cliente. Importante quando o ambiente é multi-fuso.

8. DToS() / SToD() — conversão data ↔ string

cDataStr := DToS(Date())       // 2026-05-06 → "20260506"
dData    := SToD("20260506")   // "20260506" → 06/05/2026
cDataBR  := DToC(Date())       // 2026-05-06 → "06/05/2026"

Use DToS pra gravar data em campo Character ou em SQL (formato ISO compactado). Use DToC pra exibir.

9. AddMonth() / AdjustDate() — aritmética de datas

dVencimento := AddMonth(Date(), 3)    // hoje + 3 meses
dDiaUtil    := AdjustDate(Date(), 5)  // hoje + 5 dias úteis (pula fins de semana)

AdjustDate respeita feriados se você tiver tabela SX5 com tabela 21 cadastrada. Senão só pula sábado e domingo.

Numéricos — 3 funções

10. Val() / Str() — conversão número ↔ string

nValor := Val("1234.56")          // 1234.56
cValor := Str(nValor, 12, 2)      // "     1234.56"
cValor := AllTrim(Str(nValor))    // "1234.56"

Str() com 1 argumento alinha à direita com 10 chars de largura — quase sempre você quer AllTrim(Str(...)).

11. Round() — arredonda

Round(3.456, 2)    // 3.46
Round(3.454, 2)    // 3.45
Round(1234.78, -2) // 1200 (arredonda à esquerda)

Segundo parâmetro negativo arredonda casas inteiras. Útil pra valores de impostos.

12. Int() / NoRound() — trunca, não arredonda

Int(3.789)          // 3 (parte inteira)
NoRound(3.789, 2)   // 3.78 (trunca em 2 casas)

Diferença importante de Round: NoRound nunca aumenta o valor. Use em rateio fiscal pra evitar arredondamento favorável a um lado.

Arrays — 2 funções

13. aAdd() — adiciona elemento ao final

aLista := {}
aAdd(aLista, "Item 1")
aAdd(aLista, {"Codigo", "Descricao"})  // adiciona um sub-array

Não confunda com aSize() (redimensiona) — aAdd aumenta o array dinamicamente em 1 posição.

14. aScan() — busca elemento

// Busca simples por igualdade
nPos := aScan(aLista, "Item 1")

// Busca com bloco de código (mais flexível)
nPos := aScan(aLista, {|x| x[1] == "0001" })

Retorna a posição (base 1) ou 0 se não encontrar. Versão com bloco é o padrão pra arrays multidimensionais.

Tabelas e Banco — 3 funções

15. DBSeek() — busca por chave de índice

DBSelectArea("SA1")
DBSetOrder(1)                    // índice por filial+codigo
If DBSeek(xFilial("SA1") + cCod)
    // achou
EndIf

Mais rápido que filtro porque usa o índice. Sempre defina DBSetOrder() antes — a ordem 1 nem sempre é a que você quer.

16. RecLock() / MsUnlock() — lock para escrita

RecLock("SA1", .F.)              // .F. = altera registro existente
SA1->A1_OBSERVA := "Atualizado"
MsUnlock()

RecLock("SA1", .T.)              // .T. = inclui novo registro
SA1->A1_COD := "999999"
MsUnlock()

Sempre chame MsUnlock() depois. Esquecer trava a tabela pra outros usuários até a transação ser fechada.

17. TCQuery() / TCSqlExec() — SQL direto no DBAccess

cQuery := "SELECT A1_COD, A1_NOME FROM " + RetSqlName("SA1") + ;
          " WHERE D_E_L_E_T_ = ' '"

cAlias := GetNextAlias()
TCQuery cQuery New Alias (cAlias)

While !(cAlias)->(EOF())
    // processa
    (cAlias)->(DBSkip())
End
(cAlias)->(DBCloseArea())

Use RetSqlName() pra resolver nome físico (com filial) e sempre filtre D_E_L_E_T_ — o Protheus marca delete lógico, não físico.

Framework Microsiga — 3 funções

18. GetMv() — lê parâmetro do sistema

cEmpresa := GetMv("MV_RAZSOC")             // string
nLimite  := GetMv("MV_LIMICRD", .F., 0)    // numérico, default 0
lAtivo   := GetMv("MV_USERAU", .F., .F.)   // lógico

Segundo parâmetro a .T. retorna o conteúdo bruto (sem cache). Terceiro é o default se o MV_ não existir — nunca confie que um parâmetro está cadastrado.

19. ExistBlock() / ExecBlock() — pontos de entrada

If ExistBlock("MA030VLD")
    lOk := ExecBlock("MA030VLD", .F., .F.)
    If !lOk
        Return .F.
    EndIf
EndIf

É como o sistema permite extensão sem alterar fonte padrão. Sempre cheque com ExistBlock antes — chamar ExecBlock direto num PE inexistente lança erro.

20. ApMsgYesNo() / MsgInfo() / MsgStop() — diálogos

If ApMsgYesNo("Confirma alteração?", "Atenção")
    // user clicou Sim
EndIf

MsgInfo("Operação concluída.")
MsgStop("Erro ao salvar registro.", "Erro fatal")

Família ApMsg* é a versão ApiAware — funciona em rotina batch, web e desktop. Os antigos MsgYesNo(), Aviso() só funcionavam na interface gráfica.

Qual função usar? Fluxo de decisão

flowchart TD Start[Tarefa] --> Q1{Tipo de dado?} Q1 -->|String| S1[AllTrim, SubStr,
Upper/Lower, PadL/StrZero,
StrTran, Empty] Q1 -->|Data| D1[Date, DToS/SToD,
AddMonth/AdjustDate] Q1 -->|Numero| N1[Val/Str, Round,
Int/NoRound] Q1 -->|Array| A1[aAdd, aScan] Q1 -->|Tabela| T1{Operacao?} T1 -->|Buscar| TB[DBSeek com
DBSetOrder] T1 -->|Gravar| TG[RecLock + MsUnlock] T1 -->|SQL livre| TQ[TCQuery] Q1 -->|Sistema| F1[GetMv, ExistBlock,
ApMsgYesNo]

Tabela resumo

#FunçãoCategoriaO que faz
1AllTrim()StringRemove espaços início e fim
2SubStr()StringExtrai substring
3Upper() / Lower()StringConverte caixa
4PadL() / PadR() / StrZero()StringPreenche caracteres
5StrTran()StringSubstitui ocorrências
6Empty()String/GeralVazio? (universal)
7Date()DataData atual do servidor
8DToS() / SToD()DataConversão data ↔ string
9AddMonth() / AdjustDate()DataAritmética de datas
10Val() / Str()NuméricoConversão número ↔ string
11Round()NuméricoArredonda
12Int() / NoRound()NuméricoTrunca sem arredondar
13aAdd()ArrayAdiciona ao final
14aScan()ArrayBusca posição
15DBSeek()TabelaBusca por chave de índice
16RecLock() / MsUnlock()TabelaLock pra gravar
17TCQuery() / TCSqlExec()BancoSQL direto via DBAccess
18GetMv()FrameworkLê parâmetro MV_
19ExistBlock() / ExecBlock()FrameworkPontos de entrada
20ApMsgYesNo() / MsgInfo()UIDiálogos modais

O caminho daqui

Saber essas 20 não te faz dev sênior — ainda falta dominar tipagem de variáveis (LOCAL vs STATIC), blocos de código, query builders modernos da TLPP, e a parte boa do framework MVC. Mas garante que você não trava na sintaxe quando precisa entregar.

Se quer ir além, a referência oficial está espalhada no TDN. Pra cheat sheet de equivalentes em TLPP, fica de olho — vai ter post.