From 27214adfee2d8499577e4f22bfeaa0f5485a8400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ses=C3=B3stris=20Vieira?= Date: Wed, 5 Dec 2012 15:31:18 -0200 Subject: [PATCH] Controle de metas do BID - incluindo registro de desembolsos financeiros --- sigi/apps/financeiro/__init__.py | 0 sigi/apps/financeiro/admin.py | 9 +++ sigi/apps/financeiro/forms.py | 3 + sigi/apps/financeiro/models.py | 18 +++++ sigi/apps/financeiro/urls.py | 3 + sigi/apps/financeiro/views.py | 1 + sigi/apps/metas/admin.py | 22 ++++++ sigi/apps/metas/models.py | 119 ++++++++++++++++++++++++++++++- sigi/settings.py | 2 + sigi/sites.py | 9 +++ 10 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 sigi/apps/financeiro/__init__.py create mode 100644 sigi/apps/financeiro/admin.py create mode 100644 sigi/apps/financeiro/forms.py create mode 100644 sigi/apps/financeiro/models.py create mode 100644 sigi/apps/financeiro/urls.py create mode 100644 sigi/apps/financeiro/views.py create mode 100644 sigi/apps/metas/admin.py diff --git a/sigi/apps/financeiro/__init__.py b/sigi/apps/financeiro/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sigi/apps/financeiro/admin.py b/sigi/apps/financeiro/admin.py new file mode 100644 index 0000000..05ae4b7 --- /dev/null +++ b/sigi/apps/financeiro/admin.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +from django.contrib import admin +from sigi.apps.financeiro.models import Desembolso + +class DesembolsoAdmin(admin.ModelAdmin): + list_display = ('projeto', 'descricao', 'data', 'valor_reais', 'valor_dolar',) + fields = ('projeto', 'descricao', 'data', 'valor_reais', 'valor_dolar', ) + list_filter = ('projeto',) + date_hierarchy = 'data' \ No newline at end of file diff --git a/sigi/apps/financeiro/forms.py b/sigi/apps/financeiro/forms.py new file mode 100644 index 0000000..6b477f9 --- /dev/null +++ b/sigi/apps/financeiro/forms.py @@ -0,0 +1,3 @@ +from django import forms + +# place form definition here \ No newline at end of file diff --git a/sigi/apps/financeiro/models.py b/sigi/apps/financeiro/models.py new file mode 100644 index 0000000..6089a60 --- /dev/null +++ b/sigi/apps/financeiro/models.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from django.db import models +from sigi.apps.convenios.models import Projeto + +class Desembolso(models.Model): + projeto = models.ForeignKey(Projeto, verbose_name=u'Projeto') + descricao = models.CharField(u'Descrição da despesa', max_length=60) + data = models.DateField(u'Data do desembolso') + valor_reais = models.DecimalField(u'Valor em R$', max_digits=18, decimal_places=2) + valor_dolar = models.DecimalField(u'Valor em US$', max_digits=18, decimal_places=2) + + class Meta: + verbose_name = 'Desembolso' + verbose_name_plural = 'Desembolsos' + + def __unicode__(self): + return u"%s (US$ %s)" % (self.descricao, self.valor_dolar) + \ No newline at end of file diff --git a/sigi/apps/financeiro/urls.py b/sigi/apps/financeiro/urls.py new file mode 100644 index 0000000..5d79a9f --- /dev/null +++ b/sigi/apps/financeiro/urls.py @@ -0,0 +1,3 @@ +from django.conf.urls.defaults import * + +# place app url patterns here \ No newline at end of file diff --git a/sigi/apps/financeiro/views.py b/sigi/apps/financeiro/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/sigi/apps/financeiro/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/sigi/apps/metas/admin.py b/sigi/apps/metas/admin.py new file mode 100644 index 0000000..1bd4f02 --- /dev/null +++ b/sigi/apps/metas/admin.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +from django.contrib import admin +from sigi.apps.metas.models import Meta, PlanoDiretor + +class MetaAdmin(admin.ModelAdmin): + list_display = ('projeto', 'titulo', 'data_inicio', 'data_fim', 'valor_meta', 'hora_ultimo_calculo', 'valor_ultimo_calculo', + 'percentual_concluido', 'saude',) + fields = ('projeto', 'titulo', 'descricao', 'data_inicio', 'data_fim', 'algoritmo', 'valor_meta',) + list_filter = ('projeto',) + actions = ['recalcular',] + + def recalcular(self, request, queryset): + for meta in queryset: + meta.calcular() + return None + recalcular.short_description = u"Recalcular Metas BID selecionados" + +class PlanoDiretorAdmin(admin.ModelAdmin): + list_display = ('projeto', 'casa_legislativa', 'status', 'data_elaboracao', 'data_assinatura', 'data_rejeicao',) + fields = ('projeto', 'casa_legislativa', 'status', 'data_elaboracao', 'data_assinatura', 'data_rejeicao',) + raw_id_fields = ('casa_legislativa',) + list_filter = ('projeto', 'status',) \ No newline at end of file diff --git a/sigi/apps/metas/models.py b/sigi/apps/metas/models.py index 71a8362..9033031 100644 --- a/sigi/apps/metas/models.py +++ b/sigi/apps/metas/models.py @@ -1,3 +1,120 @@ +# -*- coding: utf-8 -*- +from datetime import date, datetime from django.db import models +from sigi.apps.convenios.models import Projeto, Convenio +from sigi.apps.diagnosticos.models import Diagnostico +from sigi.apps.casas.models import CasaLegislativa +from sigi.apps.financeiro.models import Desembolso -# Create your models here. +class Meta(models.Model): + ALGORITMO_CHOICES = ( + ('SUM_GASTOS', u'Soma dos desembolsos'), + ('COUNT_EQUI', u'Quantidade de casas equipadas'), + ('COUNT_ADER', u'Quantidade de casas aderidas'), + ('COUNT_DIAG', u'Quantidade de casas diagnosticadas'), + ('COUNT_PDIR', u'Quantidade de planos diretores'), + ('COUNT_CONV', u'Quantidade de casas conveniadas'), + ) + projeto = models.ForeignKey(Projeto, verbose_name=u'Projeto', help_text=u'Projeto ao qual a meta se refere') + titulo = models.CharField(u'Título', max_length=40, help_text=u'Título da meta que aparecerá no dashboard') + descricao = models.TextField(u'Descrição') + data_inicio = models.DateField(u'Data inicial', help_text=u'Início do período de cômputo da meta') + data_fim = models.DateField(u'Data final', help_text=u'Prazo final para cumprimento da meta') + algoritmo = models.CharField(u'Algoritmo de cálculo', max_length=10, choices=ALGORITMO_CHOICES) + valor_meta = models.FloatField(u'Valor da meta', help_text=u'Valor que deve ser atingido até o prazo final da meta') + hora_ultimo_calculo = models.DateTimeField(u'Data último calculo', blank=True, null=True, editable=False, + help_text=u'Momento em que a meta foi calculada pela última vez') + valor_ultimo_calculo = models.FloatField(u'Valor último cálculo', blank=True, null=True, editable=False, + help_text=u'Valor do último calculo da meta') + + class Meta: + verbose_name = 'Meta BID' + verbose_name_plural = 'Metas BID' + + def __unicode__(self): + return self.titulo; + + @property + def percentual_concluido(self): + return round(self.valor_ultimo_calculo / self.valor_meta * 100.0, 2) + + @property + def saude(self): + if self.valor_ultimo_calculo >= self.valor_meta: + return 'green' + + total_meses = (self.data_fim - self.data_inicio).days / 30 + meses_gastos = (date.today() - self.data_inicio).days / 30 + meta_mensal = self.valor_meta / total_meses + valor_desejado = meta_mensal * meses_gastos + + if self.valor_ultimo_calculo > (valor_desejado * 1.1): + return 'green' + + if self.valor_ultimo_calculo > (valor_desejado * 0.9): + return 'orange' + + return 'red' + + + def calcular(self): + """ + Calcula o valor da meta e salva o resultado para uso futuro + """ + algoritmo = self.algoritmo.lower() + valor = getattr(self, algoritmo)() + self.hora_ultimo_calculo = datetime.now() + self.valor_ultimo_calculo = valor + self.save() + + def sum_gastos(self): + #TODO: Para fazer este algoritmo, precisamos criar registro de desembolsos no sistema financeiro + valor = Desembolso.objects.filter(projeto=self.projeto, data__gte=self.data_inicio, data__lte=self.data_fim) \ + .aggregate(total_dolar=models.Sum('valor_dolar')) + return valor['total_dolar'] + + def count_equi(self): + valor = Convenio.objects.filter(casa_legislativa__tipo__sigla='CM', equipada=True, projeto__pk=3, data_termo_aceite__gte= + self.data_inicio, data_termo_aceite__lte=self.data_fim).exclude(data_termo_aceite=None).count() + return valor + + def count_ader(self): + valor = Convenio.objects.filter(casa_legislativa__tipo__sigla='CM', projeto=self.projeto, data_adesao__gte=self.data_inicio, + data_adesao__lte=self.data_fim).exclude(data_adesao=None).count() + return valor + + def count_diag(self): + valor = Diagnostico.objects.filter(data_visita_inicio__gte=self.data_inicio, data_visita_inicio__lte=self.data_fim, + publicado=True).count() + return valor + + def count_pdir(self): + valor = PlanoDiretor.objects.filter(projeto=self.projeto, status='A', data_assinatura__gte=self.data_inicio, + data_assinatura__lte=self.data_fim).exclude(data_assinatura=None).count() + return valor + + def count_conv(self): + valor = Convenio.objects.filter(casa_legislativa__tipo__sigla='CM', projeto=self.projeto, data_retorno_assinatura__gte= + self.data_inicio, data_retorno_assinatura__lte=self.data_fim).exclude(data_retorno_assinatura=None).count() + return valor + +class PlanoDiretor(models.Model): + STATUS_CHOICE = ( + ('E', u'Em elaboração'), + ('A', u'Aceito'), + ('R', u'Rejeitado'), + ) + projeto = models.ForeignKey(Projeto, verbose_name=u'Projeto') + casa_legislativa = models.ForeignKey(CasaLegislativa, verbose_name=u'Casa legislativa') + status = models.CharField(u'Status', max_length=1, choices=STATUS_CHOICE, default='E') + data_elaboracao = models.DateField(u'Data de elaboração') + data_assinatura = models.DateField(u'Data de assinatura', blank=True, null=True) + data_rejeicao = models.DateField(u'Data da rejeição', blank=True, null=True) + + class Meta: + verbose_name = 'Plano Diretor' + verbose_name_plural = 'Planos Diretores' + + def __unicode__(self): + return self.casa_legislativa.nome ; + \ No newline at end of file diff --git a/sigi/settings.py b/sigi/settings.py index 7ba55b3..e7d43f0 100644 --- a/sigi/settings.py +++ b/sigi/settings.py @@ -172,6 +172,8 @@ INSTALLED_APPS = ( 'sigi.apps.ocorrencias', 'sigi.apps.servicos', 'sigi.apps.relatorios', + 'sigi.apps.metas', + 'sigi.apps.financeiro', ) try: diff --git a/sigi/sites.py b/sigi/sites.py index 7737089..4312763 100644 --- a/sigi/sites.py +++ b/sigi/sites.py @@ -33,6 +33,8 @@ from sigi.apps.servidores.admin import (Servidor, ServidorAdmin, Funcao, FuncaoA from sigi.apps.ocorrencias.admin import (Ocorrencia, OcorrenciaAdmin, Anexo as AnexoOcorrencia, AnexoAdmin as AnexoOcorrenciaAdmin, Categoria) from sigi.apps.eventos.admin import (Recurso, RecursoAdmin) +from sigi.apps.metas.admin import (Meta, MetaAdmin, PlanoDiretor, PlanoDiretorAdmin) +from sigi.apps.financeiro.admin import (Desembolso, DesembolsoAdmin) class DefaultSite(AdminSite): index_template = 'index.html' @@ -115,3 +117,10 @@ default.register(Categoria) # sigi.apps.eventos default.register(Recurso, RecursoAdmin) + +# sigi.apps.metas +default.register(Meta, MetaAdmin) +default.register(PlanoDiretor, PlanoDiretorAdmin) + +# sigi.apps.financeiro +default.register(Desembolso, DesembolsoAdmin) \ No newline at end of file