Pragiva — Tecnologia na prática

RecLock ou ExecAuto: como decidir a forma de gravar um registro em AdvPL

RecLock ou ExecAuto: RecLock grava direto na tabela sem regra do padrão; ExecAuto chama a rotina com validação, gatilho e ponto de entrada.

Desenvolvimento Tutorial 4 min de leitura

Assista o vídeo

Resposta rápida

Tabela sua (prefixo Z)
RecLock e MsUnlock, com os campos de controle preenchidos por você.
Tabela do padrão
rotina automática, MsExecAuto, que passa por validação, gatilho e ponto de entrada.
Rotina em MVC
a mesma chamada, ou o modelo direto com FWLoadModel.
Cliente (SA1) hoje
CRMA980; o MATA030 só repassa para ele quando MV_MVCSA1 está ligado.
Mais de uma gravação
tudo dentro de Begin Transaction, com DisarmTransaction no erro.
Nesta página6 seções

Toda customização que grava dado chega nessa escolha. Escolher errado não dá erro de compilação: dá um cliente sem os complementos que o cadastro criaria, um título sem o rateio da rotina ou uma integração nunca avisada. O erro aparece meses depois, em outro módulo.

É tentador ir direto com RecLock numa tabela do padrão:

ADVPL / TLPP
DbSelectArea("SA1")
If RecLock("SA1", .T.)
    SA1->A1_COD  := cCodigo
    SA1->A1_NOME := cNome
    SA1->(MsUnlock())
EndIf

Compila e grava, e não dispara mais nada: sem gatilho, sem validação do dicionário, sem o complemento que a rotina padrão geraria sozinha. É esse atalho que o resto deste texto evita.

O que cada caminho faz

RecLock trava um registro da área de trabalho, e você atribui campos diretamente até o MsUnlock. Nada mais roda — nem validação do dicionário, nem gatilho do SX7, nem ponto de entrada, nem integração: você está escrevendo no banco com a mão.

MsExecAuto executa a rotina de tela sem tela. Ela guarda o contexto do chamador (aHeader, aCols, Inclui, Altera), declara lMsHelpAuto como Private se ele ainda não existir, marca __cInterNet como "AUTOMATICO" e avalia o bloco que você passou. No fim, restaura o contexto e devolve .T. sempre.

E quem liga essa variável não é mágica: AutoGRLog(), no mesmo fonte, faz lMSErroAuto := .T. quando a variável existe como lógica. Por isso ela precisa ser Private no seu fonte, declarada antes da chamada — o conceito está em escopo de variáveis.

A regra de decisão

SituaçãoCaminho
Tabela customizada, regra toda suaRecLock
Campo customizado (_X) num registro padrão já gravadoRecLock em alteração
Incluir, alterar ou excluir documento/cadastro do padrãoMsExecAuto
Rotina padrão em MVCMsExecAuto na função da rotina, ou o modelo direto

A segunda linha é a exceção que confunde: marcar um campo seu num pedido que já existe não precisa de rotina automática — é o que o MT410INC faz. Já criar um pedido com RecLock pula estoque, liberação e tudo que o MATA410 faria — o erro caro.

RecLock numa tabela sua

Com RecLock, os campos que o padrão preencheria são responsabilidade sua, a começar pela filial. O exemplo usa ZP1, uma tabela de log criada por você:

ADVPL / TLPP
Static Function GravaLog(cChave, cTexto)

    Local aArea := GetArea()

    DbSelectArea("ZP1")

 //.T. = inclusão (registro novo)..F. = alteração do registro posicionado.
 If RecLock("ZP1", .T.)
        ZP1->ZP1_FILIAL := xFilial("ZP1")
        ZP1->ZP1_CHAVE  := cChave
        ZP1->ZP1_TEXTO  := cTexto
        ZP1->ZP1_USER   := RetCodUsr()
        ZP1->ZP1_DATA   := Date()
        ZP1->ZP1_HORA   := Time()
        ZP1->(MsUnlock())
    EndIf

    RestArea(aArea)

Return Nil

Para alterar, posicione antes (DbSeek pelo índice) e chame RecLock("ZP1", .F.). Guardar e devolver a área é assunto de GetArea e RestArea.

MsExecAuto no cadastro de cliente

A dúvida sobre qual rotina chamar tem resposta no fonte. MATA030 ainda existe, mas pergunta MA030IsMVC(), que lê . Se o parâmetro estiver ligado, ele simplesmente chama CRMA980(xRotAuto,nOpcAuto,,xRAutoFOJ).

O CRMA980 tem esta assinatura:

ADVPL / TLPP
Function CRMA980(aSA1Auto,nOpcAuto,aAI0Auto,aFOJAuto)

aFOJAuto é o quarto parâmetro — o mesmo xRAutoFOJ da chamada do MATA030 acima. O fonte o descreve como “Tipo de retenção x Clientes”: dados do submodelo FOJDETAIL, usado só com o motor de retenção ativo.

Com aSA1Auto preenchido, o CRMA980 carrega o modelo com FWLoadModel("CRMA980") e chama FwMvcRotAuto — duas variantes. Com FindFunction("FTemMotor") .and. FTemMotor(), a chamada inclui o terceiro submodelo:

ADVPL / TLPP
FwMvcRotAuto(oModel,"SA1",nOpcAuto,{{"SA1MASTER",aSA1Auto},{"AI0CHILD",aAI0Auto}, {"FOJDETAIL",aFOJAuto}},/*lSeek*/, .T.)

Sem o motor, aFOJAuto fica de fora: só SA1MASTER e AI0CHILD entram. Chamar o CRMA980 direto evita depender do parâmetro:

ADVPL / TLPP
#Include "Protheus.ch"
#Include "FWMVCDef.ch"

User Function PGV001(cCodigo, cLoja, cNome)

    Local aCampos := {}
    Local aLog    := {}
 Local lOk := .F.

 Private lMsErroAuto := .F.
 Private lMsHelpAuto := .T.
 Private lAutoErrNoFile := .T.

 aAdd(aCampos, {"A1_COD", cCodigo, Nil})
 aAdd(aCampos, {"A1_LOJA", cLoja, Nil})
 aAdd(aCampos, {"A1_NOME", cNome, Nil})
 aAdd(aCampos, {"A1_NREDUZ", Left(cNome,20), Nil})
 aAdd(aCampos, {"A1_PESSOA", "J", Nil})
 aAdd(aCampos, {"A1_TIPO", "F", Nil})
 aAdd(aCampos, {"A1_END", "RUA EXEMPLO, 100", Nil})
 aAdd(aCampos, {"A1_EST", "SP", Nil})
 aAdd(aCampos, {"A1_COD_MUN", "50308", Nil})

    Begin Transaction

        MsExecAuto({|x, y| CRMA980(x, y)}, aCampos, MODEL_OPERATION_INSERT)

        If lMsErroAuto
            aLog := GetAutoGRLog()
            DisarmTransaction()
        Else
            GravaLog(cCodigo + cLoja, "Cliente incluido por PGV001")
 lOk := .T.
        EndIf

    End Transaction

    If !lOk
        aEval(aLog, {|cLinha| ConOut("[PGV001] " + cLinha)})
    EndIf

Return lOk

Os campos do exemplo existem no SX3 com esses nomes e tamanhos ( tem 20 posições, daí o Left) — o conjunto obrigatório depende do seu dicionário; confira antes. O log volta como array de linhas; percorrê-lo é assunto de arrays em AdvPL.

Três escolhas nesse código merecem explicação.

lAutoErrNoFile := .T. Em AutoGRLog(), essa variável decide entre o array interno, lido por GetAutoGRLog(), e o arquivo .log padrão, mostrado por MostraErro() (mesmo fonte) em diálogo — que uma rotina em job não pode abrir. O mecanismo completo está em MsExecAuto: como descobrir por que a rotina falhou.

GetAutoGRLog() uma vez só. Ela esvazia o buffer ao ler — chamada de novo, o array vem vazio.

DisarmTransaction() e não Return. Ele marca a transação para ser desfeita, e o fluxo segue até o End Transaction. Sair do bloco com Return no meio deixa a transação aberta. Com isso, o log da ZP1 só existe se o cliente existir: ou grava os dois, ou nenhum.

O modelo MVC direto

Quando você precisa só de um submodelo, ou quer a mensagem estruturada do MVC, carregue o modelo e faça o que a tela faria:

ADVPL / TLPP
oModel := FWLoadModel("CRMA980")
oModel:SetOperation(MODEL_OPERATION_INSERT)
oModel:Activate()

oModel:SetValue("SA1MASTER", "A1_COD", cCodigo)
oModel:SetValue("SA1MASTER", "A1_LOJA", cLoja)
oModel:SetValue("SA1MASTER", "A1_NOME", cNome)

If oModel:VldData()
    lOk := oModel:CommitData()
EndIf

If !lOk
    aErro := oModel:GetErrorMessage()
EndIf

oModel:DeActivate()
oModel:Destroy()

Esse caminho dispensa lMsErroAuto: o erro vem de GetErrorMessage(). A contrapartida é que você passa a depender dos IDs do modelo (SA1MASTER), que são da TOTVS e podem mudar numa atualização — um SetValue que hoje grava calado passa a devolver .F. sem aviso na versão seguinte, se o ID do submodelo mudar de nome. A definição de FwMvcRotAuto não vem nos fontes distribuídos — só a chamada, presente em muitas outras rotinas do padrão (CRMA980, FINA010, CTBA020, FINA171, PCOA010, entre outras) — por isso a referência de uso aqui são as chamadas do próprio CRMA980.

O que fica

Tabela sua: RecLock, com filial e campos de controle na sua conta. Tabela do padrão: a rotina, porque carrega a regra de negócio que você não quer reescrever — e toda gravação dupla, dentro de uma transação só. Próximo passo da série: tabela temporária, para quando o dado não deve ir para tabela nenhuma.


Se uma integração sua grava pelo caminho errado e você quer uma segunda leitura, fale com a gente.

Referências

  1. TDNtdn.totvs.com

    TDN — Cadastro de Clientes (CRMA980)Endereço citado pelo próprio MATA030.
  2. TDNtdn.totvs.com

    TDN — Variáveis de controle do MsExecAuto
  3. TDNtdn.totvs.com

    TDN — AdvPL, documentação da linguagem

Continue por aqui