diff --git a/.travis.yml b/.travis.yml index d3ba4e10e..d02e929c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ language: python python: - 3.4.3 - services: - postgresql @@ -20,4 +19,4 @@ script: - ./manage.py migrate - ./manage.py bower install - py.test - # - ./test_and_check_qa.sh \ No newline at end of file + # - ./test_and_check_qa.sh diff --git a/sapl/base/templatetags/common_tags.py b/sapl/base/templatetags/common_tags.py index dc38f1014..0c589e847 100644 --- a/sapl/base/templatetags/common_tags.py +++ b/sapl/base/templatetags/common_tags.py @@ -1,6 +1,8 @@ from compressor.utils import get_class from django import template +from sapl.parlamentares.models import Filiacao + register = template.Library() @@ -36,3 +38,70 @@ def lookup(d, key): def isinst(value, class_str): classe = value.__class__.__name__ return classe == class_str + + +@register.filter +def get_add_perm(value, arg): + perm = value + view = arg + + try: + nome_app = view.__class__.model._meta.app_label + except AttributeError: + return None + nome_model = view.__class__.model.__name__.lower() + can_add = '.add_' + nome_model + + return perm.__contains__(nome_app + can_add) + + +@register.filter +def get_change_perm(value, arg): + perm = value + view = arg + + try: + nome_app = view.__class__.model._meta.app_label + except AttributeError: + return None + nome_model = view.__class__.model.__name__.lower() + can_change = '.change_' + nome_model + + return perm.__contains__(nome_app + can_change) + + +@register.filter +def get_delete_perm(value, arg): + perm = value + view = arg + + try: + nome_app = view.__class__.model._meta.app_label + except AttributeError: + return None + nome_model = view.__class__.model.__name__.lower() + can_delete = '.delete_' + nome_model + + return perm.__contains__(nome_app + can_delete) + + +@register.filter +def ver_menu_sistema_perm(value): + u = value + if u.groups.filter(name='Operador Geral').exists() or u.is_superuser: + return True + else: + return False + + +@register.filter +def ultima_filiacao(value): + parlamentar = value + + ultima_filiacao = Filiacao.objects.filter( + parlamentar=parlamentar).order_by('-data').first() + + if ultima_filiacao: + return ultima_filiacao.partido + else: + return None diff --git a/sapl/base/urls.py b/sapl/base/urls.py index 54e50feef..05b977ffc 100644 --- a/sapl/base/urls.py +++ b/sapl/base/urls.py @@ -4,7 +4,7 @@ from django.views.generic.base import TemplateView from .apps import AppConfig from .forms import LoginForm -from .views import CasaLegislativaCrud, HelpView +from .views import CasaLegislativaCrud, HelpView, SistemaView app_name = AppConfig.name @@ -21,5 +21,5 @@ urlpatterns = [ url(r'^login/$', views.login, { 'template_name': 'base/login.html', 'authentication_form': LoginForm}, name='login'), - url(r'^logout/$', views.logout, {'next_page': '/login'}, name='logout') + url(r'^logout/$', views.logout, {'next_page': '/login'}, name='logout'), ] diff --git a/sapl/base/views.py b/sapl/base/views.py index eef8f7f4b..c52b5b6fa 100644 --- a/sapl/base/views.py +++ b/sapl/base/views.py @@ -1,9 +1,11 @@ +from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect from django.views.generic.base import TemplateView from sapl.crud.base import (Crud, CrudBaseMixin, CrudCreateView, CrudDetailView, CrudUpdateView) +from sapl.utils import permissao_tb_aux from .forms import CasaLegislativaForm from .models import CasaLegislativa @@ -17,13 +19,21 @@ class CasaLegislativaCrud(Crud): model = CasaLegislativa help_path = '' - class BaseMixin(CrudBaseMixin): + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): list_field_names = ['codigo', 'nome', 'sigla'] - class CreateView(CrudCreateView): + def has_permission(self): + if self.request.user.is_superuser: + return True + else: + return False + + class CreateView(PermissionRequiredMixin, CrudCreateView): + permission_required = {'base.add_casa_legislativa'} form_class = CasaLegislativaForm - class UpdateView(CrudUpdateView): + class UpdateView(PermissionRequiredMixin, CrudUpdateView): + permission_required = {'base.change_casalegislativa'} form_class = CasaLegislativaForm class DetailView(CrudDetailView): @@ -35,8 +45,16 @@ class CasaLegislativaCrud(Crud): kwargs={'pk': self.kwargs['pk']})) -class HelpView(TemplateView): +class HelpView(PermissionRequiredMixin, TemplateView): # XXX treat non existing template as a 404!!!! def get_template_names(self): return ['ajuda/%s.html' % self.kwargs['topic']] + + +class SistemaView(PermissionRequiredMixin, TemplateView): + template_name = 'sistema.html' + permission_required = '' + + def has_permission(self): + return permissao_tb_aux(self) diff --git a/sapl/comissoes/tests/test_comissoes.py b/sapl/comissoes/tests/test_comissoes.py index af5e71da7..fee303192 100644 --- a/sapl/comissoes/tests/test_comissoes.py +++ b/sapl/comissoes/tests/test_comissoes.py @@ -44,14 +44,14 @@ def make_filiacao(): @pytest.mark.django_db(transaction=False) -def test_incluir_parlamentar_errors(client): +def test_incluir_parlamentar_errors(admin_client): comissao = make_comissao() composicao = make_composicao(comissao) - response = client.post(reverse('sapl.comissoes:participacao_create', - kwargs={'pk': composicao.pk}), - {'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.comissoes:participacao_create', + kwargs={'pk': composicao.pk}), + {'salvar': 'salvar'}, + follow=True) assert (response.context_data['form'].errors['parlamentar'] == ['Este campo é obrigatório.']) @@ -62,18 +62,18 @@ def test_incluir_parlamentar_errors(client): @pytest.mark.django_db(transaction=False) -def test_incluir_comissao_submit(client): +def test_incluir_comissao_submit(admin_client): tipo = mommy.make(TipoComissao, sigla='T', nome='Teste') - response = client.post(reverse('sapl.comissoes:comissao_create'), - {'tipo': tipo.pk, - 'nome': 'Comissão Teste', - 'sigla': 'CT', - 'data_criacao': '2016-03-22', - 'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.comissoes:comissao_create'), + {'tipo': tipo.pk, + 'nome': 'Comissão Teste', + 'sigla': 'CT', + 'data_criacao': '2016-03-22', + 'salvar': 'salvar'}, + follow=True) assert response.status_code == 200 comissao = Comissao.objects.first() @@ -82,11 +82,11 @@ def test_incluir_comissao_submit(client): @pytest.mark.django_db(transaction=False) -def test_incluir_comissao_errors(client): +def test_incluir_comissao_errors(admin_client): - response = client.post(reverse('sapl.comissoes:comissao_create'), - {'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.comissoes:comissao_create'), + {'salvar': 'salvar'}, + follow=True) assert (response.context_data['form'].errors['tipo'] == ['Este campo é obrigatório.']) diff --git a/sapl/comissoes/views.py b/sapl/comissoes/views.py index 4ca5bb768..d4333f63d 100644 --- a/sapl/comissoes/views.py +++ b/sapl/comissoes/views.py @@ -1,17 +1,16 @@ +from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.urlresolvers import reverse from django.views.generic import ListView -from sapl.crud.base import Crud, CrudBaseMixin +from sapl.crud.base import (Crud, CrudBaseMixin, CrudCreateView, + CrudDeleteView, CrudUpdateView) from sapl.crud.masterdetail import MasterDetailCrud from sapl.materia.models import Tramitacao +from sapl.utils import permissao_tb_aux, permissoes_comissoes from .models import (CargoComissao, Comissao, Composicao, Participacao, Periodo, TipoComissao) -CargoCrud = Crud.build(CargoComissao, 'cargo_comissao') -PeriodoComposicaoCrud = Crud.build(Periodo, 'periodo_composicao_comissao') -TipoComissaoCrud = Crud.build(TipoComissao, 'tipo_comissao') - def pegar_url_composicao(pk): participacao = Participacao.objects.get(id=pk) @@ -20,13 +19,46 @@ def pegar_url_composicao(pk): return url +class CargoCrud(Crud): + model = CargoComissao + help_path = 'cargo_comissao' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + list_field_names = ['nome', 'unico'] + + def has_permission(self): + return permissao_tb_aux(self) + + +class PeriodoComposicaoCrud(Crud): + model = Periodo + help_path = 'periodo_composicao_comissao' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + list_field_names = ['data_inicio', 'data_fim'] + + def has_permission(self): + return permissao_tb_aux(self) + + +class TipoComissaoCrud(Crud): + model = TipoComissao + help_path = 'tipo_comissao' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + list_field_names = ['sigla', 'nome', 'natureza', + 'dispositivo_regimental'] + + def has_permission(self): + return permissao_tb_aux(self) + + class ParticipacaoCrud(MasterDetailCrud): model = Participacao parent_field = 'composicao' help_path = '' class DetailView(MasterDetailCrud.DetailView): - def get(self, request, *args, **kwargs): self.object = self.get_object() context = self.get_context_data(object=self.object) @@ -63,6 +95,10 @@ class ParticipacaoCrud(MasterDetailCrud): def cancel_url(self): return pegar_url_composicao(self.kwargs['pk']) + class BaseMixin(PermissionRequiredMixin, MasterDetailCrud.BaseMixin): + permission_required = permissoes_comissoes() + list_field_names = ['composicao', 'parlamentar', 'cargo'] + class ComposicaoCrud(MasterDetailCrud): model = Composicao @@ -78,11 +114,29 @@ class ComposicaoCrud(MasterDetailCrud): context['participacoes'] = composicao.participacao_set.all() return self.render_to_response(context) + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): + permission_required = permissoes_comissoes() + + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): + permission_required = permissoes_comissoes() + + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_comissoes() + class ComissaoCrud(Crud): model = Comissao help_path = 'modulo_comissoes' + class CreateView(PermissionRequiredMixin, CrudCreateView): + permission_required = permissoes_comissoes() + + class UpdateView(PermissionRequiredMixin, CrudUpdateView): + permission_required = permissoes_comissoes() + + class DeleteView(PermissionRequiredMixin, CrudDeleteView): + permission_required = permissoes_comissoes() + class BaseMixin(CrudBaseMixin): list_field_names = ['nome', 'sigla', 'tipo', 'data_criacao', 'ativa'] diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index 89892ba18..054854e54 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -4,8 +4,10 @@ import django_filters from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout from django import forms +from django.contrib.auth.models import Group, User +from django.contrib.auth.password_validation import validate_password from django.core.exceptions import ObjectDoesNotExist, ValidationError -from django.db import models +from django.db import models, transaction from django.db.models import Max from django.forms import ModelForm from django.utils.translation import ugettext_lazy as _ @@ -136,7 +138,8 @@ class ProposicaoForm(ModelForm): class Meta: model = Proposicao - fields = ['tipo', 'data_envio', 'descricao', 'texto_original'] + fields = ['tipo', 'data_envio', 'descricao', 'texto_original', 'autor'] + widgets = {'autor': forms.HiddenInput()} class AcompanhamentoMateriaForm(ModelForm): @@ -580,3 +583,105 @@ class AutoriaForm(ModelForm): raise ValidationError(msg) return self.cleaned_data + + +class AutorForm(ModelForm): + senha = forms.CharField( + max_length=20, + label=_('Senha'), + required=True, + widget=forms.PasswordInput()) + + senha_confirma = forms.CharField( + max_length=20, + label=_('Confirmar Senha'), + required=True, + widget=forms.PasswordInput()) + + confirma_email = forms.EmailField( + required=True, + label=_('Confirmar Email')) + + class Meta: + model = Autor + fields = ['username', + 'senha', + 'email', + 'nome', + 'tipo', + 'cargo', + 'parlamentar', + 'comissao'] + + def valida_igualdade(self, texto1, texto2, msg): + if texto1 != texto2: + raise ValidationError(msg) + return True + + def valida_email_existente(self): + return User.objects.filter( + email=self.cleaned_data['email']).exists() + + def usuario_existente(self): + return User.objects.filter( + username=self.cleaned_data['username']).exists() + + def clean(self): + if ('senha' not in self.cleaned_data or + 'senha_confirma' not in self.cleaned_data): + raise ValidationError(_('Favor informar as senhas')) + + msg = _('As senhas não conferem.') + self.valida_igualdade( + self.cleaned_data['senha'], + self.cleaned_data['senha_confirma'], + msg) + + if ('email' not in self.cleaned_data or + 'confirma_email' not in self.cleaned_data): + raise ValidationError(_('Favor informar endereços de email')) + + msg = _('Os emails não conferem.') + self.valida_igualdade( + self.cleaned_data['email'], + self.cleaned_data['confirma_email'], + msg) + + email_existente = self.valida_email_existente() + + if email_existente: + msg = _('Este email já foi cadastrado.') + raise ValidationError(msg) + + if self.usuario_existente(): + msg = _('Este nome de usuario já foi cadastrado.') + raise ValidationError(msg) + + try: + validate_password(self.cleaned_data['senha']) + except ValidationError as error: + raise ValidationError(error) + + return self.cleaned_data + + @transaction.atomic + def save(self, commit=False): + + autor = super(AutorForm, self).save(commit) + + u = User.objects.get_or_create( + username=autor.username, + email=autor.email) + u = u[0] + u.set_password(self.cleaned_data['senha']) + u.is_active = False + u.save() + + autor.user = u + + autor.save() + + grupo = Group.objects.filter(name='Autor')[0] + u.groups.add(grupo) + + return autor diff --git a/sapl/materia/migrations/0039_auto_20160628_1251.py b/sapl/materia/migrations/0039_auto_20160628_1251.py new file mode 100644 index 000000000..a730729e2 --- /dev/null +++ b/sapl/materia/migrations/0039_auto_20160628_1251.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.5 on 2016-06-28 15:51 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import sapl.materia.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth', '0007_alter_validators_add_error_messages'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('materia', '0038_auto_20160612_1506'), + ] + + operations = [ + migrations.AddField( + model_name='autor', + name='email', + field=models.EmailField(default=' ', max_length=254, verbose_name='Email'), + preserve_default=False, + ), + migrations.AddField( + model_name='autor', + name='grupo_usuario', + field=models.ForeignKey(default=sapl.materia.models.grupo_autor, on_delete=django.db.models.deletion.CASCADE, to='auth.Group'), + ), + migrations.AddField( + model_name='autor', + name='user', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + ] diff --git a/sapl/protocoloadm/migrations/0013_merge.py b/sapl/materia/migrations/0044_merge.py similarity index 56% rename from sapl/protocoloadm/migrations/0013_merge.py rename to sapl/materia/migrations/0044_merge.py index af5341f83..43a6fdb36 100644 --- a/sapl/protocoloadm/migrations/0013_merge.py +++ b/sapl/materia/migrations/0044_merge.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.5 on 2016-05-19 17:03 +# Generated by Django 1.9.7 on 2016-08-22 20:18 from __future__ import unicode_literals from django.db import migrations @@ -8,8 +8,8 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('protocoloadm', '0012_tipoinstituicao'), - ('protocoloadm', '0012_auto_20160503_0926'), + ('materia', '0039_auto_20160628_1251'), + ('materia', '0043_auto_20160810_1738'), ] operations = [ diff --git a/sapl/materia/migrations/0045_auto_20160823_1658.py b/sapl/materia/migrations/0045_auto_20160823_1658.py new file mode 100644 index 000000000..6c0b8ddbf --- /dev/null +++ b/sapl/materia/migrations/0045_auto_20160823_1658.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-08-23 19:58 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('materia', '0044_merge'), + ] + + operations = [ + migrations.RemoveField( + model_name='autor', + name='grupo_usuario', + ), + migrations.AlterField( + model_name='autor', + name='username', + field=models.CharField(blank=True, max_length=50, verbose_name='Nome de Usuário'), + ), + ] diff --git a/sapl/materia/models.py b/sapl/materia/models.py index 2e56d6cad..c38d76c0a 100644 --- a/sapl/materia/models.py +++ b/sapl/materia/models.py @@ -1,3 +1,4 @@ +from django.contrib.auth.models import Group, User from django.db import models from django.utils.translation import ugettext_lazy as _ from model_utils import Choices @@ -8,6 +9,14 @@ from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, restringe_tipos_de_arquivo_txt, xstr) +def grupo_autor(): + try: + grupo = Group.objects.get(name='Autor') + except Group.DoesNotExist: + return None + return grupo.id + + class TipoMateriaLegislativa(models.Model): sigla = models.CharField(max_length=5, verbose_name=_('Sigla')) descricao = models.CharField(max_length=50, verbose_name=_('Descrição ')) @@ -199,6 +208,7 @@ class TipoAutor(models.Model): class Autor(models.Model): + user = models.ForeignKey(User) partido = models.ForeignKey(Partido, blank=True, null=True) comissao = models.ForeignKey(Comissao, blank=True, null=True) parlamentar = models.ForeignKey(Parlamentar, blank=True, null=True) @@ -206,18 +216,23 @@ class Autor(models.Model): nome = models.CharField( max_length=50, blank=True, verbose_name=_('Autor')) cargo = models.CharField(max_length=50, blank=True) - username = models.CharField(max_length=50, blank=True) + username = models.CharField( + max_length=50, + blank=True, + verbose_name=_('Nome de Usuário')) + email = models.EmailField( + verbose_name=_('Email')) class Meta: verbose_name = _('Autor') verbose_name_plural = _('Autores') def __str__(self): - if str(self.tipo) == 'Parlamentar': + if str(self.tipo) == 'Parlamentar' and self.parlamentar: return self.parlamentar.nome_parlamentar - elif str(self.tipo) == 'Comissao': + elif str(self.tipo) == 'Comissao' and self.comissao: return str(self.comissao) - elif str(self.tipo) == 'Partido': + elif str(self.tipo) == 'Partido' and self.partido: return str(self.partido) else: if str(self.cargo): diff --git a/sapl/materia/tests/test_materia.py b/sapl/materia/tests/test_materia.py index 6e82c51c5..b8ad79280 100644 --- a/sapl/materia/tests/test_materia.py +++ b/sapl/materia/tests/test_materia.py @@ -1,12 +1,16 @@ import pytest + +from django.contrib.auth.models import User +from django.core.files.uploadedfile import SimpleUploadedFile from django.core.urlresolvers import reverse -from model_mommy import mommy +from model_mommy import mommy from sapl.comissoes.models import Comissao, TipoComissao from sapl.materia.models import (Anexada, Autor, Autoria, DespachoInicial, DocumentoAcessorio, MateriaLegislativa, - Numeracao, RegimeTramitacao, StatusTramitacao, - TipoAutor, TipoDocumento, + Numeracao, Proposicao, + RegimeTramitacao, StatusTramitacao, + TipoAutor, TipoProposicao, TipoDocumento, TipoMateriaLegislativa, Tramitacao, UnidadeTramitacao) from sapl.norma.models import (LegislacaoCitada, NormaJuridica, @@ -80,7 +84,7 @@ def make_materia_principal(): @pytest.mark.django_db(transaction=False) -def test_materia_anexada_submit(client): +def test_materia_anexada_submit(admin_client): materia_principal = make_materia_principal() # Cria a matéria que será anexada @@ -99,14 +103,14 @@ def test_materia_anexada_submit(client): materia_anexada = MateriaLegislativa.objects.get(numero=32, ano=2004) # Testa POST - response = client.post(reverse('sapl.materia:anexada_create', - kwargs={'pk': materia_principal.pk}), - {'tipo': materia_anexada.tipo.pk, - 'numero': materia_anexada.numero, - 'ano': materia_anexada.ano, - 'data_anexacao': '2016-03-18', - 'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.materia:anexada_create', + kwargs={'pk': materia_principal.pk}), + {'tipo': materia_anexada.tipo.pk, + 'numero': materia_anexada.numero, + 'ano': materia_anexada.ano, + 'data_anexacao': '2016-03-18', + 'salvar': 'salvar'}, + follow=True) assert response.status_code == 200 # Verifica se a matéria foi anexada corretamente @@ -116,24 +120,26 @@ def test_materia_anexada_submit(client): @pytest.mark.django_db(transaction=False) -def test_autoria_submit(client): +def test_autoria_submit(admin_client): materia_principal = make_materia_principal() - # Cria um tipo de Autor tipo_autor = mommy.make(TipoAutor, descricao='Teste Tipo_Autor') # Cria um Autor - autor = mommy.make(Autor, tipo=tipo_autor, nome='Autor Teste') + autor = mommy.make( + Autor, + tipo=tipo_autor, + nome='Autor Teste') # Testa POST - response = client.post(reverse('sapl.materia:autoria_create', - kwargs={'pk': materia_principal.pk}), - {'autor': autor.pk, - 'primeiro_autor': True, - 'materia_id': materia_principal.pk, - 'partido': '', - 'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.materia:autoria_create', + kwargs={'pk': materia_principal.pk}), + {'autor': autor.pk, + 'primeiro_autor': True, + 'materia_id': materia_principal.pk, + 'partido': '', + 'salvar': 'salvar'}, + follow=True) assert response.status_code == 200 # Verifica se o autor foi realmente criado @@ -144,7 +150,7 @@ def test_autoria_submit(client): @pytest.mark.django_db(transaction=False) -def test_despacho_inicial_submit(client): +def test_despacho_inicial_submit(admin_client): materia_principal = make_materia_principal() # Cria uma comissão @@ -156,11 +162,11 @@ def test_despacho_inicial_submit(client): data_criacao='2016-03-18') # Testa POST - response = client.post(reverse('sapl.materia:despachoinicial_create', - kwargs={'pk': materia_principal.pk}), - {'comissao': comissao.pk, - 'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.materia:despachoinicial_create', + kwargs={'pk': materia_principal.pk}), + {'comissao': comissao.pk, + 'salvar': 'salvar'}, + follow=True) assert response.status_code == 200 # Verifica se o despacho foi criado @@ -170,19 +176,19 @@ def test_despacho_inicial_submit(client): @pytest.mark.django_db(transaction=False) -def test_numeracao_submit(client): +def test_numeracao_submit(admin_client): materia_principal = make_materia_principal() materia = make_materia_principal() # Testa POST - response = client.post(reverse('sapl.materia:numeracao_create', - kwargs={'pk': materia_principal.pk}), - {'tipo_materia': materia.tipo.pk, - 'numero_materia': materia.numero, - 'ano_materia': materia.ano, - 'data_materia': '2016-03-21', - 'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.materia:numeracao_create', + kwargs={'pk': materia_principal.pk}), + {'tipo_materia': materia.tipo.pk, + 'numero_materia': materia.numero, + 'ano_materia': materia.ano, + 'data_materia': '2016-03-21', + 'salvar': 'salvar'}, + follow=True) assert response.status_code == 200 @@ -193,29 +199,33 @@ def test_numeracao_submit(client): @pytest.mark.django_db(transaction=False) -def test_documento_acessorio_submit(client): +def test_documento_acessorio_submit(admin_client): materia_principal = make_materia_principal() # Cria um tipo de Autor tipo_autor = mommy.make(TipoAutor, descricao='Teste Tipo_Autor') # Cria um Autor - autor = mommy.make(Autor, tipo=tipo_autor, nome='Autor Teste') + autor = mommy.make( + Autor, + tipo=tipo_autor, + nome='Autor Teste') # Cria um tipo de documento tipo = mommy.make(TipoDocumento, descricao='Teste') # Testa POST - response = client.post(reverse('sapl.materia:documentoacessorio_create', - kwargs={'pk': materia_principal.pk}), - {'tipo': tipo.pk, - 'nome': 'teste_nome', - 'data_materia': '2016-03-21', - 'autor': autor, - 'ementa': 'teste_ementa', - 'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse( + 'sapl.materia:documentoacessorio_create', + kwargs={'pk': materia_principal.pk}), + {'tipo': tipo.pk, + 'nome': 'teste_nome', + 'data_materia': '2016-03-21', + 'autor': autor, + 'ementa': 'teste_ementa', + 'salvar': 'salvar'}, + follow=True) assert response.status_code == 200 @@ -227,19 +237,20 @@ def test_documento_acessorio_submit(client): @pytest.mark.django_db(transaction=False) -def test_legislacao_citada_submit(client): +def test_legislacao_citada_submit(admin_client): materia_principal = make_materia_principal() norma = make_norma() # Testa POST - response = client.post(reverse('sapl.materia:legislacaocitada_create', - kwargs={'pk': materia_principal.pk}), - {'tipo': norma.tipo.pk, - 'numero': norma.numero, - 'ano': norma.ano, - 'disposicao': 'disposicao', - 'salvar': 'salvar'}, - follow=True) + response = admin_client.post( + reverse('sapl.materia:legislacaocitada_create', + kwargs={'pk': materia_principal.pk}), + {'tipo': norma.tipo.pk, + 'numero': norma.numero, + 'ano': norma.ano, + 'disposicao': 'disposicao', + 'salvar': 'salvar'}, + follow=True) assert response.status_code == 200 @@ -249,7 +260,7 @@ def test_legislacao_citada_submit(client): @pytest.mark.django_db(transaction=False) -def test_tramitacao_submit(client): +def test_tramitacao_submit(admin_client): materia_principal = make_materia_principal() # Cria status para tramitação status_tramitacao = mommy.make(StatusTramitacao, @@ -257,7 +268,7 @@ def test_tramitacao_submit(client): sigla='ST', descricao='Status_Teste') # Testa POST - response = client.post( + response = admin_client.post( reverse('sapl.materia:tramitacao_create', kwargs={'pk': materia_principal.pk}), {'unidade_tramitacao_local': make_unidade_tramitacao( @@ -283,12 +294,12 @@ def test_tramitacao_submit(client): @pytest.mark.django_db(transaction=False) -def test_form_errors_anexada(client): +def test_form_errors_anexada(admin_client): materia_principal = make_materia_principal() - response = client.post(reverse('sapl.materia:anexada_create', - kwargs={'pk': materia_principal.pk}), - {'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.materia:anexada_create', + kwargs={'pk': materia_principal.pk}), + {'salvar': 'salvar'}, + follow=True) assert (response.context_data['form'].errors['tipo'] == ['Este campo é obrigatório.']) @@ -301,42 +312,43 @@ def test_form_errors_anexada(client): @pytest.mark.django_db(transaction=False) -def test_form_errors_autoria(client): +def test_form_errors_autoria(admin_client): materia_principal = make_materia_principal() - response = client.post(reverse('sapl.materia:autoria_create', - kwargs={'pk': materia_principal.pk}), - {'materia_id': materia_principal.pk, - 'partido': '', - 'autor': '', - 'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.materia:autoria_create', + kwargs={'pk': materia_principal.pk}), + {'materia_id': materia_principal.pk, + 'partido': '', + 'autor': '', + 'salvar': 'salvar'}, + follow=True) assert (response.context_data['form'].errors['autor'] == ['Este campo é obrigatório.']) @pytest.mark.django_db(transaction=False) -def test_form_errors_despacho_inicial(client): +def test_form_errors_despacho_inicial(admin_client): materia_principal = make_materia_principal() - response = client.post(reverse('sapl.materia:despachoinicial_create', - kwargs={'pk': materia_principal.pk}), - {'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.materia:despachoinicial_create', + kwargs={'pk': materia_principal.pk}), + {'salvar': 'salvar'}, + follow=True) assert (response.context_data['form'].errors['comissao'] == ['Este campo é obrigatório.']) @pytest.mark.django_db(transaction=False) -def test_form_errors_documento_acessorio(client): +def test_form_errors_documento_acessorio(admin_client): materia_principal = make_materia_principal() - response = client.post(reverse('sapl.materia:documentoacessorio_create', - kwargs={'pk': materia_principal.pk}), - {'salvar': 'salvar'}, - follow=True) + response = admin_client.post( + reverse('sapl.materia:documentoacessorio_create', + kwargs={'pk': materia_principal.pk}), + {'salvar': 'salvar'}, + follow=True) assert (response.context_data['form'].errors['tipo'] == ['Este campo é obrigatório.']) @@ -345,13 +357,14 @@ def test_form_errors_documento_acessorio(client): @pytest.mark.django_db(transaction=False) -def test_form_errors_legislacao_citada(client): +def test_form_errors_legislacao_citada(admin_client): materia_principal = make_materia_principal() - response = client.post(reverse('sapl.materia:legislacaocitada_create', - kwargs={'pk': materia_principal.pk}), - {'salvar': 'salvar'}, - follow=True) + response = admin_client.post( + reverse('sapl.materia:legislacaocitada_create', + kwargs={'pk': materia_principal.pk}), + {'salvar': 'salvar'}, + follow=True) assert (response.context_data['form'].errors['tipo'] == ['Este campo é obrigatório.']) @@ -362,13 +375,13 @@ def test_form_errors_legislacao_citada(client): @pytest.mark.django_db(transaction=False) -def test_form_errors_numeracao(client): +def test_form_errors_numeracao(admin_client): materia_principal = make_materia_principal() - response = client.post(reverse('sapl.materia:numeracao_create', - kwargs={'pk': materia_principal.pk}), - {'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.materia:numeracao_create', + kwargs={'pk': materia_principal.pk}), + {'salvar': 'salvar'}, + follow=True) assert (response.context_data['form'].errors['tipo_materia'] == ['Este campo é obrigatório.']) @@ -381,13 +394,14 @@ def test_form_errors_numeracao(client): @pytest.mark.django_db(transaction=False) -def test_form_errors_tramitacao(client): +def test_form_errors_tramitacao(admin_client): materia_principal = make_materia_principal() - response = client.post(reverse('sapl.materia:tramitacao_create', - kwargs={'pk': materia_principal.pk}), - {'salvar': 'salvar'}, - follow=True) + response = admin_client.post( + reverse('sapl.materia:tramitacao_create', + kwargs={'pk': materia_principal.pk}), + {'salvar': 'salvar'}, + follow=True) assert (response.context_data['form'].errors['data_tramitacao'] == ['Este campo é obrigatório.']) @@ -402,15 +416,78 @@ def test_form_errors_tramitacao(client): @pytest.mark.django_db(transaction=False) -def test_form_errors_relatoria(client): +def test_form_errors_relatoria(admin_client): materia_principal = make_materia_principal() - response = client.post(reverse('sapl.materia:relatoria_create', - kwargs={'pk': materia_principal.pk}), - {'salvar': 'salvar'}, - follow=True) + response = admin_client.post( + reverse('sapl.materia:relatoria_create', + kwargs={'pk': materia_principal.pk}), + {'salvar': 'salvar'}, + follow=True) assert (response.context_data['form'].errors['data_designacao_relator'] == ['Este campo é obrigatório.']) assert (response.context_data['form'].errors['parlamentar'] == ['Este campo é obrigatório.']) + + +@pytest.mark.django_db(transaction=False) +def test_proposicao_submit(admin_client): + tipo_autor = mommy.make(TipoAutor, descricao='Teste Tipo_Autor') + user = User.objects.filter(is_active=True)[0] + + autor = mommy.make( + Autor, + user=user, + tipo=tipo_autor, + nome='Autor Teste') + + file_content = 'file_content' + texto = SimpleUploadedFile("file.txt", file_content.encode('UTF-8')) + + response = admin_client.post(reverse('sapl.materia:proposicao_create'), + {'tipo': mommy.make(TipoProposicao, pk=3).pk, + 'descricao': 'Teste proposição', + 'justificativa_devolucao': ' ', + 'status': 'E', + 'autor': autor.pk, + 'texto_original': texto, + 'salvar': 'salvar', + }, + follow=True) + + assert response.status_code == 200 + + proposicao = Proposicao.objects.first() + + assert proposicao is not None + assert proposicao.descricao == 'Teste proposição' + assert proposicao.tipo.pk == 3 + + +@pytest.mark.django_db(transaction=False) +def test_form_errors_proposicao(admin_client): + tipo_autor = mommy.make(TipoAutor, descricao='Teste Tipo_Autor') + + user = User.objects.filter(is_active=True)[0] + + autor = mommy.make( + Autor, + user=user, + tipo=tipo_autor, + nome='Autor Teste') + + file_content = 'file_content' + texto = SimpleUploadedFile("file.txt", file_content.encode('UTF-8')) + + response = admin_client.post(reverse('sapl.materia:proposicao_create'), + {'autor': autor.pk, + 'justificativa_devolucao': ' ', + 'texto_original': texto, + 'salvar': 'salvar'}, + follow=True) + + assert (response.context_data['form'].errors['tipo'] == + ['Este campo é obrigatório.']) + assert (response.context_data['form'].errors['descricao'] == + ['Este campo é obrigatório.']) diff --git a/sapl/materia/urls.py b/sapl/materia/urls.py index b831fbfae..39d02776e 100644 --- a/sapl/materia/urls.py +++ b/sapl/materia/urls.py @@ -3,9 +3,10 @@ from django.conf.urls import include, url from sapl.materia.views import (AcompanhamentoConfirmarView, AcompanhamentoExcluirView, AcompanhamentoMateriaView, AnexadaCrud, - AutorCrud, AutoriaCrud, ConfirmarProposicao, - DespachoInicialCrud, DocumentoAcessorioCrud, - LegislacaoCitadaCrud, MateriaLegislativaCrud, + AutorCrud, AutoriaCrud, ConfirmarEmailView, + ConfirmarProposicao, DespachoInicialCrud, + DocumentoAcessorioCrud, LegislacaoCitadaCrud, + MateriaLegislativaCrud, MateriaLegislativaPesquisaView, MateriaTaView, NumeracaoCrud, OrgaoCrud, OrigemCrud, ProposicaoCrud, ProposicaoDevolvida, @@ -33,6 +34,9 @@ urlpatterns = [ RelatoriaCrud.get_urls() + DocumentoAcessorioCrud.get_urls())), + url(r'^confirmar/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})$', + ConfirmarEmailView.as_view(), name='confirmar_email'), + url(r'^proposicao/', include(ProposicaoCrud.get_urls())), url(r'^proposicao/recibo/(?P\d+)', ReciboProposicaoView.as_view(), name='recibo-proposicao'), diff --git a/sapl/materia/views.py b/sapl/materia/views.py index 7648d1afd..c6c00dd8a 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -4,28 +4,38 @@ from string import ascii_letters, digits from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML, Button +from django.conf import settings from django.contrib import messages -from django.core.exceptions import ObjectDoesNotExist +from django.contrib.auth.mixins import PermissionRequiredMixin +from django.contrib.auth.models import User +from django.contrib.auth.tokens import default_token_generator +from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist from django.core.mail import send_mail from django.core.urlresolvers import reverse from django.http.response import HttpResponseRedirect +from django.shortcuts import redirect from django.template import Context, loader +from django.utils.encoding import force_bytes +from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode from django.utils.translation import ugettext_lazy as _ -from django.views.generic import CreateView, ListView, TemplateView, UpdateView +from django.views.generic import (CreateView, DetailView, ListView, + TemplateView, UpdateView) from django_filters.views import FilterView from sapl.base.models import CasaLegislativa from sapl.compilacao.views import IntegracaoTaView from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row -from sapl.crud.base import (Crud, CrudBaseMixin, CrudCreateView, CrudListView, +from sapl.crud.base import (Crud, CrudBaseMixin, CrudCreateView, + CrudDeleteView, CrudDetailView, CrudListView, CrudUpdateView, make_pagination) from sapl.crud.masterdetail import MasterDetailCrud from sapl.norma.models import LegislacaoCitada from sapl.utils import (autor_label, autor_modal, gerar_hash_arquivo, - get_base_url) + get_base_url, permissao_tb_aux, permissoes_autor, + permissoes_materia) -from .forms import (AcompanhamentoMateriaForm, AnexadaForm, AutoriaForm, - ConfirmarProposicaoForm, DespachoInicialForm, +from .forms import (AcompanhamentoMateriaForm, AnexadaForm, AutorForm, + AutoriaForm, ConfirmarProposicaoForm, DespachoInicialForm, DocumentoAcessorioForm, LegislacaoCitadaForm, MateriaLegislativaFilterSet, NumeracaoForm, ProposicaoForm, ReceberProposicaoForm, RelatoriaForm, TramitacaoForm, @@ -39,18 +49,145 @@ from .models import (AcompanhamentoMateria, Anexada, Autor, Autoria, TipoFimRelatoria, TipoMateriaLegislativa, TipoProposicao, Tramitacao, UnidadeTramitacao) -OrigemCrud = Crud.build(Origem, 'origem') -TipoMateriaCrud = Crud.build(TipoMateriaLegislativa, - 'tipo_materia_legislativa') -RegimeTramitacaoCrud = Crud.build(RegimeTramitacao, 'regime_tramitacao') -TipoDocumentoCrud = Crud.build(TipoDocumento, 'tipo_documento') -TipoFimRelatoriaCrud = Crud.build(TipoFimRelatoria, 'fim_relatoria') AnexadaCrud = Crud.build(Anexada, '') -TipoAutorCrud = Crud.build(TipoAutor, 'tipo_autor') -AutorCrud = Crud.build(Autor, 'autor') -OrgaoCrud = Crud.build(Orgao, 'orgao') -TipoProposicaoCrud = Crud.build(TipoProposicao, 'tipo_proposicao') -StatusTramitacaoCrud = Crud.build(StatusTramitacao, 'status_tramitacao') + + +class OrigemCrud(Crud): + model = Origem + help_path = 'origem' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class TipoMateriaCrud(Crud): + model = TipoMateriaLegislativa + help_path = 'tipo_materia_legislativa' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class RegimeTramitacaoCrud(Crud): + model = RegimeTramitacao + help_path = 'regime_tramitacao' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class TipoDocumentoCrud(Crud): + model = TipoDocumento + help_path = 'tipo_documento' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class TipoFimRelatoriaCrud(Crud): + model = TipoFimRelatoria + help_path = 'fim_relatoria' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class TipoAutorCrud(Crud): + model = TipoAutor + help_path = 'tipo_autor' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class AutorCrud(Crud): + model = Autor + help_path = 'autor' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + list_field_names = ['tipo', 'nome', + 'username', 'cargo'] + + def has_permission(self): + return permissao_tb_aux(self) + + class CreateView(CrudCreateView): + form_class = AutorForm + layout_key = 'AutorCreate' + + def get_success_url(self): + pk_autor = Autor.objects.get( + email=self.request.POST.get('email')).id + kwargs = {} + user = User.objects.get(email=self.request.POST.get('email')) + kwargs['token'] = default_token_generator.make_token(user) + kwargs['uidb64'] = urlsafe_base64_encode(force_bytes(user.pk)) + assunto = "SAPL - Confirmação de Conta" + full_url = self.request.get_raw_uri() + url_base = full_url[:full_url.find('sistema') - 1] + + mensagem = ("Este e-mail foi utilizado para fazer cadastro no " + + "SAPL com o perfil de Autor. Agora você pode " + + "criar/editar/enviar Proposições.\n" + + "Seu nome de usuário é: " + + self.request.POST['username'] + "\n" + "Caso você não tenha feito este cadastro, por favor " + + "ignore esta mensagem. Caso tenha, clique " + + "no link abaixo\n" + url_base + + reverse('sapl.materia:confirmar_email', kwargs=kwargs)) + remetente = settings.EMAIL_SEND_USER + destinatario = [self.request.POST.get('email')] + send_mail(assunto, mensagem, remetente, destinatario, + fail_silently=False) + return reverse('sapl.materia:autor_detail', + kwargs={'pk': pk_autor}) + + +class ConfirmarEmailView(TemplateView): + template_name = "confirma_email.html" + + def get(self, request, *args, **kwargs): + uid = urlsafe_base64_decode(self.kwargs['uidb64']) + user = User.objects.get(id=uid) + user.is_active = True + user.save() + context = self.get_context_data(**kwargs) + return self.render_to_response(context) + + +class OrgaoCrud(Crud): + model = Orgao + help_path = 'orgao' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + + def has_permission(self): + return permissao_tb_aux(self) + + +class TipoProposicaoCrud(Crud): + model = TipoProposicao + help_path = 'tipo_proposicao' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class StatusTramitacaoCrud(Crud): + model = StatusTramitacao + help_path = 'status_tramitacao' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + + def has_permission(self): + return permissao_tb_aux(self) def criar_materia_proposicao(proposicao): @@ -94,12 +231,17 @@ class UnidadeTramitacaoCrud(Crud): model = UnidadeTramitacao help_path = 'unidade_tramitacao' - class CreateView(CrudCreateView): + class CreateView(PermissionRequiredMixin, CrudCreateView): + permission_required = permissoes_materia() form_class = UnidadeTramitacaoForm - class UpdateView(CrudUpdateView): + class UpdateView(PermissionRequiredMixin, CrudUpdateView): + permission_required = permissoes_materia() form_class = UnidadeTramitacaoForm + class DeleteView(PermissionRequiredMixin, CrudDeleteView): + permission_required = permissoes_materia() + class ProposicaoDevolvida(ListView): template_name = 'materia/prop_devolvidas_list.html' @@ -245,18 +387,57 @@ class ProposicaoCrud(Crud): list_field_names = ['data_envio', 'descricao', 'tipo', 'data_recebimento'] - class CreateView(CrudCreateView): + class CreateView(PermissionRequiredMixin, CrudCreateView): form_class = ProposicaoForm + permission_required = {'materia.add_proposicao'} @property def layout_key(self): return 'ProposicaoCreate' - class UpdateView(CrudUpdateView): + def get_initial(self): + try: + autor_id = Autor.objects.get(user=self.request.user.id).id + except MultipleObjectsReturned: + msg = _('Este usuário está relacionado a mais de um autor. ' + + 'Operação cancelada') + messages.add_message(self.request, messages.ERROR, msg) + return redirect(self.get_success_url()) + else: + return {'autor': autor_id} + + class UpdateView(PermissionRequiredMixin, CrudUpdateView): form_class = ProposicaoForm + permission_required = permissoes_autor() + + @property + def layout_key(self): + return 'ProposicaoCreate' + + def has_permission(self): + perms = self.get_permission_required() + if self.request.user.has_perms(perms): + if (Proposicao.objects.filter( + id=self.kwargs['pk'], + autor__user_id=self.request.user.id).exists()): + proposicao = Proposicao.objects.get( + id=self.kwargs['pk'], + autor__user_id=self.request.user.id) + if not proposicao.data_recebimento: + return True + else: + msg = _('Essa proposição já foi recebida. ' + + 'Não pode mais ser editada') + messages.add_message(self.request, messages.ERROR, msg) + return False + else: + return False + + class DetailView(PermissionRequiredMixin, CrudDetailView): + permission_required = permissoes_autor() def get_context_data(self, **kwargs): - context = super(UpdateView, self).get_context_data(**kwargs) + context = super(DetailView, self).get_context_data(**kwargs) if self.object.materia: context['form'].fields['tipo_materia'].initial = ( self.object.materia.tipo.id) @@ -270,8 +451,21 @@ class ProposicaoCrud(Crud): def layout_key(self): return 'ProposicaoCreate' - class ListView(CrudListView): - ordering = ['-data_envio', '-descricao'] + def has_permission(self): + perms = self.get_permission_required() + if self.request.user.has_perms(perms): + if (Proposicao.objects.filter( + id=self.kwargs['pk'], + autor__user_id=self.request.user.id).exists()): + return True + else: + return False + else: + return False + + class ListView(PermissionRequiredMixin, CrudListView): + ordering = ['-data_envio', 'descricao'] + permission_required = permissoes_autor() def get_rows(self, object_list): @@ -288,7 +482,13 @@ class ProposicaoCrud(Crud): return [self._as_row(obj) for obj in object_list] - class DeleteView(MasterDetailCrud.DeleteView): + def get_queryset(self): + lista = Proposicao.objects.filter( + autor__user_id=self.request.user.id) + return lista + + class DeleteView(PermissionRequiredMixin, CrudDeleteView): + permission_required = permissoes_materia() def delete(self, request, *args, **kwargs): proposicao = Proposicao.objects.get(id=self.kwargs['pk']) @@ -324,7 +524,8 @@ class RelatoriaCrud(MasterDetailCrud): parent_field = 'materia' help_path = '' - class CreateView(MasterDetailCrud.CreateView): + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): + permission_required = permissoes_materia() form_class = RelatoriaForm def get_initial(self): @@ -344,9 +545,13 @@ class RelatoriaCrud(MasterDetailCrud): return {'comissao': localizacao} - class UpdateView(MasterDetailCrud.UpdateView): + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): + permission_required = permissoes_materia() form_class = RelatoriaForm + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_materia() + class TramitacaoCrud(MasterDetailCrud): model = Tramitacao @@ -357,16 +562,18 @@ class TramitacaoCrud(MasterDetailCrud): list_field_names = ['data_tramitacao', 'unidade_tramitacao_local', 'unidade_tramitacao_destino', 'status'] - class CreateView(MasterDetailCrud.CreateView): + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): form_class = TramitacaoForm + permission_required = permissoes_materia() def post(self, request, *args, **kwargs): materia = MateriaLegislativa.objects.get(id=kwargs['pk']) do_envia_email_tramitacao(request, materia) return super(CreateView, self).post(request, *args, **kwargs) - class UpdateView(MasterDetailCrud.UpdateView): + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): form_class = TramitacaoForm + permission_required = permissoes_materia() def post(self, request, *args, **kwargs): materia = MateriaLegislativa.objects.get(id=kwargs['pk']) @@ -380,7 +587,8 @@ class TramitacaoCrud(MasterDetailCrud): kwargs = {self.crud.parent_field: self.kwargs['pk']} return qs.filter(**kwargs).order_by('-data_tramitacao') - class DeleteView(MasterDetailCrud.DeleteView): + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_materia() def delete(self, request, *args, **kwargs): tramitacao = Tramitacao.objects.get(id=self.kwargs['pk']) @@ -437,8 +645,9 @@ class DocumentoAcessorioCrud(MasterDetailCrud): class BaseMixin(MasterDetailCrud.BaseMixin): list_field_names = ['nome', 'tipo', 'data', 'autor', 'arquivo'] - class CreateView(MasterDetailCrud.CreateView): + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): form_class = DocumentoAcessorioForm + permission_required = permissoes_materia() def __init__(self, *args, **kwargs): montar_helper_documento_acessorio(self) @@ -449,8 +658,9 @@ class DocumentoAcessorioCrud(MasterDetailCrud): context['helper'] = self.helper return context - class UpdateView(MasterDetailCrud.UpdateView): + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): form_class = DocumentoAcessorioForm + permission_required = permissoes_materia() def __init__(self, *args, **kwargs): montar_helper_documento_acessorio(self) @@ -461,17 +671,25 @@ class DocumentoAcessorioCrud(MasterDetailCrud): context['helper'] = self.helper return context + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_materia() + class AutoriaCrud(MasterDetailCrud): model = Autoria parent_field = 'materia' help_path = '' - class CreateView(MasterDetailCrud.CreateView): + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): form_class = AutoriaForm + permission_required = permissoes_materia() - class UpdateView(MasterDetailCrud.UpdateView): + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): form_class = AutoriaForm + permission_required = permissoes_materia() + + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_materia() class DespachoInicialCrud(MasterDetailCrud): @@ -479,11 +697,16 @@ class DespachoInicialCrud(MasterDetailCrud): parent_field = 'materia' help_path = '' - class CreateView(MasterDetailCrud.CreateView): + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): form_class = DespachoInicialForm + permission_required = permissoes_materia() - class UpdateView(MasterDetailCrud.UpdateView): + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): form_class = DespachoInicialForm + permission_required = permissoes_materia() + + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_materia() class LegislacaoCitadaCrud(MasterDetailCrud): @@ -499,11 +722,13 @@ class LegislacaoCitadaCrud(MasterDetailCrud): return reverse('%s:%s' % (namespace, self.url_name(suffix)), args=args) - class CreateView(MasterDetailCrud.CreateView): + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): form_class = LegislacaoCitadaForm + permission_required = permissoes_materia() - class UpdateView(MasterDetailCrud.UpdateView): + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): form_class = LegislacaoCitadaForm + permission_required = permissoes_materia() def get_initial(self): self.initial['tipo'] = self.object.norma.tipo.id @@ -511,6 +736,9 @@ class LegislacaoCitadaCrud(MasterDetailCrud): self.initial['ano'] = self.object.norma.ano return self.initial + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_materia() + class DetailView(MasterDetailCrud.DetailView): @property @@ -523,11 +751,16 @@ class NumeracaoCrud(MasterDetailCrud): parent_field = 'materia' help_path = '' - class CreateView(MasterDetailCrud.CreateView): + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): form_class = NumeracaoForm + permission_required = permissoes_materia() - class UpdateView(MasterDetailCrud.UpdateView): + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): form_class = NumeracaoForm + permission_required = permissoes_materia() + + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_materia() class AnexadaCrud(MasterDetailCrud): @@ -538,11 +771,13 @@ class AnexadaCrud(MasterDetailCrud): class BaseMixin(MasterDetailCrud.BaseMixin): list_field_names = ['materia_anexada', 'data_anexacao'] - class CreateView(MasterDetailCrud.CreateView): + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): form_class = AnexadaForm + permission_required = permissoes_materia() - class UpdateView(MasterDetailCrud.UpdateView): + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): form_class = AnexadaForm + permission_required = permissoes_materia() def get_initial(self): self.initial['tipo'] = self.object.materia_anexada.tipo.id @@ -557,6 +792,9 @@ class AnexadaCrud(MasterDetailCrud): def layout_key(self): return 'AnexadaDetail' + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_materia() + class MateriaLegislativaCrud(Crud): model = MateriaLegislativa @@ -565,10 +803,20 @@ class MateriaLegislativaCrud(Crud): class BaseMixin(CrudBaseMixin): list_field_names = ['tipo', 'numero', 'ano', 'data_apresentacao'] + class CreateView(PermissionRequiredMixin, CrudCreateView): + permission_required = permissoes_materia() + + class UpdateView(PermissionRequiredMixin, CrudUpdateView): + permission_required = permissoes_materia() + + class DeleteView(PermissionRequiredMixin, CrudDeleteView): + permission_required = permissoes_materia() + -class DocumentoAcessorioView(CreateView): +class DocumentoAcessorioView(PermissionRequiredMixin, CreateView): template_name = "materia/documento_acessorio.html" form_class = DocumentoAcessorioForm + permission_required = permissoes_materia() def get(self, request, *args, **kwargs): materia = MateriaLegislativa.objects.get(id=kwargs['pk']) @@ -602,7 +850,8 @@ class DocumentoAcessorioView(CreateView): return reverse('sapl.materia:documento_acessorio', kwargs={'pk': pk}) -class AcompanhamentoConfirmarView(TemplateView): +class AcompanhamentoConfirmarView(PermissionRequiredMixin, TemplateView): + permission_required = permissoes_materia() def get_redirect_url(self): return reverse('sapl.sessao:list_pauta_sessao') @@ -619,7 +868,8 @@ class AcompanhamentoConfirmarView(TemplateView): return HttpResponseRedirect(self.get_redirect_url()) -class AcompanhamentoExcluirView(TemplateView): +class AcompanhamentoExcluirView(PermissionRequiredMixin, TemplateView): + permission_required = permissoes_materia() def get_redirect_url(self): return reverse('sapl.sessao:list_pauta_sessao') @@ -703,8 +953,9 @@ class ProposicaoTaView(IntegracaoTaView): model_type_foreignkey = TipoProposicao -class AcompanhamentoMateriaView(CreateView): +class AcompanhamentoMateriaView(PermissionRequiredMixin, CreateView): template_name = "materia/acompanhamento_materia.html" + permission_required = permissoes_materia() def get_random_chars(self): s = ascii_letters + digits diff --git a/sapl/norma/tests/test_norma.py b/sapl/norma/tests/test_norma.py index 16c980d15..65c0f28b1 100644 --- a/sapl/norma/tests/test_norma.py +++ b/sapl/norma/tests/test_norma.py @@ -6,22 +6,22 @@ from sapl.norma.models import NormaJuridica, TipoNormaJuridica @pytest.mark.django_db(transaction=False) -def test_incluir_norma_submit(client): +def test_incluir_norma_submit(admin_client): # Cria um tipo de norma tipo = mommy.make(TipoNormaJuridica, sigla='T', descricao='Teste') # Testa POST - response = client.post(reverse('sapl.norma:normajuridica_create'), - {'tipo': tipo.pk, - 'numero': '1', - 'ano': '2016', - 'data': '2016-03-22', - 'esfera_federacao': 'E', - 'ementa': 'Teste_Ementa', - 'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.norma:normajuridica_create'), + {'tipo': tipo.pk, + 'numero': '1', + 'ano': '2016', + 'data': '2016-03-22', + 'esfera_federacao': 'E', + 'ementa': 'Teste_Ementa', + 'salvar': 'salvar'}, + follow=True) assert response.status_code == 200 norma = NormaJuridica.objects.first() @@ -31,11 +31,11 @@ def test_incluir_norma_submit(client): @pytest.mark.django_db(transaction=False) -def test_incluir_norma_errors(client): +def test_incluir_norma_errors(admin_client): - response = client.post(reverse('sapl.norma:normajuridica_create'), - {'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.norma:normajuridica_create'), + {'salvar': 'salvar'}, + follow=True) assert (response.context_data['form'].errors['tipo'] == ['Este campo é obrigatório.']) diff --git a/sapl/norma/views.py b/sapl/norma/views.py index 5f7624ae6..2e05b819d 100644 --- a/sapl/norma/views.py +++ b/sapl/norma/views.py @@ -1,27 +1,46 @@ from datetime import datetime +from django.contrib.auth.mixins import PermissionRequiredMixin from django.shortcuts import redirect from django.views.generic import FormView, ListView from sapl.compilacao.views import IntegracaoTaView from sapl.crud.base import (Crud, CrudBaseMixin, CrudCreateView, - CrudUpdateView, make_pagination) + CrudDeleteView, CrudUpdateView, make_pagination) +from sapl.utils import permissoes_norma from .forms import NormaJuridicaForm, NormaJuridicaPesquisaForm from .models import (AssuntoNorma, LegislacaoCitada, NormaJuridica, TipoNormaJuridica) -AssuntoNormaCrud = Crud.build(AssuntoNorma, 'assunto_norma_juridica') -TipoNormaCrud = Crud.build(TipoNormaJuridica, 'tipo_norma_juridica') LegislacaoCitadaCrud = Crud.build(LegislacaoCitada, '') +class AssuntoNormaCrud(Crud): + model = AssuntoNorma + help_path = 'assunto_norma_juridica' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + permission_required = permissoes_norma() + list_field_names = ['assunto', 'descricao'] + + +class TipoNormaCrud(Crud): + model = TipoNormaJuridica + help_path = 'tipo_norma_juridica' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + permission_required = permissoes_norma() + list_field_names = ['equivalente_lexml', 'sigla', 'descricao'] + + class NormaCrud(Crud): model = NormaJuridica help_path = 'norma_juridica' - class UpdateView(CrudUpdateView): + class UpdateView(PermissionRequiredMixin, CrudUpdateView): form_class = NormaJuridicaForm + permission_required = permissoes_norma() @property def layout_key(self): @@ -35,13 +54,17 @@ class NormaCrud(Crud): self.initial['numero_materia'] = norma.materia.numero return self.initial.copy() - class CreateView(CrudCreateView): + class CreateView(PermissionRequiredMixin, CrudCreateView): form_class = NormaJuridicaForm + permission_required = permissoes_norma() @property def layout_key(self): return 'NormaJuridicaCreate' + class DeleteView(PermissionRequiredMixin, CrudDeleteView): + permission_required = permissoes_norma() + class BaseMixin(CrudBaseMixin): list_field_names = ['tipo', 'numero', 'ano', 'ementa'] diff --git a/sapl/painel/views.py b/sapl/painel/views.py index 712e66583..4422c9445 100644 --- a/sapl/painel/views.py +++ b/sapl/painel/views.py @@ -1,5 +1,6 @@ from datetime import date +from django.contrib.auth.decorators import user_passes_test from django.core.exceptions import ObjectDoesNotExist from django.http import HttpResponse, JsonResponse from django.shortcuts import render @@ -11,12 +12,18 @@ from sapl.parlamentares.models import Filiacao from sapl.sessao.models import (ExpedienteMateria, OrdemDia, PresencaOrdemDia, RegistroVotacao, SessaoPlenaria, SessaoPlenariaPresenca, VotoParlamentar) +from sapl.utils import permissoes_painel from .models import Cronometro CronometroPainelCrud = Crud.build(Cronometro, '') +def check_permission(user): + return user.has_perms(permissoes_painel()) + + +@user_passes_test(check_permission) def controlador_painel(request): painel_created = Painel.objects.get_or_create(data_painel=date.today()) @@ -37,23 +44,28 @@ def controlador_painel(request): return render(request, 'painel/controlador.html', context) +@user_passes_test(check_permission) def painel_view(request, pk): context = {'head_title': str(_('Painel Plenário')), 'sessao_id': pk} return render(request, 'painel/index.html', context) +@user_passes_test(check_permission) def painel_mensagem_view(request): return render(request, 'painel/mensagem.html') +@user_passes_test(check_permission) def painel_parlamentar_view(request): return render(request, 'painel/parlamentares.html') +@user_passes_test(check_permission) def painel_votacao_view(request): return render(request, 'painel/votacao.html') +@user_passes_test(check_permission) def cronometro_painel(request): request.session[request.GET['tipo']] = request.GET['action'] return HttpResponse({}) @@ -320,6 +332,7 @@ def get_votos_nominal(response, materia): return response +@user_passes_test(check_permission) def get_dados_painel(request, pk): sessao = SessaoPlenaria.objects.get(id=pk) cronometro_discurso = get_cronometro_status(request, 'discurso') diff --git a/sapl/parlamentares/forms.py b/sapl/parlamentares/forms.py index 9b636e882..fe21aeb2c 100644 --- a/sapl/parlamentares/forms.py +++ b/sapl/parlamentares/forms.py @@ -29,9 +29,9 @@ class LegislaturaForm(ModelForm): data_eleicao = cleaned_data['data_eleicao'] if data_inicio >= data_fim or data_eleicao >= data_inicio: - raise ValidationError(_('A data início deve ser menor que a data \ - fim, e a data eleição deve ser menor que\ - a data início')) + raise ValidationError(_('A data início deve ser menor que a ' + + 'data fim, e a data eleição deve ser ' + + 'menor que a data início')) return cleaned_data diff --git a/sapl/parlamentares/migrations/0022_auto_20160624_1124.py b/sapl/parlamentares/migrations/0022_auto_20160624_1124.py new file mode 100644 index 000000000..a395bc806 --- /dev/null +++ b/sapl/parlamentares/migrations/0022_auto_20160624_1124.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.5 on 2016-06-24 14:24 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import sapl.parlamentares.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('parlamentares', '0021_merge'), + ] + + operations = [ + migrations.AlterField( + model_name='sessaolegislativa', + name='legislatura', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='parlamentares.Legislatura', verbose_name=sapl.parlamentares.models.Legislatura), + ), + ] diff --git a/sapl/parlamentares/migrations/0022_auto_20160702_1519.py b/sapl/parlamentares/migrations/0022_auto_20160702_1519.py new file mode 100644 index 000000000..6573268e1 --- /dev/null +++ b/sapl/parlamentares/migrations/0022_auto_20160702_1519.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.5 on 2016-07-02 18:19 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('parlamentares', '0021_merge'), + ] + + operations = [ + migrations.AlterField( + model_name='sessaolegislativa', + name='legislatura', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='parlamentares.Legislatura', verbose_name='Legislatura'), + ), + ] diff --git a/sapl/parlamentares/migrations/0023_auto_20160628_1247.py b/sapl/parlamentares/migrations/0023_auto_20160628_1247.py new file mode 100644 index 000000000..d40680e48 --- /dev/null +++ b/sapl/parlamentares/migrations/0023_auto_20160628_1247.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.5 on 2016-06-28 15:47 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('parlamentares', '0022_auto_20160624_1124'), + ] + + operations = [ + migrations.AlterField( + model_name='sessaolegislativa', + name='legislatura', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='parlamentares.Legislatura', verbose_name='Legislatura'), + ), + ] diff --git a/sapl/parlamentares/migrations/0024_merge.py b/sapl/parlamentares/migrations/0024_merge.py new file mode 100644 index 000000000..f8e63c317 --- /dev/null +++ b/sapl/parlamentares/migrations/0024_merge.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-08-18 21:39 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('parlamentares', '0022_auto_20160702_1519'), + ('parlamentares', '0023_auto_20160628_1247'), + ] + + operations = [ + ] diff --git a/sapl/parlamentares/models.py b/sapl/parlamentares/models.py index 77c7319d4..d4c8d817a 100644 --- a/sapl/parlamentares/models.py +++ b/sapl/parlamentares/models.py @@ -40,7 +40,9 @@ class SessaoLegislativa(models.Model): ('E', 'extraordinaria', _('Extraordinária')), ) - legislatura = models.ForeignKey(Legislatura) + legislatura = models.ForeignKey( + Legislatura, + verbose_name=Legislatura._meta.verbose_name) numero = models.PositiveIntegerField(verbose_name=_('Número')) tipo = models.CharField( max_length=1, verbose_name=_('Tipo'), choices=TIPO_SESSAO_CHOICES) diff --git a/sapl/parlamentares/tests/test_parlamentares.py b/sapl/parlamentares/tests/test_parlamentares.py index 2739dcd62..16b0fb158 100644 --- a/sapl/parlamentares/tests/test_parlamentares.py +++ b/sapl/parlamentares/tests/test_parlamentares.py @@ -8,20 +8,20 @@ from sapl.parlamentares.models import (Dependente, Filiacao, Legislatura, @pytest.mark.django_db(transaction=False) -def test_cadastro_parlamentar(client): +def test_cadastro_parlamentar(admin_client): legislatura = mommy.make(Legislatura) url = reverse('sapl.parlamentares:parlamentar_create') - response = client.get(url) + response = admin_client.get(url) assert response.status_code == 200 - response = client.post(url, {'nome_completo': 'Teresa Barbosa', - 'nome_parlamentar': 'Terezinha', - 'sexo': 'F', - 'ativo': 'True', - 'legislatura': legislatura.id, - 'data_expedicao_diploma': '2001-01-01'}, - follow=True) + response = admin_client.post(url, {'nome_completo': 'Teresa Barbosa', + 'nome_parlamentar': 'Terezinha', + 'sexo': 'F', + 'ativo': 'True', + 'legislatura': legislatura.id, + 'data_expedicao_diploma': '2001-01-01'}, + follow=True) [parlamentar] = Parlamentar.objects.all() assert parlamentar.nome_parlamentar == 'Terezinha' @@ -36,9 +36,9 @@ def test_cadastro_parlamentar(client): @pytest.mark.django_db(transaction=False) -def test_incluir_parlamentar_errors(client): +def test_incluir_parlamentar_errors(admin_client): url = reverse('sapl.parlamentares:parlamentar_create') - response = client.post(url) + response = admin_client.post(url) erros_esperados = {campo: ['Este campo é obrigatório.'] for campo in ['legislatura', 'data_expedicao_diploma', @@ -50,34 +50,34 @@ def test_incluir_parlamentar_errors(client): @pytest.mark.django_db(transaction=False) -def test_filiacao_submit(client): +def test_filiacao_submit(admin_client): mommy.make(Parlamentar, pk=14) mommy.make(Partido, pk=32) - client.post(reverse('sapl.parlamentares:filiacao_create', - kwargs={'pk': 14}), - {'partido': 32, - 'data': '2016-03-22', - 'salvar': 'salvar'}, - follow=True) + admin_client.post(reverse('sapl.parlamentares:filiacao_create', + kwargs={'pk': 14}), + {'partido': 32, + 'data': '2016-03-22', + 'salvar': 'salvar'}, + follow=True) filiacao = Filiacao.objects.first() assert 32 == filiacao.partido.pk @pytest.mark.django_db(transaction=False) -def test_dependente_submit(client): +def test_dependente_submit(admin_client): mommy.make(Parlamentar, pk=14) mommy.make(Partido, pk=32) mommy.make(TipoDependente, pk=3) - client.post(reverse('sapl.parlamentares:dependente_create', - kwargs={'pk': 14}), - {'nome': 'Eduardo', - 'tipo': 3, - 'sexo': 'M', - 'salvar': 'salvar'}, - follow=True) + admin_client.post(reverse('sapl.parlamentares:dependente_create', + kwargs={'pk': 14}), + {'nome': 'Eduardo', + 'tipo': 3, + 'sexo': 'M', + 'salvar': 'salvar'}, + follow=True) dependente = Dependente.objects.first() assert 3 == dependente.tipo.pk @@ -85,12 +85,13 @@ def test_dependente_submit(client): @pytest.mark.django_db(transaction=False) -def test_form_errors_dependente(client): +def test_form_errors_dependente(admin_client): mommy.make(Parlamentar, pk=14) - response = client.post(reverse('sapl.parlamentares:dependente_create', - kwargs={'pk': 14}), - {'salvar': 'salvar'}, - follow=True) + response = admin_client.post( + reverse('sapl.parlamentares:dependente_create', + kwargs={'pk': 14}), + {'salvar': 'salvar'}, + follow=True) assert (response.context_data['form'].errors['nome'] == ['Este campo é obrigatório.']) @@ -101,14 +102,14 @@ def test_form_errors_dependente(client): @pytest.mark.django_db(transaction=False) -def test_form_errors_filiacao(client): +def test_form_errors_filiacao(admin_client): mommy.make(Parlamentar, pk=14) - response = client.post(reverse('sapl.parlamentares:filiacao_create', - kwargs={'pk': 14}), - {'partido': '', - 'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.parlamentares:filiacao_create', + kwargs={'pk': 14}), + {'partido': '', + 'salvar': 'salvar'}, + follow=True) assert (response.context_data['form'].errors['partido'] == ['Este campo é obrigatório.']) @@ -117,31 +118,31 @@ def test_form_errors_filiacao(client): @pytest.mark.django_db(transaction=False) -def test_mandato_submit(client): +def test_mandato_submit(admin_client): mommy.make(Parlamentar, pk=14) mommy.make(Legislatura, pk=5) - client.post(reverse('sapl.parlamentares:mandato_create', - kwargs={'pk': 14}), - {'legislatura': 5, - 'data_fim_mandato': '2016-01-01', - 'data_expedicao_diploma': '2016-03-22', - 'observacao': 'Observação do mandato', - 'salvar': 'salvar'}, - follow=True) + admin_client.post(reverse('sapl.parlamentares:mandato_create', + kwargs={'pk': 14}), + {'legislatura': 5, + 'data_fim_mandato': '2016-01-01', + 'data_expedicao_diploma': '2016-03-22', + 'observacao': 'Observação do mandato', + 'salvar': 'salvar'}, + follow=True) mandato = Mandato.objects.first() assert 'Observação do mandato' == mandato.observacao @pytest.mark.django_db(transaction=False) -def test_form_errors_mandato(client): +def test_form_errors_mandato(admin_client): mommy.make(Parlamentar, pk=14) - response = client.post(reverse('sapl.parlamentares:mandato_create', - kwargs={'pk': 14}), - {'legislatura': '', - 'salvar': 'salvar'}, - follow=True) + response = admin_client.post(reverse('sapl.parlamentares:mandato_create', + kwargs={'pk': 14}), + {'legislatura': '', + 'salvar': 'salvar'}, + follow=True) assert (response.context_data['form'].errors['legislatura'] == ['Este campo é obrigatório.']) diff --git a/sapl/parlamentares/views.py b/sapl/parlamentares/views.py index 910a1c655..93e49f120 100644 --- a/sapl/parlamentares/views.py +++ b/sapl/parlamentares/views.py @@ -1,13 +1,19 @@ from django.contrib import messages -from django.core.exceptions import ObjectDoesNotExist +from django.contrib.auth.mixins import PermissionRequiredMixin +from django.contrib.auth.models import Permission +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist from django.core.urlresolvers import reverse_lazy from django.shortcuts import redirect from django.utils.datastructures import MultiValueDictKeyError from django.utils.translation import ugettext_lazy as _ from django.views.generic import FormView -from sapl.crud.base import Crud, CrudCreateView, CrudListView, CrudUpdateView +from sapl.crud.base import (Crud, CrudBaseMixin, CrudCreateView, + CrudDeleteView, CrudDetailView, + CrudListView, CrudUpdateView) from sapl.crud.masterdetail import MasterDetailCrud +from sapl.utils import permissao_tb_aux, permissoes_parlamentares from .forms import (ComposicaoColigacaoForm, FiliacaoForm, LegislaturaForm, ParlamentarCreateForm, ParlamentarForm) @@ -16,15 +22,89 @@ from .models import (CargoMesa, Coligacao, ComposicaoColigacao, ComposicaoMesa, NivelInstrucao, Parlamentar, Partido, SessaoLegislativa, SituacaoMilitar, TipoAfastamento, TipoDependente) -CargoMesaCrud = Crud.build(CargoMesa, 'cargo_mesa') -PartidoCrud = Crud.build(Partido, 'partidos') -SessaoLegislativaCrud = Crud.build(SessaoLegislativa, 'sessao_legislativa') -TipoDependenteCrud = Crud.build(TipoDependente, 'tipo_dependente') -NivelInstrucaoCrud = Crud.build(NivelInstrucao, 'nivel_instrucao') -TipoAfastamentoCrud = Crud.build(TipoAfastamento, 'tipo_afastamento') -TipoMilitarCrud = Crud.build(SituacaoMilitar, 'tipo_situa_militar') -DependenteCrud = MasterDetailCrud.build(Dependente, 'parlamentar', '') +class CargoMesaCrud(Crud): + model = CargoMesa + help_path = 'cargo_mesa' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class PartidoCrud(Crud): + model = Partido + help_path = 'partidos' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class SessaoLegislativaCrud(Crud): + model = SessaoLegislativa + help_path = 'sessao_legislativa' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class TipoDependenteCrud(Crud): + model = TipoDependente + help_path = 'nivel_instrucao' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class NivelInstrucaoCrud(Crud): + model = NivelInstrucao + help_path = 'tipo_dependente' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class TipoAfastamentoCrud(Crud): + model = TipoAfastamento + help_path = 'tipo_afastamento' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class TipoMilitarCrud(Crud): + model = SituacaoMilitar + help_path = 'tipo_situa_militar' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class DependenteCrud(MasterDetailCrud): + model = Dependente + parent_field = 'parlamentar' + help_path = '' + + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): + permission_required = permissoes_parlamentares() + + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): + permission_required = permissoes_parlamentares() + + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_parlamentares() + + class ListView(PermissionRequiredMixin, MasterDetailCrud.ListView): + permission_required = permissoes_parlamentares() + + class DetailView(PermissionRequiredMixin, MasterDetailCrud.DetailView): + permission_required = permissoes_parlamentares() class MandatoCrud(MasterDetailCrud): @@ -35,6 +115,15 @@ class MandatoCrud(MasterDetailCrud): class ListView(MasterDetailCrud.ListView): ordering = ('-legislatura__data_inicio') + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): + permission_required = permissoes_parlamentares() + + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): + permission_required = permissoes_parlamentares() + + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_parlamentares() + class ColigacaoCrud(Crud): model = Coligacao @@ -43,6 +132,10 @@ class ColigacaoCrud(Crud): class ListView(CrudListView): ordering = ('-legislatura__data_inicio', 'nome') + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + class ComposicaoColigacaoCrud(MasterDetailCrud): model = ComposicaoColigacao @@ -66,6 +159,10 @@ class ComposicaoColigacaoCrud(MasterDetailCrud): class ListView(MasterDetailCrud.ListView): ordering = '-partido__sigla' + class BaseMixin(PermissionRequiredMixin, MasterDetailCrud.BaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + class LegislaturaCrud(Crud): model = Legislatura @@ -77,36 +174,72 @@ class LegislaturaCrud(Crud): class UpdateView(CrudUpdateView): form_class = LegislaturaForm + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + class FiliacaoCrud(MasterDetailCrud): model = Filiacao parent_field = 'parlamentar' help_path = '' - class CreateView(MasterDetailCrud.CreateView): + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): form_class = FiliacaoForm + permission_required = permissoes_parlamentares() - class UpdateView(MasterDetailCrud.UpdateView): + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): form_class = FiliacaoForm + permission_required = permissoes_parlamentares() + + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_parlamentares() class ListView(MasterDetailCrud.ListView): ordering = '-data' +def get_parlamentar_permissions(): + lista_permissoes = [] + cts = ContentType.objects.filter(app_label='parlamentares') + perms_parlamentares = list(Permission.objects.filter( + content_type__in=cts)) + for p in perms_parlamentares: + lista_permissoes.append('parlamentares.' + p.codename) + return set(lista_permissoes) + + class ParlamentarCrud(Crud): model = Parlamentar help_path = '' - class UpdateView(CrudUpdateView): + class DetailView(CrudDetailView): + def get_template_names(self): + usuario = self.request.user + lista_permissoes = get_parlamentar_permissions() + + if usuario.has_perms(lista_permissoes): + return ['crud/detail.html'] + + else: + return ['parlamentares/parlamentar_perfil_publico.html'] + + class UpdateView(PermissionRequiredMixin, CrudUpdateView): form_class = ParlamentarForm + permission_required = permissoes_parlamentares() - class CreateView(CrudCreateView): + class CreateView(PermissionRequiredMixin, CrudCreateView): form_class = ParlamentarCreateForm + permission_required = permissoes_parlamentares() @property def layout_key(self): return 'ParlamentarCreate' + class DeleteView(PermissionRequiredMixin, CrudDeleteView): + form_class = ParlamentarCreateForm + permission_required = permissoes_parlamentares() + class ListView(CrudListView): template_name = "parlamentares/parlamentares_list.html" paginate_by = None @@ -142,6 +275,9 @@ class ParlamentarCrud(Crud): partido = _('Sem Partido') parlamentar = [ + ("" if m.parlamentar.fotografia + else '', ''), (m.parlamentar.nome_parlamentar, m.parlamentar.id), (partido, None), ('Sim' if m.parlamentar.ativo else 'Não', None) @@ -150,7 +286,7 @@ class ParlamentarCrud(Crud): return parlamentares def get_headers(self): - return ['Parlamentar', 'Partido', 'Ativo?'] + return ['', 'Parlamentar', 'Partido', 'Ativo?'] def get_context_data(self, **kwargs): context = super(ParlamentarCrud.ListView, self @@ -165,15 +301,26 @@ class ParlamentarCrud(Crud): return context +def check_permission_mesa(request): + lista_permissoes = [] + cts = ContentType.objects.filter(app_label='parlamentares') + cts = cts.filter(model__icontains='mesa') + perms = list(Permission.objects.filter(content_type__in=cts)) + for p in perms: + lista_permissoes.append('parlamentares.' + p.codename) + + return request.user.has_perms(set(lista_permissoes)) + + class MesaDiretoraView(FormView): template_name = "mesa_diretora/mesa_diretora.html" success_url = reverse_lazy('sapl.parlamentares:mesa_diretora') - # Essa função avisa quando se pode compor uma Mesa Legislativa) + # Essa função avisa quando se pode compor uma Mesa Legislativa def validation(self, request): - mensagem = _("Não há nenhuma Sessão Legislativa cadastrada. \ - Só é possível compor uma Mesa Diretora quando há uma Sessão \ - Legislativa cadastrada.") + mensagem = _('Não há nenhuma Sessão Legislativa cadastrada. ' + + 'Só é possível compor uma Mesa Diretora quando ' + + 'há uma Sessão Legislativa cadastrada.') messages.add_message(request, messages.INFO, mensagem) return self.render_to_response( @@ -217,7 +364,7 @@ class MesaDiretoraView(FormView): }) def post(self, request, *args, **kwargs): - if 'Incluir' in request.POST: + if 'Incluir' in request.POST and check_permission_mesa(request): if (not Legislatura.objects.all() or not SessaoLegislativa.objects.all()): @@ -234,7 +381,7 @@ class MesaDiretoraView(FormView): return redirect('sapl.parlamentares:mesa_diretora') - elif 'Excluir' in request.POST: + elif 'Excluir' in request.POST and check_permission_mesa(request): if (not Legislatura.objects.all() or not SessaoLegislativa.objects.all()): diff --git a/sapl/protocoloadm/forms.py b/sapl/protocoloadm/forms.py index d791ab501..3d9a0be7f 100644 --- a/sapl/protocoloadm/forms.py +++ b/sapl/protocoloadm/forms.py @@ -1,16 +1,18 @@ +from datetime import datetime + import django_filters from crispy_forms.bootstrap import InlineRadios from crispy_forms.helper import FormHelper -from crispy_forms.layout import HTML, Button, Field, Fieldset, Layout, Submit +from crispy_forms.layout import HTML, Button, Fieldset, Layout, Submit from django import forms -from django.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.db import models from django.forms import ModelForm from django.utils.translation import ugettext_lazy as _ from sapl.crispy_layout_mixin import form_actions, to_row from sapl.materia.forms import RangeWidgetOverride -from sapl.materia.models import Autor +from sapl.materia.models import Autor, UnidadeTramitacao from sapl.utils import RANGE_ANOS, autor_label, autor_modal from .models import (DocumentoAcessorioAdministrativo, DocumentoAdministrativo, @@ -494,7 +496,6 @@ class TramitacaoAdmForm(ModelForm): 'data_encaminhamento', 'data_fim_prazo', 'texto', - 'documento', ] widgets = { @@ -503,22 +504,78 @@ class TramitacaoAdmForm(ModelForm): 'data_fim_prazo': forms.DateInput(format='%d/%m/%Y'), } - def __init__(self, *args, **kwargs): - self.helper = FormHelper() - self.helper.layout = Layout( - Fieldset(_('Incluir Tramitação'), - 'data_tramitacao', - 'unidade_tramitacao_local', - 'status', - 'unidade_tramitacao_destino', - 'data_encaminhamento', - 'data_fim_prazo', - 'texto'), - Field('documento', type="hidden"), - form_actions() - ) - super(TramitacaoAdmForm, self).__init__( - *args, **kwargs) + def clean(self): + data_enc_form = self.cleaned_data['data_encaminhamento'] + data_prazo_form = self.cleaned_data['data_fim_prazo'] + data_tram_form = self.cleaned_data['data_tramitacao'] + + if self.errors: + return self.errors + + ultima_tramitacao = TramitacaoAdministrativo.objects.filter( + documento_id=self.instance.documento_id).exclude( + id=self.instance.id).last() + + if not self.instance.data_tramitacao: + + if ultima_tramitacao: + destino = ultima_tramitacao.unidade_tramitacao_destino + if (destino != self.cleaned_data['unidade_tramitacao_local']): + msg = _('A origem da nova tramitação deve ser igual ao ' + 'destino da última adicionada!') + raise ValidationError(msg) + + if self.cleaned_data['data_tramitacao'] > datetime.now().date(): + msg = _( + 'A data de tramitação deve ser ' + + 'menor ou igual a data de hoje!') + raise ValidationError(msg) + + if (ultima_tramitacao and + data_tram_form < ultima_tramitacao.data_tramitacao): + msg = _('A data da nova tramitação deve ser ' + + 'maior que a data da última tramitação!') + raise ValidationError(msg) + + if data_enc_form < data_tram_form or data_prazo_form < data_tram_form: + msg = _('A data fim de prazo e encaminhamento devem ser ' + + 'maiores que a data de tramitação!') + raise ValidationError(msg) + + return self.cleaned_data + + +class TramitacaoAdmEditForm(TramitacaoAdmForm): + + unidade_tramitacao_local = forms.ModelChoiceField( + queryset=UnidadeTramitacao.objects.all(), + widget=forms.HiddenInput()) + + data_tramitacao = forms.DateField(widget=forms.HiddenInput()) + + class Meta: + model = TramitacaoAdministrativo + fields = ['data_tramitacao', + 'unidade_tramitacao_local', + 'status', + 'unidade_tramitacao_destino', + 'data_encaminhamento', + 'data_fim_prazo', + 'texto', + ] + + widgets = { + 'data_encaminhamento': forms.DateInput(format='%d/%m/%Y'), + 'data_fim_prazo': forms.DateInput(format='%d/%m/%Y'), + } + + def clean(self): + local = self.instance.unidade_tramitacao_local + data_tram = self.instance.data_tramitacao + + self.cleaned_data['data_tramitacao'] = data_tram + self.cleaned_data['unidade_tramitacao_local'] = local + return super(TramitacaoAdmEditForm, self).clean() class DocumentoAdministrativoForm(ModelForm): diff --git a/sapl/protocoloadm/migrations/0001_initial.py b/sapl/protocoloadm/migrations/0001_initial.py index a9f0277e4..5f9a851f8 100644 --- a/sapl/protocoloadm/migrations/0001_initial.py +++ b/sapl/protocoloadm/migrations/0001_initial.py @@ -1,147 +1,163 @@ # -*- coding: utf-8 -*- +# Generated by Django 1.9.5 on 2016-06-24 14:31 from __future__ import unicode_literals from django.db import migrations, models +import django.db.models.deletion +import sapl.protocoloadm.models class Migration(migrations.Migration): + initial = True + dependencies = [ - ('materia', '0001_initial'), + ('materia', '0038_auto_20160612_1506'), ] operations = [ migrations.CreateModel( name='DocumentoAcessorioAdministrativo', fields=[ - ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('nome', models.CharField(max_length=30, verbose_name='Nome')), - ('arquivo', models.CharField(max_length=100, verbose_name='Arquivo')), + ('arquivo', models.FileField(blank=True, null=True, upload_to=sapl.protocoloadm.models.texto_upload_path, verbose_name='Arquivo')), ('data', models.DateField(blank=True, null=True, verbose_name='Data')), - ('autor', models.CharField(blank=True, max_length=50, null=True, verbose_name='Autor')), - ('assunto', models.TextField(blank=True, null=True, verbose_name='Assunto')), - ('indexacao', models.TextField(blank=True, null=True)), + ('autor', models.CharField(blank=True, max_length=50, verbose_name='Autor')), + ('assunto', models.TextField(blank=True, verbose_name='Assunto')), + ('indexacao', models.TextField(blank=True)), ], options={ - 'verbose_name_plural': 'Documentos Acessórios', 'verbose_name': 'Documento Acessório', + 'verbose_name_plural': 'Documentos Acessórios', }, ), migrations.CreateModel( name='DocumentoAdministrativo', fields=[ - ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)), - ('numero', models.IntegerField(verbose_name='Número')), - ('ano', models.SmallIntegerField(verbose_name='Ano')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('numero', models.PositiveIntegerField(verbose_name='Número')), + ('ano', models.PositiveSmallIntegerField(choices=[(2016, 2016), (2015, 2015), (2014, 2014), (2013, 2013), (2012, 2012), (2011, 2011), (2010, 2010), (2009, 2009), (2008, 2008), (2007, 2007), (2006, 2006), (2005, 2005), (2004, 2004), (2003, 2003), (2002, 2002), (2001, 2001), (2000, 2000), (1999, 1999), (1998, 1998), (1997, 1997), (1996, 1996), (1995, 1995), (1994, 1994), (1993, 1993), (1992, 1992), (1991, 1991), (1990, 1990), (1989, 1989), (1988, 1988), (1987, 1987), (1986, 1986), (1985, 1985), (1984, 1984), (1983, 1983), (1982, 1982), (1981, 1981), (1980, 1980), (1979, 1979), (1978, 1978), (1977, 1977), (1976, 1976), (1975, 1975), (1974, 1974), (1973, 1973), (1972, 1972), (1971, 1971), (1970, 1970), (1969, 1969), (1968, 1968), (1967, 1967), (1966, 1966), (1965, 1965), (1964, 1964), (1963, 1963), (1962, 1962), (1961, 1961), (1960, 1960), (1959, 1959), (1958, 1958), (1957, 1957), (1956, 1956), (1955, 1955), (1954, 1954), (1953, 1953), (1952, 1952), (1951, 1951), (1950, 1950), (1949, 1949), (1948, 1948), (1947, 1947), (1946, 1946), (1945, 1945), (1944, 1944), (1943, 1943), (1942, 1942), (1941, 1941), (1940, 1940), (1939, 1939), (1938, 1938), (1937, 1937), (1936, 1936), (1935, 1935), (1934, 1934), (1933, 1933), (1932, 1932), (1931, 1931), (1930, 1930), (1929, 1929), (1928, 1928), (1927, 1927), (1926, 1926), (1925, 1925), (1924, 1924), (1923, 1923), (1922, 1922), (1921, 1921), (1920, 1920), (1919, 1919), (1918, 1918), (1917, 1917), (1916, 1916), (1915, 1915), (1914, 1914), (1913, 1913), (1912, 1912), (1911, 1911), (1910, 1910), (1909, 1909), (1908, 1908), (1907, 1907), (1906, 1906), (1905, 1905), (1904, 1904), (1903, 1903), (1902, 1902), (1901, 1901), (1900, 1900), (1899, 1899), (1898, 1898), (1897, 1897), (1896, 1896), (1895, 1895), (1894, 1894), (1893, 1893), (1892, 1892), (1891, 1891), (1890, 1890)], verbose_name='Ano')), ('data', models.DateField(verbose_name='Data')), - ('numero_protocolo', models.IntegerField(blank=True, null=True, verbose_name='Núm. Protocolo')), - ('interessado', models.CharField(blank=True, max_length=50, null=True, verbose_name='Interessado')), - ('dias_prazo', models.IntegerField(blank=True, null=True, verbose_name='Dias Prazo')), + ('numero_protocolo', models.PositiveIntegerField(blank=True, null=True, verbose_name='Núm. Protocolo')), + ('interessado', models.CharField(blank=True, max_length=50, verbose_name='Interessado')), + ('dias_prazo', models.PositiveIntegerField(blank=True, null=True, verbose_name='Dias Prazo')), ('data_fim_prazo', models.DateField(blank=True, null=True, verbose_name='Data Fim Prazo')), - ('tramitacao', models.BooleanField(verbose_name='Em Tramitação?')), + ('tramitacao', models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], verbose_name='Em Tramitação?')), ('assunto', models.TextField(verbose_name='Assunto')), - ('observacao', models.TextField(blank=True, null=True, verbose_name='Observação')), - ('autor', models.ForeignKey(blank=True, null=True, to='materia.Autor')), + ('observacao', models.TextField(blank=True, verbose_name='Observação')), + ('texto_integral', models.FileField(blank=True, null=True, upload_to=sapl.protocoloadm.models.texto_upload_path, verbose_name='Texto Integral')), + ('autor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='materia.Autor')), ], options={ - 'verbose_name_plural': 'Documentos Administrativos', 'verbose_name': 'Documento Administrativo', + 'verbose_name_plural': 'Documentos Administrativos', }, ), migrations.CreateModel( name='Protocolo', fields=[ - ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)), - ('numero', models.IntegerField(blank=True, null=True, verbose_name='Número do Protocolo')), - ('ano', models.SmallIntegerField()), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('numero', models.PositiveIntegerField(verbose_name='Número de Protocolo')), + ('ano', models.PositiveSmallIntegerField(choices=[(2016, 2016), (2015, 2015), (2014, 2014), (2013, 2013), (2012, 2012), (2011, 2011), (2010, 2010), (2009, 2009), (2008, 2008), (2007, 2007), (2006, 2006), (2005, 2005), (2004, 2004), (2003, 2003), (2002, 2002), (2001, 2001), (2000, 2000), (1999, 1999), (1998, 1998), (1997, 1997), (1996, 1996), (1995, 1995), (1994, 1994), (1993, 1993), (1992, 1992), (1991, 1991), (1990, 1990), (1989, 1989), (1988, 1988), (1987, 1987), (1986, 1986), (1985, 1985), (1984, 1984), (1983, 1983), (1982, 1982), (1981, 1981), (1980, 1980), (1979, 1979), (1978, 1978), (1977, 1977), (1976, 1976), (1975, 1975), (1974, 1974), (1973, 1973), (1972, 1972), (1971, 1971), (1970, 1970), (1969, 1969), (1968, 1968), (1967, 1967), (1966, 1966), (1965, 1965), (1964, 1964), (1963, 1963), (1962, 1962), (1961, 1961), (1960, 1960), (1959, 1959), (1958, 1958), (1957, 1957), (1956, 1956), (1955, 1955), (1954, 1954), (1953, 1953), (1952, 1952), (1951, 1951), (1950, 1950), (1949, 1949), (1948, 1948), (1947, 1947), (1946, 1946), (1945, 1945), (1944, 1944), (1943, 1943), (1942, 1942), (1941, 1941), (1940, 1940), (1939, 1939), (1938, 1938), (1937, 1937), (1936, 1936), (1935, 1935), (1934, 1934), (1933, 1933), (1932, 1932), (1931, 1931), (1930, 1930), (1929, 1929), (1928, 1928), (1927, 1927), (1926, 1926), (1925, 1925), (1924, 1924), (1923, 1923), (1922, 1922), (1921, 1921), (1920, 1920), (1919, 1919), (1918, 1918), (1917, 1917), (1916, 1916), (1915, 1915), (1914, 1914), (1913, 1913), (1912, 1912), (1911, 1911), (1910, 1910), (1909, 1909), (1908, 1908), (1907, 1907), (1906, 1906), (1905, 1905), (1904, 1904), (1903, 1903), (1902, 1902), (1901, 1901), (1900, 1900), (1899, 1899), (1898, 1898), (1897, 1897), (1896, 1896), (1895, 1895), (1894, 1894), (1893, 1893), (1892, 1892), (1891, 1891), (1890, 1890)], verbose_name='Ano do Protocolo')), ('data', models.DateField()), ('hora', models.TimeField()), ('timestamp', models.DateTimeField()), - ('tipo_protocolo', models.IntegerField(verbose_name='Tipo de Protocolo')), - ('tipo_processo', models.IntegerField()), - ('interessado', models.CharField(blank=True, max_length=60, null=True, verbose_name='Interessado')), - ('assunto_ementa', models.TextField(blank=True, null=True)), - ('numero_paginas', models.IntegerField(blank=True, null=True, verbose_name='Número de Páginas')), - ('observacao', models.TextField(blank=True, null=True, verbose_name='Observação')), + ('tipo_protocolo', models.PositiveIntegerField(verbose_name='Tipo de Protocolo')), + ('tipo_processo', models.PositiveIntegerField()), + ('interessado', models.CharField(blank=True, max_length=60, verbose_name='Interessado')), + ('assunto_ementa', models.TextField(blank=True)), + ('numero_paginas', models.PositiveIntegerField(blank=True, null=True, verbose_name='Número de Páginas')), + ('observacao', models.TextField(blank=True, verbose_name='Observação')), ('anulado', models.BooleanField()), - ('user_anulacao', models.CharField(blank=True, max_length=20, null=True)), - ('ip_anulacao', models.CharField(blank=True, max_length=15, null=True)), - ('justificativa_anulacao', models.CharField(blank=True, max_length=60, null=True)), + ('user_anulacao', models.CharField(blank=True, max_length=20)), + ('ip_anulacao', models.CharField(blank=True, max_length=15)), + ('justificativa_anulacao', models.CharField(blank=True, max_length=60, verbose_name='Motivo')), ('timestamp_anulacao', models.DateTimeField(blank=True, null=True)), - ('autor', models.ForeignKey(blank=True, null=True, to='materia.Autor')), + ('autor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='materia.Autor')), ], options={ - 'verbose_name_plural': 'Protocolos', 'verbose_name': 'Protocolo', + 'verbose_name_plural': 'Protocolos', }, ), migrations.CreateModel( name='StatusTramitacaoAdministrativo', fields=[ - ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('sigla', models.CharField(max_length=10, verbose_name='Sigla')), ('descricao', models.CharField(max_length=60, verbose_name='Descrição')), ('indicador', models.CharField(choices=[('F', 'Fim'), ('R', 'Retorno')], max_length=1, verbose_name='Indicador da Tramitação')), ], options={ - 'verbose_name_plural': 'Status de Tramitação', 'verbose_name': 'Status de Tramitação', + 'verbose_name_plural': 'Status de Tramitação', }, ), migrations.CreateModel( name='TipoDocumentoAdministrativo', fields=[ - ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('sigla', models.CharField(max_length=5, verbose_name='Sigla')), ('descricao', models.CharField(max_length=50, verbose_name='Descrição')), ], options={ - 'verbose_name_plural': 'Tipos de Documento Administrativo', 'verbose_name': 'Tipo de Documento Administrativo', + 'verbose_name_plural': 'Tipos de Documento Administrativo', + }, + ), + migrations.CreateModel( + name='TipoInstituicao', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('descricao', models.CharField(max_length=50, verbose_name='Descrição')), + ], + options={ + 'verbose_name': 'Tipo de Instituição', + 'verbose_name_plural': 'Tipos de Instituições', }, ), migrations.CreateModel( name='TramitacaoAdministrativo', fields=[ - ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)), - ('data_tramitacao', models.DateField(blank=True, null=True, verbose_name='Data Tramitação')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('data_tramitacao', models.DateField(verbose_name='Data Tramitação')), ('data_encaminhamento', models.DateField(blank=True, null=True, verbose_name='Data Encaminhamento')), - ('ultima', models.BooleanField()), - ('texto', models.TextField(blank=True, null=True, verbose_name='Texto da Ação')), + ('texto', models.TextField(blank=True, verbose_name='Texto da Ação')), ('data_fim_prazo', models.DateField(blank=True, null=True, verbose_name='Data Fim do Prazo')), - ('documento', models.ForeignKey(to='protocoloadm.DocumentoAdministrativo')), - ('status', models.ForeignKey(blank=True, null=True, to='protocoloadm.StatusTramitacaoAdministrativo', verbose_name='Status')), - ('unidade_tramitacao_destino', models.ForeignKey(blank=True, null=True, to='materia.UnidadeTramitacao', verbose_name='Unidade Destino', related_name='+')), - ('unidade_tramitacao_local', models.ForeignKey(blank=True, null=True, to='materia.UnidadeTramitacao', verbose_name='Unidade Local', related_name='+')), + ('documento', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='protocoloadm.DocumentoAdministrativo')), + ('status', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='protocoloadm.StatusTramitacaoAdministrativo', verbose_name='Status')), + ('unidade_tramitacao_destino', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='adm_tramitacoes_destino', to='materia.UnidadeTramitacao', verbose_name='Unidade Destino')), + ('unidade_tramitacao_local', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='adm_tramitacoes_origem', to='materia.UnidadeTramitacao', verbose_name='Unidade Local')), ], options={ - 'verbose_name_plural': 'Tramitações de Documento Administrativo', 'verbose_name': 'Tramitação de Documento Administrativo', + 'verbose_name_plural': 'Tramitações de Documento Administrativo', }, ), migrations.AddField( model_name='protocolo', name='tipo_documento', - field=models.ForeignKey(blank=True, null=True, to='protocoloadm.TipoDocumentoAdministrativo', verbose_name='Tipo de documento'), + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='protocoloadm.TipoDocumentoAdministrativo', verbose_name='Tipo de documento'), ), migrations.AddField( model_name='protocolo', name='tipo_materia', - field=models.ForeignKey(blank=True, null=True, to='materia.TipoMateriaLegislativa', verbose_name='Tipo Matéria'), + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='materia.TipoMateriaLegislativa', verbose_name='Tipo Matéria'), ), migrations.AddField( model_name='documentoadministrativo', name='tipo', - field=models.ForeignKey(to='protocoloadm.TipoDocumentoAdministrativo', verbose_name='Tipo Documento'), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='protocoloadm.TipoDocumentoAdministrativo', verbose_name='Tipo Documento'), ), migrations.AddField( model_name='documentoacessorioadministrativo', name='documento', - field=models.ForeignKey(to='protocoloadm.DocumentoAdministrativo'), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='protocoloadm.DocumentoAdministrativo'), ), migrations.AddField( model_name='documentoacessorioadministrativo', name='tipo', - field=models.ForeignKey(to='protocoloadm.TipoDocumentoAdministrativo', verbose_name='Tipo'), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='protocoloadm.TipoDocumentoAdministrativo', verbose_name='Tipo'), ), ] diff --git a/sapl/protocoloadm/migrations/0002_auto_20150729_1717.py b/sapl/protocoloadm/migrations/0002_auto_20150729_1717.py deleted file mode 100644 index 253bbb0eb..000000000 --- a/sapl/protocoloadm/migrations/0002_auto_20150729_1717.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('protocoloadm', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='documentoadministrativo', - name='ano', - field=models.PositiveSmallIntegerField(verbose_name='Ano'), - ), - migrations.AlterField( - model_name='documentoadministrativo', - name='dias_prazo', - field=models.PositiveIntegerField(blank=True, verbose_name='Dias Prazo', null=True), - ), - migrations.AlterField( - model_name='documentoadministrativo', - name='numero', - field=models.PositiveIntegerField(verbose_name='Número'), - ), - migrations.AlterField( - model_name='documentoadministrativo', - name='numero_protocolo', - field=models.PositiveIntegerField(blank=True, verbose_name='Núm. Protocolo', null=True), - ), - migrations.AlterField( - model_name='protocolo', - name='ano', - field=models.PositiveSmallIntegerField(), - ), - migrations.AlterField( - model_name='protocolo', - name='numero', - field=models.PositiveIntegerField(blank=True, verbose_name='Número do Protocolo', null=True), - ), - migrations.AlterField( - model_name='protocolo', - name='numero_paginas', - field=models.PositiveIntegerField(blank=True, verbose_name='Número de Páginas', null=True), - ), - migrations.AlterField( - model_name='protocolo', - name='tipo_processo', - field=models.PositiveIntegerField(), - ), - migrations.AlterField( - model_name='protocolo', - name='tipo_protocolo', - field=models.PositiveIntegerField(verbose_name='Tipo de Protocolo'), - ), - ] diff --git a/sapl/protocoloadm/migrations/0003_documentoacessorioadministrativo_texto_integral.py b/sapl/protocoloadm/migrations/0003_documentoacessorioadministrativo_texto_integral.py deleted file mode 100644 index 24aa43a71..000000000 --- a/sapl/protocoloadm/migrations/0003_documentoacessorioadministrativo_texto_integral.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - -import sapl.protocoloadm.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('protocoloadm', '0002_auto_20150729_1717'), - ] - - operations = [ - migrations.AddField( - model_name='documentoacessorioadministrativo', - name='texto_integral', - field=models.FileField(verbose_name='Texto Integral', blank=True, - null=True, upload_to=sapl.protocoloadm.models.texto_upload_path), - ), - ] diff --git a/sapl/protocoloadm/migrations/0004_auto_20151007_1035.py b/sapl/protocoloadm/migrations/0004_auto_20151007_1035.py deleted file mode 100644 index b5bb90d19..000000000 --- a/sapl/protocoloadm/migrations/0004_auto_20151007_1035.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - -import sapl.protocoloadm.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('protocoloadm', - '0003_documentoacessorioadministrativo_texto_integral'), - ] - - operations = [ - migrations.RemoveField( - model_name='documentoacessorioadministrativo', - name='texto_integral', - ), - migrations.AddField( - model_name='documentoadministrativo', - name='texto_integral', - field=models.FileField( - blank=True, null=True, upload_to=sapl.protocoloadm.models.texto_upload_path, verbose_name='Texto Integral'), - ), - ] diff --git a/sapl/protocoloadm/migrations/0005_auto_20151008_0744.py b/sapl/protocoloadm/migrations/0005_auto_20151008_0744.py deleted file mode 100644 index b52937c7f..000000000 --- a/sapl/protocoloadm/migrations/0005_auto_20151008_0744.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - -import sapl.protocoloadm.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('protocoloadm', '0004_auto_20151007_1035'), - ] - - operations = [ - migrations.AlterField( - model_name='documentoacessorioadministrativo', - name='arquivo', - field=models.FileField( - blank=True, null=True, upload_to=sapl.protocoloadm.models.texto_upload_path, verbose_name='Arquivo'), - ), - ] diff --git a/sapl/protocoloadm/migrations/0006_auto_20160216_1015.py b/sapl/protocoloadm/migrations/0006_auto_20160216_1015.py deleted file mode 100644 index 05791b6bd..000000000 --- a/sapl/protocoloadm/migrations/0006_auto_20160216_1015.py +++ /dev/null @@ -1,74 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('protocoloadm', '0005_auto_20151008_0744'), - ] - - operations = [ - migrations.AlterField( - model_name='documentoacessorioadministrativo', - name='assunto', - field=models.TextField(verbose_name='Assunto', blank=True), - ), - migrations.AlterField( - model_name='documentoacessorioadministrativo', - name='autor', - field=models.CharField(verbose_name='Autor', max_length=50, blank=True), - ), - migrations.AlterField( - model_name='documentoacessorioadministrativo', - name='indexacao', - field=models.TextField(blank=True), - ), - migrations.AlterField( - model_name='documentoadministrativo', - name='interessado', - field=models.CharField(verbose_name='Interessado', max_length=50, blank=True), - ), - migrations.AlterField( - model_name='documentoadministrativo', - name='observacao', - field=models.TextField(verbose_name='Observação', blank=True), - ), - migrations.AlterField( - model_name='protocolo', - name='assunto_ementa', - field=models.TextField(blank=True), - ), - migrations.AlterField( - model_name='protocolo', - name='interessado', - field=models.CharField(verbose_name='Interessado', max_length=60, blank=True), - ), - migrations.AlterField( - model_name='protocolo', - name='ip_anulacao', - field=models.CharField(max_length=15, blank=True), - ), - migrations.AlterField( - model_name='protocolo', - name='justificativa_anulacao', - field=models.CharField(max_length=60, blank=True), - ), - migrations.AlterField( - model_name='protocolo', - name='observacao', - field=models.TextField(verbose_name='Observação', blank=True), - ), - migrations.AlterField( - model_name='protocolo', - name='user_anulacao', - field=models.CharField(max_length=20, blank=True), - ), - migrations.AlterField( - model_name='tramitacaoadministrativo', - name='texto', - field=models.TextField(verbose_name='Texto da Ação', blank=True), - ), - ] diff --git a/sapl/protocoloadm/migrations/0007_auto_20160218_1429.py b/sapl/protocoloadm/migrations/0007_auto_20160218_1429.py deleted file mode 100644 index 51f753b83..000000000 --- a/sapl/protocoloadm/migrations/0007_auto_20160218_1429.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('protocoloadm', '0006_auto_20160216_1015'), - ] - - operations = [ - migrations.AlterField( - model_name='documentoadministrativo', - name='tramitacao', - field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], verbose_name='Em Tramitação?'), - ), - ] diff --git a/sapl/protocoloadm/migrations/0008_auto_20160308_1436.py b/sapl/protocoloadm/migrations/0008_auto_20160308_1436.py deleted file mode 100644 index 913921ac3..000000000 --- a/sapl/protocoloadm/migrations/0008_auto_20160308_1436.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2016-03-08 17:36 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('protocoloadm', '0007_auto_20160218_1429'), - ] - - operations = [ - migrations.AlterField( - model_name='protocolo', - name='ano', - field=models.PositiveSmallIntegerField(verbose_name='Ano do Protocolo'), - ), - ] diff --git a/sapl/protocoloadm/migrations/0009_auto_20160309_1323.py b/sapl/protocoloadm/migrations/0009_auto_20160309_1323.py deleted file mode 100644 index aa7760eca..000000000 --- a/sapl/protocoloadm/migrations/0009_auto_20160309_1323.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2016-03-09 16:23 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('protocoloadm', '0008_auto_20160308_1436'), - ] - - operations = [ - migrations.AlterField( - model_name='protocolo', - name='numero', - field=models.PositiveIntegerField(verbose_name='Número de Protocolo'), - ), - ] diff --git a/sapl/protocoloadm/migrations/0010_auto_20160309_1407.py b/sapl/protocoloadm/migrations/0010_auto_20160309_1407.py deleted file mode 100644 index b6e22f20c..000000000 --- a/sapl/protocoloadm/migrations/0010_auto_20160309_1407.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2016-03-09 17:07 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('protocoloadm', '0009_auto_20160309_1323'), - ] - - operations = [ - migrations.AlterField( - model_name='documentoadministrativo', - name='ano', - field=models.PositiveSmallIntegerField(choices=[(2016, 2016), (2015, 2015), (2014, 2014), (2013, 2013), (2012, 2012), (2011, 2011), (2010, 2010), (2009, 2009), (2008, 2008), (2007, 2007), (2006, 2006), (2005, 2005), (2004, 2004), (2003, 2003), (2002, 2002), (2001, 2001), (2000, 2000), (1999, 1999), (1998, 1998), (1997, 1997), (1996, 1996), (1995, 1995), (1994, 1994), (1993, 1993), (1992, 1992), (1991, 1991), (1990, 1990), (1989, 1989), (1988, 1988), (1987, 1987), (1986, 1986), (1985, 1985), (1984, 1984), (1983, 1983), (1982, 1982), (1981, 1981), (1980, 1980), (1979, 1979), (1978, 1978), (1977, 1977), (1976, 1976), (1975, 1975), (1974, 1974), (1973, 1973), (1972, 1972), (1971, 1971), (1970, 1970), (1969, 1969), (1968, 1968), (1967, 1967), (1966, 1966), (1965, 1965), (1964, 1964), (1963, 1963), (1962, 1962), (1961, 1961), (1960, 1960), (1959, 1959), (1958, 1958), (1957, 1957), (1956, 1956), (1955, 1955), (1954, 1954), (1953, 1953), (1952, 1952), (1951, 1951), (1950, 1950), (1949, 1949), (1948, 1948), (1947, 1947), (1946, 1946), (1945, 1945), (1944, 1944), (1943, 1943), (1942, 1942), (1941, 1941), (1940, 1940), (1939, 1939), (1938, 1938), (1937, 1937), (1936, 1936), (1935, 1935), (1934, 1934), (1933, 1933), (1932, 1932), (1931, 1931), (1930, 1930), (1929, 1929), (1928, 1928), (1927, 1927), (1926, 1926), (1925, 1925), (1924, 1924), (1923, 1923), (1922, 1922), (1921, 1921), (1920, 1920), (1919, 1919), (1918, 1918), (1917, 1917), (1916, 1916), (1915, 1915), (1914, 1914), (1913, 1913), (1912, 1912), (1911, 1911), (1910, 1910), (1909, 1909), (1908, 1908), (1907, 1907), (1906, 1906), (1905, 1905), (1904, 1904), (1903, 1903), (1902, 1902), (1901, 1901), (1900, 1900), (1899, 1899), (1898, 1898), (1897, 1897), (1896, 1896), (1895, 1895), (1894, 1894), (1893, 1893), (1892, 1892), (1891, 1891), (1890, 1890)], verbose_name='Ano'), - ), - migrations.AlterField( - model_name='protocolo', - name='ano', - field=models.PositiveSmallIntegerField(choices=[(2016, 2016), (2015, 2015), (2014, 2014), (2013, 2013), (2012, 2012), (2011, 2011), (2010, 2010), (2009, 2009), (2008, 2008), (2007, 2007), (2006, 2006), (2005, 2005), (2004, 2004), (2003, 2003), (2002, 2002), (2001, 2001), (2000, 2000), (1999, 1999), (1998, 1998), (1997, 1997), (1996, 1996), (1995, 1995), (1994, 1994), (1993, 1993), (1992, 1992), (1991, 1991), (1990, 1990), (1989, 1989), (1988, 1988), (1987, 1987), (1986, 1986), (1985, 1985), (1984, 1984), (1983, 1983), (1982, 1982), (1981, 1981), (1980, 1980), (1979, 1979), (1978, 1978), (1977, 1977), (1976, 1976), (1975, 1975), (1974, 1974), (1973, 1973), (1972, 1972), (1971, 1971), (1970, 1970), (1969, 1969), (1968, 1968), (1967, 1967), (1966, 1966), (1965, 1965), (1964, 1964), (1963, 1963), (1962, 1962), (1961, 1961), (1960, 1960), (1959, 1959), (1958, 1958), (1957, 1957), (1956, 1956), (1955, 1955), (1954, 1954), (1953, 1953), (1952, 1952), (1951, 1951), (1950, 1950), (1949, 1949), (1948, 1948), (1947, 1947), (1946, 1946), (1945, 1945), (1944, 1944), (1943, 1943), (1942, 1942), (1941, 1941), (1940, 1940), (1939, 1939), (1938, 1938), (1937, 1937), (1936, 1936), (1935, 1935), (1934, 1934), (1933, 1933), (1932, 1932), (1931, 1931), (1930, 1930), (1929, 1929), (1928, 1928), (1927, 1927), (1926, 1926), (1925, 1925), (1924, 1924), (1923, 1923), (1922, 1922), (1921, 1921), (1920, 1920), (1919, 1919), (1918, 1918), (1917, 1917), (1916, 1916), (1915, 1915), (1914, 1914), (1913, 1913), (1912, 1912), (1911, 1911), (1910, 1910), (1909, 1909), (1908, 1908), (1907, 1907), (1906, 1906), (1905, 1905), (1904, 1904), (1903, 1903), (1902, 1902), (1901, 1901), (1900, 1900), (1899, 1899), (1898, 1898), (1897, 1897), (1896, 1896), (1895, 1895), (1894, 1894), (1893, 1893), (1892, 1892), (1891, 1891), (1890, 1890)], verbose_name='Ano do Protocolo'), - ), - ] diff --git a/sapl/protocoloadm/migrations/0011_auto_20160318_1504.py b/sapl/protocoloadm/migrations/0011_auto_20160318_1504.py deleted file mode 100644 index e887fafb5..000000000 --- a/sapl/protocoloadm/migrations/0011_auto_20160318_1504.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2016-03-18 18:04 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('protocoloadm', '0010_auto_20160309_1407'), - ] - - operations = [ - migrations.AlterField( - model_name='protocolo', - name='justificativa_anulacao', - field=models.CharField(blank=True, max_length=60, verbose_name='Motivo'), - ), - ] diff --git a/sapl/protocoloadm/migrations/0012_auto_20160503_0926.py b/sapl/protocoloadm/migrations/0012_auto_20160503_0926.py deleted file mode 100644 index cc7da204c..000000000 --- a/sapl/protocoloadm/migrations/0012_auto_20160503_0926.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.5 on 2016-05-03 12:26 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('protocoloadm', '0011_auto_20160318_1504'), - ] - - operations = [ - migrations.AlterField( - model_name='tramitacaoadministrativo', - name='unidade_tramitacao_destino', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='adm_tramitacoes_destino', to='materia.UnidadeTramitacao', verbose_name='Unidade Destino'), - ), - migrations.AlterField( - model_name='tramitacaoadministrativo', - name='unidade_tramitacao_local', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='adm_tramitacoes_origem', to='materia.UnidadeTramitacao', verbose_name='Unidade Local'), - ), - ] diff --git a/sapl/protocoloadm/migrations/0012_tipoinstituicao.py b/sapl/protocoloadm/migrations/0012_tipoinstituicao.py deleted file mode 100644 index 91884018a..000000000 --- a/sapl/protocoloadm/migrations/0012_tipoinstituicao.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.5 on 2016-05-17 18:07 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('protocoloadm', '0011_auto_20160318_1504'), - ] - - operations = [ - migrations.CreateModel( - name='TipoInstituicao', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('descricao', models.CharField(max_length=50, verbose_name='Descrição')), - ], - options={ - 'verbose_name': 'Tipo de Instituição', - 'verbose_name_plural': 'Tipos de Instituições', - }, - ), - ] diff --git a/sapl/protocoloadm/models.py b/sapl/protocoloadm/models.py index 4d5c0b248..013ba89e2 100644 --- a/sapl/protocoloadm/models.py +++ b/sapl/protocoloadm/models.py @@ -167,16 +167,12 @@ class StatusTramitacaoAdministrativo(models.Model): class TramitacaoAdministrativo(models.Model): status = models.ForeignKey( StatusTramitacaoAdministrativo, - blank=True, - null=True, verbose_name=_('Status')) documento = models.ForeignKey(DocumentoAdministrativo) data_tramitacao = models.DateField( - blank=True, null=True, verbose_name=_('Data Tramitação')) + verbose_name=_('Data Tramitação')) unidade_tramitacao_local = models.ForeignKey( UnidadeTramitacao, - blank=True, - null=True, related_name='adm_tramitacoes_origem', verbose_name=_('Unidade Local')) data_encaminhamento = models.DateField( @@ -187,7 +183,6 @@ class TramitacaoAdministrativo(models.Model): null=True, related_name='adm_tramitacoes_destino', verbose_name=_('Unidade Destino')) - ultima = models.BooleanField() texto = models.TextField( blank=True, verbose_name=_('Texto da Ação')) data_fim_prazo = models.DateField( diff --git a/sapl/protocoloadm/tests/test_protocoloadm.py b/sapl/protocoloadm/tests/test_protocoloadm.py index 40dccb2dc..b4d39942e 100644 --- a/sapl/protocoloadm/tests/test_protocoloadm.py +++ b/sapl/protocoloadm/tests/test_protocoloadm.py @@ -8,22 +8,22 @@ from sapl.protocoloadm.models import Protocolo @pytest.mark.django_db(transaction=False) -def test_anular_protocolo_acessivel(client): - response = client.get(reverse('sapl.protocoloadm:anular_protocolo')) +def test_anular_protocolo_acessivel(admin_client): + response = admin_client.get(reverse('sapl.protocoloadm:anular_protocolo')) assert response.status_code == 200 @pytest.mark.django_db(transaction=False) -def test_anular_protocolo_submit(client): +def test_anular_protocolo_submit(admin_client): mommy.make(Protocolo, numero='76', ano='2016', anulado=False) # TODO: setar usuario e IP - response = client.post(reverse('sapl.protocoloadm:anular_protocolo'), - {'numero': '76', - 'ano': '2016', - 'justificativa_anulacao': 'TESTE', - 'salvar': 'Anular'}, - follow=True) + response = admin_client.post(reverse('sapl.protocoloadm:anular_protocolo'), + {'numero': '76', + 'ano': '2016', + 'justificativa_anulacao': 'TESTE', + 'salvar': 'Anular'}, + follow=True) assert response.status_code == 200 diff --git a/sapl/protocoloadm/urls.py b/sapl/protocoloadm/urls.py index 6108b7847..1c24d0e1a 100644 --- a/sapl/protocoloadm/urls.py +++ b/sapl/protocoloadm/urls.py @@ -17,12 +17,7 @@ from sapl.protocoloadm.views import (AnularProtocoloAdmView, ProtocoloPesquisaView, StatusTramitacaoAdministrativoCrud, TipoDocumentoAdministrativoCrud, - TipoInstituicaoCrud, - TramitacaoAdmDeleteView, - TramitacaoAdmEditView, - TramitacaoAdmIncluirView, - TramitacaoAdministrativoCrud, - TramitacaoAdmView) + TipoInstituicaoCrud) from .apps import AppConfig @@ -39,8 +34,6 @@ urlpatterns = [ include(StatusTramitacaoAdministrativoCrud.get_urls())), url(r'^protocoloadm/tipo-instituicao/', include(TipoInstituicaoCrud.get_urls())), - url(r'^protocoloadm/tramitacao-adm/', - include(TramitacaoAdministrativoCrud.get_urls())), url(r'^protocoloadm/protocolo-doc/', include(ProtocoloDocumentoCrud.get_urls())), url(r'^protocoloadm/protocolo-mat/', @@ -67,14 +60,6 @@ urlpatterns = [ DocumentoAcessorioAdministrativoEditView.as_view(), name='doc_ace_adm_edit'), - url(r'^protocoloadm/(?P\d+)/tramitacao$', - TramitacaoAdmView.as_view(), name='tramitacao_adm'), - url(r'^protocoloadm/(?P\d+)/tramitacao_incluir', - TramitacaoAdmIncluirView.as_view(), name='tramitacao_incluir'), - url(r'^protocoloadm/(?P\d+)/tramitacao_edit', - TramitacaoAdmEditView.as_view(), name='tramitacao_edit'), - url(r'^protocoloadm/(?P\d+)/tramitacao_delete/(?P\d+)', - TramitacaoAdmDeleteView.as_view(), name='tramitacao_delete'), url(r'^protocoloadm/(?P\d+)/(?P\d+)/comprovante$', ComprovanteProtocoloView.as_view(), name='comprovante_protocolo'), diff --git a/sapl/protocoloadm/views.py b/sapl/protocoloadm/views.py index 9d86f0dde..ca549aba3 100644 --- a/sapl/protocoloadm/views.py +++ b/sapl/protocoloadm/views.py @@ -2,6 +2,7 @@ import json from datetime import date, datetime from braces.views import FormValidMessageMixin +from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.exceptions import ObjectDoesNotExist from django.core.urlresolvers import reverse from django.db.models import Max, Q @@ -12,14 +13,20 @@ from django.views.generic import CreateView, DetailView, FormView, ListView from django.views.generic.base import TemplateView from django_filters.views import FilterView -from sapl.crud.base import Crud, CrudBaseMixin, CrudListView, make_pagination +import sapl.crud.base +from sapl.crud.base import (Crud, CrudBaseMixin, CrudCreateView, + CrudDeleteView, CrudListView, CrudUpdateView, + make_pagination) +from sapl.crud.masterdetail import MasterDetailCrud from sapl.materia.models import TipoMateriaLegislativa -from sapl.utils import create_barcode, get_client_ip +from sapl.utils import (create_barcode, get_client_ip, permissoes_adm, + permissoes_protocoloadm) from .forms import (AnularProcoloAdmForm, DocumentoAcessorioAdministrativoForm, DocumentoAdministrativoFilterSet, - DocumentoAdministrativoForm, ProtocoloDocumentForm, - ProtocoloFilterSet, ProtocoloMateriaForm, + DocumentoAdministrativoForm, + ProtocoloDocumentForm, ProtocoloFilterSet, + ProtocoloMateriaForm, TramitacaoAdmEditForm, TramitacaoAdmForm) from .models import (Autor, DocumentoAcessorioAdministrativo, DocumentoAdministrativo, Protocolo, @@ -28,16 +35,33 @@ from .models import (Autor, DocumentoAcessorioAdministrativo, TramitacaoAdministrativo) TipoDocumentoAdministrativoCrud = Crud.build(TipoDocumentoAdministrativo, '') -DocumentoAdministrativoCrud = Crud.build(DocumentoAdministrativo, '') DocumentoAcessorioAdministrativoCrud = Crud.build( DocumentoAcessorioAdministrativo, '') -TramitacaoAdministrativoCrud = Crud.build(TramitacaoAdministrativo, '') ProtocoloDocumentoCrud = Crud.build(Protocolo, '') # FIXME precisa de uma chave diferente para o layout ProtocoloMateriaCrud = Crud.build(Protocolo, '') TipoInstituicaoCrud = Crud.build(TipoInstituicao, '') +class DocumentoAdministrativoCrud(Crud): + model = DocumentoAdministrativo + help_path = '' + + class BaseMixin(sapl.crud.base.CrudBaseMixin): + list_field_names = ['tipo', 'numero', 'ano', 'data', + 'numero_protocolo', 'assunto', + 'interessado', 'tramitacao', 'texto_integral'] + + class CreateView(PermissionRequiredMixin, CrudCreateView): + permission_required = permissoes_adm() + + class UpdateView(PermissionRequiredMixin, CrudUpdateView): + permission_required = permissoes_adm() + + class DeleteView(PermissionRequiredMixin, CrudDeleteView): + permission_required = permissoes_adm() + + class StatusTramitacaoAdministrativoCrud(Crud): model = StatusTramitacaoAdministrativo help_path = '' @@ -48,11 +72,21 @@ class StatusTramitacaoAdministrativoCrud(Crud): class ListView(CrudListView): ordering = 'sigla' + class CreateView(PermissionRequiredMixin, CrudCreateView): + permission_required = permissoes_adm() + + class UpdateView(PermissionRequiredMixin, CrudUpdateView): + permission_required = permissoes_adm() + + class DeleteView(PermissionRequiredMixin, CrudDeleteView): + permission_required = permissoes_adm() + -class ProtocoloPesquisaView(FilterView): +class ProtocoloPesquisaView(PermissionRequiredMixin, FilterView): model = Protocolo filterset_class = ProtocoloFilterSet paginate_by = 10 + permission_required = permissoes_protocoloadm() def get_filterset_kwargs(self, filterset_class): super(ProtocoloPesquisaView, @@ -108,11 +142,12 @@ class ProtocoloPesquisaView(FilterView): return self.render_to_response(context) -class ProtocoloListView(ListView): +class ProtocoloListView(PermissionRequiredMixin, ListView): template_name = 'protocoloadm/protocolo_list.html' context_object_name = 'protocolos' model = Protocolo paginate_by = 10 + permission_required = permissoes_protocoloadm() def get_queryset(self): kwargs = self.request.session['kwargs'] @@ -131,10 +166,11 @@ class ProtocoloListView(ListView): return context -class AnularProtocoloAdmView(CreateView): +class AnularProtocoloAdmView(PermissionRequiredMixin, CreateView): template_name = 'protocoloadm/anular_protocoloadm.html' form_class = AnularProcoloAdmForm form_valid_message = _('Protocolo anulado com sucesso!') + permission_required = permissoes_protocoloadm() def get_success_url(self): return reverse('sapl.protocoloadm:protocolo') @@ -157,10 +193,13 @@ class AnularProtocoloAdmView(CreateView): return redirect(self.get_success_url()) -class ProtocoloDocumentoView(FormValidMessageMixin, CreateView): +class ProtocoloDocumentoView(PermissionRequiredMixin, + FormValidMessageMixin, + CreateView): template_name = "protocoloadm/protocolar_documento.html" form_class = ProtocoloDocumentForm form_valid_message = _('Protocolo cadastrado com sucesso!') + permission_required = permissoes_protocoloadm() def get_success_url(self): return reverse('sapl.protocoloadm:protocolo') @@ -189,9 +228,10 @@ class ProtocoloDocumentoView(FormValidMessageMixin, CreateView): return redirect(self.get_success_url()) -class CriarDocumentoProtocolo(CreateView): +class CriarDocumentoProtocolo(PermissionRequiredMixin, CreateView): template_name = "protocoloadm/criar_documento.html" form_class = DocumentoAdministrativoForm + permission_required = permissoes_protocoloadm() def get_initial(self): numero = self.kwargs['pk'] @@ -226,9 +266,10 @@ class CriarDocumentoProtocolo(CreateView): return doc -class ProtocoloMostrarView(TemplateView): +class ProtocoloMostrarView(PermissionRequiredMixin, TemplateView): template_name = "protocoloadm/protocolo_mostrar.html" + permission_required = permissoes_protocoloadm() def get_context_data(self, **kwargs): context = super(ProtocoloMostrarView, self).get_context_data(**kwargs) @@ -239,9 +280,10 @@ class ProtocoloMostrarView(TemplateView): return context -class ComprovanteProtocoloView(TemplateView): +class ComprovanteProtocoloView(PermissionRequiredMixin, TemplateView): template_name = "protocoloadm/comprovante.html" + permission_required = permissoes_protocoloadm() def get_context_data(self, **kwargs): context = super(ComprovanteProtocoloView, self).get_context_data( @@ -267,11 +309,12 @@ class ComprovanteProtocoloView(TemplateView): return context -class ProtocoloMateriaView(CreateView): +class ProtocoloMateriaView(PermissionRequiredMixin, CreateView): template_name = "protocoloadm/protocolar_materia.html" form_class = ProtocoloMateriaForm form_valid_message = _('Matéria cadastrada com sucesso!') + permission_required = permissoes_protocoloadm() def get_success_url(self): return reverse('sapl.protocoloadm:protocolo') @@ -306,10 +349,12 @@ class ProtocoloMateriaView(CreateView): return redirect(self.get_success_url()) -class PesquisarDocumentoAdministrativoView(FilterView): +class PesquisarDocumentoAdministrativoView(PermissionRequiredMixin, + FilterView): model = DocumentoAdministrativo filterset_class = DocumentoAdministrativoFilterSet paginate_by = 10 + permission_required = permissoes_adm() def get_filterset_kwargs(self, filterset_class): super(PesquisarDocumentoAdministrativoView, @@ -365,8 +410,9 @@ class PesquisarDocumentoAdministrativoView(FilterView): return self.render_to_response(context) -class DetailDocumentoAdministrativo(DetailView): +class DetailDocumentoAdministrativo(PermissionRequiredMixin, DetailView): template_name = "protocoloadm/detail_doc_adm.html" + permission_required = permissoes_adm() def get(self, request, *args, **kwargs): documento = DocumentoAdministrativo.objects.get( @@ -405,8 +451,10 @@ class DetailDocumentoAdministrativo(DetailView): 'pk': self.kwargs['pk']}) -class DocumentoAcessorioAdministrativoEditView(FormView): +class DocumentoAcessorioAdministrativoEditView(PermissionRequiredMixin, + FormView): template_name = "protocoloadm/documento_acessorio_administrativo_edit.html" + permission_required = permissoes_adm() def get(self, request, *args, **kwargs): doc = DocumentoAdministrativo.objects.get( @@ -452,8 +500,9 @@ class DocumentoAcessorioAdministrativoEditView(FormView): return reverse('sapl.protocoloadm:doc_ace_adm', kwargs={'pk': pk}) -class DocumentoAcessorioAdministrativoView(FormView): +class DocumentoAcessorioAdministrativoView(PermissionRequiredMixin, FormView): template_name = "protocoloadm/documento_acessorio_administrativo.html" + permission_required = permissoes_adm() def get(self, request, *args, **kwargs): form = DocumentoAcessorioAdministrativoForm() @@ -463,8 +512,8 @@ class DocumentoAcessorioAdministrativoView(FormView): doc_acessorio = DocumentoAcessorioAdministrativo.objects.filter( documento_id=kwargs['pk']) if not doc_acessorio: - doc_ace_null = _('Nenhum documento acessório \ - cadastrado para este processo.') + doc_ace_null = _('Nenhum documento acessório' + + 'cadastrado para este processo.') return self.render_to_response({'pk': kwargs['pk'], 'doc': doc, @@ -491,90 +540,30 @@ class DocumentoAcessorioAdministrativoView(FormView): return reverse('sapl.protocoloadm:doc_ace_adm', kwargs={'pk': pk}) -class TramitacaoAdmView(FormView): - template_name = "protocoloadm/tramitacao.html" - - def get(self, request, *args, **kwargs): - - pk = kwargs['pk'] - documento = DocumentoAdministrativo.objects.get(id=pk) - tramitacoes = TramitacaoAdministrativo.objects.filter( - documento=documento).order_by('-data_tramitacao') - - return self.render_to_response({'documento': documento, - 'tramitacoes': tramitacoes}) - - -class TramitacaoAdmIncluirView(FormView): - template_name = "protocoloadm/tramitacao_incluir.html" - - def get(self, request, *args, **kwargs): - pk = kwargs['pk'] - documento = DocumentoAdministrativo.objects.get(id=pk) - data = {'documento': documento} - form = TramitacaoAdmForm(initial=data) - - return self.render_to_response({'documento': documento, 'form': form}) - - def post(self, request, *args, **kwargs): - pk = kwargs['pk'] - form = TramitacaoAdmForm(request.POST or None) - - if form.is_valid(): - tramitacao = form.save(commit=False) - tramitacao.ultima = False - tramitacao.save() - return HttpResponseRedirect(reverse( - 'sapl.protocoloadm:tramitacao_adm', kwargs={'pk': pk})) - else: - return self.form_invalid(form) - - -class TramitacaoAdmEditView(FormView): - - template_name = "protocoloadm/tramitacao_edit.html" - - def get(self, request, *args, **kwargs): - pk = kwargs['pk'] - tramitacao = TramitacaoAdministrativo.objects.get(id=pk) - documento = tramitacao.documento - form = TramitacaoAdmForm(instance=tramitacao) - - return self.render_to_response({'documento': documento, 'form': form}) - - def post(self, request, *args, **kwargs): - pk = kwargs['pk'] - tramitacao = TramitacaoAdministrativo.objects.get(id=pk) - form = TramitacaoAdmForm(request.POST, instance=tramitacao) - - if form.is_valid(): - tramitacao = form.save(commit=False) - tramitacao.ultima = False - tramitacao.save() - return HttpResponseRedirect( - reverse('sapl.protocoloadm:tramitacao_adm', - kwargs={'pk': tramitacao.documento.id})) - else: - return self.form_invalid(form) - - -class TramitacaoAdmDeleteView(DetailView): +class TramitacaoAdmCrud(MasterDetailCrud): + model = TramitacaoAdministrativo + parent_field = 'documento' + help_path = '' - template_name = "protocoloadm/tramitacao.html" + class BaseMixin(MasterDetailCrud.BaseMixin): + list_field_names = ['data_tramitacao', 'unidade_tramitacao_local', + 'unidade_tramitacao_destino', 'status'] - def get(self, request, *args, **kwargs): - pk = kwargs['pk'] - oid = kwargs['oid'] + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): + form_class = TramitacaoAdmForm + permission_required = permissoes_adm() - documento = DocumentoAdministrativo.objects.get(id=pk) + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): + form_class = TramitacaoAdmEditForm + permission_required = permissoes_adm() - tramitacao = TramitacaoAdministrativo.objects.get(id=oid) - tramitacao.delete() - tramitacoes = TramitacaoAdministrativo.objects.filter( - documento=documento) + class ListView(PermissionRequiredMixin, MasterDetailCrud.ListView): + permission_required = permissoes_adm() - return self.render_to_response({'documento': documento, - 'tramitacoes': tramitacoes}) + def get_queryset(self): + qs = super(MasterDetailCrud.ListView, self).get_queryset() + kwargs = {self.crud.parent_field: self.kwargs['pk']} + return qs.filter(**kwargs).order_by('-id') def get_nome_autor(request): diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py index 29340ff6c..8faef90f2 100644 --- a/sapl/sessao/views.py +++ b/sapl/sessao/views.py @@ -2,6 +2,7 @@ from datetime import datetime from re import sub from django.contrib import messages +from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.urlresolvers import reverse from django.forms.utils import ErrorList @@ -15,8 +16,8 @@ from django_filters.views import FilterView from rest_framework import generics from sapl.crud.base import (Crud, CrudBaseMixin, CrudCreateView, - CrudDetailView, CrudListView, CrudUpdateView, - make_pagination) + CrudDeleteView, CrudDetailView, CrudListView, + CrudUpdateView, make_pagination) from sapl.crud.masterdetail import MasterDetailCrud from sapl.materia.forms import pega_ultima_tramitacao from sapl.materia.models import (Autoria, DocumentoAcessorio, @@ -25,6 +26,7 @@ from sapl.materia.views import MateriaLegislativaPesquisaView from sapl.norma.models import NormaJuridica from sapl.parlamentares.models import Parlamentar from sapl.sessao.serializers import SessaoPlenariaSerializer +from sapl.utils import permissao_tb_aux, permissoes_painel, permissoes_sessao from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm, ExpedienteForm, ExpedienteMateriaForm, @@ -39,13 +41,8 @@ from .models import (Bancada, CargoBancada, CargoMesa, ExpedienteMateria, TipoExpediente, TipoResultadoVotacao, TipoSessaoPlenaria, VotoParlamentar) -TipoSessaoCrud = Crud.build(TipoSessaoPlenaria, 'tipo_sessao_plenaria') OrdemDiaCrud = Crud.build(OrdemDia, '') -TipoResultadoVotacaoCrud = Crud.build( - TipoResultadoVotacao, 'tipo_resultado_votacao') -TipoExpedienteCrud = Crud.build(TipoExpediente, 'tipo_expediente') RegistroVotacaoCrud = Crud.build(RegistroVotacao, '') -CargoBancadaCrud = Crud.build(CargoBancada, '') def reordernar_materias_expediente(request, pk): @@ -78,9 +75,12 @@ class BancadaCrud(Crud): model = Bancada help_path = '' - class BaseMixin(CrudBaseMixin): + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): list_field_names = ['nome', 'legislatura'] + def has_permission(self): + return permissao_tb_aux(self) + class ListView(CrudListView): ordering = 'legislatura' @@ -91,6 +91,42 @@ class BancadaCrud(Crud): form_class = BancadaForm +class TipoSessaoCrud(Crud): + model = TipoSessaoPlenaria + help_path = 'tipo_sessao_plenaria' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class TipoResultadoVotacaoCrud(Crud): + model = TipoResultadoVotacao + help_path = 'tipo_resultado_votacao' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class TipoExpedienteCrud(Crud): + model = TipoExpediente + help_path = 'tipo_expediente' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + +class CargoBancadaCrud(Crud): + model = CargoBancada + help_path = '' + + class BaseMixin(PermissionRequiredMixin, CrudBaseMixin): + def has_permission(self): + return permissao_tb_aux(self) + + def abrir_votacao_expediente_view(request, pk, spk): existe_votacao_aberta = ExpedienteMateria.objects.filter( sessao_plenaria_id=spk, votacao_aberta=True @@ -289,11 +325,13 @@ class ExpedienteMateriaCrud(MasterDetailCrud): obj.resultado) return [self._as_row(obj) for obj in object_list] - class CreateView(MasterDetailCrud.CreateView): + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): form_class = ExpedienteMateriaForm + permission_required = permissoes_sessao() - class UpdateView(MasterDetailCrud.UpdateView): + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): form_class = ExpedienteMateriaForm + permission_required = permissoes_sessao() def get_initial(self): self.initial['tipo_materia'] = self.object.materia.tipo.id @@ -301,6 +339,9 @@ class ExpedienteMateriaCrud(MasterDetailCrud): self.initial['ano_materia'] = self.object.materia.ano return self.initial + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_sessao() + class DetailView(MasterDetailCrud.DetailView): @property @@ -316,14 +357,41 @@ class OradorCrud(MasterDetailCrud): class ListView(MasterDetailCrud.ListView): ordering = ['numero_ordem', 'parlamentar'] + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): + permission_required = permissoes_sessao() + + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): + permission_required = permissoes_sessao() + + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_sessao() + class OradorExpedienteCrud(OradorCrud): model = OradorExpediente + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): + permission_required = permissoes_sessao() + + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): + permission_required = permissoes_sessao() + + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_sessao() + class OradorCrud(OradorCrud): model = Orador + class CreateView(PermissionRequiredMixin, MasterDetailCrud.CreateView): + permission_required = permissoes_sessao() + + class UpdateView(PermissionRequiredMixin, MasterDetailCrud.UpdateView): + permission_required = permissoes_sessao() + + class DeleteView(PermissionRequiredMixin, MasterDetailCrud.DeleteView): + permission_required = permissoes_sessao() + class SessaoCrud(Crud): model = SessaoPlenaria @@ -342,6 +410,15 @@ class SessaoCrud(Crud): class ListView(CrudListView): ordering = ['-data_inicio'] + class CreateView(PermissionRequiredMixin, CrudCreateView): + permission_required = permissoes_sessao() + + class UpdateView(PermissionRequiredMixin, CrudUpdateView): + permission_required = permissoes_sessao() + + class DeleteView(PermissionRequiredMixin, CrudDeleteView): + permission_required = permissoes_sessao() + class PresencaMixin: @@ -360,10 +437,14 @@ class PresencaMixin: yield (parlamentar, False) -class PresencaView(FormMixin, PresencaMixin, SessaoCrud.DetailView): +class PresencaView(PermissionRequiredMixin, + FormMixin, + PresencaMixin, + SessaoCrud.CrudDetailView): template_name = 'sessao/presenca.html' form_class = PresencaForm model = SessaoPlenaria + permission_required = permissoes_sessao() def post(self, request, *args, **kwargs): self.object = self.get_object() @@ -401,15 +482,18 @@ class PresencaView(FormMixin, PresencaMixin, SessaoCrud.DetailView): return reverse('sapl.sessao:presenca', kwargs={'pk': pk}) -class PainelView(TemplateView): +class PainelView(PermissionRequiredMixin, TemplateView): template_name = 'sessao/painel.html' + permission_required = permissoes_painel() -class PresencaOrdemDiaView(FormMixin, +class PresencaOrdemDiaView(PermissionRequiredMixin, + FormMixin, PresencaMixin, SessaoCrud.CrudDetailView): template_name = 'sessao/presenca_ordemdia.html' form_class = PresencaForm + permission_required = permissoes_sessao() def post(self, request, *args, **kwargs): @@ -450,9 +534,113 @@ class PresencaOrdemDiaView(FormMixin, return reverse('sapl.sessao:presencaordemdia', kwargs={'pk': pk}) -class MesaView(FormMixin, SessaoCrud.CrudDetailView): +class ListMateriaOrdemDiaView(FormMixin, SessaoCrud.CrudDetailView): + template_name = 'sessao/materia_ordemdia_list.html' + form_class = ListMateriaForm + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + context = self.get_context_data(object=self.object) + + pk = self.kwargs['pk'] + ordem = OrdemDia.objects.filter(sessao_plenaria_id=pk) + + materias_ordem = [] + for o in ordem: + ementa = o.observacao + titulo = o.materia + numero = o.numero_ordem + + autoria = Autoria.objects.filter(materia_id=o.materia_id) + autor = [str(a.autor) for a in autoria] + + mat = {'pk': pk, + 'oid': o.materia_id, + 'ordem_id': o.id, + 'ementa': ementa, + 'titulo': titulo, + 'numero': numero, + 'resultado': o.resultado, + 'autor': autor, + 'votacao_aberta': o.votacao_aberta, + 'tipo_votacao': o.tipo_votacao + } + materias_ordem.append(mat) + + sorted(materias_ordem, key=lambda x: x['numero']) + + context.update({'materias_ordem': materias_ordem}) + + return self.render_to_response(context) + + def post(self, request, *args, **kwargs): + self.object = self.get_object() + context = self.get_context_data(object=self.object) + + pk = self.kwargs['pk'] + form = ListMateriaForm(request.POST) + + # TODO: Existe uma forma de atualizar em lote de acordo + # com a forma abaixo, mas como setar o primeiro para "1"? + # OrdemDia.objects.filter(sessao_plenaria_id=pk) + # .order_by('numero_ordem').update(numero_ordem=3) + + if 'materia_reorder' in request.POST: + ordens = OrdemDia.objects.filter(sessao_plenaria_id=pk) + ordem_num = 1 + for o in ordens: + o.numero_ordem = ordem_num + o.save() + ordem_num += 1 + elif 'abrir-votacao' in request.POST: + existe_votacao_aberta = OrdemDia.objects.filter( + sessao_plenaria_id=pk, votacao_aberta=True).exists() + if existe_votacao_aberta: + context = self.get_context_data(object=self.object) + + form._errors = {'error_message': 'error_message'} + context.update({'form': form}) + + pk = self.kwargs['pk'] + ordem = OrdemDia.objects.filter(sessao_plenaria_id=pk) + + materias_ordem = [] + for o in ordem: + ementa = o.observacao + titulo = o.materia + numero = o.numero_ordem + + autoria = Autoria.objects.filter(materia_id=o.materia_id) + autor = [str(a.autor) for a in autoria] + + mat = {'pk': pk, + 'oid': o.materia_id, + 'ordem_id': o.id, + 'ementa': ementa, + 'titulo': titulo, + 'numero': numero, + 'resultado': o.resultado, + 'autor': autor, + 'votacao_aberta': o.votacao_aberta, + 'tipo_votacao': o.tipo_votacao + } + materias_ordem.append(mat) + + sorted(materias_ordem, key=lambda x: x['numero']) + context.update({'materias_ordem': materias_ordem}) + return self.render_to_response(context) + else: + ordem_id = request.POST['ordem_id'] + ordem = OrdemDia.objects.get(id=ordem_id) + ordem.votacao_aberta = True + ordem.save() + return self.get(self, request, args, kwargs) + + +class MesaView(PermissionRequiredMixin, FormMixin, SessaoCrud.CrudDetailView): template_name = 'sessao/mesa.html' form_class = MesaForm + permission_required = permissoes_sessao() def get(self, request, *args, **kwargs): self.object = self.get_object() @@ -720,9 +908,12 @@ class ResumoView(SessaoCrud.CrudDetailView): return self.render_to_response(context) -class ExpedienteView(FormMixin, SessaoCrud.CrudDetailView): +class ExpedienteView(PermissionRequiredMixin, + FormMixin, + SessaoCrud.CrudDetailView): template_name = 'sessao/expediente.html' form_class = ExpedienteForm + permission_required = permissoes_sessao() def post(self, request, *args, **kwargs): self.object = self.get_object() @@ -784,13 +975,16 @@ class ExpedienteView(FormMixin, SessaoCrud.CrudDetailView): return reverse('sapl.sessao:expediente', kwargs={'pk': pk}) -class VotacaoEditView(FormMixin, SessaoCrud.CrudDetailView): +class VotacaoEditView(PermissionRequiredMixin, + FormMixin, + SessaoCrud.CrudDetailView): ''' Votação Simbólica e Secreta ''' template_name = 'sessao/votacao/votacao_edit.html' + permission_required = permissoes_sessao() def post(self, request, *args, **kwargs): @@ -857,7 +1051,9 @@ class VotacaoEditView(FormMixin, SessaoCrud.CrudDetailView): kwargs={'pk': pk}) -class VotacaoView(FormMixin, SessaoCrud.CrudDetailView): +class VotacaoView(PermissionRequiredMixin, + FormMixin, + SessaoCrud.CrudDetailView): ''' Votação Simbólica e Secreta @@ -865,6 +1061,7 @@ class VotacaoView(FormMixin, SessaoCrud.CrudDetailView): template_name = 'sessao/votacao/votacao.html' form_class = VotacaoForm + permission_required = permissoes_sessao() def get(self, request, *args, **kwargs): self.object = self.get_object() @@ -976,8 +1173,11 @@ class VotacaoView(FormMixin, SessaoCrud.CrudDetailView): kwargs={'pk': pk}) -class VotacaoNominalView(FormMixin, SessaoCrud.CrudDetailView): +class VotacaoNominalView(PermissionRequiredMixin, + FormMixin, + SessaoCrud.CrudDetailView): template_name = 'sessao/votacao/nominal.html' + permission_required = permissoes_sessao() def get(self, request, *args, **kwargs): ordem_id = kwargs['mid'] @@ -1101,8 +1301,11 @@ class VotacaoNominalView(FormMixin, SessaoCrud.CrudDetailView): kwargs={'pk': pk}) -class VotacaoNominalEditView(FormMixin, SessaoCrud.CrudDetailView): +class VotacaoNominalEditView(PermissionRequiredMixin, + FormMixin, + SessaoCrud.CrudDetailView): template_name = 'sessao/votacao/nominal_edit.html' + permission_required = permissoes_sessao() def get(self, request, *args, **kwargs): context = {} @@ -1178,8 +1381,11 @@ class VotacaoNominalEditView(FormMixin, SessaoCrud.CrudDetailView): kwargs={'pk': pk}) -class VotacaoNominalExpedienteView(FormMixin, SessaoCrud.CrudDetailView): +class VotacaoNominalExpedienteView(PermissionRequiredMixin, + FormMixin, + SessaoCrud.CrudDetailView): template_name = 'sessao/votacao/nominal.html' + permission_required = permissoes_sessao() def get(self, request, *args, **kwargs): expediente_id = kwargs['mid'] @@ -1301,8 +1507,11 @@ class VotacaoNominalExpedienteView(FormMixin, SessaoCrud.CrudDetailView): kwargs={'pk': pk}) -class VotacaoNominalExpedienteEditView(FormMixin, SessaoCrud.CrudDetailView): +class VotacaoNominalExpedienteEditView(PermissionRequiredMixin, + FormMixin, + SessaoCrud.CrudDetailView): template_name = 'sessao/votacao/nominal_edit.html' + permission_required = permissoes_sessao() def get(self, request, *args, **kwargs): context = {} @@ -1377,7 +1586,9 @@ class VotacaoNominalExpedienteEditView(FormMixin, SessaoCrud.CrudDetailView): kwargs={'pk': pk}) -class VotacaoExpedienteView(FormMixin, SessaoCrud.CrudDetailView): +class VotacaoExpedienteView(PermissionRequiredMixin, + FormMixin, + SessaoCrud.CrudDetailView): ''' Votação Simbólica e Secreta @@ -1385,6 +1596,7 @@ class VotacaoExpedienteView(FormMixin, SessaoCrud.CrudDetailView): template_name = 'sessao/votacao/votacao.html' form_class = VotacaoForm + permission_required = permissoes_sessao() def get(self, request, *args, **kwargs): self.object = self.get_object() @@ -1498,7 +1710,9 @@ class VotacaoExpedienteView(FormMixin, SessaoCrud.CrudDetailView): kwargs={'pk': pk}) -class VotacaoExpedienteEditView(FormMixin, SessaoCrud.CrudDetailView): +class VotacaoExpedienteEditView(PermissionRequiredMixin, + FormMixin, + SessaoCrud.CrudDetailView): ''' Votação Simbólica e Secreta @@ -1506,6 +1720,7 @@ class VotacaoExpedienteEditView(FormMixin, SessaoCrud.CrudDetailView): template_name = 'sessao/votacao/votacao_edit.html' form_class = VotacaoEditForm + permission_required = permissoes_sessao() def get_success_url(self): pk = self.kwargs['pk'] diff --git a/sapl/settings.py b/sapl/settings.py index 96dee01eb..3c28078b3 100644 --- a/sapl/settings.py +++ b/sapl/settings.py @@ -38,6 +38,7 @@ LOGIN_URL = '/login/?next=' # SAPL business apps in dependency order SAPL_APPS = ( 'sapl.base', + 'sapl.crud', 'sapl.parlamentares', 'sapl.comissoes', 'sapl.materia', @@ -129,6 +130,7 @@ EMAIL_PORT = config('EMAIL_PORT', cast=int, default=587) EMAIL_HOST_USER = config('EMAIL_HOST_USER', default='') EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD', default='') EMAIL_USE_TLS = config('EMAIL_USE_TLS', cast=bool, default=True) +EMAIL_SEND_USER = config('EMAIL_SEND_USER', cast=str, default='') MAX_DOC_UPLOAD_SIZE = 5 * 1024 * 1024 # 5MB MAX_IMAGE_UPLOAD_SIZE = 2 * 1024 * 1024 # 2MB diff --git a/sapl/templates/base.html b/sapl/templates/base.html index 5e0eabfe9..cec533fc0 100644 --- a/sapl/templates/base.html +++ b/sapl/templates/base.html @@ -1,4 +1,5 @@ {% load i18n staticfiles sass_tags menus %} +{% load common_tags %} @@ -45,24 +46,35 @@ - + {% if perms.protocoloadm %} + + + + + - + {% if user|ver_menu_sistema_perm %} + {% endif %}