A ordem das chamadas
- M410INIC gravação
- A410Grava gravação
- 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.
MTA410 — Cabeçalho e itens já validados, antes de gravar.
O padrão descarta o retorno: não dá para bloquear aqui. Ler o artigo.
MT410INC — Depois de gravar, fora da transação.
O padrão descarta o retorno: não dá para bloquear aqui. Ler o artigo.
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 é:
If lMt410TOK
lRet := Execblock("MT410TOK", .F., .F.,{nOpc,aRecnoSE1RA})
EndifRepare 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ção | Tipo | Conteúdo |
|---|---|---|
PARAMIXB[1] | Numérico | Operação: 3 inclusão, 4 alteração, 5 exclusão |
PARAMIXB[2] | Array de arrays | Adiantamentos 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:
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/SaldoEntã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:
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:
// 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"
NextIsso 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
#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 lRetRepare 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
| PE | Momento | Retorno é usado? |
|---|---|---|
MT410TOK | Ao clicar OK, antes da validação do grid | Sim, sobrescrevendo |
MTA410 | Cabeçalho e itens já validados, antes de gravar | Sim, só aperta |
MT410INC | Depois de gravar, fora da transação | Não |
Se a regra depende dos itens já validados pelo padrão, o lugar é o
MTA410, não este.
Referências
TDNtdn.totvs.com
TDN — MATA410, Pedidos de Venda
Continue por aqui
- Vídeo
DesenvolvimentoReferência técnica
MT410INC: o ponto de entrada da inclusão do pedido de venda
Ele dispara depois de o pedido estar gravado, fora da transação, sem PARAMIXB e com o retorno descartado. Isso define exatamente para que ele serve.
- Vídeo
DesenvolvimentoReferência técnica
M410STTS: como identificar a operação do pedido de venda
M410STTS não descreve por completo a mudança de status: o padrão passa um número, o código da operação, e o retorno não é usado. Serve para observar.