diff --git a/sapl/norma/forms.py b/sapl/norma/forms.py index 5fa26f0fd..818d6ba38 100644 --- a/sapl/norma/forms.py +++ b/sapl/norma/forms.py @@ -5,17 +5,18 @@ from crispy_forms.layout import Fieldset, Layout from django import forms from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.db import models -from django.forms import ModelForm, widgets +from django.forms import ModelForm, widgets, ModelChoiceField from django.utils import timezone from django.utils.translation import ugettext_lazy as _ +from sapl.base.models import Autor, TipoAutor from sapl.crispy_layout_mixin import form_actions, to_row from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa from sapl.settings import MAX_DOC_UPLOAD_SIZE from sapl.utils import NormaPesquisaOrderingFilter, RANGE_ANOS, RangeWidgetOverride from .models import (AnexoNormaJuridica, AssuntoNorma, NormaJuridica, NormaRelacionada, - TipoNormaJuridica) + TipoNormaJuridica, AutoriaNorma) def ANO_CHOICES(): @@ -191,6 +192,51 @@ class NormaJuridicaForm(ModelForm): return norma +class AutoriaNormaForm(ModelForm): + + tipo_autor = ModelChoiceField(label=_('Tipo Autor'), + required=False, + queryset=TipoAutor.objects.all(), + empty_label=_('Selecione'),) + + data_relativa = forms.DateField( + widget=forms.HiddenInput(), required=False) + + def __init__(self, *args, **kwargs): + super(AutoriaNormaForm, self).__init__(*args, **kwargs) + + row1 = to_row([('tipo_autor', 4), + ('autor', 4), + ('primeiro_autor', 4)]) + + self.helper = FormHelper() + self.helper.layout = Layout( + Fieldset(_('Autoria'), + row1, 'data_relativa', form_actions(label='Salvar'))) + + if not kwargs['instance']: + self.fields['autor'].choices = [] + + class Meta: + model = AutoriaNorma + fields = ['tipo_autor', 'autor', 'primeiro_autor', 'data_relativa'] + + def clean(self): + cd = super(AutoriaNormaForm, self).clean() + + if not self.is_valid(): + return self.cleaned_data + + autorias = AutoriaNorma.objects.filter( + norma=self.instance.norma, autor=cd['autor']) + pk = self.instance.pk + + if ((not pk and autorias.exists()) or + (pk and autorias.exclude(pk=pk).exists())): + raise ValidationError(_('Esse Autor já foi cadastrado.')) + + return cd + class AnexoNormaJuridicaForm(ModelForm): class Meta: model = AnexoNormaJuridica diff --git a/sapl/norma/migrations/0014_auto_20181008_1655.py b/sapl/norma/migrations/0014_auto_20181008_1655.py new file mode 100644 index 000000000..da9bf4add --- /dev/null +++ b/sapl/norma/migrations/0014_auto_20181008_1655.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2018-10-08 19:55 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0021_appconfig_esfera_federacao'), + ('norma', '0013_anexonormajuridica_assunto_anexo'), + ] + + operations = [ + migrations.CreateModel( + name='AutoriaNorma', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('primeiro_autor', models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Primeiro Autor')), + ('autor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.Autor', verbose_name='Autor')), + ('norma', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='norma.NormaJuridica', verbose_name='Matéria Legislativa')), + ], + options={ + 'ordering': ('-primeiro_autor', 'autor__nome'), + 'verbose_name': 'Autoria', + 'verbose_name_plural': 'Autorias', + }, + ), + migrations.AddField( + model_name='normajuridica', + name='autores', + field=models.ManyToManyField(through='norma.AutoriaNorma', to='base.Autor'), + ), + migrations.AlterUniqueTogether( + name='autorianorma', + unique_together=set([('autor', 'norma')]), + ), + ] diff --git a/sapl/norma/models.py b/sapl/norma/models.py index e4e3677b3..46f34a0ef 100644 --- a/sapl/norma/models.py +++ b/sapl/norma/models.py @@ -5,6 +5,7 @@ from django.utils.translation import ugettext_lazy as _ from model_utils import Choices import reversion +from sapl.base.models import Autor from sapl.compilacao.models import TextoArticulado from sapl.materia.models import MateriaLegislativa from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, @@ -129,6 +130,12 @@ class NormaJuridica(models.Model): auto_now=True, verbose_name=_('Data')) + autores = models.ManyToManyField( + Autor, + through='AutoriaNorma', + through_fields=('norma', 'autor'), + symmetrical=False) + class Meta: verbose_name = _('Norma Jurídica') verbose_name_plural = _('Normas Jurídicas') @@ -184,6 +191,28 @@ class NormaJuridica(models.Model): update_fields=update_fields) +@reversion.register() +class AutoriaNorma(models.Model): + autor = models.ForeignKey(Autor, + verbose_name=_('Autor'), + on_delete=models.CASCADE) + norma = models.ForeignKey( + NormaJuridica, on_delete=models.CASCADE, + verbose_name=_('Matéria Legislativa')) + primeiro_autor = models.BooleanField(verbose_name=_('Primeiro Autor'), + choices=YES_NO_CHOICES, + default=False) + + class Meta: + verbose_name = _('Autoria') + verbose_name_plural = _('Autorias') + unique_together = (('autor', 'norma'), ) + ordering = ('-primeiro_autor', 'autor__nome') + + def __str__(self): + return _('Autoria: %(autor)s - %(norma)s') % { + 'autor': self.autor, 'norma': self.norma} + @reversion.register() class LegislacaoCitada(models.Model): materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE) diff --git a/sapl/norma/urls.py b/sapl/norma/urls.py index d943f71e8..800707915 100644 --- a/sapl/norma/urls.py +++ b/sapl/norma/urls.py @@ -3,7 +3,7 @@ from django.conf.urls import include, url from sapl.norma.views import (AnexoNormaJuridicaCrud,AssuntoNormaCrud, NormaCrud, NormaPesquisaView, NormaRelacionadaCrud, NormaTaView, TipoNormaCrud, TipoVinculoNormaJuridicaCrud, recuperar_norma, - recuperar_numero_norma) + recuperar_numero_norma, AutoriaNormaCrud) from .apps import AppConfig @@ -13,7 +13,8 @@ app_name = AppConfig.name urlpatterns = [ url(r'^norma/', include(NormaCrud.get_urls() + NormaRelacionadaCrud.get_urls() + - AnexoNormaJuridicaCrud.get_urls())), + AnexoNormaJuridicaCrud.get_urls() + + AutoriaNormaCrud.get_urls())), # Integração com Compilação url(r'^norma/(?P[0-9]+)/ta$', NormaTaView.as_view(), name='norma_ta'), diff --git a/sapl/norma/views.py b/sapl/norma/views.py index beae10d4d..b91aec2a5 100644 --- a/sapl/norma/views.py +++ b/sapl/norma/views.py @@ -21,9 +21,9 @@ from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux, from sapl.utils import show_results_filter_set from .forms import (AnexoNormaJuridicaForm, NormaFilterSet, NormaJuridicaForm, - NormaPesquisaSimplesForm, NormaRelacionadaForm) + NormaPesquisaSimplesForm, NormaRelacionadaForm, AutoriaNormaForm) from .models import (AnexoNormaJuridica, AssuntoNorma, NormaJuridica, NormaRelacionada, - TipoNormaJuridica, TipoVinculoNormaJuridica) + TipoNormaJuridica, TipoVinculoNormaJuridica, AutoriaNorma) # LegislacaoCitadaCrud = Crud.build(LegislacaoCitada, '') @@ -274,6 +274,39 @@ def recuperar_numero_norma(request): return response +class AutoriaNormaCrud(MasterDetailCrud): + model = AutoriaNorma + parent_field = 'norma' + help_topic = 'despacho_autoria' + public = [RP_LIST, RP_DETAIL] + list_field_names = ['autor', 'autor__tipo__descricao', 'primeiro_autor'] + + class LocalBaseMixin(): + form_class = AutoriaNormaForm + + @property + def layout_key(self): + return None + + class CreateView(LocalBaseMixin, MasterDetailCrud.CreateView): + + def get_initial(self): + initial = super().get_initial() + norma = NormaJuridica.objects.get(id=self.kwargs['pk']) + initial['data_relativa'] = norma.data + initial['autor'] = [] + return initial + + class UpdateView(LocalBaseMixin, MasterDetailCrud.UpdateView): + + def get_initial(self): + initial = super().get_initial() + initial.update({ + 'data_relativa': self.object.norma.data_apresentacao, + 'tipo_autor': self.object.autor.tipo.id, + }) + return initial + class ImpressosView(PermissionRequiredMixin, TemplateView): template_name = 'materia/impressos/impressos.html' permission_required = ('materia.can_access_impressos', ) diff --git a/sapl/rules/map_rules.py b/sapl/rules/map_rules.py index bba114149..e4ea00f2c 100644 --- a/sapl/rules/map_rules.py +++ b/sapl/rules/map_rules.py @@ -116,6 +116,7 @@ rules_group_materia = { (materia.Numeracao, __base__), (materia.Tramitacao, __base__), (norma.LegislacaoCitada, __base__), + (norma.AutoriaNorma, __base__), (compilacao.Dispositivo, __base__ + [ 'change_dispositivo_edicao_dinamica', @@ -136,6 +137,7 @@ rules_group_norma = { (norma.NormaJuridica, __base__), (norma.NormaRelacionada, __base__), (norma.AnexoNormaJuridica, __base__), + (norma.AutoriaNorma, __base__), # Publicacao está com permissão apenas para norma e não para matéria # e proposições apenas por análise do contexto, não é uma limitação diff --git a/sapl/templates/norma/autorianorma_form.html b/sapl/templates/norma/autorianorma_form.html new file mode 100644 index 000000000..35338c9de --- /dev/null +++ b/sapl/templates/norma/autorianorma_form.html @@ -0,0 +1,47 @@ +{% extends "crud/form.html" %} +{% load i18n %} +{% load crispy_forms_tags %} +{% load common_tags %} + +{% block extra_js %} + +{% endblock %} \ No newline at end of file diff --git a/sapl/templates/norma/layouts.yaml b/sapl/templates/norma/layouts.yaml index 0075317c1..d881ae186 100644 --- a/sapl/templates/norma/layouts.yaml +++ b/sapl/templates/norma/layouts.yaml @@ -70,3 +70,7 @@ NormaRelacionadaDetail: {% trans 'Norma Relacionada' %}: - norma_relacionada - tipo_vinculo + +AutoriaNorma: + {% trans 'Autoria' %}: + - autor primeiro_autor \ No newline at end of file diff --git a/sapl/templates/norma/subnav.yaml b/sapl/templates/norma/subnav.yaml index 9358787ff..f85433e72 100644 --- a/sapl/templates/norma/subnav.yaml +++ b/sapl/templates/norma/subnav.yaml @@ -7,7 +7,8 @@ check_permission: norma.list_normarelacionada - title: {% trans 'Anexos da Norma' %} url: anexonormajuridica_list - +- title: {% trans 'Autoria' %} + url: autorianorma_list # Opção adicionada para chamar o TextoArticulado da norma. # para integração foram necessárias apenas criar a url norma_ta em urls.py # e a view NormaTaView(IntegracaoTaView) em views.py