Pragiva — Tecnologia na prática

MT410TOK o ponto de entrada do OK do pedido de venda

MT410TOK bloqueia de verdade, recebe PARAMIXB e pode alterar o pedido antes de gravar — mas o padrão sobrescreve o retorno sem conferir.

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

MT410TOK — Ao clicar OK, antes da validação do grid.

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

MT410TOK em resumo

Onde fica
função A410VldTOk(), da validação do pedido.
Quando dispara
ao confirmar o pedido, antes da validação do grid e antes de gravar.
PARAMIXB
{nOpc, aRecnoSE1RA}.
Retorno
usado — .F. interrompe a confirmação.
Serve para bloquear?
Sim. É o lugar certo para isso.
Consulta rápida6 seções

É o oposto do MT410INC: lá o pedido já está gravado e o retorno é jogado fora; aqui nada foi gravado e o retorno decide.

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

A armadilha que ninguém conta

Esta é a parte que muda o jeito de escrever o PE. No MATA410, a chamada é:

ADVPL / TLPP
If lMt410TOK
    lRet := Execblock("MT410TOK", .F., .F.,{nOpc,aRecnoSE1RA})
Endif

Repare no que não está ali: um If lRet .And. antes. A atribuição é incondicional.

E antes dessa linha o padrão já tem validações próprias que fazem lRet := .F. — moeda diferente da corrente, regras de localização, entre outras. Se alguma delas reprovou e o seu MT410TOK termina com um Return .T. tranquilo, você acaba de desfazer a reprovação do padrão.

O usuário confirma um pedido que a TOTVS tinha barrado, ninguém vê erro, e o problema aparece depois — na emissão, no fiscal, ou num dado inconsistente que ninguém liga à customização.

Pior: diferente de outros pontos de entrada de validação, o MT410TOK não recebe o retorno anterior no PARAMIXB. Ele recebe {nOpc, aRecnoSE1RA}. Não há como consultar o que o padrão tinha decidido.

O que dá para fazer é não piorar: retorne .T. apenas quando a sua regra tiver de fato passado, e saia cedo, sem tocar em lRet, sempre que a sua regra não se aplicar àquela operação.

O que ele recebe

PosiçãoTipoConteúdo
PARAMIXB[1]NuméricoOperação: 3 inclusão, 4 alteração, 5 exclusão
PARAMIXB[2]Array de arraysAdiantamentos vinculados. Cada entrada é {pedido, recno do SE1, valor} — veja abaixo

A posição 2 engana pelo nome. aRecnoSE1RA não é uma lista de recnos: é uma lista de arrays de três posições. Quem a preenche é Ma410Bar(), que recebe o array por referência — Ma410Bar(...,nOpc,oGetD,nTotalPed,@aRecnoSE1RA,...) — e o monta chamando aRecnoSE1 := FPedAdtPed("R", {M->C5_NUM},...), antes de devolver o bloco que chama A410VldTOk para o framework processar o clique em OK. É FPedAdtPed() quem documenta o layout, com os próprios comentários do padrão numerando cada posição — a prova mais direta de que a leitura da função bate com o que chega em PARAMIXB[2], e não uma suposição sobre o nome da variável:

ADVPL / TLPP
aAdd(aRet, { FIE->FIE_PEDIDO,;        // 1 - Codigo do Pedido
             SE1->(RecNo()),;         // 2 - Recno do adiantamento (SE1)
             If(lRetVlr, FIE->FIE_VALOR, FIE->FIE_SALDO) })   // 3 - Valor/Saldo

Então o recno está em PARAMIXB[2][n][2], e não em PARAMIXB[2][n]. A prova mais curta está na própria função A410CkClRA(), onde o padrão consome o array que ele mesmo passou:

ADVPL / TLPP
SE1->(DbGoTo(aRecnoSE1RA[01][02]))

Quem tratar a posição 2 como lista de recnos leva erro de tipo no primeiro adiantamento que aparecer.

A posição 1 é o que você usa em quase todo MT410TOK: a maioria das regras vale só para inclusão, e deixar alteração e exclusão passarem intactas evita um monte de efeito colateral.

Detalhes de como PARAMIXB funciona — e por que conferir antes de ler: PARAMIXB: o array que às vezes não é seu.

O poder extra: você pode alterar o pedido

Como nada foi gravado ainda, os dados vivem em variáveis de memória — e elas são graváveis. Este é um padrão que aparece em código de produção:

ADVPL / TLPP
// Corrige o status antes de gravar, depois de confirmar com o usuário
If MsgYesNo(cMsg, "Estorno da liberação do pedido")
    M->C5_XSTATUS := "4"
EndIf

// E o grid também: força o armazém de todos os itens
nPosLocal := Ascan(aHeader, {|x| AllTrim(x[2]) == "C6_LOCAL" })
For n := 1 To Len(aCols)
    aCols[n][nPosLocal] := "02"
Next

Isso não tem equivalente no MT410INC. Lá, mudar o pedido exige RecLock("SC5", .F.) sobre um registro já gravado. Aqui é só atribuir, e o valor entra na gravação como se o usuário tivesse digitado.

É a razão prática para preferir o MT410TOK sempre que a regra puder ser resolvida antes de gravar.

Exemplo

ADVPL / TLPP
#Include "Protheus.ch"

/*/{Protheus.doc} MT410TOK
Executado em A410VldTOk(), ao confirmar o pedido e ANTES de gravar.
Bloqueia inclusão de pedido para cliente com bloqueio cadastral.
@type user function
@return logical, .F. interrompe a confirmação
/*/
User Function MT410TOK()

    Local aArea := GetArea()
 Local lRet := .T.
    Local nOpc  := 0

    // Chave de desligamento: sempre exista um jeito de desativar a
    // customização sem recompilar.
 If !SuperGetMv("MV_XPVBLQ", .F., .F.)
        RestArea(aArea)
 Return .T.
    EndIf

    // PARAMIXB existe aqui, mas conferir é barato e protege contra
    // mudança de assinatura entre releases.
 If Type("PARAMIXB") <> "A" .Or. Len(PARAMIXB) < 1 .Or. ValType(PARAMIXB[1]) <> "N"
        ConOut("[MT410TOK] PARAMIXB inesperado. Regra ignorada.")
        RestArea(aArea)
 Return .T.
    EndIf

    nOpc := PARAMIXB[1]

    // Só inclusão. Alteração e exclusão seguem o padrão intactas.
    If nOpc <> 3
        RestArea(aArea)
 Return .T.
    EndIf

    // Nada foi gravado: o cabeçalho vive nas variáveis de memória M->
    SA1->(DbSetOrder(1))
    If SA1->(DbSeek(FWxFilial("SA1") + M->C5_CLIENTE + M->C5_LOJACLI))
        If SA1->A1_MSBLQL == "1"
            // Bloquear calado faz o botão OK não funcionar e o usuário
            // abrir chamado dizendo que o sistema travou.
 Help(" ", 1, "MT410TOK",, ;
                "Cliente bloqueado no cadastro. Inclusão não permitida.", 1, 0)
 lRet := .F.
        EndIf
        EndIf

    ConOut("[MT410TOK] Cliente " + M->C5_CLIENTE + " | Retorno: " + cValToChar(lRet))

    RestArea(aArea)

Return lRet

Repare nos três Return .T. antecipados. Cada um é um caminho em que a sua regra não se aplica — e sair cedo é melhor do que cair no fim da função com um lRet que você não controlou.

Armadilhas específicas

Retornar .T. sem pensar. Já explicado: você sobrescreve a decisão do padrão. É a armadilha número um deste PE.

Esquecer o Return. Aqui o retorno é usado. Sem Return explícito, o padrão avalia If lRet .And.... com Nil, e o comportamento depende da versão do engine.

Bloquear sem mensagem. O botão OK simplesmente não fecha a tela. Sempre Help() ou equivalente antes de lRet := .F..

Abrir diálogo sob ExecAuto. MsgYesNo é ótimo na tela e desastroso em carga automática: trava a thread até o timeout do server. Guarde com Type("l410Auto") == "U" .Or. !l410Auto, ou IsBlind().

Aplicar a regra em alteração e exclusão sem querer. PARAMIXB[1] existe justamente para isso. Sem esse filtro, uma regra de inclusão impede o usuário de corrigir um pedido antigo que já nasceu fora da regra.

Os vizinhos

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

Se a regra depende dos itens já validados pelo padrão, o lugar é o MTA410, não este.

Referências

  1. TDNtdn.totvs.com

    TDN — MATA410, Pedidos de Venda

Continue por aqui