diff --git a/requirements/requirements.txt b/requirements/requirements.txt index abfd1d4..ab6351f 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -21,3 +21,4 @@ requests==2.8.1 six==1.10.0 djangorestframework==2.4.8 django-ipware==1.1.6 +django-tinymce==2.6.0 \ No newline at end of file diff --git a/sigi/apps/casas/admin.py b/sigi/apps/casas/admin.py index c09d079..3685f93 100644 --- a/sigi/apps/casas/admin.py +++ b/sigi/apps/casas/admin.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- +from unicodedata import name from django.contrib import admin from django.contrib.contenttypes import generic from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect from django.shortcuts import render from django.utils.translation import ugettext as _ +#from geraldo.site.newsite.django_1_0.django.forms import extras from image_cropping import ImageCroppingMixin from sigi.apps.casas.forms import OrgaoForm @@ -50,7 +52,31 @@ class PresidenteInline(admin.StackedInline): extra = 1 max_num = 1 verbose_name_plural = _(u'Presidente') + def get_queryset(self, request): + return (self.model.objects.exclude(desativado=True) + .extra(select={'ult_null': 'ult_alteracao is null'}) + .order_by('ult_null', '-ult_alteracao') + # A função extra foi usada para quando existir um registro com o campo igual a null não aparecer na frente dos mais novos + ) +class ContatoInterlegisInline(admin.StackedInline): + model = Funcionario + fields = ('nome', 'sexo', 'data_nascimento', 'nota', 'email', 'cargo', + 'funcao', 'setor', 'tempo_de_servico', 'ult_alteracao', + 'endereco', 'municipio', 'bairro', 'cep', 'redes_sociais', + 'desativado', 'observacoes') + raw_id_fields = ('municipio',) + readonly_fields = ('ult_alteracao',) + extra = 1 + inlines = (TelefonesInline,) + verbose_name_plural = _(u'Contato(s) Interlegis Vigente(s)') + def get_queryset(self, request): + return (self.model.objects.filter(setor='contato_interlegis') + .extra(select={'ult_null': 'ult_alteracao is null'}).order_by('-ult_alteracao') + ) + def get_extra(self, request, obj=None , **kwargs): + extra = 0 + return extra class FuncionariosInline(admin.StackedInline): model = Funcionario @@ -59,6 +85,7 @@ class FuncionariosInline(admin.StackedInline): 'endereco', 'municipio', 'bairro', 'cep', 'redes_sociais', 'desativado', 'observacoes') raw_id_fields = ('municipio',) + # fieldsets = ((None, { # 'fields': ( # ('nome', 'sexo', 'data_nascimento'), @@ -74,22 +101,22 @@ class FuncionariosInline(admin.StackedInline): readonly_fields = ('ult_alteracao',) extra = 1 inlines = (TelefonesInline,) + verbose_name_plural = _(u'Outros Contatos da Casa') def get_queryset(self, request): - return (self.model.objects.exclude( - cargo='Presidente').exclude(desativado=True) + return (self.model.objects.exclude(cargo='Presidente',) + .exclude(desativado=True).extra(select={'ult_null': 'ult_alteracao is null'}) + .order_by('ult_null', '-ult_alteracao') + # A função extra foi usada para quando existir um registro com o campo igual a null não aparecer na frente dos mais novos ) - class ConveniosInline(admin.TabularInline): model = Convenio fieldsets = ( (None, {'fields': ( ('link_sigad', 'status_convenio', 'num_convenio', 'projeto', 'observacao'), - ('data_adesao', 'data_retorno_assinatura', 'data_termo_aceite', - 'data_pub_diario', 'data_devolucao_via', 'data_postagem_correio'), - ('data_devolucao_sem_assinatura', 'data_retorno_sem_assinatura',), + ('data_retorno_assinatura', 'data_pub_diario',), ('get_anexos',), ('link_convenio',), )}), @@ -103,6 +130,7 @@ class ConveniosInline(admin.TabularInline): extra = 0 can_delete = False template = 'admin/casas/convenios_inline.html' + ordering = ('-data_retorno_assinatura',) def has_add_permission(self, request): return False @@ -210,10 +238,10 @@ class ServicoInline(admin.TabularInline): model = Servico fields = ('link_url', 'contato_tecnico', 'contato_administrativo', 'hospedagem_interlegis', 'data_ativacao', 'data_alteracao', - 'data_desativacao') + 'data_desativacao', 'link_servico') readonly_fields = ['link_url', 'contato_tecnico', 'contato_administrativo', 'hospedagem_interlegis', 'data_ativacao', - 'data_alteracao', 'data_desativacao'] + 'data_alteracao', 'data_desativacao', 'link_servico'] extra = 0 max_num = 0 can_delete = False @@ -225,6 +253,24 @@ class ServicoInline(admin.TabularInline): link_url.short_description = _(u'URL do serviço') link_url.allow_tags = True + ordering = ('-data_alteracao',) + + def link_servico(self, obj): + if obj.pk is None: + return "" + url = reverse('admin:%s_%s_change' % (obj._meta.app_label, obj._meta.module_name), args=[obj.pk]) + url = url + '?_popup=1' + return """ + + Editar + """ % (obj.pk, obj.pk, url) + + link_servico.short_description = _(u'Editar Serviço') + link_servico.allow_tags = True + + def has_add_permission(self, request): + return False + # class PlanoDiretorInline(admin.TabularInline): # model = PlanoDiretor @@ -237,6 +283,8 @@ class OcorrenciaInline(admin.TabularInline): can_delete = False template = 'admin/casas/ocorrencia_inline.html' + ordering = ('-data_modificacao',) + def link_editar(self, obj): if obj.pk is None: return "" @@ -264,7 +312,7 @@ class ConvenioFilter(admin.SimpleListFilter): def lookups(self, request, model_admin): return ( ('SC', _(u"Sem nenhum convênio")), - ('CC', _(u"Com algum convênio")) + ('CC', _(u"Com algum convênio")), ) + tuple([(p.pk, p.sigla) for p in Projeto.objects.all()]) def queryset(self, request, queryset): @@ -278,6 +326,19 @@ class ConvenioFilter(admin.SimpleListFilter): return queryset.distinct('municipio__uf__nome', 'nome') +class ExcluirConvenioFilter(admin.SimpleListFilter): + title=_(u"Excluir convênio da pesquisa") + parameter_name = 'excluir_convenio' + + def lookups(self, request, model_admin): + return tuple([(p.pk, p.sigla) for p in Projeto.objects.all()]) + + def queryset(self, request, queryset): + if (self.value() is None): + return queryset + else: + queryset = queryset.exclude(convenio__projeto_id=self.value()).distinct('municipio__uf__nome', 'nome') + return queryset class ServicoFilter(admin.SimpleListFilter): title = _(u"Serviço") @@ -313,17 +374,34 @@ class ServicoFilter(admin.SimpleListFilter): return queryset.distinct('municipio__uf__nome', 'nome') +class ServicoAtivoFilter(admin.SimpleListFilter): + title = _(u"Serviço ativo") + parameter_name = 'ativo' + + def lookups(self, request, model_admin): + return ( + ('ativo', _(u"Ativo")), + ('desativado', _(u"Desativado")), + ) + + def queryset(self, request, queryset): + if self.value() is not None: + if self.value() == 'ativo': + queryset = queryset.filter(servico__data_desativacao__isnull=True) + else: + queryset = queryset.filter(servico__data_desativacao__isnull=False) + return queryset @admin.register(Orgao) class OrgaoAdmin(ImageCroppingMixin, BaseModelAdmin): form = OrgaoForm actions = ['adicionar_casas', ] - inlines = (TelefonesInline, PresidenteInline, FuncionariosInline, + inlines = (TelefonesInline, PresidenteInline, ContatoInterlegisInline, FuncionariosInline, ConveniosInline, ServicoInline, OcorrenciaInline,) list_display = ('id', 'sigla', 'nome', 'get_uf', 'get_gerentes', 'get_convenios', 'get_servicos') list_display_links = ('sigla', 'nome',) list_filter = ('tipo', ('gerentes_interlegis', GerentesInterlegisFilter), - 'municipio__uf__nome', ConvenioFilter, ServicoFilter, + 'municipio__uf__nome', ConvenioFilter, ServicoAtivoFilter, ExcluirConvenioFilter, ServicoFilter, 'inclusao_digital',) ordering = ('municipio__uf__nome', 'nome') queryset = queryset_ascii diff --git a/sigi/apps/casas/models.py b/sigi/apps/casas/models.py index 29c93e8..1ca27b7 100644 --- a/sigi/apps/casas/models.py +++ b/sigi/apps/casas/models.py @@ -194,6 +194,19 @@ class Orgao(models.Model): return self.funcionario_set.get(setor='presidente') except Funcionario.DoesNotExist: return None + + @property + def contato_interlegis(self): + """ Link para acessar diretamente o contato do presidente da casa + Util para relatorios antigos + """ + try: + if self.funcionario_set.filter(setor='contato_interlegis').count() > 1: + return self.funcionario_set.filter(setor='contato_interlegis')[0] + else: + return self.funcionario_set.get(setor='contato_interlegis') + except Funcionario.DoesNotExist: + return None @property def total_parlamentares(self): diff --git a/sigi/apps/casas/views.py b/sigi/apps/casas/views.py index 694e84b..741786d 100644 --- a/sigi/apps/casas/views.py +++ b/sigi/apps/casas/views.py @@ -2,6 +2,9 @@ import csv from datetime import datetime from functools import reduce + +from django.contrib import messages +from sigi.apps.utils import to_ascii from geraldo.generators import PDFGenerator from django.conf import settings @@ -257,9 +260,10 @@ class importa_casas(View): for reg in reader: self.total_registros += 1 reg[self.ERROS] = [] + nome_orgao = to_ascii(reg[self.MUNICIPIO]) orgao = Orgao.objects.filter( tipo__sigla=reg[self.TIPO], - municipio__nome=reg[self.MUNICIPIO], + municipio__search_text__icontains=nome_orgao, municipio__uf__sigla=reg[self.UF] ) if orgao.count() == 0: @@ -444,7 +448,8 @@ def visualizar_carrinho(request): def excluir_carrinho(request): if 'carrinho_casas' in request.session: del request.session['carrinho_casas'] - return HttpResponseRedirect('.') + messages.info(request, u'O carrinho foi esvaziado') + return HttpResponseRedirect('../../') @login_required diff --git a/sigi/apps/convenios/admin.py b/sigi/apps/convenios/admin.py index ce5a6fe..971dd1e 100644 --- a/sigi/apps/convenios/admin.py +++ b/sigi/apps/convenios/admin.py @@ -6,7 +6,8 @@ from geraldo.generators import PDFGenerator from sigi.apps.convenios.models import (Projeto, StatusConvenio, TipoSolicitacao, Convenio, - EquipamentoPrevisto, Anexo, Tramitacao) + EquipamentoPrevisto, Anexo, Tramitacao, + Gescon) from sigi.apps.convenios.reports import ConvenioReport from sigi.apps.convenios.views import adicionar_convenios_carrinho from sigi.apps.utils import queryset_ascii @@ -51,35 +52,37 @@ class ConvenioAdmin(BaseModelAdmin): 'projeto', 'data_sigi',)} ), (_(u"Acompanhamento no gabinete"), - {'fields': ('data_solicitacao', 'data_sigad', 'tipo_solicitacao', - 'status', 'acompanha', 'observacao',)} + {'fields': ('data_solicitacao', 'data_sigad', 'observacao',)} ), (_(u"Gestão do convênio"), {'fields': ('servico_gestao', 'servidor_gestao',)} ), (_(u'Datas'), - {'fields': ('data_retorno_assinatura', 'duracao', - 'data_pub_diario',)} + {'fields': ('data_retorno_assinatura', 'data_termino_vigencia', + 'data_pub_diario',)} ), + (_(u'Gescon'), + {'fields': ('atualizacao_gescon', 'observacao_gescon', 'link_gescon')} + ), ) - readonly_fields = ('data_sigi',) + readonly_fields = ('data_sigi', 'atualizacao_gescon', 'observacao_gescon', 'link_gescon') actions = ['adicionar_convenios'] inlines = (AnexosInline,) - list_display = ('num_convenio', 'casa_legislativa', 'get_uf', + list_display = ('num_convenio', 'projeto','casa_legislativa', 'get_uf', 'status_convenio', 'link_sigad', 'data_retorno_assinatura', - 'duracao', 'projeto', 'status', 'acompanha',) + 'data_termino_vigencia',) list_display_links = ('num_convenio', 'casa_legislativa',) - list_filter = ('status', ('acompanha', AcompanhaFilter), - ('casa_legislativa__gerentes_interlegis', + list_filter = (('casa_legislativa__gerentes_interlegis', GerentesInterlegisFilter), 'projeto', 'casa_legislativa__tipo', 'conveniada','equipada', 'casa_legislativa__municipio__uf',) #date_hierarchy = 'data_adesao' - ordering = ('casa_legislativa__tipo__sigla', 'casa_legislativa__municipio__uf', 'casa_legislativa') + ordering = ('casa_legislativa', '-data_retorno_assinatura') raw_id_fields = ('casa_legislativa',) get_queryset = queryset_ascii - search_fields = ('id', 'search_text', # 'casa_legislativa__nome', - 'num_processo_sf', 'num_convenio') + search_fields = ('id', 'casa_legislativa__search_text', + 'casa_legislativa__sigla', 'num_processo_sf', + 'num_convenio') def get_uf(self, obj): return obj.casa_legislativa.municipio.uf.sigla @@ -112,6 +115,16 @@ class ConvenioAdmin(BaseModelAdmin): link_sigad.short_description = _("Processo no Senado") link_sigad.allow_tags = True + def link_gescon(self, obj): + if not obj.id_contrato_gescon: + return u"" + return ( + u"https://adm.senado.gov.br/" + u"gestao-contratos/api/{id}").format(id=obj.id_contrato_gescon) + link_gescon.short_description = _("Download MINUTA ASSINADA do Gescon") + link_gescon.allow_tags = True + def changelist_view(self, request, extra_context=None): from sigi.apps.convenios.views import normaliza_data request.GET._mutable = True @@ -172,8 +185,13 @@ class EquipamentoPrevistoAdmin(BaseModelAdmin): search_fields = ('convenio__id', 'equipamento__fabricante__nome', 'equipamento__modelo__modelo', 'equipamento__modelo__tipo__tipo') +@admin.register(Gescon) +class GesconAdmin(admin.ModelAdmin): + list_display = ('url_gescon', 'email', 'ultima_importacao') + readonly_fields = ('ultima_importacao',) + admin.site.register(Projeto) admin.site.register(StatusConvenio) admin.site.register(TipoSolicitacao) admin.site.register(Convenio, ConvenioAdmin) -admin.site.register(EquipamentoPrevisto, EquipamentoPrevistoAdmin) +admin.site.register(EquipamentoPrevisto, EquipamentoPrevistoAdmin) \ No newline at end of file diff --git a/sigi/apps/convenios/management/commands/duracao_act.py b/sigi/apps/convenios/management/commands/duracao_act.py deleted file mode 100644 index 2144582..0000000 --- a/sigi/apps/convenios/management/commands/duracao_act.py +++ /dev/null @@ -1,43 +0,0 @@ -# -*- coding: utf-8 -*- -# -# sigi.apps.casas.management.commands.importa_gerentes -# -# Copyright (c) 2015 by Interlegis -# -# GNU General Public License (GPL) -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -# 02110-1301, USA. -# - -from django.core.management.base import BaseCommand, CommandError -from sigi.apps.convenios.models import Projeto, Convenio - -class Command(BaseCommand): - help = u"""Define a duração de todos os ACT para 60 meses. - * A sigla do Projeto precisa ser ACT; - * O campo duracao precisa estar em branco. - """ - - def handle(self, *args, **options): - self.stdout.write(u"Atualizando ACTs... ") - act = Projeto.objects.get(sigla='ACT') - for conv in Convenio.objects.filter(projeto=act, duracao=None): - conv.duracao = 60 - conv.save() - self.stdout.write(u"\tACT {sigad} da Casa {casa} atualizado".format( - sigad=conv.num_processo_sf, casa=conv.casa_legislativa.nome - )) - self.stdout.write(u"Pronto!") diff --git a/sigi/apps/convenios/migrations/0010_auto_20210819_0833.py b/sigi/apps/convenios/migrations/0010_auto_20210819_0833.py new file mode 100644 index 0000000..e1827de --- /dev/null +++ b/sigi/apps/convenios/migrations/0010_auto_20210819_0833.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('convenios', '0009_auto_20210611_0946'), + ] + + operations = [ + migrations.AlterField( + model_name='convenio', + name='data_retorno_assinatura', + field=models.DateField(help_text='Conv\xeanio firmado.', null=True, verbose_name='data in\xedcio vig\xeancia', blank=True), + preserve_default=True, + ), + ] diff --git a/sigi/apps/convenios/migrations/0011_convenio_data_termino_vigencia.py b/sigi/apps/convenios/migrations/0011_convenio_data_termino_vigencia.py new file mode 100644 index 0000000..c5065c1 --- /dev/null +++ b/sigi/apps/convenios/migrations/0011_convenio_data_termino_vigencia.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('convenios', '0010_auto_20210819_0833'), + ] + + operations = [ + migrations.AddField( + model_name='convenio', + name='data_termino_vigencia', + field=models.DateField(help_text='T\xe9rmino da vig\xeancia do conv\xeanio.', null=True, verbose_name='Data t\xe9rmino vig\xeancia', blank=True), + preserve_default=True, + ), + ] diff --git a/sigi/apps/convenios/migrations/0012_auto_20210831_0844.py b/sigi/apps/convenios/migrations/0012_auto_20210831_0844.py new file mode 100644 index 0000000..b4e2692 --- /dev/null +++ b/sigi/apps/convenios/migrations/0012_auto_20210831_0844.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +from datetime import date +from django.db import models, migrations + +def migra_data_termino_vigencia(apps, schema_editor): + Convenio = apps.get_model('convenios', 'Convenio') + + for c in Convenio.objects.all(): + if (c.data_retorno_assinatura is None or c.duracao is None): + continue + + ano = c.data_retorno_assinatura.year + int(c.duracao / 12) + mes = int(c.data_retorno_assinatura.month + int(c.duracao % 12)) + if mes > 12: + ano = ano + 1 + mes = mes - 12 + dia = c.data_retorno_assinatura.day + + while True: + try: + data_fim = date(year=ano, month=mes,day=dia) + break + except: + dia = dia - 1 + + c.data_termino_vigencia = data_fim + c.save() + +class Migration(migrations.Migration): + + dependencies = [ + ('convenios', '0011_convenio_data_termino_vigencia'), + ] + + operations = [ + migrations.RunPython(migra_data_termino_vigencia), + ] diff --git a/sigi/apps/convenios/migrations/0013_remove_convenio_duracao.py b/sigi/apps/convenios/migrations/0013_remove_convenio_duracao.py new file mode 100644 index 0000000..bcc66bc --- /dev/null +++ b/sigi/apps/convenios/migrations/0013_remove_convenio_duracao.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('convenios', '0012_auto_20210831_0844'), + ] + + operations = [ + migrations.RemoveField( + model_name='convenio', + name='duracao', + ), + ] diff --git a/sigi/apps/convenios/migrations/0014_gescon.py b/sigi/apps/convenios/migrations/0014_gescon.py new file mode 100644 index 0000000..7fe216c --- /dev/null +++ b/sigi/apps/convenios/migrations/0014_gescon.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('convenios', '0013_remove_convenio_duracao'), + ] + + operations = [ + migrations.CreateModel( + name='Gescon', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('url_gescon', models.URLField(default='https://adm.senado.gov.br/gestao-contratos/api/contratos/busca?especie={s}', help_text='Informe o ponto de consulta do webservice do Gescon, inclusive com a querystring. No ponto onde deve ser inserida a sigla da subespecie do contrato, use a marca\xe7\xe3o {s}.
Por exemplo: https://adm.senado.gov.br/gestao-contratos/api/contratos/busca?especie={s}', verbose_name='Webservice Gescon')), + ('subespecies', models.TextField(default='AC=ACT\nPI=PI\nCN=PML\nTA=PML', help_text='Informe as siglas das subesp\xe9cies de contratos que devem ser pesquisados no Gescon com a sigla correspondente do projeto no SIGI. Coloque um par de siglas por linha, no formato SIGLA_GESTON=SIGLA_SIGI. As siglas n\xe3o encontradas ser\xe3o ignoradas.', verbose_name='Subesp\xe9cies')), + ('palavras', models.TextField(default='ILB\nINTERLEGIS', help_text='Palavras que devem aparecer no campo OBJETO dos dados do Gescon para identificar se o contrato pertence ao ILB. ', verbose_name='Palavras de filtro')), + ('email', models.EmailField(help_text='Caixa de e-mail para onde o relat\xf3rio di\xe1rio de importa\xe7\xe3o ser\xe1 enviado.', max_length=75, verbose_name='E-mail')), + ('ultima_importacao', models.TextField(verbose_name='Resultado da \xfaltima importa\xe7\xe3o', blank=True)), + ], + options={ + 'verbose_name': 'Configura\xe7\xe3o do Gescon', + 'verbose_name_plural': 'Configura\xe7\xf5es do Gescon', + }, + bases=(models.Model,), + ), + ] diff --git a/sigi/apps/convenios/migrations/0015_remove_convenio_search_text.py b/sigi/apps/convenios/migrations/0015_remove_convenio_search_text.py new file mode 100644 index 0000000..c187381 --- /dev/null +++ b/sigi/apps/convenios/migrations/0015_remove_convenio_search_text.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('convenios', '0014_gescon'), + ] + + operations = [ + migrations.RemoveField( + model_name='convenio', + name='search_text', + ), + ] diff --git a/sigi/apps/convenios/migrations/0016_auto_20210909_0732.py b/sigi/apps/convenios/migrations/0016_auto_20210909_0732.py new file mode 100644 index 0000000..9c98cbc --- /dev/null +++ b/sigi/apps/convenios/migrations/0016_auto_20210909_0732.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('convenios', '0015_remove_convenio_search_text'), + ] + + operations = [ + migrations.AddField( + model_name='convenio', + name='atualizacao_gescon', + field=models.DateTimeField(null=True, verbose_name='Data de atualiza\xe7\xe3o pelo Gescon', blank=True), + preserve_default=True, + ), + migrations.AddField( + model_name='convenio', + name='observacao_gescon', + field=models.TextField(verbose_name='Observa\xe7\xf5es da atualiza\xe7\xe3o do Gescon', blank=True), + preserve_default=True, + ), + ] diff --git a/sigi/apps/convenios/migrations/0017_convenio_id_contrato_gescon.py b/sigi/apps/convenios/migrations/0017_convenio_id_contrato_gescon.py new file mode 100644 index 0000000..83bddab --- /dev/null +++ b/sigi/apps/convenios/migrations/0017_convenio_id_contrato_gescon.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('convenios', '0016_auto_20210909_0732'), + ] + + operations = [ + migrations.AddField( + model_name='convenio', + name='id_contrato_gescon', + field=models.CharField(default=b'', verbose_name='ID do contrato no Gescon', max_length=20, editable=False, blank=True), + preserve_default=True, + ), + ] diff --git a/sigi/apps/convenios/migrations/0018_auto_20211208_1256.py b/sigi/apps/convenios/migrations/0018_auto_20211208_1256.py new file mode 100644 index 0000000..ed6b3c2 --- /dev/null +++ b/sigi/apps/convenios/migrations/0018_auto_20211208_1256.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('convenios', '0017_convenio_id_contrato_gescon'), + ] + + operations = [ + migrations.AlterField( + model_name='convenio', + name='projeto', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, verbose_name='Tipo de Convenio', to='convenios.Projeto'), + preserve_default=True, + ), + ] diff --git a/sigi/apps/convenios/models.py b/sigi/apps/convenios/models.py index 775f066..7e69675 100644 --- a/sigi/apps/convenios/models.py +++ b/sigi/apps/convenios/models.py @@ -1,9 +1,14 @@ #-*- coding: utf-8 -*- import re +import requests from datetime import datetime, date from django.db import models +from django.db.models import Q, fields +from django.core.mail import send_mail +from django.core.urlresolvers import reverse from django.utils.translation import ugettext as _ -from sigi.apps.utils import SearchField +from sigi.apps.utils import SearchField, to_ascii +from sigi.apps.casas.models import Orgao from sigi.apps.servidores.models import Servidor, Servico class Projeto(models.Model): @@ -15,10 +20,10 @@ class Projeto(models.Model): def __unicode__(self): return self.sigla - + class Meta: ordering = ('nome',) - + class StatusConvenio(models.Model): nome = models.CharField(max_length=100) cancela = models.BooleanField(_(u"Cancela o convênio"), default=False) @@ -49,10 +54,10 @@ class Convenio(models.Model): verbose_name=_(u'órgão conveniado') ) # campo de busca em caixa baixa e sem acentos - search_text = SearchField(field_names=['casa_legislativa']) projeto = models.ForeignKey( Projeto, on_delete=models.PROTECT, + verbose_name=_(u'Tipo de Convenio') ) # numero designado pelo Senado Federal para o convênio num_processo_sf = models.CharField( @@ -61,11 +66,19 @@ class Convenio(models.Model): blank=True, help_text=_(u'Formatos:
Antigo: XXXXXX/XX-X.
SIGAD: XXXXX.XXXXXX/XXXX-XX') ) + # link_processo_stf = ('get_sigad_url') num_convenio = models.CharField( _(u'número do convênio'), max_length=10, blank=True ) + id_contrato_gescon = models.CharField( + _(u"ID do contrato no Gescon"), + max_length=20, + blank=True, + default="", + editable=False + ) data_sigi = models.DateField( _(u"data de cadastro no SIGI"), blank=True, @@ -130,17 +143,17 @@ class Convenio(models.Model): blank=True, ) data_retorno_assinatura = models.DateField( - _(u'conveniadas'), + _(u'data início vigência'), null=True, blank=True, help_text=_(u'Convênio firmado.') ) - duracao = models.PositiveIntegerField( - _(u"duração (meses)"), + data_termino_vigencia = models.DateField( + _(u'Data término vigência'), null=True, blank=True, - help_text=_(u"Deixar em branco caso a duração seja indefinida") - ) + help_text=_(u'Término da vigência do convênio.') + ) data_pub_diario = models.DateField( _(u'data da publicação no Diário Oficial'), null=True, @@ -177,35 +190,23 @@ class Convenio(models.Model): ) conveniada = models.BooleanField(default=False) equipada = models.BooleanField(default=False) - - def get_termino_convenio(self): - if (self.data_retorno_assinatura is None or - self.duracao is None): - return None - - ano = self.data_retorno_assinatura.year + int(self.duracao / 12) - mes = int(self.data_retorno_assinatura.month + int(self.duracao % 12)) - if mes > 12: - ano = ano + 1 - mes = mes - 12 - dia = self.data_retorno_assinatura.day - - while True: - try: - data_fim = date(year=ano, month=mes,day=dia) - break - except: - dia = dia - 1 - - return data_fim + atualizacao_gescon = models.DateTimeField( + _(u"Data de atualização pelo Gescon"), + blank=True, + null=True + ) + observacao_gescon = models.TextField( + _(u"Observações da atualização do Gescon"), + blank=True + ) def get_status(self): if self.status and self.status.cancela: return _(u"Cancelado") if self.data_retorno_assinatura is not None: - if self.duracao is not None: - if date.today() >= self.get_termino_convenio(): + if self.data_termino_vigencia is not None: + if date.today() >= self.data_termino_vigencia: return _(u"Vencido") return _(u"Vigente") @@ -219,6 +220,14 @@ class Convenio(models.Model): return _(u"Indefinido") + def link_sigad(self, obj): + if obj.pk is None: + return "" + return obj.get_sigad_url() + + link_sigad.short_description = _("Processo no Senado") + link_sigad.allow_tags = True + def get_sigad_url(self): m = re.match( r'(?P00100|00200)\.(?P\d{6})/(?P\d{4})-\d{2}', @@ -257,18 +266,18 @@ class Convenio(models.Model): if ((self.data_retorno_assinatura is None) and (self.equipada and self.data_termo_aceite is not None)): - return _(u"Convênio nº {number} - equipada em {date} pelo {project}" + return _(u"{project} nº {number} - equipada em {date}" ).format(number=self.num_convenio, date=self.data_termo_aceite.strftime('%d/%m/%Y'), project=self.projeto.sigla) elif self.data_retorno_assinatura is None: - return _(u"Convênio nº {number} - adesão ao projeto {project}, " + return _(u"{project}, nº {number}, início " u"em {date}").format(number=self.num_convenio, project=self.projeto.sigla, date=self.data_adesao) if ((self.data_retorno_assinatura is not None) and not (self.equipada and self.data_termo_aceite is not None)): - return _(u"Convênio nº {number} - conveniada ao {project} em " + return _(u"{project}, nº {number}, inicio em " u"{date}. Status: {status}").format( number=self.num_convenio, project=self.projeto.sigla, @@ -276,7 +285,7 @@ class Convenio(models.Model): status=self.get_status()) if ((self.data_retorno_assinatura is not None) and (self.equipada and self.data_termo_aceite is not None)): - return _(u"Convẽnio nº {number} - conveniada ao {project} em {date}" + return _(u"{project}, nº {number}, início em {date}" u" e equipada em {equipped_date}. Status: {status}" ).format(number=self.num_convenio, project=self.projeto.sigla, @@ -381,3 +390,412 @@ class Tramitacao(models.Model): if self.observacao: result = result + u" (%s)" % (self.observacao) return unicode(result) # XXX is this unicode(...) really necessary??? + +class Gescon(models.Model): + url_gescon = models.URLField( + _(u"Webservice Gescon"), + default=(u"https://adm.senado.gov.br/gestao-contratos/api/contratos" + u"/busca?especie={s}"), + help_text=_(u"Informe o ponto de consulta do webservice do Gescon, " + u"inclusive com a querystring. No ponto onde deve ser " + u"inserida a sigla da subespecie do contrato, use a " + u"marcação {s}.
Por exemplo: " + u"https://adm.senado.gov.br/gestao-contratos/api/contratos" + u"/busca?especie={s}") + ) + subespecies = models.TextField( + _(u"Subespécies"), + default=u"AC=ACT\nPI=PI\nCN=PML\nTA=PML", + help_text=_(u"Informe as siglas das subespécies de contratos que " + u"devem ser pesquisados no Gescon com a sigla " + u"correspondente do projeto no SIGI. Coloque um par de " + u"siglas por linha, no formato SIGLA_GESTON=SIGLA_SIGI. " + u"As siglas não encontradas serão ignoradas.") + ) + palavras = models.TextField( + _(u"Palavras de filtro"), + default=u"ILB\nINTERLEGIS", + help_text=_(u"Palavras que devem aparecer no campo OBJETO dos dados do " + u"Gescon para identificar se o contrato pertence ao ILB. " + u"
  • Informe uma palavra por linha.
  • " + u"
  • Ocorrendo qualquer uma das palavras, o contrato será " + u"importado.
") + ) + email = models.EmailField( + _(u"E-mail"), + help_text=_(u"Caixa de e-mail para onde o relatório diário de " + u"importação será enviado.") + ) + ultima_importacao = models.TextField( + _(u"Resultado da última importação"), + blank=True + ) + + class Meta: + verbose_name = _(u"Configuração do Gescon") + verbose_name_plural = _(u"Configurações do Gescon") + + def __unicode__(self): + return self.url_gescon + + def save(self, *args, **kwargs): + self.pk = 1 # Highlander (singleton pattern) + return super(Gescon, self).save(*args, **kwargs) + + def delete(self, *args, **kwargs): + pass # Highlander is immortal + + def add_message(self, msg, save=False): + self.ultima_importacao += msg + "\n" + if save: + self.save() + self.email_report() + + def email_report(self): + if self.email: + send_mail( + subject=_(u"Relatório de importação GESCON"), + message=self.ultima_importacao, + recipient_list=self.email, + fail_silently=True + ) + else: + self.ultima_importacao += _( + u"\n\n*Não foi definida uma caixa de e-mail nas configurações " + u"do Gescon*" + ) + self.save() + + def importa_contratos(self): + self.ultima_importacao = "" + self.add_message( + _(u"Importação iniciada em {:%d/%m/%Y %H:%M:%S}\n" + u"==========================================\n").format( + datetime.now() + ) + ) + + if self.palavras == "": + self.add_message(_(u"Nenhuma palavra de pesquisa definida - " + u"processo abortado."), True) + return + + if self.subespecies == "": + self.add_message(_(u"Nenhuma subespécie definida - processo " + u"abortado."), True) + return + + if "{s}" not in self.url_gescon: + self.add_message( + _( + u"Falta a marcação {s} na URL para indicar o local onde " + u"inserir a sigla da subespécia na consulta ao webservice " + u"- processo abortado." + ), + True + ) + return + + palavras = self.palavras.split() + subespecies = {tuple(s.split("=")) for s in self.subespecies.split()} + + for sigla_gescon, sigla_sigi in subespecies: + self.add_message(_(u"\nImportando subespécie {s}".format( + s=sigla_gescon))) + url = self.url_gescon.format(s=sigla_gescon) + + projeto = Projeto.objects.get(sigla=sigla_sigi) + + try: + response = requests.get(url, verify=False) + except Exception as e: + self.add_message( + _(u"\tErro ao acessar {url}: {errmsg}").format( + url=url, + errmsg=e.message.decode("utf8") + ) + ) + continue + + if not response.ok: + self.add_message( + _(u"\tErro ao acessar {url}: {reason}").format( + url=url, + reason=response.reason + ) + ) + continue + + if not 'application/json' in response.headers.get('Content-Type'): + self.add_message(_(u"\tResultado da consulta à {url} não " + u"retornou dados em formato json").format( + url=url + ) + ) + continue + + contratos = response.json() + + # Pegar só os contratos que possuem alguma das palavras-chave + + nossos = [c for c in contratos + if any(palavra in c['objeto'] for palavra in palavras)] + + self.add_message( + _(u"\t{count} contratos encontrados no Gescon").format( + count=len(nossos) + ) + ) + + novos = 0 + erros = 0 + alertas = 0 + atualizados = 0 + + for contrato in nossos: + numero = contrato['numero'].zfill(8) + numero = "{}/{}".format(numero[:4], numero[4:]) + sigad = contrato['processo'].zfill(17) + sigad = "{}.{}/{}-{}".format(sigad[:5], sigad[5:11], + sigad[11:15], sigad[15:]) + + + if contrato['cnpjCpfFornecedor']: + cnpj = contrato['cnpjCpfFornecedor'].zfill(14) + cnpj = "{}.{}.{}/{}-{}".format(cnpj[:2], cnpj[2:5], + cnpj[5:8], cnpj[8:12], + cnpj[12:]) + else: + cnpj = None + + if contrato['nomeFornecedor']: + nome = contrato['nomeFornecedor'] + nome = nome.replace(u'VEREADORES DE', '') + nome = nome.split('-')[0] + nome = nome.split('/')[0] + nome = nome.strip() + nome = nome.replace(" ", " ") + nome = to_ascii(nome) + else: + nome = None + + if (cnpj is None) and (nome is None): + self.add_message( + _(u"\tO contrato {numero} no Gescon não informa o CNPJ " + u"nem o nome do órgão.").format(numero=numero) + ) + erros += 1 + continue + + orgao = None + + if cnpj is not None: + try: + orgao = Orgao.objects.get(cnpj=cnpj) + except ( + Orgao.DoesNotExist, + Orgao.MultipleObjectsReturned) as e: + orgao = None + pass + + if (orgao is None) and (nome is not None): + try: + orgao = Orgao.objects.get(search_text__iexact=nome) + except ( + Orgao.DoesNotExist, + Orgao.MultipleObjectsReturned) as e: + orgao = None + pass + + if orgao is None: + self.add_message( + _(u"\tÓrgão não encontrado no SIGI ou mais de um órgão" + u"encontrado com o mesmo CNPJ ou nome. Favor " + u"regularizar o cadastro: CNPJ: {cnpj}, " + u"Nome: {nome}".format( + cnpj=contrato['cnpjCpfFornecedor'], + nome=contrato['nomeFornecedor'] + ) + ) + ) + erros += 1 + continue + + # O mais seguro é o NUP sigad + convenios = Convenio.objects.filter(num_processo_sf=sigad) + chk = convenios.count() + + if chk == 0: + # NUP não encontrado, talvez exista apenas com o número + # do GESCON + convenios = Convenio.objects.filter( + Q(num_convenio=numero) | + Q(num_processo_sf=numero) + ) + chk = convenios.count() + if chk > 1: + # Pode ser que existam vários contratos de subespécies + # diferentes com o mesmo número Gescon. Neste caso, o + # ideal é filtrar pelo tipo de projeto. Existindo, é + # ele mesmo. Se não existir, então segue com os + # múltiplos para registrar o problema mais adiante + if convenios.filter(projeto=projeto).count() == 1: + convenios = convenios.filter(projeto=projeto) + chk = 1 + + if chk == 0: + convenio = Convenio( + casa_legislativa=orgao, + projeto=projeto, + num_processo_sf=sigad, + num_convenio=numero, + data_sigi=date.today(), + data_sigad=contrato['assinatura'], + observacao=contrato['objeto'], + data_retorno_assinatura=contrato['inicioVigencia'], + data_termino_vigencia=contrato['terminoVigencia'], + data_pub_diario=contrato['publicacao'], + atualizacao_gescon=datetime.now(), + observacao_gescon=_(u"Importado integralmente do" + u"Gescon") + ) + convenio.save() + novos += 1 + continue + elif chk == 1: + convenio = convenios.get() + convenio.atualizacao_gescon = datetime.now() + convenio.observacao_gescon = '' + if convenio.casa_legislativa != orgao: + self.add_message( + _(u"\tO órgao no convênio {url} diverge do que " + u"consta no Gescon ({cnpj}, {nome})").format( + url=reverse('admin:%s_%s_change' % ( + convenio._meta.app_label, + convenio._meta.model_name), + args=[convenio.id]), + cnpj=cnpj, + nome=contrato['nomeFornecedor'] + ) + ) + convenio.observacao_gescon = _( + u'ERRO: Órgão diverge do Gescon. Não atualizado!' + ) + convenio.save() + erros += 1 + continue + + if convenio.num_processo_sf != sigad: + self.add_message( + _(u"\tO contrato Gescon nº {numero} corresponde" + u" ao convênio SIGI {url}, mas o NUP sigad " + u"diverge (Gescon: {sigad_gescon}, " + u"SIGI: {sigad_sigi}). CORRIGIDO!").format( + numero=numero, + url=reverse('admin:%s_%s_change' % ( + convenio._meta.app_label, + convenio._meta.model_name), + args=[convenio.id]), + sigad_gescon=sigad, + sigad_sigi=convenio.num_processo_sf + ) + ) + convenio.num_processo_sf = sigad + convenio.observacao_gescon += _( + u"Número do SIGAD atualizado.\n" + ) + alertas += 1 + + if convenio.num_convenio != numero: + self.add_message( + _(u"\tO contrato Gescon ID {id} corresponde ao " + u"convênio SIGI {url}, mas o número do convênio" + u" diverge (Gescon: {numero_gescon}, SIGI: " + u"{numero_sigi}). CORRIGIDO!").format( + id=contrato['id'], + url=reverse('admin:%s_%s_change' % ( + convenio._meta.app_label, + convenio._meta.model_name), + args=[convenio.id] + ), + numero_gescon=numero, + numero_sigi=convenio.num_convenio + ) + ) + convenio.num_convenio = numero + convenio.observacao_gescon += _( + u"Número do convênio atualizado.\n" + ) + alertas += 1 + + if contrato['objeto'] not in convenio.observacao: + convenio.observacao += "\n" + contrato['objeto'] + convenio.observacao_gescon += _( + u"Observação atualizada.\n" + ) + + convenio.data_sigad = contrato['assinatura'] + convenio.data_retorno_assinatura = contrato[ + 'inicioVigencia' + ] + convenio.data_termino_vigencia = contrato[ + 'terminoVigencia' + ] + convenio.data_pub_diario = contrato['publicacao'] + if contrato['codTextoContrato']: + convenio.id_contrato_gescon = contrato[ + 'codTextoContrato' + ] + else: + convenio.id_contrato_gescon = "" + + try: + convenio.save() + except Exception as e: + self.add_message( + _(u"Ocorreu um erro ao salvar o convênio {url} no " + u"SIGI. Alguma informação do Gescon pode ter " + u"quebrado o sistema. Informe ao suporte. Erro:" + u"{errmsg}").format( + url=reverse('admin:%s_%s_change' % ( + convenio._meta.app_label, + convenio._meta.model_name), + args=[convenio.id] + ), + errmsg=e.message.decode("utf8") + ) + ) + erros += 1 + continue + + atualizados += 1 + else: + self.add_message(_(u"\tExistem {count} convênios no SIGI " + u"que correspondem ao mesmo contrato no " + u"Gescon (contrato {numero}, sigad " + u"{sigad})").format( + count=chk, + numero=numero, + sigad=sigad + ) + ) + erros += 1 + continue + + self.add_message( + _(u"\t{novos} novos convenios adicionados ao SIGI, " + u"{atualizados} atualizados, sendo {alertas} com alertas, e " + u"{erros} reportados com erro.").format( + novos=novos, + atualizados=atualizados, + alertas=alertas, + erros=erros + ) + ) + + self.save() + + @classmethod + def load(cls): + obj, created = cls.objects.get_or_create(pk=1) + return obj diff --git a/sigi/apps/convenios/reports.py b/sigi/apps/convenios/reports.py index 15f68a8..dea94fc 100644 --- a/sigi/apps/convenios/reports.py +++ b/sigi/apps/convenios/reports.py @@ -26,12 +26,12 @@ class SemEquipamentosReport(object): class ConvenioReport(ReportDefault): - title = _(u'Relatório de Convênios') - + title = _(u'Relatório de Parcerias') + class band_page_header(ReportDefault.band_page_header): label_top = ReportDefault.band_page_header.label_top - label_left = [0, 1.5, 7, 9, 11, 13, 15, 17] + label_left = [0, 2.5, 6, 8, 10, 12, 14, 16] elements = list(ReportDefault.band_page_header.elements) height = 4.7 * cm @@ -47,15 +47,15 @@ class ConvenioReport(ReportDefault): top=label_top + 0.4 * cm, ), Label( - text=_(u"Data de Adesão"), + text=_(u"Número do Convênio"), left=label_left[2] * cm, top=label_top, width=2 * cm, ), Label( - text=_(u"Número do Convênio"), + text=_(u"Projeto"), left=label_left[3] * cm, - top=label_top, + top=label_top + 0.4 * cm, width=2 * cm, ), Label( @@ -71,14 +71,8 @@ class ConvenioReport(ReportDefault): width=2 * cm, ), Label( - text=_(u"Data de Aceite"), + text=_(u"Orgão"), left=label_left[6] * cm, - top=label_top, - width=2 * cm, - ), - Label( - text=_(u"Projeto"), - left=label_left[7] * cm, top=label_top + 0.4 * cm, width=2 * cm, ), @@ -89,7 +83,7 @@ class ConvenioReport(ReportDefault): class band_detail(ReportDefault.band_detail): - label_left = [0, 1.5, 7, 9, 11, 13, 15, 17] + label_left = [0, 2.5, 6, 8, 10, 12, 14, 16] elements = [ ObjectValue( @@ -101,13 +95,11 @@ class ConvenioReport(ReportDefault): left=label_left[1] * cm ), ObjectValue( - attribute_name='data_adesao', - left=label_left[2] * cm, - get_value=lambda instance: - instance.data_adesao.strftime('%d/%m/%Y') if instance.data_adesao is not None else '-' + attribute_name='num_convenio', + left=label_left[2] * cm ), ObjectValue( - attribute_name='num_convenio', + attribute_name='projeto.sigla', left=label_left[3] * cm ), ObjectValue( @@ -123,30 +115,24 @@ class ConvenioReport(ReportDefault): instance.data_pub_diario.strftime('%d/%m/%Y') if instance.data_pub_diario is not None else '-' ), ObjectValue( - attribute_name='data_termo_aceite', - left=label_left[6] * cm, - get_value=lambda instance: - instance.data_termo_aceite.strftime('%d/%m/%Y') if instance.data_termo_aceite is not None else '-' - ), - ObjectValue( - attribute_name='projeto.sigla', - left=label_left[7] * cm + attribute_name='casa_legislativa.nome', + left=label_left[6] * cm ), ] - groups = [ - ReportGroup(attribute_name='casa_legislativa.municipio.uf', - band_header=ReportBand( - height=0.7 * cm, - elements=[ - ObjectValue(attribute_name='casa_legislativa.municipio.uf', - get_Value=lambda instance: '%s: %s' % (_(u'Casa Legislativa'), instance.casa_legislativa.uf) - ) - ], - borders={'top': True}, - ) - ) - ] + #groups = [ + # ReportGroup(attribute_name='casa_legislativa.municipio.uf', + # band_header=ReportBand( + # height=0.7 * cm, + # elements=[ + # ObjectValue(attribute_name='casa_legislativa.municipio.uf', + # get_Value=lambda instance: '%s: %s' % (_(u'Casa Legislativa'), instance.casa_legislativa.uf) + # ) + # ], + # borders={'top': True}, + # ) + # ) + #] class ConvenioReportSemAceite(ConvenioReport): @@ -154,7 +140,7 @@ class ConvenioReportSemAceite(ConvenioReport): class band_page_header(ReportDefault.band_page_header): label_top = ReportDefault.band_page_header.label_top - label_left = [0, 1.5, 7, 9, 11, 13, 15, 17] + label_left = [0, 2.5, 6, 8, 10, 12, 14, 16] elements = list(ReportDefault.band_page_header.elements) height = 4.7 * cm @@ -169,33 +155,27 @@ class ConvenioReportSemAceite(ConvenioReport): left=label_left[1] * cm, top=label_top + 0.4 * cm, ), - Label( - text=_(u"Data de Adesão"), - left=label_left[3] * cm, - top=label_top, - width=2 * cm, - ), Label( text=_(u"Número do Convênio"), - left=label_left[4] * cm, + left=label_left[2] * cm, top=label_top, width=2 * cm, - ), + ), Label( - text=_(u"Data do Convênio"), - left=label_left[5] * cm, + text=_(u"Projeto"), + left=label_left[3] * cm, top=label_top, width=2 * cm, ), Label( - text=_(u"Data de Publicação"), - left=label_left[6] * cm, + text=_(u"Data do Convênio"), + left=label_left[4] * cm, top=label_top, width=2 * cm, ), Label( - text=_(u"Projeto"), - left=label_left[7] * cm, + text=_(u"Orgão"), + left=label_left[5] * cm, top=label_top + 0.4 * cm, width=2 * cm, ), @@ -203,7 +183,7 @@ class ConvenioReportSemAceite(ConvenioReport): class band_detail(ReportDefault.band_detail): - label_left = [0, 1.5, 7, 9, 11, 13, 15, 17] + label_left = [0, 2.5, 6, 8, 10, 12, 14, 16] elements = [ ObjectValue( @@ -215,30 +195,22 @@ class ConvenioReportSemAceite(ConvenioReport): left=label_left[1] * cm ), ObjectValue( - attribute_name='data_adesao', - left=label_left[3] * cm, - get_value=lambda instance: - instance.data_adesao.strftime('%d/%m/%Y') if instance.data_adesao is not None else '-' + attribute_name='num_convenio', + left=label_left[2] * cm ), ObjectValue( - attribute_name='num_convenio', - left=label_left[4] * cm + attribute_name='projeto.sigla', + left=label_left[3], ), ObjectValue( attribute_name='data_retorno_assinatura', - left=label_left[5] * cm, + left=label_left[4] * cm, get_value=lambda instance: instance.data_retorno_assinatura.strftime('%d/%m/%Y') if instance.data_retorno_assinatura is not None else '-' ), ObjectValue( - attribute_name='data_pub_diario', - left=label_left[6] * cm, - get_value=lambda instance: - instance.data_pub_diario.strftime('%d/%m/%Y') if instance.data_pub_diario is not None else '-' - ), - ObjectValue( - attribute_name='projeto.sigla', - left=label_left[7] * cm + attribute_name='casa_legislativa.nome', + left=label_left[5] * cm ), ] @@ -247,7 +219,7 @@ float_duas_casas = lambda instance: '%.2f' % (instance) class ConvenioReportRegiao(ReportDefault): - title = _(u'Relatório de Convênios por Região') + title = _(u'Relatório de Parcerias por Região') class band_page_header(ReportDefault.band_page_header): label_top = ReportDefault.band_page_header.label_top @@ -298,16 +270,16 @@ class ConvenioReportRegiao(ReportDefault): class ConvenioPorCMReport(ConvenioReport): - title = _(u'Relatório de Convênios por Câmara Municipal') + title = _(u'Relatório de Parcerias por Câmara Municipal') class ConvenioPorALReport(ConvenioReport): - title = _(u'Relatório de Convênios por Assembléia Legislativa') + title = _(u'Relatório de Parcerias por Assembléia Legislativa') class ConvenioReportSemAceiteCM(ConvenioReportSemAceite): - title = _(u'Relatório de Convênios por Câmara Municipal') + title = _(u'Relatório de Parcerias por Câmara Municipal') class ConvenioReportSemAceiteAL(ConvenioReportSemAceite): - title = _(u'Relatório de Convênios por Assembléia Legislativa') + title = _(u'Relatório de Parcerias por Assembléia Legislativa') diff --git a/sigi/apps/convenios/templates/convenios/change_list.html b/sigi/apps/convenios/templates/convenios/change_list.html index f9aae7b..1c5a553 100644 --- a/sigi/apps/convenios/templates/convenios/change_list.html +++ b/sigi/apps/convenios/templates/convenios/change_list.html @@ -14,32 +14,34 @@ Filtro de datas +