PARAMIXB em resumo
- O que é
- o quarto argumento do
ExecBlockouExecTemplateque chamou o ponto de entrada. - Sempre chega
- não. Só quando o padrão passa esse quarto argumento.
- Escopo
Private— continua visível em tudo que a rotina padrão chamar depois.- Layout
- fixo por ponto de entrada — a linha que chama define o conteúdo — mas não documentado.
- Risco maior
- pode carregar o retorno de validações anteriores; sobrescrever com
.T.apaga esse retorno em silêncio.
Consulta rápida6 seções
PARAMIXB é a variável que o engine cria quando executa um ponto de entrada,
contendo exatamente o que a rotina padrão passou no quarto argumento de
ExecBlock. É o que acontece em A410VldTOk(), ao chamar o MT410TOK:
lRet := Execblock("MT410TOK", .F., .F., {nOpc, aRecnoSE1RA})
// └─────────┬────────┘
// vira PARAMIXBEla vale para todos os PEs e carrega três comportamentos raramente escritos, cada um capaz de gerar um bug que não dá erro.
Se você ainda não viu como o padrão chama um PE, comece por Ponto de entrada no Protheus.
1. Nem todo PE recebe
Se a rotina padrão chama sem o quarto argumento, não há array:
Execblock("MT410INC", .F., .F.) // sem 4º argumentoNão existe PARAMIXB[1], não existe PARAMIXB[2]. Escrever PARAMIXB[1] num
PE desses é erro de execução — na melhor das hipóteses.
Qual é a pior? A próxima seção.
2. Ele pode existir com o conteúdo de outro
PARAMIXB é Private. Em AdvPL, Private é visível na função que declara e
em tudo que ela chamar abaixo na pilha.
Consequência: se um ExecBlock mais acima na pilha criou PARAMIXB, ele pode
continuar visível dentro do seu PE — com o conteúdo de outro completamente
diferente.
O resultado é o pior tipo de bug: Type("PARAMIXB") == "A" devolve verdadeiro,
PARAMIXB[1] devolve um valor plausível, o código roda, ninguém vê erro
nenhum, e a decisão foi tomada com o dado errado.
Regra prática: em ponto de entrada que o padrão chama sem parâmetro, não leia
PARAMIXB. Pegue o que precisa das áreas de trabalho e das variáveis Private
da rotina padrão.
3. Onde ele existe, o layout é fixo — e não documentado
Cada ponto de entrada tem a sua própria assinatura, definida pela linha que o chama dentro da rotina padrão. Não há um layout comum entre pontos de entrada diferentes, nem uma tabela oficial que resuma todos eles.
O MT410TOK, por exemplo — a mesma chamada de A410VldTOk() de cima:
| 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: cada entrada é {pedido, recno do SE1, valor} |
A segunda linha é a lição inteira do artigo num exemplo: o nome mente.
aRecnoSE1RA sugere uma lista de recnos e é uma lista de arrays de três
posições. O recno está em PARAMIXB[2][n][2].
Quem descobre isso lendo o nome da variável escreve
SE1->(DbGoTo(PARAMIXB[2][n])) e leva erro de tipo. Quem descobre lendo a
rotina padrão vê o padrão consumindo o próprio array dentro de
A410CkClRA():
SE1->(DbGoTo(aRecnoSE1RA[01][02]))É a diferença entre deduzir e conferir. O GMCVndVLD, adiante, recebe na
primeira posição o retorno de validações anteriores — não um código de
operação nem um objeto de grid.
Como descobrir o layout de um PE que você não conhece
Dois caminhos, nesta ordem:
1. Ler a linha que chama, dentro da rotina padrão. É a fonte da verdade.
Localize a chamada pelo nome do PE e olhe o quarto argumento do ExecBlock.
O que estiver ali é o seu PARAMIXB.
2. Imprimir. Quando o fonte não está à mão:
ConOut("[MEU_PE] PARAMIXB = " + cValToChar(PARAMIXB))Rode a operação e leia o log: em dois minutos você tem o layout real da sua versão, não o de outra release.
O caso que muda o jeito de escrever: PARAMIXB como retorno anterior
Alguns pontos de entrada de validação passam, numa das posições, o valor
que as validações anteriores do padrão já decidiram. O layout aparece no
GMCVndVLD, chamado dentro de A410VldTOk():
If lRet
If ExistBlock("GMCVndVLD")
lRet := ExecBlock("GMCVndVLD", .F., .F.,{ lRet,aHeader,aCols,aGEMCVnd })
ElseIf ExistTemplate("GMCVndVLD",, .T.)
//
// executa a validacao da condicao de venda
//
lRet := ExecTemplate("GMCVndVLD", .F., .F.,{ lRet,aHeader,aCols,aGEMCVnd })
EndIf
EndIfPARAMIXB[1] é literalmente o lRet de quem chamou. [2], [3] e [4]
são aHeader, aCols e aGEMCVnd, como o próprio PE os lê, na Template Function GMCVndVLD() — um template de segmento da TOTVS, não uma
customização de cliente:
Local lRetorno := iIf( ParamIXB[1] == NIL, .T.,ParamIXB[1] )
Local aHeader := iIf( ParamIXB[2] == NIL,{},ParamIXB[2] )
Local aCols := iIf( ParamIXB[3] == NIL,{},ParamIXB[3] )
Local aCondVnd := iIf( ParamIXB[4] == NIL,{},ParamIXB[4] )Mas a chamada está dentro de If lRet: nesse call-site, PARAMIXB[1]
sempre chega .T.. O GMCVndVLD mostra o layout, não o risco de recebê-lo
já reprovado — para isso, o call-site certo é outro.
Onde o veredito anterior pode mesmo chegar reprovado
Lj010EvaLi valida a linha digitada no LOJA010, a rotina de Vendas -
Balcão do SigaLoja — não o PDV: o Front Loja é LOJA701 e não chama essa
função. Ele é chamado sem essa guarda, dentro do LOJA010B:
If ExistBlock("Lj010EvaLi")
lRet := ExecBlock("Lj010EvaLi", .F., .F.,{lRet})
EndifEntre o início da própria lj010EvaLi() do padrão e o ponto onde essa
chamada dispara, ela atribui lRet := .F. onze vezes: produto vazio, TES
inválido, série de outro produto, local, localização, lote ou sub-lote
divergente e saldo insuficiente são oito delas — as outras três vivem num
ramo de legislação de El Salvador (cPaisLoc == "SAL"), inalcançável num
ambiente brasileiro. PARAMIXB[1], ali, pode chegar genuinamente reprovado:
User Function Lj010EvaLi()
Local lRet := PARAMIXB[1] // o que a validação padrão já decidiu
Local cProduto := GDFieldGet("L2_PRODUTO", n) // "n" é Private da rotina padrão
If lRet .And. Rastro(cProduto, "S") .And. Empty(GDFieldGet("L2_NLOTE", n))
MsgAlert("Informe o lote antes de confirmar a linha.")
lRet := .F.
EndIf
Return lRetO MsgAlert antes de lRet := .F. não é estética: é a mesma função que a
rotina padrão usa em seis dos oito reprovos alcançáveis num ambiente
brasileiro — por exemplo MsgAlert(STR0100+SBF->BF_PRODUTO), quando o serial
pertence a outro produto. Os outros dois (produto vazio e TES inválido), mais
cedo na função, usam Help() em vez de MsgAlert. Sem uma mensagem, de um
jeito ou de outro, o botão só não confirma, e o usuário abre chamado dizendo
que “o sistema travou”.
A primeira linha da customização começa do que a validação anterior
decidiu, em vez de começar de .T.:
- Iniciando com
PARAMIXB[1], a sua regra só consegue apertar — se algo antes reprovou a linha, ela continua reprovada. - Iniciando com
.T., você derruba silenciosamente toda validação anterior, e o problema que existia antes some sem deixar rastro.
O segundo caso é fácil de escrever sem perceber, e o sintoma aparece semanas depois, num dado que ninguém liga à customização.
O M410LIOK é o contraste: recebe o objeto MsGetDados do grid, não um
array com retorno anterior, e só é chamado quando o padrão já aprovou a
linha, dentro de A410LinOk() (nas duas chamadas, Template e Block, ambas
guardadas por If lRetorno .And.). Ele só aperta,
nunca afrouxa.
A leitura defensiva
Onde PARAMIXB existe e você depende dele, conferir é barato:
If Type("PARAMIXB") <> "A" .Or. Len(PARAMIXB) < 1 .Or. ValType(PARAMIXB[1]) <> "N"
ConOut("[MEU_PE] PARAMIXB inesperado. Regra ignorada.")
Return .T. // fail-safe: nunca bloquear por omissão
EndIfTrês coisas nesse trecho:
- Confere tipo, tamanho e o tipo do elemento — não só se o array existe.
- Registra no console. Se uma atualização de release mudar a assinatura, você descobre pelo log, e não por um chamado.
- Sai com
.T., não com.F.: o padrão seguro é deixar passar na dúvida — bloquear por omissão trava a operação inteira por causa de uma incompatibilidade sua.
Em uma linha
PARAMIXB é Private, chega só quando o padrão manda, e o layout é o que a
linha que chama, dentro da rotina padrão, define. Se o seu PE não recebe,
não leia. Se recebe e começa com o retorno anterior, respeite ele.
Para ver isso aplicado a um caso concreto: MT410INC, o ponto de entrada que não recebe PARAMIXB e cujo retorno é descartado.
Referências
TDNtdn.totvs.com
TDN — Pontos de Entrada
Continue por aqui
- Vídeo
DesenvolvimentoReferência técnica
F340VALOK: o ponto de entrada que barra o OK da compensação a pagar
F340VALOK roda ao clicar OK na lista de títulos da compensação a pagar, recebe a lista em PARAMIXB e, retornando .F., mantém o usuário na lista.
- Vídeo
DesenvolvimentoReferência técnica
F340VLD: o ponto de entrada que barra a compensação no Contas a Pagar
F340VLD roda ao confirmar a primeira tela da compensação a pagar (FINA340), sem PARAMIXB. Retornando .F., a tela não fecha e nenhum título chega a ser listado.