MarkBrowse que só permite marcar 1 registro

No artigo de hoje, vamos demonstrar em como criar um MarkBrowse que permite marcar apenas 1 registro.

Recentemente nos perguntaram, se seria possível criar algum browse com marcação de registros, mas que ele fosse definido para aceitar marcar apenas 1 registro do browse.

Pensando nisso, montamos esse exemplo, onde:

  1. A lógica é baseada no vídeo “Como fazer um MarkBrowse com coluna editável”, desse link
  2. No caso, ao marcar o registro, o primeiro passo é desmarcar todos os outros, e nesse cenário é feito um UPDATE alterando o campo do tipo lógico para .F.
  3. Em seguida, é feito a atualização do registro corrente
  4. E por fim é dado um refresh geral na tela

 

Abaixo um print de como ficou a tela:

Exemplo da tela com marcação de dados

Exemplo da tela com marcação de dados

 

Abaixo segue o código fonte desenvolvido (o trecho acima está a partir da linha 306):

//Bibliotecas
#Include "Totvs.ch"
#Include "FWMVCDef.ch"
 
/*/{Protheus.doc} User Function zTstMark
Exemplo de tela com marcação de dados
@author Atilio
@since 20/02/2023
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/
 
User Function zTstMark()
    Local aArea := FWGetArea()
    Local aPergs   := {}
    Local xPar0 := Space(TamSX3('B1_COD')[1])
    Local xPar1 := StrTran(xPar0, ' ', 'Z')
     
    //Adicionando os parametros do ParamBox
    aAdd(aPergs, {1, "Produto De",  xPar0,  "", ".T.", "SB1", ".T.", 80,  .F.})
    aAdd(aPergs, {1, "Produto Até", xPar1,  "", ".T.", "SB1", ".T.", 80,  .T.})
     
    //Se a pergunta for confirma, chama a tela
    If ParamBox(aPergs, 'Informe os parâmetros', /*aRet*/, /*bOk*/, /*aButtons*/, /*lCentered*/, /*nPosx*/, /*nPosy*/, /*oDlgWizard*/, /*cLoad*/, .F., .F.)
        fMontaTela()
    EndIf
     
    FWRestArea(aArea)
Return
 
/*/{Protheus.doc} fMontaTela
Monta a tela com a marcação de dados
@author Atilio
@since 20/02/2023
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/
 
Static Function fMontaTela()
    Local aArea         := GetArea()
    Local aCampos := {}
    Local aColunas := {}
    Local cFontPad    := 'Tahoma'
    Local oFontGrid   := TFont():New(cFontPad, /*uPar2*/, -14)
    //Barra de Botões
    Local bBlocoOk     := {|| lOk := .T., oDlgBrow:End()}
    Local bBlocoCan    := {|| lOk := .F., oDlgBrow:End()}
    Local aOutrasAc    := {}
    Local bBlocoIni    := {|| EnchoiceBar(oDlgBrow, bBlocoOk, bBlocoCan, , aOutrasAc)}
    Private lOk        := .F.
    Private oTempTable := Nil
    //Janela e componentes
    Private oDlgBrow
    Private oPanGrid
    Private oFWBrowse
    Private cAliasTmp := GetNextAlias()
    Private aRotina   := MenuDef()
    //Tamanho da janela
    Private aTamanho := MsAdvSize()
    Private nJanLarg := aTamanho[5]
    Private nJanAltu := aTamanho[6]
      
    //Adiciona as colunas que serão criadas na temporária
    aAdd(aCampos, { 'FLAG_OK', 'L', 1,                    0}) //Flag para marcação
    aAdd(aCampos, { 'B1_COD',  'C', TamSX3('B1_COD')[1],  0}) //Produto
    aAdd(aCampos, { 'B1_TIPO', 'C', TamSX3('B1_TIPO')[1], 0}) //Tipo
    aAdd(aCampos, { 'B1_UM',   'C', TamSX3('B1_UM')[1],   0}) //Unid. Med.
    aAdd(aCampos, { 'B1_DESC', 'C', TamSX3('B1_DESC')[1], 0}) //Descrição
    aAdd(aCampos, { 'RECNUM',  'N', 16,                   0}) //RecNo da SB1
 
    //Cria a tabela temporária
    oTempTable:= FWTemporaryTable():New(cAliasTmp)
    oTempTable:SetFields( aCampos )
    oTempTable:Create()  
 
    //Popula a tabela temporária
    Processa({|| fPopula()}, 'Processando...')
 
    //Adiciona as colunas que serão exibidas no browse
    aColunas := fCriaCols()
      
    //Criando a janela
    oDlgBrow := TDialog():New(0, 0, nJanAltu, nJanLarg, 'Tela para Consulta de Dados - Autumn Code Maker', , , , , , /*nCorFundo*/, , , .T.)
        //Dados
        oPanGrid := tPanel():New(035, 001, '', oDlgBrow, /*oFont*/, /*lCentered*/, /*uParam7*/, RGB(000,000,000), RGB(254,254,254), (nJanLarg/2) - 1, (nJanAltu/2) - 1)
        oFWBrowse := FWBrowse():New()
        oFWBrowse:DisableFilter()
        oFWBrowse:DisableConfig()
        oFWBrowse:DisableReport()
        oFWBrowse:DisableSeek()
        oFWBrowse:DisableSaveConfig()
        oFWBrowse:SetFontBrowse(oFontGrid)
        oFWBrowse:SetAlias(cAliasTmp)
        oFWBrowse:SetDataTable()
        oFWBrowse:SetEditCell(.T., {|| .T.}) 
        oFWBrowse:lHeaderClick := .F.
        oFWBrowse:AddMarkColumns(;
            {|| Iif((cAliasTmp)->FLAG_OK, 'LBOK', 'LBNO') },;      //ícones
            {|| fMarcaReg()};   //ao dar duplo clique
        )
         
        //Define as colunas, vincula ao painel e exibe
        oFWBrowse:SetColumns(aColunas)
        oFWBrowse:SetOwner(oPanGrid)
        oFWBrowse:Activate()
         
    oDlgBrow:Activate(, , , .T., , , bBlocoIni)
 
    //Se usuário clicou no botão confirmar
    If lOk
        fConfirmou()
    EndIf
     
    //Deleta a temporária e desativa a tela de marcação
    oTempTable:Delete()
    oFWBrowse:DeActivate()
     
    RestArea(aArea)
Return
 
/*/{Protheus.doc} MenuDef
Botões usados no Browse
@author Atilio
@since 20/02/2023
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/
 
Static Function MenuDef()
    Local aRotina := {}
      
    //Criação das opções
    ADD OPTION aRotina TITLE 'Continuar'  ACTION 'u_zVid72Ok'     OPERATION 2 ACCESS 0
Return aRotina
 
/*/{Protheus.doc} fPopula
Executa a query SQL e popula essa informação na tabela temporária usada no browse
@author Atilio
@since 20/02/2023
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/
 
Static Function fPopula()
    Local cQryDados := ''
    Local nTotal := 0
    Local nAtual := 0
 
    //Monta a consulta
    cQryDados += "SELECT B1_COD, B1_TIPO, B1_UM, B1_DESC, SB1.R_E_C_N_O_ AS SB1REC "        + CRLF
    cQryDados += "FROM SB1990 SB1 "        + CRLF
    cQryDados += "WHERE B1_FILIAL = '' AND B1_COD >= '" + MV_PAR01 + "' AND B1_COD <= '" + MV_PAR02 + "' AND SB1.D_E_L_E_T_ = ' ' "        + CRLF
    cQryDados += "ORDER BY B1_COD"        + CRLF
    PLSQuery(cQryDados, 'QRYDADTMP')
 
    //Definindo o tamanho da régua
    DbSelectArea('QRYDADTMP')
    Count to nTotal
    ProcRegua(nTotal)
    QRYDADTMP->(DbGoTop())
 
    //Enquanto houver registros, adiciona na temporária
    While ! QRYDADTMP->(EoF())
        nAtual++
        IncProc('Analisando registro ' + cValToChar(nAtual) + ' de ' + cValToChar(nTotal) + '...')
 
        RecLock(cAliasTmp, .T.)
            (cAliasTmp)->FLAG_OK := .F.
            (cAliasTmp)->B1_COD := QRYDADTMP->B1_COD
            (cAliasTmp)->B1_TIPO := QRYDADTMP->B1_TIPO
            (cAliasTmp)->B1_UM := QRYDADTMP->B1_UM
            (cAliasTmp)->B1_DESC := QRYDADTMP->B1_DESC
            (cAliasTmp)->RECNUM := QRYDADTMP->SB1REC
        (cAliasTmp)->(MsUnlock())
 
        QRYDADTMP->(DbSkip())
    EndDo
    QRYDADTMP->(DbCloseArea())
    (cAliasTmp)->(DbGoTop())
Return
 
/*/{Protheus.doc} fCriaCols
Função que gera as colunas usadas no browse (similar ao antigo aHeader)
@author Atilio
@since 20/02/2023
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/
 
Static Function fCriaCols()
    Local nAtual       := 0 
    Local aColunas := {}
    Local aEstrut  := {}
    Local oColumn
     
    //Adicionando campos que serão mostrados na tela
    //[1] - Campo da Temporaria
    //[2] - Titulo
    //[3] - Tipo
    //[4] - Tamanho
    //[5] - Decimais
    //[6] - Máscara
    //[7] - Editável? .T. = sim, .F. = não
    aAdd(aEstrut, { 'B1_COD',  'Produto',    'C', TamSX3('B1_COD')[1],  0, '',     .F.})
    aAdd(aEstrut, { 'B1_TIPO', 'Tipo',       'C', TamSX3('B1_TIPO')[1], 0, '',     .F.})
    aAdd(aEstrut, { 'B1_UM',   'Unid. Med.', 'C', TamSX3('B1_UM')[1],   0, '',     .F.})
    aAdd(aEstrut, { 'B1_DESC', 'Descrição',  'C', TamSX3('B1_DESC')[1], 0, '',     .T.})
 
    //Percorrendo todos os campos da estrutura
    For nAtual := 1 To Len(aEstrut)
        //Cria a coluna
        oColumn := FWBrwColumn():New()
        oColumn:SetData(&('{|| ' + cAliasTmp + '->' + aEstrut[nAtual][1] +'}'))
        oColumn:SetTitle(aEstrut[nAtual][2])
        oColumn:SetType(aEstrut[nAtual][3])
        oColumn:SetSize(aEstrut[nAtual][4])
        oColumn:SetDecimal(aEstrut[nAtual][5])
        oColumn:SetPicture(aEstrut[nAtual][6])
 
        //Se for ser possível ter o duplo clique
        If aEstrut[nAtual][7]
            oColumn:SetEdit(.T.)
            oColumn:SetReadVar(aEstrut[nAtual][1])
            //oColumn:SetValid({|| fSuaValid()})
        EndIf
 
        //Adiciona a coluna
        aAdd(aColunas, oColumn)
    Next
Return aColunas
 
/*/{Protheus.doc} fConfirmou
Função acionada após fechar a tela se o usuário confirmou
@author Atilio
@since 20/02/2023
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/
 
Static Function fConfirmou()
    Processa({|| fProcessa()}, 'Processando...')
Return
 
/*/{Protheus.doc} fProcessa
Função que percorre os registros da tela
@author Atilio
@since 20/02/2023
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/
 
Static Function fProcessa()
    Local aArea     := FWGetArea()
    Local nAtual    := 0
    Local nTotal    := 0
    Local nTotMarc := 0
     
    //Define o tamanho da régua
    DbSelectArea(cAliasTmp)
    (cAliasTmp)->(DbGoTop())
    Count To nTotal
    ProcRegua(nTotal)
     
    //Percorrendo os registros
    (cAliasTmp)->(DbGoTop())
    While ! (cAliasTmp)->(EoF())
        nAtual++
        IncProc('Analisando registro ' + cValToChar(nAtual) + ' de ' + cValToChar(nTotal) + '...')
     
        //Caso esteja marcado
        If (cAliasTmp)->FLAG_OK
            nTotMarc++
             
            //Posiciona no registro original
            RecLock("SB1")
            SB1->(DbGoTo((cAliasTmp)->RECNUM))
 
            //Grava a alteração
            RecLock("SB1", .F.)
                SB1->B1_DESC := (cAliasTmp)->B1_DESC
            SB1->(MsUnlock())
        EndIf
          
        (cAliasTmp)->(DbSkip())
    EndDo
     
    //Mostra a mensagem de término e caso queria fechar a dialog, basta usar o método End()
    FWAlertInfo('Dos [' + cValToChar(nTotal) + '] registros, foram processados [' + cValToChar(nTotMarc) + '] registros', 'Atenção')
 
    FWRestArea(aArea)
Return

Static Function fMarcaReg()
    //Marca todos com .F. para limpar a Flag com exceção do registro atual
    //   Necessário baixar o zExecQry em https://terminaldeinformacao.com/2021/04/21/como-fazer-um-update-via-advpl/
    u_zExecQry("UPDATE " + oTempTable:GetRealName() + " SET FLAG_OK = 'F' WHERE R_E_C_N_O_ != " + cValToChar((cAliasTmp)->(RecNo())) )

    //Atualiza o registro atual, marcando ou desmarcando ele
    (cAliasTmp)->FLAG_OK := ! (cAliasTmp)->FLAG_OK

    //Atualiza a tela posicionando no topo
    oFWBrowse:Refresh(.T.)
Return

Bom pessoal, por hoje é só.

Abraços e até a próxima.

Dan (Daniel Atilio)
Cristão de ramificação protestante. Especialista em Engenharia de Software pela FIB, graduado em Banco de Dados pela FATEC Bauru e técnico em informática pelo CTI da Unesp. Entusiasta de soluções Open Source e blogueiro nas horas vagas. Autor e mantenedor do portal Terminal de Informação.

Deixe uma resposta

Terminal de Informação