Nesse artigo vamos demonstrar em como criar um totalizador conforme os registros são marcados numa tela com MarkBrowse.
Recentemente nos perguntaram, como seria possível criar um label, que é atualizado conforme é marcado os registros em um MarkBrowse.
Pensando nisso, montamos esse exemplo, onde é exibido informações da SE1 (Títulos a Receber) e ao marcar um registro é atualizado a informação em um TSay no rodapé.
A lógica efetuada para isso, foi a seguinte:
- O primeiro passo é declarar as variáveis oSayLog e cSayLog como Private
- O segundo passo, na criação do FWMarkBrowse, vamos usar o método SetAfterMark acionando uma função para contar os registros (função fAposMarcar)
- O terceiro passo, vai ser antes de criar a tela, criar o TSay abaixo do grid
- Na função fAposMarcar, vamos percorrer todos os dados da tela, e onde tiver marcado vamos incrementar variáveis (nTotSaldo e nTotMarc)
- Por fim, será atualizado a variável cSayLog e dado um refresh no objeto oSayLog
Abaixo um gif de como ficou a tela:
E abaixo o código fonte desenvolvido:
//Bibliotecas #Include "Totvs.ch" #Include "FWMVCDef.ch" /*/{Protheus.doc} User Function zTstMk Teste MarkBrowse @author Atilio @since 05/02/2025 @version 1.0 @type function @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ User Function zTstMk() Local aArea := FWGetArea() Local aPergs := {} Local dDataDe := FirstDate(Date()) Local dDataAte := LastDate(dDataDe) //Adicionando os parametros do ParamBox aAdd(aPergs, {1, "Vencimento De", dDataDe, "", ".T.", "", ".T.", 80, .T.}) aAdd(aPergs, {1, "Vencimento Até", dDataAte, "", ".T.", "", ".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 05/02/2025 @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) Local oFontLog := TFont():New(cFontPad, , -14) Private oTempTable := Nil //Log que vai ter o total Private oSayLog Private cSayLog := "..." //Janela e componentes Private oDlgMark Private oPanGrid Private oMarkBrowse 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, { 'OK' , 'C', 2, 0 }) //Flag para marcação aAdd(aCampos, { 'E1_NUM' , 'C', TamSX3('E1_NUM' )[1], 0 }) //Título aAdd(aCampos, { 'E1_PREFIXO', 'C', TamSX3('E1_PREFIXO')[1], 0 }) //Prefixo aAdd(aCampos, { 'E1_PARCELA', 'C', TamSX3('E1_PARCELA')[1], 0 }) //Parcela aAdd(aCampos, { 'E1_EMISSAO', 'D', TamSX3('E1_EMISSAO')[1], 0 }) //Emissão aAdd(aCampos, { 'E1_VENCREA', 'D', TamSX3('E1_VENCREA')[1], 0 }) //Venc. Real aAdd(aCampos, { 'E1_SALDO' , 'N', TamSX3('E1_SALDO' )[1], TamSX3('E1_SALDO' )[2]}) //Saldo aAdd(aCampos, { 'E1_CLIENTE', 'C', TamSX3('E1_CLIENTE')[1], 0 }) //Cliente aAdd(aCampos, { 'E1_NOMCLI' , 'C', TamSX3('E1_NOMCLI' )[1], 0 }) //Nome aAdd(aCampos, { 'E1_HIST' , 'C', TamSX3('E1_HIST' )[1], 0 }) //Histórico //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 FWMarkBrowse aColunas := fCriaCols() //Criando a janela DEFINE MSDIALOG oDlgMark TITLE 'Tela para Marcação de dados - Autumn Code Maker' FROM 000, 000 TO nJanAltu, nJanLarg COLORS 0, 16777215 PIXEL //Dados oPanGrid := tPanel():New(001, 001, '', oDlgMark, /*oFont*/, /*lCentered*/, /*uParam7*/, RGB(000,000,000), RGB(254,254,254), (nJanLarg/2) - 1, (nJanAltu/2) - 20) oMarkBrowse := FWMarkBrowse():New() oMarkBrowse:SetAlias(cAliasTmp) oMarkBrowse:SetDescription('Teste MarkBrowse') oMarkBrowse:DisableFilter() oMarkBrowse:DisableConfig() oMarkBrowse:DisableSeek() oMarkBrowse:DisableSaveConfig() oMarkBrowse:SetFontBrowse(oFontGrid) oMarkBrowse:SetFieldMark('OK') oMarkBrowse:SetTemporary(.T.) oMarkBrowse:SetColumns(aColunas) //oMarkBrowse:AllMark() oMarkBrowse:SetOwner(oPanGrid) oMarkBrowse:SetAfterMark({|| fAposMarcar()}) oMarkBrowse:Activate() //Cria um TSay no fim da tela que conterá o totalizador nLinObj := (nJanAltu/2) - 15 oSayLog := TSay():New(nLinObj, 003, {|| cSayLog}, oDlgMark, "", oFontLog, , , , .T., , , (nJanLarg/2) - 6, 10, , , , , , .F., , ) ACTIVATE MsDialog oDlgMark CENTERED //Deleta a temporária e desativa a tela de marcação oTempTable:Delete() oMarkBrowse:DeActivate() RestArea(aArea) Return /*/{Protheus.doc} MenuDef Botões usados no Browse @author Atilio @since 05/02/2025 @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_zTstMkO' 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 05/02/2025 @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 " + CRLF cQryDados += " E1_NUM, " + CRLF cQryDados += " E1_PREFIXO, " + CRLF cQryDados += " E1_PARCELA, " + CRLF cQryDados += " E1_EMISSAO, " + CRLF cQryDados += " E1_VENCREA, " + CRLF cQryDados += " E1_SALDO, " + CRLF cQryDados += " E1_CLIENTE, " + CRLF cQryDados += " E1_NOMCLI, " + CRLF cQryDados += " E1_HIST " + CRLF cQryDados += "FROM " + CRLF cQryDados += " " + RetSQLName("SE1") + " SE1 " + CRLF cQryDados += "WHERE " + CRLF cQryDados += " E1_FILIAL = '" + FWxFilial("SE1") + "' " + CRLF cQryDados += " AND E1_VENCREA >= '" + dToS(MV_PAR01) + "' " + CRLF cQryDados += " AND E1_VENCREA <= '" + dToS(MV_PAR02) + "' " + CRLF cQryDados += " AND E1_BAIXA = '' " + CRLF cQryDados += " AND SE1.D_E_L_E_T_ = ' '" + CRLF If '--' $ cQryDados .Or. 'WITH' $ Upper(cQryDados) .Or. 'NOLOCK' $ Upper(cQryDados) FWAlertInfo('Alguns comandos (como --, WITH e NOLOCK), não são executados pela PLSQuery devido ao ChangeQuery. Tente migrar da PLSQuery para TCQuery.', 'Atenção') EndIf 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)->OK := Space(2) (cAliasTmp)->E1_NUM := QRYDADTMP->E1_NUM (cAliasTmp)->E1_PREFIXO := QRYDADTMP->E1_PREFIXO (cAliasTmp)->E1_PARCELA := QRYDADTMP->E1_PARCELA (cAliasTmp)->E1_EMISSAO := QRYDADTMP->E1_EMISSAO (cAliasTmp)->E1_VENCREA := QRYDADTMP->E1_VENCREA (cAliasTmp)->E1_SALDO := QRYDADTMP->E1_SALDO (cAliasTmp)->E1_CLIENTE := QRYDADTMP->E1_CLIENTE (cAliasTmp)->E1_NOMCLI := QRYDADTMP->E1_NOMCLI (cAliasTmp)->E1_HIST := QRYDADTMP->E1_HIST (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 05/02/2025 @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 aAdd(aEstrut, { 'E1_NUM' , 'Título' , 'C', TamSX3('E1_NUM' )[1], 0, ''}) aAdd(aEstrut, { 'E1_PREFIXO', 'Prefixo' , 'C', TamSX3('E1_PREFIXO')[1], 0, ''}) aAdd(aEstrut, { 'E1_PARCELA', 'Parcela' , 'C', TamSX3('E1_PARCELA')[1], 0, ''}) aAdd(aEstrut, { 'E1_EMISSAO', 'Emissão' , 'D', TamSX3('E1_EMISSAO')[1], 0, ''}) aAdd(aEstrut, { 'E1_VENCREA', 'Venc. Real', 'D', TamSX3('E1_VENCREA')[1], 0, ''}) aAdd(aEstrut, { 'E1_SALDO' , 'Saldo' , 'N', TamSX3('E1_SALDO' )[1], TamSX3('E1_SALDO' )[2], PesqPict('SE1', 'E1_SALDO')}) aAdd(aEstrut, { 'E1_CLIENTE', 'Cliente' , 'C', TamSX3('E1_CLIENTE')[1], 0, ''}) aAdd(aEstrut, { 'E1_NOMCLI' , 'Nome' , 'C', TamSX3('E1_NOMCLI' )[1], 0, ''}) aAdd(aEstrut, { 'E1_HIST' , 'Histórico' , 'C', TamSX3('E1_HIST' )[1], 0, ''}) //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]) //Muda o alinhamento conforme o tipo, Data será Centralizado If aEstrut[nAtual][3] == 'D' oColumn:nAlign := 0 //Numérico, direita ElseIf aEstrut[nAtual][3] == 'N' oColumn:nAlign := 2 //Senão, esquerda (caractere) Else oColumn:nAlign := 1 EndIf //Adiciona a coluna aAdd(aColunas, oColumn) Next Return aColunas /*/{Protheus.doc} User Function zTstMkO Função acionada pelo botão continuar da rotina @author Atilio @since 05/02/2025 @version 1.0 @type function @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ User Function zTstMkO() Processa({|| fProcessa()}, 'Processando...') Return /*/{Protheus.doc} fProcessa Função que percorre os registros da tela @author Atilio @since 05/02/2025 @version 1.0 @type function @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ Static Function fProcessa() Local aArea := FWGetArea() Local cMarca := oMarkBrowse:Mark() 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 oMarkBrowse:IsMark(cMarca) nTotMarc++ /* //Aqui dentro você pode fazer o seu processamento FWAlertInfo((cAliasTmp)->E1_NUM, "Mensagem de teste") */ 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') //oDlgMark:End() FWRestArea(aArea) Return Static Function fAposMarcar() Local aArea := FWGetArea() Local cMarca := oMarkBrowse:Mark() Local nTotal := 0 Local nTotMarc := 0 Local nTotSaldo := 0 //Define o tamanho da régua DbSelectArea(cAliasTmp) (cAliasTmp)->(DbGoTop()) Count To nTotal //Percorrendo os registros (cAliasTmp)->(DbGoTop()) While ! (cAliasTmp)->(EoF()) //Caso esteja marcado If oMarkBrowse:IsMark(cMarca) nTotMarc++ nTotSaldo += (cAliasTmp)->E1_SALDO EndIf (cAliasTmp)->(DbSkip()) EndDo //Atualiza o log em baixo do browse cSayLog := "" cSayLog += "Dos [" + cValToChar(nTotal) + "] registros, " cSayLog += "foram marcados [" + cValToChar(nTotMarc) + "] registros, " cSayLog += "totalizando o valor [" + cValToChar(nTotSaldo) + "] de saldo dos títulos a receber." oSayLog:Refresh() FWRestArea(aArea) Return
Bom pessoal, por hoje é só.
Abraços e até a próxima.