From 028e48e8b196a893a72d615b3ef3db32baba098e Mon Sep 17 00:00:00 2001 From: Edward <9326037+edwardoliveira@users.noreply.github.com> Date: Wed, 3 Mar 2021 11:03:50 -0300 Subject: [PATCH] =?UTF-8?q?Hist=C3=B3rico=20de=20Proposi=C3=A7=C3=B5es=20(?= =?UTF-8?q?#3358)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixes #3356 Co-authored-by: eribeiro --- sapl/materia/forms.py | 29 +++++++++++- .../migrations/0078_historicoproposicao.py | 34 ++++++++++++++ sapl/materia/models.py | 45 ++++++++++++++++++ sapl/materia/urls.py | 4 +- sapl/materia/views.py | 47 ++++++++++++++++++- sapl/rules/map_rules.py | 2 + .../materia/historico_proposicao.html | 44 +++++++++++++++++ sapl/templates/materia/proposicao_list.html | 12 +++++ sapl/templates/materia/subnav_prop.yaml | 2 + 9 files changed, 215 insertions(+), 4 deletions(-) create mode 100644 sapl/materia/migrations/0078_historicoproposicao.py create mode 100644 sapl/templates/materia/historico_proposicao.html create mode 100644 sapl/templates/materia/proposicao_list.html diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index f6d9ab2b8..ec385ffe8 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -29,7 +29,7 @@ from sapl.materia.models import (AssuntoMateria, MateriaAssunto, MateriaLegislativa, Orgao, RegimeTramitacao, StatusTramitacao, TipoDocumento, TipoProposicao, - ConfigEtiquetaMateriaLegislativa) + ConfigEtiquetaMateriaLegislativa, HistoricoProposicao) from sapl.norma.models import (LegislacaoCitada, NormaJuridica, TipoNormaJuridica) from sapl.parlamentares.models import Legislatura, Partido @@ -2084,7 +2084,13 @@ class DevolverProposicaoForm(forms.ModelForm): fields = [ 'justificativa_devolucao', 'observacao', + 'user', + 'ip' ] + widgets = { + 'user': forms.HiddenInput(), + 'ip': forms.HiddenInput(), + } def __init__(self, *args, **kwargs): @@ -2139,6 +2145,13 @@ class DevolverProposicaoForm(forms.ModelForm): ta.editing_locked = False ta.save() + observacao = self.instance.justificativa_devolucao + HistoricoProposicao.objects.create(proposicao=self.instance, + status='D', + user=self.initial['user'], + ip=self.initial['ip'], + observacao=observacao) + self.instance.results = { 'messages': { 'success': [_('Devolução efetuada com sucesso.'), ] @@ -2181,13 +2194,17 @@ class ConfirmarProposicaoForm(ProposicaoForm): 'observacao', 'gerar_protocolo', 'numero_de_paginas', - 'numero_materia_futuro' + 'numero_materia_futuro', + 'user', + 'ip' ] widgets = { 'descricao': widgets.Textarea( attrs={'readonly': 'readonly', 'rows': 4}), 'data_envio': widgets.DateTimeInput( attrs={'readonly': 'readonly'}), + 'user': forms.HiddenInput(), + 'ip': forms.HiddenInput(), } def __init__(self, *args, **kwargs): @@ -2395,6 +2412,12 @@ class ConfirmarProposicaoForm(ProposicaoForm): proposicao = self.instance conteudo_gerado = None + HistoricoProposicao.objects.create(proposicao=proposicao, + status='R', + user=self.initial['user'], + ip=self.initial['ip'], + ) + if self.instance.tipo.content_type.model_class( ) == TipoMateriaLegislativa: @@ -2591,6 +2614,8 @@ class ConfirmarProposicaoForm(ProposicaoForm): protocolo.tipo_processo = '0' protocolo.save() + HistoricoProposicao.objects.create(proposicao=proposicao, + status='E') self.instance.results['messages']['success'].append(_( 'Protocolo realizado com sucesso')) diff --git a/sapl/materia/migrations/0078_historicoproposicao.py b/sapl/materia/migrations/0078_historicoproposicao.py new file mode 100644 index 000000000..cf764b43c --- /dev/null +++ b/sapl/materia/migrations/0078_historicoproposicao.py @@ -0,0 +1,34 @@ +# Generated by Django 2.2.13 on 2021-03-02 17:17 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('materia', '0077_auto_20210209_1047'), + ] + + operations = [ + migrations.CreateModel( + name='HistoricoProposicao', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status', models.CharField(choices=[('E', 'Enviada'), ('R', 'Recebida'), ('T', 'Retornada'), ('D', 'Devolvida')], db_index=True, max_length=1, verbose_name='Status de Proposição')), + ('data_hora', models.DateTimeField(blank=True, db_index=True, default=django.utils.timezone.now, null=True, verbose_name='Data/Hora')), + ('observacao', models.CharField(blank=True, max_length=200, verbose_name='Observação')), + ('ip', models.CharField(blank=True, default='', max_length=60, verbose_name='IP')), + ('proposicao', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='materia.Proposicao', verbose_name='Proposição')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='Usuário')), + ], + options={ + 'verbose_name': 'Histórico de Proposição', + 'verbose_name_plural': 'Histórico de Proposições', + 'ordering': ('-data_hora', '-proposicao'), + }, + ), + ] diff --git a/sapl/materia/models.py b/sapl/materia/models.py index c904974ad..6ea83b2e9 100644 --- a/sapl/materia/models.py +++ b/sapl/materia/models.py @@ -1025,6 +1025,51 @@ class Proposicao(models.Model): update_fields=update_fields) +class HistoricoProposicao(models.Model): + STATUS_PROPOSICAO = Choices(('E', 'ENVIADA', _('Enviada')), + ('R', 'RECEBIDA', _('Recebida')), + ('T', 'RETORNADA', _('Retornada')), + ('D', 'DEVOLVIDA', _('Devolvida'))) + + proposicao = models.ForeignKey(Proposicao, + verbose_name=_('Proposição'), + db_index=True, + on_delete=models.CASCADE) + status = models.CharField(max_length=1, + verbose_name=_('Status de Proposição'), + db_index=True, + choices=STATUS_PROPOSICAO) + data_hora = models.DateTimeField(null=True, + blank=True, + default=timezone.now, + db_index=True, + verbose_name=_('Data/Hora')) + observacao = models.CharField(max_length=200, + blank=True, + verbose_name=_('Observação')) + user = models.ForeignKey(get_settings_auth_user_model(), + verbose_name=_('Usuário'), + on_delete=models.PROTECT, + null=True, + blank=True) + ip = models.CharField(verbose_name=_('IP'), + max_length=60, + blank=True, + default='') + + @property + def status_descricao(self): + return self.STATUS_PROPOSICAO[self.status] + + class Meta: + verbose_name = _('Histórico de Proposição') + verbose_name_plural = _('Histórico de Proposições') + ordering = ('-data_hora', '-proposicao') + + def __str__(self): + return f'{self.data_hora} - {self.STATUS_PROPOSICAO[self.status]} - {str(self.proposicao)}' + + @reversion.register() class StatusTramitacao(models.Model): INDICADOR_CHOICES = Choices(('F', 'fim', _('Fim')), diff --git a/sapl/materia/urls.py b/sapl/materia/urls.py index 12e392ef4..272970b82 100644 --- a/sapl/materia/urls.py +++ b/sapl/materia/urls.py @@ -31,7 +31,7 @@ from sapl.materia.views import (AcompanhamentoConfirmarView, DespachoInicialMultiCreateView, get_zip_docacessorios, get_pdf_docacessorios, configEtiquetaMateriaLegislativaCrud, - PesquisarStatusTramitacaoView) + PesquisarStatusTramitacaoView, HistoricoProposicaoView) from sapl.norma.views import NormaPesquisaSimplesView from sapl.protocoloadm.views import ( FichaPesquisaAdmView, FichaSelecionaAdmView @@ -156,6 +156,8 @@ urlpatterns_proposicao = [ name='proposicao_texto'), url(r'^proposicao/(?P\d+)/retornar', RetornarProposicao.as_view(), name='retornar-proposicao'), + url(r'^proposicao/historico', HistoricoProposicaoView.as_view(), + name='historico-proposicao'), ] diff --git a/sapl/materia/views.py b/sapl/materia/views.py index bf43879fd..0d2c0aced 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -75,7 +75,7 @@ from .models import (AcompanhamentoMateria, Anexada, AssuntoMateria, Autoria, De DocumentoAcessorio, MateriaAssunto, MateriaLegislativa, Numeracao, Orgao, Origem, Proposicao, RegimeTramitacao, Relatoria, StatusTramitacao, TipoDocumento, TipoFimRelatoria, TipoMateriaLegislativa, TipoProposicao, - Tramitacao, UnidadeTramitacao, ConfigEtiquetaMateriaLegislativa) + Tramitacao, UnidadeTramitacao, ConfigEtiquetaMateriaLegislativa, HistoricoProposicao) AssuntoMateriaCrud = CrudAux.build(AssuntoMateria, 'assunto_materia') @@ -680,6 +680,12 @@ class ConfirmarProposicao(PermissionRequiredForAppCrudMixin, UpdateView): form_class = ConfirmarProposicaoForm, DevolverProposicaoForm logger = logging.getLogger(__name__) + def get_initial(self): + initial = super().get_initial() + initial['ip'] = get_client_ip(self.request) + initial['user'] = self.request.user + return initial + def get_success_url(self): msgs = self.object.results['messages'] @@ -897,6 +903,11 @@ class ProposicaoCrud(Crud): p.data_devolucao = None p.data_envio = timezone.now() p.save() + HistoricoProposicao.objects.create( + proposicao=p, + status='E', + ip=get_client_ip(self.request), + user=self.request.user) messages.success(request, _( 'Proposição enviada com sucesso.')) @@ -939,6 +950,12 @@ class ProposicaoCrud(Crud): else: p.data_envio = None p.save() + HistoricoProposicao.objects.create( + proposicao=p, + status='T', + ip=get_client_ip(self.request), + user=self.request.user) + if p.texto_articulado.exists(): ta = p.texto_articulado.first() ta.privacidade = STATUS_TA_PRIVATE @@ -1216,6 +1233,34 @@ class ReciboProposicaoView(TemplateView): kwargs={'pk': proposicao.pk})) +class HistoricoProposicaoView(PermissionRequiredMixin, ListView): + logger = logging.getLogger(__name__) + template_name = "materia/historico_proposicao.html" + ordering = ['-data_hora'] + paginate_by = 10 + model = HistoricoProposicao + permission_required = ('materia.detail_proposicao', ) + + def get_queryset(self): + qs = super().get_queryset() + user = self.request.user + if not user.is_superuser: + autores = Autor.objects.filter(user=user) + qs = qs.filter(proposicao__autor__in=autores) + return qs + + def get_context_data(self, **kwargs): + context = super(HistoricoProposicaoView, self).get_context_data(**kwargs) + paginator = context['paginator'] + page_obj = context['page_obj'] + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages) + context['NO_ENTRIES_MSG'] = 'Nenhuma proposição' + if self.request.user.is_superuser: + context['subnav_template_name'] = 'materia/subnav_prop.yaml' + return context + + class RelatoriaCrud(MasterDetailCrud): model = Relatoria parent_field = 'materia' diff --git a/sapl/rules/map_rules.py b/sapl/rules/map_rules.py index 9cd52c706..cfc138ca7 100644 --- a/sapl/rules/map_rules.py +++ b/sapl/rules/map_rules.py @@ -95,6 +95,7 @@ rules_group_protocolo = { (materia.Proposicao, ['detail_proposicao_enviada', 'detail_proposicao_devolvida', 'detail_proposicao_incorporada'], set()), # TODO: tratar em sapl.api questão de que proposições incorporadas serem públicas + (materia.HistoricoProposicao, __base__, set()), (compilacao.TextoArticulado, [ 'view_restricted_textoarticulado'], __perms_publicas__) ] @@ -203,6 +204,7 @@ rules_group_autor = { 'group': SAPL_GROUP_AUTOR, 'rules': [ (materia.Proposicao, __base__, set()), + (materia.HistoricoProposicao, __base__, set()), (compilacao.Dispositivo, __base__ + [ 'change_your_dispositivo_edicao_dinamica', ], __perms_publicas__) diff --git a/sapl/templates/materia/historico_proposicao.html b/sapl/templates/materia/historico_proposicao.html new file mode 100644 index 000000000..f4bdcc34c --- /dev/null +++ b/sapl/templates/materia/historico_proposicao.html @@ -0,0 +1,44 @@ +{% extends "base.html" %} +{% load i18n %} +{% load tz %} +{% block base_content %} +
+ Histórico de Proposições + {% if not object_list %} +

{{ NO_ENTRIES_MSG }}

+ {% else %} + + + + + + + + + + + + + + {% for hist in object_list %} + + + + + + + + + + {% endfor %} + +
Data/HoraStatusOperadorProposiçãoDescriçãoAutorObservação
{{ hist.data_hora|localtime|date:"d/m/Y H:i:s" }}{{ hist.status_descricao}}{{ hist.user }} + + {{ hist.proposicao.numero_proposicao }}/{{ hist.proposicao.ano }} + + {{ hist.proposicao.descricao }}{{ hist.proposicao.autor }}{{ hist.observacao }}
+ {% endif %} +
+ {% include 'paginacao.html'%} +{% endblock %} + diff --git a/sapl/templates/materia/proposicao_list.html b/sapl/templates/materia/proposicao_list.html new file mode 100644 index 000000000..afd407763 --- /dev/null +++ b/sapl/templates/materia/proposicao_list.html @@ -0,0 +1,12 @@ +{% extends "crud/list.html" %} +{% load i18n %} +{% block base_content %} +{% block sub_actions %} + +{% endblock sub_actions %} + {{ block.super }} +{% endblock %} \ No newline at end of file diff --git a/sapl/templates/materia/subnav_prop.yaml b/sapl/templates/materia/subnav_prop.yaml index facfe876f..1ace1b185 100644 --- a/sapl/templates/materia/subnav_prop.yaml +++ b/sapl/templates/materia/subnav_prop.yaml @@ -7,3 +7,5 @@ url: proposicao-devolvida - title: {% trans 'Proposições Incorporadas' %} url: proposicao-recebida +- title: {% trans 'Histórico Proposições' %} + url: historico-proposicao \ No newline at end of file