Pragiva — Tecnologia na prática

MTA410 o ponto de entrada da validação final do pedido

MTA410 só roda depois que o padrão já aprovou, então a regra só consegue apertar. Dispara também a partir de Compras — daí o guard de FunName().

Desenvolvimento Referência técnica 4 min de leitura

Assista o vídeo

A ordem das chamadas
  1. M410INIC gravação
  2. A410Grava gravação
  3. M410STTS gravação

MTA410 — Cabeçalho e itens validados, antes de gravar.

O padrão descarta o retorno: não dá para bloquear aqui.

MTA410 em resumo

Onde fica
função A410TudOk(), a validação final do pedido.
Quando dispara
com cabeçalho e itens já validados pelo padrão, antes de gravar.
PARAMIXB
não recebe.
Retorno
usado — .F. interrompe.
Serve para bloquear?
Sim, e é o lugar certo para regra que depende dos itens.
Consulta rápida6 seções

Para o mecanismo geral, veja Ponto de entrada no Protheus.

A diferença que define este PE

No MATA410, a chamada é assim:

ADVPL / TLPP
If lRetorna.AND. ExistTemplate("MTA410",, .T.)
    lRetorna := ExecTemplate("MTA410", .F., .F.)
EndIf
If lRetorna.AND. ExistBlock("MTA410",, .T.)
    lRetorna := ExecBlock("MTA410", .F., .F.)
EndIf

Aquele If lRetorna.AND. na frente muda tudo. O seu ponto de entrada só é chamado se a validação padrão já passou. Se o padrão reprovou, ele nem roda.

A consequência é boa: aqui você não tem como derrubar uma validação nativa. Sua regra só consegue apertar. É o oposto do MT410TOK, cujo retorno é atribuído sem essa checagem — lá um Return .T. distraído desfaz o que o padrão tinha barrado.

Se você pode escolher entre os dois, e a regra depende dos itens, o MTA410 é o lugar mais seguro.

Repare também que o Template roda antes, e se ele reprovar, o seu PE não é chamado. Vale saber quando o cliente tem template de segmento instalado.

O que ele tem que o MT410TOK não tem

O momento. Quando o MTA410 roda, o padrão já validou cabeçalho e itens: aCols está completo e consistente, com os gatilhos disparados e os campos calculados preenchidos.

No MT410TOK, que roda antes da validação do grid, o aCols está lá — mas você está olhando dados que o padrão ainda não conferiu.

Regra prática:

  • Regra de cabeçalho (cliente, condição de pagamento, tipo de pedido) → MT410TOK, que é mais barato e falha mais cedo.
  • Regra que depende dos itens → MTA410.

O detalhe que surpreende: ele não é só do pedido de venda

Este é o achado que explica um trecho estranho que aparece em código de campo.

No documento de entrada e na nota fiscal de saída, rotinas de Compras, existe esta linha:

ADVPL / TLPP
PRIVATE lMTA410 := (ExistBlock("MTA410"))

Ela só guarda se o bloco existe. Não aparece lida em nenhum outro ponto do padrão, e nenhuma das duas rotinas chama MATA410() depois disso — não é essa linha que executa o seu ponto de entrada. Quem só olhar essa variável e concluir “então o MTA410 roda a partir daqui” está seguindo uma pista morta.

Quem executa é A410TudOk() — a função já citada no início deste artigo —, que testa ExistBlock("MTA410",, .T.) por conta própria, sem depender daquela variável de Compras. A rota comprovada para um fluxo que não é a tela de pedido de venda está em outra rotina: MATA310(), a transferência de saldos entre filiais, chama MATA410(aCabec,aItens,3) diretamente. Essa chamada cai no trecho de A410Inclui() que roda via ExecAuto — o bloco {|| A410VldTOk(nOpc) .and. A410TudOk()} —, e é A410TudOk(), de novo, quem dispara o MTA410.

Na prática: o seu MTA410 pode ser chamado de fluxos que não são a tela de pedido de venda — comprovadamente, da transferência de saldos entre filiais. Uma regra escrita presumindo “estou incluindo um pedido” passa a rodar em contexto onde ela não faz sentido.

É por isso que código de produção sério guarda o contexto. Dois padrões que aparecem no campo:

ADVPL / TLPP
// 1. Pela rotina que está no topo da pilha
If AllTrim(FunName()) <> "MATA310"      // MATA310 = transferência de saldos entre filiais
 //... a regra...
EndIf

// 2. Pelo caminho específico dentro da própria rotina
If IsInCallStack("A410COPIA") .And. !l410Auto
 //... regra que vale só na cópia de pedido, e só com tela...
EndIf

FunName() devolve a rotina corrente; IsInCallStack() responde se uma função específica está na pilha de chamadas. Os dois juntos dão o contexto que o PARAMIXB daria — se ele existisse aqui, que não é o caso.

Exemplo

Validação de cabeçalho que só vale na tela de pedido, escrita como se vê em produção:

ADVPL / TLPP
#Include "Protheus.ch"

/*/{Protheus.doc} MTA410
Executado em A410TudOk(), com cabeçalho e itens já validados pelo padrão
e ANTES da gravação. Só é chamado se a validação padrão passou.
Exige vendedor preenchido, exceto em devolução e beneficiamento.
@type user function
@return logical, .F. interrompe a gravação
/*/
User Function MTA410()

    Local aArea := SC5->(GetArea())
 Local lRet := .T.

    // Este PE também é alcançado por rotinas de Compras e pela transferência
    // de saldos entre filiais (MATA310). Sem este guard, a regra roda onde
    // não deveria.
    If AllTrim(FunName()) <> "MATA410"
        RestArea(aArea)
 Return .T.
    EndIf

    // Nada foi gravado ainda: o cabeçalho vive em M->
 If !(M->C5_TIPO $ "DB") .And. Empty(M->C5_VEND1)
        MsgStop("Informe o vendedor antes de concluir o pedido.", ;
                "Vendedor em branco — MTA410")
 lRet := .F.
    EndIf

    ConOut("[MTA410] Pedido " + M->C5_NUM + " | Retorno: " + cValToChar(lRet))

    RestArea(aArea)

Return lRet

Duas coisas nesse código.

M->C5_TIPO $ "DB" — devolução (D) e beneficiamento (B) não têm vendedor, então a regra não se aplica. Regra que ignora os tipos de pedido existentes vira chamado no dia em que alguém precisa lançar uma devolução.

MsgStop antes de lRet := .F. — sem a mensagem, o usuário vê o botão simplesmente não funcionar.

Armadilhas específicas

Presumir que veio da tela de pedido. É a principal. Use FunName() ou IsInCallStack().

Abrir diálogo sem checar contexto. MsgStop numa carga ExecAuto trava a thread até o timeout do server. Type("l410Auto") == "U" .Or. !l410Auto resolve — e é exatamente o teste que aparece em código de campo.

Trabalho pesado aqui. Este PE roda a cada confirmação de pedido. Consulta não indexada dentro dele custa em toda inclusão, inclusive nas cargas automáticas.

Esquecer o Return lógico. O retorno é usado. Sem ele, o padrão avalia com Nil.

Os vizinhos

PEMomentoRetorno é usado?
MT410TOKAo clicar OK, antes da validação do gridSim, sobrescrevendo
MTA410Cabeçalho e itens validados, antes de gravarSim, só aperta
MT410INCDepois de gravar, fora da transaçãoNão

Se a sua regra precisa acontecer depois de o pedido existir — auditoria, integração, marcar campo no registro gravado — o lugar é o MT410INC.

Referências

  1. TDNtdn.totvs.com

    TDN — MATA410, Pedidos de Venda

Continue por aqui