Pesquisa de Campos em Telas do Protheus

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:

Filtragem de campos na tela de TES

Filtragem de campos na tela de TES

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.

Dan Atilio (Daniel Atilio)
Especialista em Engenharia de Software pela FIB. Entusiasta de soluções Open Source. E blogueiro nas horas vagas.

7 Responses

  1. Welder Loiola disse:

    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?

  2. Welder Loiola disse:

    Debugando os meus cReadVar estão vazio…

    • Dan_Atilio disse:

      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.

  3. Welder Loiola disse:

    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.

    • Dan_Atilio disse:

      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.

  4. George Lopes disse:

    Excelente para pesquisar campos relacionados a certo assunto em tabelas que possuem diversos campos! Parabéns mestre!

Deixe uma resposta para George Lopes Cancelar resposta