From ad9c8a9cdb3c0f7db1b9585e2ac42c6059411b1b Mon Sep 17 00:00:00 2001 From: LeandroJatai Date: Fri, 5 Aug 2022 14:00:04 -0300 Subject: [PATCH] impl crud de vinculodocadminmateria --- sapl/crispy_layout_mixin.py | 2 +- sapl/crud/base.py | 11 +- sapl/protocoloadm/forms.py | 79 +++++++- .../migrations/0042_auto_20220805_1236.py | 23 +++ sapl/protocoloadm/models.py | 13 ++ sapl/protocoloadm/urls.py | 10 +- sapl/protocoloadm/views.py | 186 +++++++++++++++++- .../materia/materialegislativa_detail.html | 3 +- .../documentoadministrativo_detail.html | 1 + sapl/templates/protocoloadm/layouts.yaml | 16 +- sapl/templates/protocoloadm/subnav.yaml | 2 + .../vinculodocadminmateria_form.html | 38 ++++ 12 files changed, 364 insertions(+), 20 deletions(-) create mode 100644 sapl/protocoloadm/migrations/0042_auto_20220805_1236.py create mode 100644 sapl/templates/protocoloadm/vinculodocadminmateria_form.html diff --git a/sapl/crispy_layout_mixin.py b/sapl/crispy_layout_mixin.py index ea92ce09f..0f85b669f 100644 --- a/sapl/crispy_layout_mixin.py +++ b/sapl/crispy_layout_mixin.py @@ -170,7 +170,7 @@ def get_field_display(obj, fieldname): display = '
{}
'.format(display) else: display = str(value) - return verbose_name, display + return verbose_name, display or ' ' class CrispyLayoutFormMixin: diff --git a/sapl/crud/base.py b/sapl/crud/base.py index 5b0aab24c..435bf06a6 100644 --- a/sapl/crud/base.py +++ b/sapl/crud/base.py @@ -1119,12 +1119,15 @@ class MasterDetailCrud(Crud): root_pk = self.kwargs['pk'] if 'pkk' not in self.request.GET\ else self.request.GET['pkk'] kwargs.setdefault('root_pk', root_pk) - context = super(CrudBaseMixin, self).get_context_data(**kwargs) - if parent_object: - context['title'] = '%s (%s)' % ( - self.object, parent_object) + title = '%s (%s)' % ( + self.object, + parent_object + ) if parent_object else '' + context = super(CrudBaseMixin, self).get_context_data(**kwargs) + if 'title' not in context and title: + context['title'] = title return context class ListView(Crud.ListView): diff --git a/sapl/protocoloadm/forms.py b/sapl/protocoloadm/forms.py index 1d70d3a31..601d430ab 100644 --- a/sapl/protocoloadm/forms.py +++ b/sapl/protocoloadm/forms.py @@ -20,6 +20,7 @@ from sapl.crispy_layout_mixin import (form_actions, SaplFormHelper, from sapl.materia.models import (MateriaLegislativa, TipoMateriaLegislativa, UnidadeTramitacao) +from sapl.protocoloadm.models import VinculoDocAdminMateria from sapl.utils import (AnoNumeroOrderingFilter, autor_label, autor_modal, choice_anos_com_documentoadministrativo, choice_anos_com_materias, timing, @@ -793,7 +794,7 @@ class TramitacaoAdmForm(ModelForm): ip=tramitacao.ip, ultima_edicao=tramitacao.ultima_edicao )) - ## TODO: BULK UPDATE não envia Signal para Tramitacao + # TODO: BULK UPDATE não envia Signal para Tramitacao TramitacaoAdministrativo.objects.bulk_create(lista_tramitacao) return tramitacao @@ -915,7 +916,7 @@ class TramitacaoAdmEditForm(TramitacaoAdmForm): da.tramitacao = False if nova_tram_principal.status.indicador == "F" else True da.save() - ## TODO: refatorar? + # TODO: refatorar? return nova_tram_principal @@ -1694,7 +1695,7 @@ class TramitacaoEmLoteAdmForm(ModelForm): ip=tramitacao.ip, ultima_edicao=tramitacao.ultima_edicao )) - ## TODO: BULK UPDATE não envia Signal para Tramitacao + # TODO: BULK UPDATE não envia Signal para Tramitacao TramitacaoAdministrativo.objects.bulk_create(lista_tramitacao) return tramitacao @@ -1732,3 +1733,75 @@ class TramitacaoEmLoteAdmFilterSet(django_filters.FilterSet): self.form.helper.layout = Layout( Fieldset(_('Tramitação em Lote'), row1, row2, form_actions(label=_('Pesquisar')))) + + +class VinculoDocAdminMateriaForm(ModelForm): + + logger = logging.getLogger(__name__) + + tipo = forms.ModelChoiceField( + label='Tipo', + required=True, + queryset=TipoMateriaLegislativa.objects.all(), + empty_label='Selecione', + ) + + numero = forms.IntegerField(label='Número', required=True) + + ano = forms.CharField(label='Ano', required=True) + + class Meta: + model = VinculoDocAdminMateria + fields = ['tipo', 'numero', 'ano', 'data_anexacao', 'data_desanexacao'] + + def __init__(self, *args, **kwargs): + return super().__init__(*args, **kwargs) + + def clean(self): + super().clean() + + if not self.is_valid(): + return self.cleaned_data + + cleaned_data = self.cleaned_data + + data_anexacao = cleaned_data['data_anexacao'] + data_desanexacao = cleaned_data['data_desanexacao'] if cleaned_data['data_desanexacao'] else data_anexacao + + if data_anexacao > data_desanexacao: + self.logger.error( + "Data de anexação posterior à data de desanexação.") + raise ValidationError( + _("Data de anexação posterior à data de desanexação.")) + + try: + self.logger.info("Tentando obter objeto MateriaLegislativa (numero={}, ano={}, tipo={})." + .format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo'])) + materia = MateriaLegislativa.objects.get( + numero=cleaned_data['numero'], + ano=cleaned_data['ano'], + tipo=cleaned_data['tipo']) + except ObjectDoesNotExist: + msg = _('A {} {}/{} não existe no cadastro de matérias legislativas.' + .format(cleaned_data['tipo'], cleaned_data['numero'], cleaned_data['ano'])) + self.logger.warning( + "A matéria a ser anexada não existe no cadastro de matérias legislativas.") + raise ValidationError(msg) + + if VinculoDocAdminMateria.objects.filter( + documento=self.instance.documento, materia=materia + ).exclude(pk=self.instance.pk).exists(): + self.logger.error( + "Matéria já se encontra vinculada a este documento.") + raise ValidationError( + _('Matéria já se encontra vinculada a este documento')) + + cleaned_data['materia'] = materia + + return cleaned_data + + def save(self, commit=False): + anexada = super().save(commit) + anexada.materia = self.cleaned_data['materia'] + anexada.save() + return anexada diff --git a/sapl/protocoloadm/migrations/0042_auto_20220805_1236.py b/sapl/protocoloadm/migrations/0042_auto_20220805_1236.py new file mode 100644 index 000000000..cc667daba --- /dev/null +++ b/sapl/protocoloadm/migrations/0042_auto_20220805_1236.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.28 on 2022-08-05 15:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('materia', '0081_auto_20220321_0934'), + ('protocoloadm', '0041_auto_20220804_2239'), + ] + + operations = [ + migrations.AlterField( + model_name='documentoadministrativo', + name='materiasvinculadas', + field=models.ManyToManyField(blank=True, related_name='docadmvinculados', through='protocoloadm.VinculoDocAdminMateria', to='materia.MateriaLegislativa'), + ), + migrations.AlterUniqueTogether( + name='vinculodocadminmateria', + unique_together={('documento', 'materia')}, + ), + ] diff --git a/sapl/protocoloadm/models.py b/sapl/protocoloadm/models.py index c35c4e828..82f899465 100644 --- a/sapl/protocoloadm/models.py +++ b/sapl/protocoloadm/models.py @@ -255,6 +255,14 @@ class DocumentoAdministrativo(models.Model): 'tipo': self.tipo, 'assunto': self.assunto } + @property + def epigrafe(self): + return _('%(tipo)s - %(numero)s/%(ano)s') % { + 'tipo': self.tipo, + 'numero': self.numero, + 'ano': self.ano + } + def delete(self, using=None, keep_parents=False): texto_integral = self.texto_integral result = super().delete(using=using, keep_parents=keep_parents) @@ -479,6 +487,11 @@ class VinculoDocAdminMateria(models.Model): def __str__(self): return f'Vinculo: {self.documento} - {self.materia}' + return _('Vinculo %(documento)s // %(materia)s') % { + 'documento': self.documento.epigrafe, + 'materia': self.materia + } + @reversion.register() class AcompanhamentoDocumento(models.Model): diff --git a/sapl/protocoloadm/urls.py b/sapl/protocoloadm/urls.py index 2e3e07ac1..d40f83192 100644 --- a/sapl/protocoloadm/urls.py +++ b/sapl/protocoloadm/urls.py @@ -25,7 +25,8 @@ from sapl.protocoloadm.views import (AcompanhamentoDocumentoView, AnexadoCrud, DocumentoAnexadoEmLoteView, PrimeiraTramitacaoEmLoteAdmView, TramitacaoEmLoteAdmView, - apaga_protocolos_view) + apaga_protocolos_view, + VinculoDocAdminMateriaCrud) from .apps import AppConfig @@ -34,16 +35,17 @@ app_name = AppConfig.name urlpatterns_documento_administrativo = [ url(r'^docadm/', include(DocumentoAdministrativoCrud.get_urls() + - AnexadoCrud.get_urls() + + AnexadoCrud.get_urls() + TramitacaoAdmCrud.get_urls() + - DocumentoAcessorioAdministrativoCrud.get_urls())), + DocumentoAcessorioAdministrativoCrud.get_urls() + + VinculoDocAdminMateriaCrud.get_urls())), url(r'^docadm/pesq-doc-adm', PesquisarDocumentoAdministrativoView.as_view(), name='pesq_doc_adm'), url(r'^docadm/texto_integral/(?P\d+)$', doc_texto_integral, name='doc_texto_integral'), - + url(r'^docadm/(?P\d+)/anexado_em_lote', DocumentoAnexadoEmLoteView.as_view(), name='anexado_em_lote'), ] diff --git a/sapl/protocoloadm/views.py b/sapl/protocoloadm/views.py index 01e525750..1d7c59cc2 100755 --- a/sapl/protocoloadm/views.py +++ b/sapl/protocoloadm/views.py @@ -35,7 +35,9 @@ from sapl.crud.base import (Crud, CrudAux, MasterDetailCrud, make_pagination, from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa, UnidadeTramitacao from sapl.materia.views import gerar_pdf_impressos from sapl.parlamentares.models import Legislatura, Parlamentar -from sapl.protocoloadm.models import Protocolo, DocumentoAdministrativo +from sapl.protocoloadm.forms import VinculoDocAdminMateriaForm +from sapl.protocoloadm.models import Protocolo, DocumentoAdministrativo,\ + VinculoDocAdminMateria from sapl.relatorios.views import relatorio_doc_administrativos from sapl.utils import (create_barcode, get_base_url, get_client_ip, get_mime_type_from_file_extension, lista_anexados, @@ -1126,7 +1128,7 @@ class DocumentoAnexadoEmLoteView(PermissionRequiredMixin, FilterView): context['object_list'] = [] else: context['temp_object_list'] = context['object_list'].order_by( - 'numero', '-ano') + 'numero', '-ano') context['object_list'] = [] for obj in context['temp_object_list']: if not obj.pk == int(context['root_pk']): @@ -1155,7 +1157,6 @@ class DocumentoAnexadoEmLoteView(PermissionRequiredMixin, FilterView): if not ciclico: context['object_list'].append(obj) - context['numero_res'] = len(context['object_list']) @@ -1300,7 +1301,6 @@ class TramitacaoAdmCrud(MasterDetailCrud): return initial - class ListView(DocumentoAdministrativoMixin, MasterDetailCrud.ListView): def get_queryset(self): @@ -1751,3 +1751,181 @@ def apaga_protocolos_view(request): return JsonResponse({'type': 'success', 'msg': ''}) else: return JsonResponse({'type': 'error', 'msg': 'Senha Incorreta'}) + + +class VinculoDocAdminMateriaCrud(MasterDetailCrud): + model = VinculoDocAdminMateria + parent_field = 'documento' + help_topic = 'vinculodocadminmateria' + public = [RP_LIST, RP_DETAIL] + + class BaseMixin(MasterDetailCrud.BaseMixin): + list_field_names = ['data_anexacao', ('materia', 'materia__ementa')] + + @property + def verbose_name(self): + return _('Vinculo') + + @property + def verbose_name_plural(self): + return _('Vinculos') + + @property + def title(self): + return self.object.documento.epigrafe + + class CreateView(MasterDetailCrud.CreateView): + form_class = VinculoDocAdminMateriaForm + + class UpdateView(MasterDetailCrud.UpdateView): + form_class = VinculoDocAdminMateriaForm + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['title'] = self.object.documento.epigrafe + return context + + def get_initial(self): + initial = super(UpdateView, self).get_initial() + initial['tipo'] = self.object.materia.tipo.id + initial['numero'] = self.object.materia.numero + initial['ano'] = self.object.materia.ano + return initial + + class DetailView(MasterDetailCrud.DetailView): + + @property + def layout_key(self): + return 'VinculoDocAdminMateriaDetail' + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['title'] = self.object.documento.epigrafe + return context + + +""" + +class VinculoDocAdminMateriaEmLoteView(PermissionRequiredMixin, FilterView): + filterset_class = AnexadaEmLoteFilterSet + template_name = 'materia/em_lote/anexada.html' + permission_required = ('materia.add_documentoacessorio',) + + def get_context_data(self, **kwargs): + context = super(MateriaAnexadaEmLoteView, + self).get_context_data(**kwargs) + + context['root_pk'] = self.kwargs['pk'] + + context['subnav_template_name'] = 'materia/subnav.yaml' + + context['title'] = _('Matérias Anexadas em Lote') + + # Verifica se os campos foram preenchidos + if not self.request.GET.get('tipo', " "): + msg = _('Por favor, selecione um tipo de matéria.') + messages.add_message(self.request, messages.ERROR, msg) + + if not self.request.GET.get('data_apresentacao_0', " ") or not self.request.GET.get('data_apresentacao_1', " "): + msg = _('Por favor, preencha as datas.') + messages.add_message(self.request, messages.ERROR, msg) + + return context + + if not self.request.GET.get('data_apresentacao_0', " ") or not self.request.GET.get('data_apresentacao_1', " "): + msg = _('Por favor, preencha as datas.') + messages.add_message(self.request, messages.ERROR, msg) + return context + + qr = self.request.GET.copy() + if not len(qr): + context['object_list'] = [] + else: + context['object_list'] = context['object_list'].order_by( + 'numero', '-ano') + principal = MateriaLegislativa.objects.get(pk=self.kwargs['pk']) + not_list = [self.kwargs['pk']] + \ + [m for m in principal.materia_principal_set.all( + ).values_list('materia_anexada_id', flat=True)] + context['object_list'] = context['object_list'].exclude( + pk__in=not_list) + + context['temp_object_list'] = context['object_list'] + context['object_list'] = [] + for obj in context['temp_object_list']: + materia_anexada = obj + ciclico = False + anexadas_anexada = Anexada.objects.filter( + materia_principal=materia_anexada + ) + + while anexadas_anexada and not ciclico: + anexadas = [] + + for anexa in anexadas_anexada: + + if principal == anexa.materia_anexada: + ciclico = True + else: + for a in Anexada.objects.filter(materia_principal=anexa.materia_anexada): + anexadas.append(a) + + anexadas_anexada = anexadas + + if not ciclico: + context['object_list'].append(obj) + + context['numero_res'] = len(context['object_list']) + + context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else '' + + context['show_results'] = show_results_filter_set(qr) + + return context + + def post(self, request, *args, **kwargs): + marcadas = request.POST.getlist('materia_id') + + data_anexacao = datetime.strptime( + request.POST['data_anexacao'], "%d/%m/%Y").date() + + if request.POST['data_desanexacao'] == '': + data_desanexacao = None + v_data_desanexacao = data_anexacao + else: + data_desanexacao = datetime.strptime( + request.POST['data_desanexacao'], "%d/%m/%Y").date() + v_data_desanexacao = data_desanexacao + + if len(marcadas) == 0: + msg = _('Nenhuma máteria foi selecionada.') + messages.add_message(request, messages.ERROR, msg) + + if data_anexacao > v_data_desanexacao: + msg = _('Data de anexação posterior à data de desanexação.') + messages.add_message(request, messages.ERROR, msg) + + return self.get(request, self.kwargs) + + if data_anexacao > v_data_desanexacao: + msg = _('Data de anexação posterior à data de desanexação.') + messages.add_message(request, messages.ERROR, msg) + return self.get(request, self.kwargs) + + principal = MateriaLegislativa.objects.get(pk=kwargs['pk']) + for materia in MateriaLegislativa.objects.filter(id__in=marcadas): + + anexada = Anexada() + anexada.materia_principal = principal + anexada.materia_anexada = materia + anexada.data_anexacao = data_anexacao + anexada.data_desanexacao = data_desanexacao + anexada.save() + + msg = _('Matéria(s) anexada(s).') + messages.add_message(request, messages.SUCCESS, msg) + + success_url = reverse('sapl.materia:anexada_list', + kwargs={'pk': kwargs['pk']}) + return HttpResponseRedirect(success_url) +""" diff --git a/sapl/templates/materia/materialegislativa_detail.html b/sapl/templates/materia/materialegislativa_detail.html index 13c7daae9..3b736318f 100644 --- a/sapl/templates/materia/materialegislativa_detail.html +++ b/sapl/templates/materia/materialegislativa_detail.html @@ -61,8 +61,9 @@ Data Anexação: {{vinculodocadmmateria.data_anexacao}} {% if vinculodocadmmateria.data_desanexacao %} - {{vinculodocadmmateria.data_desanexacao}}{% endif %}
Documento: - {{ vinculodocadmmateria.documento }} + {{ vinculodocadmmateria.documento.epigrafe }} +
{{ vinculodocadmmateria.documento.assunto}} {% if vinculodocadmmateria.documento.restrito %} (Documento Restrito) diff --git a/sapl/templates/protocoloadm/documentoadministrativo_detail.html b/sapl/templates/protocoloadm/documentoadministrativo_detail.html index 182c748a0..2ab80b885 100644 --- a/sapl/templates/protocoloadm/documentoadministrativo_detail.html +++ b/sapl/templates/protocoloadm/documentoadministrativo_detail.html @@ -17,6 +17,7 @@ {{ vinculodocadmmateria.materia }} +
{{vinculodocadmmateria.materia.ementa}} {% if not forloop.last %}
{% endif %} {% endfor %} diff --git a/sapl/templates/protocoloadm/layouts.yaml b/sapl/templates/protocoloadm/layouts.yaml index 7bf96372f..c223f686a 100644 --- a/sapl/templates/protocoloadm/layouts.yaml +++ b/sapl/templates/protocoloadm/layouts.yaml @@ -5,9 +5,9 @@ TipoDocumentoAdministrativo: DocumentoAdministrativo: {% trans 'Identificação Básica' %}: - - tipo + - tipo - numero complemento ano - - data protocolo + - data protocolo - assunto - interessado autor tramitacao - texto_integral|urlize @@ -33,7 +33,7 @@ TramitacaoAdministrativo: {% trans 'Tramitação' %}: - data_tramitacao unidade_tramitacao_local - unidade_tramitacao_destino data_encaminhamento data_fim_prazo - - status urgente + - status urgente - texto Anexado: @@ -62,3 +62,13 @@ Protocolo: - assunto_ementa - autor - observacao + +VinculoDocAdminMateria: + {% trans 'Matéria Vinculada' %}: + - tipo numero ano + - data_anexacao data_desanexacao + +VinculoDocAdminMateriaDetail: + {% trans 'Matéria Vinculada' %}: + - materia|fk_urlize_for_detail data_anexacao:3 data_desanexacao:3 + - documento diff --git a/sapl/templates/protocoloadm/subnav.yaml b/sapl/templates/protocoloadm/subnav.yaml index 5e0c3021b..a431819fd 100644 --- a/sapl/templates/protocoloadm/subnav.yaml +++ b/sapl/templates/protocoloadm/subnav.yaml @@ -7,3 +7,5 @@ url: tramitacaoadministrativo_list - title: {% trans 'Documento Acessório' %} url: documentoacessorioadministrativo_list +- title: {% trans 'Matérias Vinculadas' %} + url: vinculodocadminmateria_list diff --git a/sapl/templates/protocoloadm/vinculodocadminmateria_form.html b/sapl/templates/protocoloadm/vinculodocadminmateria_form.html new file mode 100644 index 000000000..f3864dba7 --- /dev/null +++ b/sapl/templates/protocoloadm/vinculodocadminmateria_form.html @@ -0,0 +1,38 @@ +{% extends "crud/form.html" %} +{% load i18n %} + +{% block extra_js %} + +{% endblock %}