001: /*
002: * Created on 03/11/2004
003: *
004: * Swing Components - visit http://sf.net/projects/gfd
005: *
006: * Copyright (C) 2004 Igor Regis da Silva Simões
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or (at your option) any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022: package br.com.gfp.features;
023:
024: import java.sql.Date;
025: import java.sql.SQLException;
026: import java.util.Calendar;
027:
028: import br.com.gfp.dao.AccountTypeDAO;
029: import br.com.gfp.dao.GFPController;
030: import br.com.gfp.dao.TransactionDAO;
031: import br.com.gfp.dao.TransactionTypeDAO;
032: import br.com.gfp.data.AccountType;
033: import br.com.gfp.data.ConfigPrevisao;
034: import br.com.gfp.data.ProjecaoFinanceira;
035: import br.com.gfp.data.Transaction;
036: import br.com.gfp.data.TransactionType;
037: import br.com.gfp.internationalization.TipoDeLancamentosMessages;
038: import br.com.gfp.util.SimpleLog;
039: import br.com.gfpshare.config.Propriedades;
040: import br.com.gfpshare.controllers.PropriedadesController;
041: import br.com.gfpshare.db.DAOEvent;
042: import br.com.gfpshare.db.DAOListener;
043: import br.com.gfpshare.db.SQLCondition;
044: import br.com.gfpshare.db.SQLCondition.Condicao;
045:
046: /**
047: * Esta classe é responsável por controlar a previsão de rendimentos para as aplicações
048: * financeiras. <br>
049: * Sempre que houver movimentação nas contas de aplicação financeira, esta classe será notificada
050: * e reagirá, fazendo os recalculos e lançamentos de acordo com o novo cenário
051: * <br>
052: * @author Igor Regis da Silva Simoes
053: */
054: public class PreverGastosEReceitas implements DAOListener<Transaction> {
055: private Integer aplicacao = Integer.valueOf("-1");
056:
057: private Integer resgate = Integer.valueOf("-1");
058:
059: private String tipoContaAplicacao = null;
060:
061: private PropriedadesController propriedadesController = new PropriedadesController();
062:
063: private TransactionDAO<Transaction> transactionDAO = new TransactionDAO<Transaction>();;
064:
065: /**
066: * Contador de Threads - indica quantas threads já fpra disparadas por esta classe
067: */
068: private int counter = 0;
069:
070: /**
071: * Constrrói uma instancia desta classe que irá monitorar as aplicações e resgates
072: */
073: public PreverGastosEReceitas() {
074: getAplicacao();
075: getResgate();
076: }
077:
078: /**
079: * Código do tipo de conta referente a aplicações financeiras
080: * @return Integer
081: */
082: private String getTipoContaAplicacao() {
083: if (tipoContaAplicacao == null)
084: try {
085: tipoContaAplicacao = new AccountTypeDAO().getBy(
086: new AccountType("aplicacao")).getId()
087: .toString();
088: } catch (Exception e) {
089: tipoContaAplicacao = "-1";
090: }
091: return tipoContaAplicacao;
092: }
093:
094: /**
095: * Código do tipo de lançamento que representa uma aplicação
096: * @return Integer
097: */
098: private Integer getAplicacao() {
099: if (aplicacao.intValue() == -1)
100: try {
101: aplicacao = new TransactionTypeDAO().getBy(
102: new TransactionType("Aplicação")).getId();
103: } catch (Exception e) {
104: aplicacao = Integer.valueOf("-1");
105: }
106: return aplicacao;
107: }
108:
109: /**
110: * Código do tipo de lançamento que representa um resgate
111: * @return Integer
112: */
113: private Integer getResgate() {
114: if (resgate.intValue() == -1)
115: try {
116: resgate = new TransactionTypeDAO().getBy(
117: new TransactionType("Resgate")).getId();
118: } catch (Exception e) {
119: resgate = Integer.valueOf("-1");
120: }
121: return resgate;
122: }
123:
124: /**
125: * Remove os lançamentos de presões para o mês atual.
126: * Ou seja, sempre que se entrar no dia 1 de um dado mes, todos os lançamentos que são
127: * previsão, neste mês serão deletados.
128: * @throws SQLException Erro durante a deleção das previsões
129: */
130: public void limparPrevisoesAteDiaAtual() throws SQLException {
131: Transaction filtro = new Transaction();
132: Calendar dia = Calendar.getInstance();
133: SQLCondition<Date> condicao = new SQLCondition<Date>("Dia",
134: new java.sql.Date(dia.getTime().getTime()),
135: Condicao.MENOR_IGUAL);
136: filtro.addCondicaoExtra(condicao);
137: filtro.setEhPrevisao(Boolean.valueOf(true));
138: filtro.setEhDeSistema(Boolean.valueOf(true));
139:
140: transactionDAO.setCheckIntegridade(false);
141: transactionDAO.deletar(filtro);
142: transactionDAO.setCheckIntegridade(true);
143: }
144:
145: /**
146: *
147: * @param lancamentoOriginal Lançamento que originou o evento
148: */
149: private void preverMovimento(final Transaction lancamentoOriginal) {
150: (new Thread("PrevisaoLancamentos"
151: + lancamentoOriginal.getTipo() + "-" + (counter++)) {
152: {
153: setPriority(Thread.MIN_PRIORITY);
154: }
155:
156: @Override
157: public void run() {
158: //Este algoritmo serve para caso já exista uma thread desta rodando nos a derrubamos para que possamos continuar.
159: Thread[] t = new Thread[Thread.activeCount()];
160: Thread.currentThread().getThreadGroup().enumerate(t);
161: for (int i = 0; i < t.length; i++) {
162: if (t[i] != null
163: && t[i].getName().indexOf(
164: "PrevisaoLancamentos"
165: + lancamentoOriginal
166: .getTipo() + "-") != -1
167: && !t[i].getName().equals(
168: Thread.currentThread().getName())
169: && t[i].getName().compareTo(
170: Thread.currentThread().getName()) <= -1) {
171: t[i].interrupt();
172: try {
173: Thread.sleep(100);
174: } catch (InterruptedException e1) {
175: // Não precisamos fazer nada
176: }
177: }
178: }
179:
180: try {
181: //Até quando vamos fazer previsões
182: Calendar dataMaxina = Calendar.getInstance();
183:
184: //Média de dos lançamentos nos ultimos 12 meses
185: ProjecaoFinanceira projecao = transactionDAO
186: .calculaMediana(lancamentoOriginal, 12);
187:
188: if (projecao.getValor() == 0)
189: return;
190:
191: //Descobrimos até quando vamos fazer previões
192: ConfigPrevisao configPrevisao = (ConfigPrevisao) Propriedades
193: .loadPropiedades(ConfigPrevisao.class);
194: try {
195: propriedadesController.getBy(configPrevisao
196: .getPersistentObject());
197: } catch (SQLException e2) {
198: //Não fazemos nada
199: }
200: dataMaxina.setTime(configPrevisao
201: .getPreverAteQueAno());
202:
203: lancamentoOriginal.setIncluirSubtipos(false);
204: //Calculamos o dia em que devemos fazer as previsões deste lancamento
205: int diaLancamento = transactionDAO
206: .getDiaComumParaLancamento(lancamentoOriginal);
207: //Montamos o tipo de lancamento que queremos alterar...
208: Calendar dia = Calendar.getInstance();
209: dia.set(Calendar.DATE, diaLancamento);
210:
211: deletarPrevisoes(lancamentoOriginal);
212:
213: Transaction transaction = new Transaction();
214: transaction.setConta(lancamentoOriginal.getConta());
215: transaction.setTipoConta(lancamentoOriginal
216: .getTipoConta());
217: transaction.setTipo(lancamentoOriginal.getTipo());
218: transaction.setEhDeSistema(Boolean.valueOf(true));
219: transaction.setEhPrevisao(Boolean.valueOf(true));
220:
221: Calendar hoje = Calendar.getInstance();
222:
223: // ...e realizamos as novas previsões mes a mês
224: transaction.setValor(Double.valueOf(""
225: + projecao.getValor()));
226: double ultimoEstimativaSemLancamentoNoMes = transaction
227: .getValor().doubleValue();
228: while (dia.get(Calendar.YEAR) != dataMaxina
229: .get(Calendar.YEAR)
230: || dia.get(Calendar.MONTH) != dataMaxina
231: .get(Calendar.MONTH)) {
232: if (Thread.interrupted())
233: return;
234: //Vamos realizar as previsões para qualquer dia a partir de hoje.
235: //Ou seja, se as previsões deste tipo de lancamento devem ser feitas em um dia 14 e estamos no dia 15 então começaremos
236: //a fazer as previões do mes seguinte, do contrário vamo fazer as previões já deste mes corrente.
237: if (hoje.get(Calendar.DAY_OF_MONTH) >= dia
238: .get(Calendar.DAY_OF_MONTH)
239: && dia.get(Calendar.MONTH) == hoje
240: .get(Calendar.MONTH)
241: && dia.get(Calendar.YEAR) == hoje
242: .get(Calendar.YEAR)) {
243: dia.add(Calendar.MONTH, 1);
244: continue;
245: }
246:
247: //Dia em que vamos registrar a previsão
248: transaction.setDia(dia.getTime());
249:
250: //Vamos ver se já existem lançamentos deste tipo no mes, caso positivo subtraimos da projeção
251: int meses = dia.get(Calendar.MONTH)
252: - hoje.get(Calendar.MONTH);
253: int anos = dia.get(Calendar.YEAR)
254: - hoje.get(Calendar.YEAR);
255: lancamentoOriginal.setIncluirSubtipos(false);
256: double soma = transactionDAO.getTotalNoPeriodo(
257: lancamentoOriginal, -1
258: * (anos * 12 + meses), -1
259: * (anos * 12 + meses));
260: if (soma != 0) {
261: ultimoEstimativaSemLancamentoNoMes = transaction
262: .getValor().doubleValue();
263: transaction
264: .setValor(Double
265: .valueOf(""
266: + (ultimoEstimativaSemLancamentoNoMes - soma)));
267: }
268:
269: //Fazemos a evolução do lançamento, conforme a projeção
270: if ((projecao.getTendencia() == ProjecaoFinanceira.ALTA && projecao
271: .getValor() > 0)
272: || (projecao.getTendencia() == ProjecaoFinanceira.QUEDA && projecao
273: .getValor() < 0)) {
274: if (dia.get(Calendar.MONTH) % 2 != 0) {
275: transaction
276: .setValor(Double
277: .valueOf(""
278: + (transaction
279: .getValor()
280: .doubleValue() + transaction
281: .getValor()
282: .doubleValue()
283: * projecao
284: .getVariacao())));
285: } else {
286: transaction
287: .setValor(Double
288: .valueOf(""
289: + ((transaction
290: .getValor()
291: .doubleValue() - (transaction
292: .getValor()
293: .doubleValue()
294: * projecao
295: .getVariacao() * 0.75)))));
296: }
297: } else if ((projecao.getTendencia() == ProjecaoFinanceira.QUEDA && projecao
298: .getValor() > 0)
299: || (projecao.getTendencia() == ProjecaoFinanceira.ALTA && projecao
300: .getValor() < 0)) {
301: if (dia.get(Calendar.MONTH) % 2 != 0) {
302: transaction
303: .setValor(transaction
304: .getValor()
305: - (transaction
306: .getValor() * projecao
307: .getVariacao()));
308: } else {
309: transaction.setValor(transaction
310: .getValor()
311: + transaction.getValor()
312: * projecao.getVariacao() * 0.7);
313: }
314: }
315:
316: if (Thread.interrupted())
317: return;
318: //Se a projeção for negativa e o lancamento também ou a projeção for positiva e o lançamento também
319: //então realizamos a adição do novo registro
320: if ((projecao.getValor() < 0 && transaction
321: .getValor() < -1)
322: || projecao.getValor() > 0
323: && transaction.getValor() > 1d) {
324: transactionDAO.adicionarNovo(transaction);
325: }
326:
327: //TODO Remover este LOG
328: // ((SimpleLog)GFPController.getGFPController().getContexto().get(GFPController.LOG)).log("Lancamento: " + lancamento.getValor());
329:
330: //Voltamos a usar o valor "projetado" atualizado pela sua evolução sem descontar os lancamentos existentes
331: if (soma != 0) {
332: transaction
333: .setValor(ultimoEstimativaSemLancamentoNoMes);
334: }
335:
336: //Vamos para o proximo mes
337: dia.add(Calendar.MONTH, 1);
338: }
339: } catch (SQLException sqle) {
340: sqle.printStackTrace();
341: } catch (InterruptedException e) {
342: return;
343: }
344: }
345: }).start();
346: }
347:
348: /**
349: * Deleta todas as previsões para dado tipo de lançamento
350: * @param transaction
351: * @throws SQLException
352: */
353: private void deletarPrevisoes(Transaction transaction)
354: throws SQLException {
355: Transaction lancamentoNovo = new Transaction();
356: lancamentoNovo.setConta(transaction.getConta());
357: lancamentoNovo.setTipoConta(transaction.getTipoConta());
358: lancamentoNovo.setTipo(transaction.getTipo());
359: lancamentoNovo.setEhDeSistema(Boolean.valueOf(true));
360: lancamentoNovo.setEhPrevisao(Boolean.valueOf(true));
361:
362: TransactionDAO<Transaction> lancamentoController = new TransactionDAO<Transaction>();
363: lancamentoController.setCheckIntegridade(false);
364: lancamentoController.deletar(lancamentoNovo);
365: lancamentoController.setCheckIntegridade(true);
366: }
367:
368: /**
369: * @see br.com.gfpshare.db.DAOListener#adicionadoNovo(br.com.gfpshare.db.DAOEvent)
370: */
371: public void adicionadoNovo(DAOEvent<Transaction> e) {
372: verSeVaiPreverMovimento(e);
373: }
374:
375: /**
376: * @see br.com.gfpshare.db.DAOListener#atualizado(br.com.gfpshare.db.DAOEvent)
377: */
378: public void atualizado(DAOEvent<Transaction> e) {
379: verSeVaiPreverMovimento(e);
380: }
381:
382: /**
383: * @see br.com.gfpshare.db.DAOListener#deletado(br.com.gfpshare.db.DAOEvent)
384: */
385: public void deletado(DAOEvent<Transaction> e) {
386: verSeVaiPreverMovimento(e);
387: }
388:
389: /**
390: * @see br.com.gfpshare.db.DAOListener#selecionado(br.com.gfpshare.db.DAOEvent)
391: */
392: public void selecionado(DAOEvent<Transaction> e) {
393: // Este evento não nos interessa
394: }
395:
396: /**
397: * @see br.com.gfpshare.db.DAOListener#filtrado(br.com.gfpshare.db.DAOEvent)
398: */
399: public void filtrado(DAOEvent<Transaction> e) {
400: // Este evento não nos interessa
401: }
402:
403: /**
404: * Verificamos se o evento gerado realmente dispara uma previsão de movimento futuro de contas de aplicação
405: * @param e
406: */
407: private void verSeVaiPreverMovimento(DAOEvent<Transaction> e) {
408: if ((e.getPersistentObject().getEhPrevisao() == null || !e
409: .getPersistentObject().getEhPrevisao())
410: && (e.getPersistentObject().getEhDeSistema() == null || !e
411: .getPersistentObject().getEhDeSistema())) {
412: Integer tipo = e.getPersistentObject().getTipo();
413: String tipoConta = e.getPersistentObject().getTipoConta();
414: if (tipo != null
415: && !tipoConta.equals(getTipoContaAplicacao())) {
416: //Aqui veremos se o GFP esta configurado para realizar previsões
417: ConfigPrevisao configPrevisao = (ConfigPrevisao) Propriedades
418: .loadPropiedades(ConfigPrevisao.class);
419: try {
420: propriedadesController.getBy(configPrevisao
421: .getPersistentObject());
422: } catch (SQLException e2) {
423: //Não fazemos nada
424: }
425: if (!configPrevisao.getRealizarPrevisoes()
426: || !configPrevisao.getPreverGastosEReceitas()) {
427: try {
428: deletarPrevisoes(e.getPersistentObject());
429: } catch (SQLException e1) {
430: ((SimpleLog) GFPController.getGFPController()
431: .getContexto().get(GFPController.LOG))
432: .log("Erro ao remover previões de lançamentos");
433: ((SimpleLog) GFPController.getGFPController()
434: .getContexto().get(GFPController.LOG))
435: .log("Lançamento: "
436: + e.getPersistentObject());
437: ((SimpleLog) GFPController.getGFPController()
438: .getContexto().get(GFPController.LOG))
439: .log(e1.getLocalizedMessage());
440: ((SimpleLog) GFPController.getGFPController()
441: .getContexto().get(GFPController.LOG))
442: .log(e1);
443: }
444: return;
445: }
446:
447: try {
448:
449: try {
450: TransactionType tipoLancamentoCrediPagCartao = new TransactionTypeDAO()
451: .getBy(new TransactionType(
452: TipoDeLancamentosMessages
453: .getMessages()
454: .getString(
455: "CreditoPagamentoCartao")));
456: if (tipoLancamentoCrediPagCartao.getId()
457: .equals(tipo))
458: return;
459: } catch (NullPointerException npe) {
460: //Não fazemos nada
461: }
462:
463: //Se o tipo de lançamento estiver configurado para não realizar previsão então agente ignora
464: //getRealizarPrevisao() == false ou == null (caindo no NullPointerException)
465: try {
466: if (!(new TransactionTypeDAO()
467: .getBy(new TransactionType(tipo)))
468: .getRealizarPrevisao().booleanValue()) {
469: try {
470: deletarPrevisoes(e
471: .getPersistentObject());
472: } catch (SQLException e1) {
473: ((SimpleLog) GFPController
474: .getGFPController()
475: .getContexto().get(
476: GFPController.LOG))
477: .log("Erro ao remover previões de lançamentos");
478: ((SimpleLog) GFPController
479: .getGFPController()
480: .getContexto().get(
481: GFPController.LOG))
482: .log("Lançamento: "
483: + e
484: .getPersistentObject());
485: ((SimpleLog) GFPController
486: .getGFPController()
487: .getContexto().get(
488: GFPController.LOG))
489: .log(e1.getLocalizedMessage());
490: ((SimpleLog) GFPController
491: .getGFPController()
492: .getContexto().get(
493: GFPController.LOG))
494: .log(e1);
495: }
496: return;
497: }
498: } catch (NullPointerException npe) {
499: return;
500: }
501: } catch (SQLException e1) {
502: ((SimpleLog) GFPController.getGFPController()
503: .getContexto().get(GFPController.LOG))
504: .log("Erro já previsto possivelmente sem IMPACTO");
505: ((SimpleLog) GFPController.getGFPController()
506: .getContexto().get(GFPController.LOG))
507: .log(e1.getLocalizedMessage());
508: ((SimpleLog) GFPController.getGFPController()
509: .getContexto().get(GFPController.LOG))
510: .log(e1);
511: }
512: preverMovimento(e.getPersistentObject());
513: }
514: }
515: }
516: }
|