advpl-specialist
Skills

ADVPL to TLPP Migration

Abordagem sistematica para converter codigo ADVPL procedural legado para TLPP moderno com programacao orientada a objetos

ADVPL to TLPP Migration

Abordagem sistematica para converter codigo ADVPL procedural legado para TLPP moderno com padroes de programacao orientada a objetos. Esta skill guia o processo de migracao de arquivos .prw com User Functions e Static Functions para arquivos .tlpp com namespaces, classes e metodos -- preservando compatibilidade retroativa com chamadores existentes.

Quando Usar

  • Converter funcoes ADVPL procedurais para classes TLPP
  • Refatorar User Functions em classes de servico orientadas a objeto
  • Substituir escopo de variaveis Private/Public por propriedades de classe
  • Modernizar multiplas diretivas #Include para includes TLPP .th (tlpp-core.th, tlpp-rest.th, etc.) e adicionar declaracoes de namespace apropriadas
  • Migrar Static Functions para metodos privados de classe
  • Encapsular chamadas de funcoes legadas para compatibilidade retroativa durante migracao gradual
  • Qualquer conversao de arquivo .prw para .tlpp

Estrategia de Migracao

  1. Analisar o arquivo .prw de origem para entender todas as funcoes, variaveis e dependencias externas
  2. Identificar cada User Function e Static Function, seus parametros e estado compartilhado
  3. Mapear funcoes para uma estrutura de classe -- metodos, propriedades, parametros do construtor
  4. Usuario aprova o design de classe proposto antes da geracao de codigo
  5. Gerar o arquivo .tlpp com namespace adequado, definicao de classe e implementacoes de metodos
  6. Executar checklist do migration-checklist.md para verificar completude
  7. Validar que o codigo compila e todos os chamadores existentes ainda funcionam

Regras de Conversao Principais

Construto ADVPLEquivalente TLPPNotas
#Include "TOTVS.CH"#Include "tlpp-core.th"Usar includes especificos TLPP (arquivos .th); Protheus.ch e obsoleto. NAO adicionar using namespace tlpp.core, tlpp.rest ou tlpp.log -- usar os includes .th
User Function Name()namespace custom.module.service; class NameService; method execute()Ponto de entrada principal se torna o metodo publico primario
Static Function Helper()method helper() as privateFuncoes internas se tornam metodos privados
Private cVar := "x"data cVar as character (propriedade de classe)Variaveis Private se tornam declaracoes data de classe
Public nGlobalRemover -- passar via construtor/parametrosVariaveis Public devem ser eliminadas completamente
Local aArray := {}Inalterado dentro dos metodosVariaveis locais permanecem como estao no corpo dos metodos

Convencoes de Nomenclatura TLPP (Padrao Oficial TOTVS)

Namespaces

Todos os nomes devem ser em minusculas, separados por pontos, sem underscores.

Para codigo de produto TOTVS:

totvs.protheus.<segmento>.<agrupador/servico>

Segmentos disponiveis: agrobusiness, backoffice, construction, distribution, educational, financial, health, hospitality, legal, manufacturing, retail, services

Exemplos:

  • totvs.protheus.backoffice.customer
  • totvs.protheus.financial.payment.receive
  • totvs.protheus.manufacturing.material.balance

Para customizacoes de cliente (mais comum em migracoes):

custom.<agrupador>.<servico>

Comeca com custom., o restante e livre. Exemplos:

  • custom.cadastros.cliente
  • custom.relatorios.customizados
  • custom.faturamento.pedido

Nomenclatura de Arquivos

Para produto TOTVS: <segmento>.<agrupador/servico>.<funcionalidade>.tlpp

Para customizacoes de cliente: custom.<agrupador>.<funcionalidade>.tlpp

Classes, Funcoes e Metodos

ElementoConvencaoExemplo
ClassesPascalCaseContactsController, PedidoService
FuncoescamelCasecontactsController(), calcTotal()
MetodoscamelCasevalidName(), processOrder()

Sem underscores em nenhum identificador.

Exemplo Antes/Depois

Antes (ADVPL Procedural) -- CalcPed.prw

#Include "TOTVS.CH"
#Include "TopConn.ch"

/*/{Protheus.doc} CalcPed
Calcula o total de um pedido de venda
@type User Function
@author Dev
@since 01/01/2024
@param cPedido, Caractere, Numero do pedido
@return nTotal, Numerico, Valor total do pedido
/*/
User Function CalcPed(cPedido)
    Local nTotal := 0
    Private cAliasPed := "SC5"
    Private cAliasItens := "SC6"

    Local aArea := GetArea()

    Begin Sequence

        DbSelectArea(cAliasPed)
        DbSetOrder(1)

        If !DbSeek(xFilial(cAliasPed) + cPedido)
            Conout("Pedido nao encontrado: " + cPedido)
            Break
        EndIf

        nTotal := fCalcTotal(cPedido)

    Recover Using oError
        Conout("Erro em CalcPed: " + oError:Description)
        nTotal := 0
    End Sequence

    RestArea(aArea)
Return nTotal

Static Function fCalcTotal(cPedido)
    Local nSoma := 0

    DbSelectArea(cAliasItens)
    DbSetOrder(1)

    DbSeek(xFilial(cAliasItens) + cPedido)
    While !Eof() .And. SC6->C6_NUM == cPedido
        nSoma += SC6->C6_VALOR * SC6->C6_QTDVEN
        DbSkip()
    EndDo

Return nSoma

Depois (TLPP Orientado a Objetos) -- custom.faturamento.pedido.tlpp

#Include "tlpp-core.th"

namespace custom.faturamento.pedido

class PedidoService

    data cAliasPed   as character
    data cAliasItens as character

    public method new() as object
    public method calcTotal(cPedido as character) as numeric
    private method somaItens(cPedido as character) as numeric

endclass

method new() class PedidoService
    ::cAliasPed   := "SC5"
    ::cAliasItens := "SC6"
return self

method calcTotal(cPedido as character) class PedidoService
    local nTotal := 0
    local aArea  := GetArea()

    begin sequence

        DbSelectArea(::cAliasPed)
        DbSetOrder(1)

        if !DbSeek(xFilial(::cAliasPed) + cPedido)
            FWLogMsg("WARN", , "PedidoService", "calcTotal", , , ;
                "Pedido nao encontrado: " + cPedido)
            break
        endif

        nTotal := ::somaItens(cPedido)

    recover using oError
        FWLogMsg("ERROR", , "PedidoService", "calcTotal", , , ;
            "Erro: " + oError:Description)
        nTotal := 0
    end sequence

    RestArea(aArea)
return nTotal

method somaItens(cPedido as character) class PedidoService
    local nSoma := 0

    DbSelectArea(::cAliasItens)
    DbSetOrder(1)

    DbSeek(xFilial(::cAliasItens) + cPedido)
    while !Eof() .And. (::cAliasItens)->(C6_NUM) == cPedido
        nSoma += (::cAliasItens)->(C6_VALOR) * (::cAliasItens)->(C6_QTDVEN)
        DbSkip()
    enddo

return nSoma

Wrapper de Compatibilidade Retroativa -- CalcPed.prw (preservado)

#Include "TOTVS.CH"

/*/{Protheus.doc} CalcPed
Wrapper de compatibilidade - delega para PedidoService (TLPP)
@type User Function
@author Dev
@since 01/01/2024
@param cPedido, Caractere, Numero do pedido
@return nTotal, Numerico, Valor total do pedido
/*/
User Function CalcPed(cPedido)
    Local oService := custom.faturamento.pedido.PedidoService():new()
Return oService:calcTotal(cPedido)

Decisoes-Chave de Migracao

DecisaoDiretriz
Uma classe por arquivoCada arquivo .tlpp deve conter uma unica classe com responsabilidade clara
Namespace = convencao oficial TOTVSPara customizacoes usar custom.<agrupador>.<servico>. Para produto TOTVS usar totvs.protheus.<segmento>.<agrupador>. Tudo minusculo, sem underscores, separado por pontos
User Function preservada como wrapperManter a User Function original .prw como wrapper fino que delega para a nova classe
Migracao gradualMigrar um grupo de funcoes por vez; wrappers garantem que chamadores existentes nao quebrem
Construtor para estado compartilhadoValores que eram variaveis Private/Public compartilhadas entre funcoes devem ser passados pelo construtor ou definidos como propriedades de classe
Logging sobre ConoutPreferir FWLogMsg() ou FWLogError() sobre Conout() raw em classes TLPP

Erros Comuns

ErroConsequenciaCorrecao
Remover User Function sem wrapperQuebra todos os chamadores externos (u_FuncName)Sempre manter wrapper .prw durante migracao
Usar variaveis Private dentro de metodosVariaveis vazam para metodos chamados inesperadamenteConverter para propriedades data de classe ou variaveis Local
Esquecer prefixo :: para propriedades de classeReferencia variavel local indefinida em vez de propriedadeSempre usar ::propertyName dentro de metodos
Colocar multiplas classes em um arquivo .tlppDificil manter, confusao de namespaceUma classe por arquivo
Nao preservar GetArea()/RestArea()Estado do cursor de banco corrompido para chamadoresSempre salvar e restaurar area em metodos que acessam DB
Pular xFilial() apos migracaoQueries multi-filial retornam dados erradosSempre usar xFilial(cAlias) em operacoes DbSeek
Ignorar variaveis Static usadas entre funcoesEstado compartilhado perdido quando funcoes viram metodosConverter variaveis Static para propriedades data de classe

Arquivos de Suporte

Esta skill inclui os seguintes arquivos de referencia:

  • migration-rules.md - Mapeamento completo de todos os construtos ADVPL para equivalentes TLPP
  • migration-checklist.md - Checklist passo a passo para executar e validar uma migracao

Nesta pagina