Você está na página 1de 8

PLANO DE CONTAS - EU PRECISAVA DE UM DESSES NESSA FORMA E NO SEI COMO FAZER!

PARTE 1 DE 2 Por Exio I - Introduo Estimados amigos, estou mais uma vez aqui e espero estar ajudando a algum. Volto a mencionar que no sou nenhum programador de alto nvel e nem detentor da verdade, porm, o mais importante tentar. Depois de muito tempo, trabalhando e tambm viajando muito consegui sentar para escrever esse material. Nesses dias que se passaram um grande colega meu entrou em contato e pediu ajuda para desenvolver um Plano de Contas. A minha primeira recomendao caiu sobre os componentes da DevExpress. O grande problema como sabemos, que cada carga de componente do DevExpress, o arquivo aumenta e a possibilidade de problemas tambm aumenta. Ao mesmo tempo ascendeu uma ideia de fazer o plano exclusivamente sobre o componente TreeView. E assim, se d a sequncia desse material. OBSERVAO Esse material destinado exclusivamente ao site PlanetaDelphi. No pode ser copiado parcialmente ou na sua totalidade sem autorizao do autor. Links podem ser realizados desde que atentem e respeitem as regras dos Direitos Autorais. II - Do que precisamos? Nada alm do bsico, Delphi e boa vontade. Os conhecimentos de programao no sero abordados. Usurios de nvel iniciante conseguiro entender e fazer o trabalho tranquilamente. III - Verso do Delphi Para esse artigo foi usado o Delphi 2010. IV - Explicando melhor o ocorrido Como lhes falei anteriormente, esse amigo no sabia mais o que fazer, queria porque queria um plano de contas que fosse gerado automaticamente e com a respectiva hierarquia de cdigos. Entendendo o problema, veio a sugesto do DevExpress, porm, os motivos foram comentados acima e a deciso foi por gua abaixo. Num segundo momento, surge os componentes VirtualTreeView. Bem, levava mais tempo aprendendo o componente e as suas restries e os esqueminhas, e no final, o problema se mantinha. Mas qual problema? Muitos planos de contas exigem uma janela secundria contendo um ou mais campos de edio, forando o usurio a pressionar sobre o "ramo" desejado e em seguida, preencher os campos, e depois mais um boto, para finalmente surgir o "ramo". Existem alguns componentes no Delphi que nunca damos muita ateno, o TreeView um deles; justamente por no darmos muita ateno, temos que aprender a utiliz-los sobre presso. Pensando que: componentes de terceiros sempre aumentam consideravelmente o cdigo-fonte; geram algumas excees que resultam na criao de blocos de proteo sem necessidade; exigem algumas programaes que aparentam ser desnecessrias, e por ai vai... Ento, mantive a ideia do TreeView in natura do Delphi. Vamos aos problemas aparentemente existentes para o meu estimado amigo: 12345A insero de itens tinha que ser feita de maneira natural, isto , com poucos procedimentos "burocrticos"; Os cdigos da hierarquia tinham que ser gerados automaticamente, isto , na hora da insero; A recriao da rvore tem que ser rpida e sem muita programao; contas excludas tinham que ficar em um tom diferente e tambm ficar impossvel de alterar; Salvar o plano alterado ou criado rapidamente;

V - Fazendo acontecer Execute o Delphi, crie um novo projeto e salve os arquivos da melhor forma que lhe convier. Adicione um componente TreeView e renomeie a sua propriedade name para trv_PlanoC. Mude a propriedade sorttype para stText.

Imagem 01 adicionando o componente treeview Existem dois problemas diretos no treeview que devemos nos preocupar, o primeiro como o usurio est procedendo com a criao dos itens. Eles podem ser criados numa raiz root sequencialmente, ou podem ser criados em um dos ramos existentes na raiz ou em outro sub-ramo. Alm disso, voc est querendo ordenar os elementos inseridos no treeview, essa ordenao pode ser dada por eventos no momento da insero dos itens, ou pode ser pela forma definida na propriedade sorttype + mtodo de insero. Ento, a esto os dois problemas com que deve se preocupar. Vamos iniciar a codificao que visualizaremos melhor como ser resolvido o problema. Clique no componente trv_PlanoC, e na aba Events localize o evento onkeyDown, d dois cliques para acionar o editor e vamos a codificao como apresentada abaixo: procedure TForm1.trv_PlanoCKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); var i, n, ponto, oldV, newV : Integer; textoPos : String; begin if ((Key = VK_INSERT) or (key = VK_F2)) then if (trv_PlanoC.Selected = nil) then begin if trv_PlanoC.Items.Count = 0 then trv_PlanoC.Items.Add(nil, '1. ') else begin oldV := 0; newV := 0; for i := 0 to pred(trv_PlanoC.Items.Count) do begin n := 1; while n <= Length(trv_PlanoC.Items.Item[i].Text) do begin if (Copy(trv_PlanoC.Items.Item[i].Text, n, 1) = '.') then begin ponto := n; if ponto > 1 then newV := StrToInt(Copy(trv_PlanoC.Items.Item[i].Text, 1, ponto 1)); if oldV < newV then begin oldV := newV; newV := 0; end; Break; end; n := n + 1; end; end; trv_PlanoC.Items.Add(nil, format('%d. ',[oldV + 1]));

end; end else begin //essa parte veremos mais adiante end; end; O que faz esse fragmento de cdigo? Quando o usurio pressiona a tecla INSERT ou a tecla de funo F2, o sistema far uma verificao procurando por algum item dentro do trv_PlanoC que esteja selecionado, se no houver uma seleo ativa (resultando em nil) o prximo passo e a primeira condio IF. Esse teste o mais importante, j que ele verificar se no existem itens registrados na raiz atravs de trv_PlanoC.items.count. Se nenhum item tiver sido registrado anteriormente, o primeiro registro ocorrer atravs de trv_PlanoC.Items.Add(nil, 1. ). Muitos tutoriais recomendam o uso de AddFirst(), essa funo registra o item no topo do lista, e isso particularmente no nos interessa. Atente que o valor do tipo string um (1) no parmetro, ser o primeiro cdigo da estrutura; o ponto que acompanha o valor 1 ser nossa referncia para sabermos em que nvel o cdigo est sendo registrado. Ele ser o ponto de partida. Continuando a leitura do cdigo, vemos a condio False do IF. As duas primeiras variveis oldV e newV (oldvalue e newvalue) so variveis de troca, a varivel ponto de posio do ponto (sinal ponto .). No primeiro For o sistema verificou que existem diversas razes (ou uma pelo menos) e que no h seleo de nenhum dos itens no TreeView. Pega o primeiro item e extrai o tamanho dele atravs da funo Length, em seguida, procura o sinal ponto .. Como consequncia, o tamanho do nmero copiado e convertido para a varivel oldV , e assim sucessivamente ser executado com os demais itens. medida que os valores copiados so maiores que os anteriores, o valor da varivel oldV ser substitudo e a ordenao vai ocorrendo naturalmente.

Imagem 02 Executando a criao de itens na raiz interessante que no precisa ser escrito nenhum cdigo para editar esses itens que foram criados, o usurio s precisa clicar sobre os itens como se fosse executar um procedimento de renomear arquivos. A parte mais simples est feita, como geramos uma formatao em nosso cdigo, temos que nos preocupar com o lado do cliente. Para termos certeza que no ocorrero saltos nos cdigos porque nossos usurios resolveram adicionar pontos nas contas, no evento onKeyPress do trv_PlanoC adicionamos o cdigo listado abaixo: procedure TForm1.trv_PlanoCKeyPress(Sender: TObject; var Key: Char); begin If CharInSet(key, ['.']) then begin MessageDlg('No digite ponto (.), o sistema usa esse sinal para nivelar as contas.', mtInformation, [mbOK], 0); key := #0; end; end; Como pode ser observado, o nosso primeiro cdigo digitado no tem uma parte final que justamente a mais importante de todas. Atravs dessa continuao que o sistema verificar as ocorrncias para itens selecionados em nossa rvore. Exemplo: Vamos supor que foram criadas as duas ou quatro contas principais de nosso plano de contas, algo como:

1. 2. 3. 4.

RECEITAS DESPESAS RECEITAS NO OPERACIONAIS DESPESAS NO OPERACIONAIS

Se desejar continuar a hierarquia atravs do pressionamento de INSERT ou F2, nada ocorrer. Vamos ento a parte complementar desse cdigo. Para que a codificao possa ser processada, temos duas novas condies que podero ocorrer, sendo: a primeira que o usurio pode estar no topo de uma raiz e quer adicionar itens; a segunda: ele pode estar no meio da raiz. Graas funo seguinte, ela nos criar todas as sequncias necessrias para executar a tarefa com sucesso. function TForm1.calcularNivelDependente(textoOrigem: String; adiciona : String): String; var nivel : array [0..20] of integer; i, nivelC : SmallInt; niveisTxt : TStringList; saida : String; begin i := 0; niveisTxt := TStringList.Create; for i := 0 to 20 do nivel[i] := -1; try niveisTxt.Text := StringReplace(textoOrigem, '.', #13, [rfReplaceAll, rfIgnoreCase]); for i := 0 to niveisTxt.Count - 2 do begin nivel[i] := StrToInt(niveisTxt.Strings[i]); nivelC := i; end; if adiciona = '=' then if nivel[nivelC + 1] = -1 then nivel[nivelC + 1] := 1; if adiciona = '+' then if nivel[nivelC] = -1 then nivel[nivelC] := 1 else nivel[nivelC] := nivel[nivelC] + 1; if adiciona = '=' then for i := 0 to nivelC + 1 do saida := saida + IntToStr(nivel[i]) + '.'; if adiciona = '+' then for i := 0 to nivelC do saida := saida + IntToStr(nivel[i]) + '.'; Result := saida; finally niveisTxt.Free; end; end; Explicando. Essa funo importante porque atravs dela, que ser gerado o cdigo para o prximo nvel de nosso plano de contas ou para o nvel atual. O parmetro textoOrigem recebe a propriedade text da posio atual ou da ltima posio, algo como 1.2.1 Receitas com Vendas na Internet. O parmetro adiciona recebe dois possveis valores, que so: = (igual) ou + (mais). Esse um padro que eu adotei, poderia ser feito de outra forma. Quem sabe outros programadores podero recomendar melhores formas programticas para essa funo. No bloco de variveis existe uma em especial que gostaria de chamar ateno, a varivel nvel do tipo array de inteiros. Essa varivel usada para medir o nvel em que a sua seleo est posicionada. Eu usei um array de 20 elementos, isso um absurdo! O problema que existem usurios e usurios; outro detalhe importante que os prprios Conselhos Regionais de Contabilidade recomendam um padro para Planos de Contas, veja,

recomendam. Na maioria dos livros que tenho, os melhores ou mais recomendados planos de contas possuem no mximo cinco (5) nveis, isso quer dizer: U.X.Y.W.Z. Mas coloquei 20 para ser bem exagerado e garantir que nenhum mortal da empresa do meu amigo vai deixar de fazer um plano de contas da forma que mais gostar. Criei um StringList que vai desmembrar o texto separado pelos pontos. Na sequncia, defini um valor padro para todos os componentes do array, o valor -1. Aps desmembrar o texto usando a funo StringReplace, que eu considero o biotnico Fontoura (risos) da programao, aplico um For subtraindo de 2. Explico o por que. Veja: Vamos dizer que estamos dentro de uma conta assim nomeada: 1.2. Vendas a Vista. Ao desmembrar esse texto com stringReplace para um destino stringlist, teremos a seguinte forma de ocupao: [0] 1 [1] 2 [2] Vendas Vista Perceba que todas as propriedades Count de um listbox, memo, combobox sempre um valor a mais. Ento uso menos 2, logo: 3 2 = 1, capturando somente os valores dos vetores nas casas 0 e 1. Agora, atente para o resto da funo. Estamos em 1. RECEITAS (selecionada), queremos criar duas contas dependentes de RECEITAS. Vamos ento pegar a posio atual e adicionar um nvel, ocupando uma nova casa do vetor. O resto da funo destinado somente soma de Strings para retorno de funo. Adicione o seguinte fragmento de cdigo no evento onKeyDown onde voc encontra o texto essa parte veremos mais adiante... with trv_PlanoC do begin Items.BeginUpdate; if (Selected.HasChildren = False) then begin with trv_PlanoC.Items.AddChild(trv_PlanoC.Selected, format('%s ',[calcularNivelDependente(trv_PlanoC.Selected.Text, '=')])) do MakeVisible; end else begin textoPos := trv_PlanoC.Selected.GetLastChild.Text; with trv_PlanoC.Items.AddChild(trv_PlanoC.Selected, format('%s ',[calcularNivelDependente(textoPos, '+')])) do MakeVisible; end; Items.EndUpdate; end;

Porque essa parte importante? Porque devemos entender o seguinte: se estamos no topo da raiz, em uma conta qualquer, necessrio saber se temos dependentes dessa conta (Child). Veja: Ex.1) 1. RECEITAS Ex.2) 1. RECEITAS 1.1 Vendas Vista No primeiro exemplo no temos dependentes (Child), no exemplo 2 temos um dependente. No exemplo um, adicionamos um nvel inferior porque testamos a existncia de dependentes atravs da funo HasChildren. Ento usamos a opo = para identificar que queremos criar nveis abaixo. Caso estivssemos no exemplo 2, o procedimento verifica a existncia de um ramo da Conta RECEITAS, ento desloca o cursor para o ramo 1.1 Vendas Vista, ocupa as duas primeiras casas do vetor e adicionar um nova casa. Veja o resultado na figura abaixo de nosso sistema em execuo.

imagem 03 Nosso plano de contas funcionando Na verdade, depois de todo o cdigo estar digitado e funcionando, no parece ser um cdigo que faz muita coisa, mas ele quebra um galho danado. Continuando a nossa expedio pelo Plano de Contas, vamos adicionar um novo cdigo no evento onKeyDown de nosso trv_PlanoC: if (Key = VK_ESCAPE) then trv_PlanoC.Selected := nil; Esse pequeno fragmento de cdigo permite remover a seleo de qualquer item que esteja selecionado atualmente no nosso Plano de Contas, til porque podemos voltar a criar novas contas na raiz. incrvel, mas depois que a parte nervosa fica pronta, parece que os tratamentos de determinados tipos de situaes de controle ficam mais fceis de serem programados. Vamos supor agora, que desejamos cancelar uma conta em nosso plano. A conta ao invs de ser excluda, ficar desabilitada. No mesmo evento onKeyDown entre com o seguinte cdigo: if (Key = VK_F4) then begin if trv_PlanoC.Selected = nil then Application.MessageBox('Para poder cancelar uma conta, voc deve selecionar a conta primeiro !', 'Sem Seleo de Conta', MB_ICONWARNING) else if Application.MessageBox(Pchar(format('Voc deseja cancelar o uso da conta "%s" ?',[ trv_PlanoC.Selected.Text])), 'Cancelar Conta', MB_ICONQUESTION + MB_YESNO) = mrYes then trv_PlanoC.Selected.Enabled := False; end; Quando o usurio pressionar a tecla de funo F4, ser feita uma verificao para ver se uma conta foi selecionada antes de ser desabilitada. Existindo um item selecionado, o sistema o desabilita atravs da propriedade Enabled, com o seguinte cdigo: trv_PlanoC.Selected.Enabled := False;

Imagem 04 Ao pressionar F4 para desabilitar uma conta

imagem 05 Itens desabilitados no plano de contas Ao desabilitar uma conta, o sistema no trava o item do TreeView, ele simplesmente fica numa cor diferente, se voc quiser renomear, nada o impedir. Mas para que exista esse impedimento e que o usurio no possa alterar uma conta desabilitada, adicione no evento onEditing do trv_PlanoC: procedure TForm1.trv_PlanoCEditing(Sender: TObject; Node: TTreeNode; var AllowEdit: Boolean); begin if Node.Enabled = False then Begin Application.MessageBox(Pchar(Format('A Conta "%s" foi Cancelada e no pode mais ser reutilizada !',[trv_PlanoC.selected.Text])), 'Conta Cancelada', MB_ICONINFORMATION); AllowEdit := False; end; end; Quando o usurio clicar sobre a conta tentando renomear, a advertncia ser lanada. Quem trava a edio desse item a propriedade AllowEdit que fica definida para false. Por hoje s! Mas na sequncia faremos o resto ao que foi proposto. A recriao do Plano de Contas e alguns pequenos detalhes que na verdade no foram colocados nesse texto e que foram solicitados por ele (meu estimado amigo) viro na segunda parte a continuao. Se outros tiverem uma forma melhor de fazer isso, e com certeza tem, gostaria de receber as contribuies para melhorar esse exemplo.

Clique aqui para obter o cdigo-fonte Abraos. Exio bitzero_000

Você também pode gostar