Olá pessoal…
Hoje mostro para vocês um recurso que criei para pesquisa de campos em telas do Protheus.
Um recurso existente no Protheus (e que poucos sabem) é o Ctrl+F, para pesquisa de campos, mas além de não funcionar em todas as telas (inclusive em telas comuns), ele tem alguns bugs de posicionamento.
Pensando nisso, decidi criar uma rotina, que você possa utilizar corretamente uma pesquisa de campos (testei no cadastro de TES, no cadastro de Produto e em um cadastro customizado em MVC). Abaixo um print dela:
Nessa rotina, tem o campo, a aba e o título dele na tela, e ao dar dois cliques ou clicar no confirmar, é posicionado no campo correto. Para funcionar, crie um ponto de entrada no carregamento das telas, no meu caso eu fiz com o AfterLogin (chamado uma única vez no SIGAADV, ou em toda aba aberta no SIGAMDI), e coloquei para ser no atalho Ctrl+L.
Abaixo o código fonte completo:
//Bibliotecas
#Include "Protheus.ch"
//Posições da Grid
#Define POS_ITE 0001
#Define POS_VAR 0002
#Define POS_ABA 0003
#Define POS_DES 0004
/*/{Protheus.doc} zSearch
Função para pesquisar campos de uma tela de cadastro
@author Atilio
@since 01/12/2017
@version 1.0
@type function
@obs Inicializar ele em um ponto de entrada, por exemplo, no ChkExec ou AfterLogin:
...
User Function AfterLogin()
SetKey(K_CTRL_L, { || u_zSearch() }) //Ctrl + L
Return
...
Essa rotina foi testada no cadastro de TES - MATA080
/*/
User Function zSearch()
Local aArea := GetArea()
Local nAtual := 0
Local nAbaAux := 0
Local cAbaAux := ""
//Variáveis de controle
Private nAtuPvt := 0
Private nAbaPvt := 0
Private oPai := GetWndDefault()
Private aControles := oPai:aControls
Private cAbaEsc := ""
Private cTipoAba := ""
//Grid
Private aDadosOrig := {}
Private oMsObj
Private aHeadObj := {}
Private aColsObj := {}
//Janela
Private oDlgPesq
Private nJanLarg := 500
Private nJanAltu := 300
//Objetos da Janela
Private oGrpPesq
Private oSayPesq
Private oGetPesq
Private cGetPesq := Space(100)
Private oGetAux
Private cGetAux := ""
Private oBtnConf
Private oBtnCanc
//Percorrendo os objetos criados da tela
For nAtual := 1 To Len(aControles)
nAtuPvt := nAtual
//Se tiver variável e descrição
If Type("aControles[nAtuPvt]:cReadVar") != "U" .And. Type("aControles[nAtuPvt]:cToolTip") != "U"
//Somente se tiver conteúdo
If ! Empty(aControles[nAtuPvt]:cReadVar) .And. ! Empty(aControles[nAtuPvt]:cToolTip) .And. 'M->' $ Upper(aControles[nAtuPvt]:cReadVar)
cAbaAux := GetSx3Cache(StrTran(Upper(aControles[nAtuPvt]:cReadVar), 'M->', ''), "X3_FOLDER")
aAdd(aDadosOrig, { nAtual,; //ID
aControles[nAtual]:cReadVar,; //Variável
cAbaAux,; //Aba
GetSx3Cache(StrTran(Upper(aControles[nAtuPvt]:cReadVar), 'M->', ''), "X3_TITULO"),; //Descrição
.F.}) //Excluído?
//Somente se houver aba
If cAbaAux != Nil
cTipoAba += Iif(" " $ cTipoAba, "", Iif(cAbaAux $ cTipoAba, "", cAbaAux))
EndIf
EndIf
EndIf
Next
aColsObj := aClone(aDadosOrig)
//Percorrendo os objetos criados da tela, procurando por abas
For nAtual := 1 To Len(aControles)
nAtuPvt := nAtual
//Se tiver aba, guarda a posição dela
If Type("aControles[nAtuPvt]:aDialogs") == 'A'
//Somente se o tamanho dela for maior que a variável encontrada
If Len(aControles[nAtuPvt]:aDialogs) >= Len(cTipoAba)
nAbaPvt := nAtual
EndIf
EndIf
Next
//Se tiver objetos, monta a tela
If Len(aDadosOrig) > 0
//Cabeçalho ... Titulo Campo Mask Tamanho Dec Valid Usado Tip F3 CBOX
aAdd(aHeadObj, {"Item", "XX_ITEM", "@E 9999", 0004, 0000, ".F.", ".F.", "N", "", ""})
aAdd(aHeadObj, {"Variável", "XX_VAR", "", 0020, 0000, ".F.", ".F.", "C", "", ""})
aAdd(aHeadObj, {"Nº Aba", "XX_ABA", "", 0001, 0000, ".F.", ".F.", "C", "", ""})
aAdd(aHeadObj, {"Título", "XX_DESC", "", 0020, 0000, ".F.", ".F.", "C", "", ""})
//Criando a janela
DEFINE MSDIALOG oDlgPesq TITLE "Pesquisar" FROM 000, 000 TO nJanAltu, nJanLarg COLORS 0, 16777215 PIXEL
//Título do Grupo
@ 003, 003 GROUP oGrpPesq TO (nJanAltu/2)-1, (nJanLarg/2)-1 PROMPT "zSearch - Pesquisa de Campos: " OF oDlgPesq COLOR 0, 16777215 PIXEL
//Campo para pesquisar
@ 016, 006 SAY oSayPesq PROMPT "Pesquisar: " SIZE 030, 012 OF oDlgPesq PIXEL
@ 013, 036 MSGET oGetPesq VAR cGetPesq SIZE (nJanLarg/2)-42, 012 OF oDlgPesq COLORS 0, 16777215 VALID (fVldPesq()) PIXEL
//Dados dos Objetos encontrados
oMsObj := MsNewGetDados():New( 030,; //nTop
006,; //nLeft
(nJanAltu/2)-23,; //nBottom
(nJanLarg/2)-4,; //nRight
0,; //nStyle
"",; //cLinhaOk
,; //cTudoOk
"",; //cIniCpos
{},; //aAlter
,; //nFreeze
99999,; //nMax
,; //cFieldOK
,; //cSuperDel
,; //cDelOk
oDlgPesq,; //oWnd
aHeadObj,; //aHeader
aColsObj) //aCols
oMsObj:lActive := .F.
oMsObj:bChange := {|| fAtuAux()}
oMsObj:oBrowse:blDblClick := {|| fConfirma() }
//Criando get invisível que terá dados do registro posicionado
@ (nJanAltu / 2) - 022, 005 MSGET oGetAux VAR cGetAux SIZE 200, 18 NO BORDER OF oDlgPesq PIXEL
oGetAux:setCSS("QLineEdit{color:#FF0000; background-color:#FEFEFE;}")
oGetAux:lActive := .F.
//Botões
@ (nJanAltu/2)-18, (nJanLarg/2)-((0042*1)+6) BUTTON oBtnConf PROMPT "Confirmar" SIZE 040, 013 OF oDlgPesq ACTION(fConfirma()) PIXEL
@ (nJanAltu/2)-18, (nJanLarg/2)-((0042*2)+6) BUTTON oBtnCanc PROMPT "Cancelar" SIZE 040, 013 OF oDlgPesq ACTION(oDlgPesq:End()) PIXEL
//Colocando o foco na pesquisa
oGetPesq:SetFocus()
ACTIVATE MSDIALOG oDlgPesq CENTERED
EndIf
//Posiciona no campo
If nAtuPvt != 0
//Se tiver abas
If nAbaPvt != 0
nAbaAux := 0
//Se a aba estiver em branco, será a última aba
If Empty(cAbaEsc)
nAbaAux := Len(aControles[nAbaPvt]:aDialogs)
//Se for numérico, posicionará corretamente
ElseIf cAbaEsc $ ("0,1,2,3,4,5,6,7,8,9")
nAbaAux := Val(cAbaEsc)
//Se for letra, converte para número e posiciona corretamente
Else
nAbaAux := (Asc(Upper(cAbaEsc)) - 64) + 9
EndIf
//Tratativa para não colocar em aba que não deve
If nAbaAux > Len(aControles[nAbaPvt]:aDialogs)
nAbaAux := Len(aControles[nAbaPvt]:aDialogs)
EndIf
aControles[nAbaPvt]:SetOption(nAbaAux)
aControles[nAbaPvt]:Refresh()
EndIf
//Coloca o foco no campo
aControles[nAtuPvt]:SetFocus()
EndIf
RestArea(aArea)
Return
/*---------------------------------------------------------------------*
| Func: fVldPesq |
| Desc: Função que filtra a pesquisa na grid |
*---------------------------------------------------------------------*/
Static Function fVldPesq()
Local lRet := .T.
Local aDadosAux := {}
Local nAtual := 0
Local cFiltro := Alltrim(FWNoAccent(Upper(cGetPesq)))
//Caso não tenha filtro
If Empty(cFiltro)
aDadosAux := aClone(aDadosOrig)
Else
//Percorre os dados originais
For nAtual := 1 To Len(aDadosOrig)
If ! Empty(aDadosOrig[nAtual][POS_VAR]) .And. ! Empty(aDadosOrig[nAtual][POS_DES])
If cFiltro $ FWNoAccent(Upper(aDadosOrig[nAtual][POS_VAR])) .Or. cFiltro $ FWNoAccent(Upper(aDadosOrig[nAtual][POS_DES]))
aAdd(aDadosAux, aClone(aDadosOrig[nAtual]))
EndIf
EndIf
Next
EndIf
//Se não tiver dados
If Len(aDadosAux) == 0
aAdd(aDadosAux, { 0,; //ID
"",; //Variável
"",; //Aba
"",; //Descrição
.F.})
EndIf
//Seta o novo array filtrado
oMsObj:SetArray(aDadosAux)
oMsObj:Refresh()
fAtuAux()
Return lRet
/*---------------------------------------------------------------------*
| Func: fConfirma |
| Desc: Função que confirma para posicionar no campo pesquisado |
*---------------------------------------------------------------------*/
Static Function fConfirma()
Local aColsAux := oMsObj:aCols
Local nLinAtu := oMsObj:nAt
//Pegando o item atual
nAtuPvt := 0
nAtuPvt := aColsAux[nLinAtu][POS_ITE]
cAbaEsc := ""
cAbaEsc := aColsAux[nLinAtu][POS_ABA]
//Fecha a tela
oDlgPesq:End()
Return
/*---------------------------------------------------------------------*
| Func: fAtuAux |
| Desc: Função que atualiza a descrição |
*---------------------------------------------------------------------*/
Static Function fAtuAux()
Local aColsAux := oMsObj:aCols
Local nLinAtu := oMsObj:nAt
//Atualiza o texto auxiliar
cGetAux := Alltrim(aColsAux[nLinAtu][POS_DES])
oGetAux:Refresh()
Return
Bom pessoal, por hoje é só.
Abraços e até a próxima.

Bom dia!
Coloquei para teste, ele entra na função zSearch coloquei um alerta lá… Porém ele não abre a tela para pesquisa. Testei tanto no cadastro de TES como no cadastro de produto.
O que poderia ser?
Debugando os meus cReadVar estão vazio…
Boa noite Welder, tudo bem?
Eu vi um caso desse em que estava ativado o parâmetro MV_ENCHOLD, tente alterar ele, para deixar da forma nova (mais de duas colunas na tela).
Um grande abraço.
Boa tarde!
Obrigado pelo retorno, realmente é a questão do parâmetro. Porém utilizamos o MV_ENCHOLD = 1. Saberia dizer se existe alguma forma de utilizar a função sem que precise alterar o parâmetro? Ou só alterando a rotina da pesquisa?
Desde já obrigado.
Boa noite Welder, tudo bem?
Então, acho que para o caso de utilizar o MV_ENCHOLD, teria que analisar o código fonte e reconstruir ele para esse tipo de tela.
Talvez o mais fácil é alterar o parâmetro mesmo.
Um grande abraço.
Excelente para pesquisar campos relacionados a certo assunto em tabelas que possuem diversos campos! Parabéns mestre!
Ahooo Georgito, obrigado jovem.
Um grande abraço.