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.
