diff --git a/docker-compose.yml b/docker-compose.yml
index 11902a565..24dbd8cd3 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -11,7 +11,7 @@ sapldb:
ports:
- "5432:5432"
sapl:
- image: interlegis/sapl:3.1.134
+ image: interlegis/sapl:3.1.136
restart: always
environment:
ADMIN_PASSWORD: interlegis
diff --git a/sapl/parlamentares/forms.py b/sapl/parlamentares/forms.py
index 36237d448..a15a4e305 100755
--- a/sapl/parlamentares/forms.py
+++ b/sapl/parlamentares/forms.py
@@ -136,7 +136,7 @@ class MandatoForm(ModelForm):
existe_mandato = Mandato.objects.filter(
parlamentar=data['parlamentar'],
legislatura=data['legislatura']).exists()
- if existe_mandato:
+ if existe_mandato and data['titular']:
self.logger.error("Mandato nesta legislatura (parlamentar={}, legislatura={}) já existe."
.format(data['parlamentar'], data['legislatura']))
raise ValidationError(_('Mandato nesta legislatura já existe.'))
diff --git a/sapl/parlamentares/tests/test_parlamentares.py b/sapl/parlamentares/tests/test_parlamentares.py
index bc404757d..0f8d6e24f 100644
--- a/sapl/parlamentares/tests/test_parlamentares.py
+++ b/sapl/parlamentares/tests/test_parlamentares.py
@@ -178,6 +178,7 @@ def test_mandato_form_duplicado():
'legislatura': str(legislatura.pk),
'data_expedicao_diploma': '01/07/2015',
'data_inicio_mandato': legislatura.data_inicio,
+ 'titular':True,
})
assert not form.is_valid()
diff --git a/sapl/relatorios/views.py b/sapl/relatorios/views.py
index 8b3e21492..65455c0ca 100755
--- a/sapl/relatorios/views.py
+++ b/sapl/relatorios/views.py
@@ -1101,8 +1101,7 @@ def get_pauta_sessao(sessao, casa):
inf_basicas_dic["nom_camara"] = casa.nome
lst_expediente_materia = []
- for expediente_materia in ExpedienteMateria.objects.filter(
- data_ordem=sessao.data_inicio, sessao_plenaria=sessao):
+ for expediente_materia in ExpedienteMateria.objects.filter(sessao_plenaria=sessao):
materia = MateriaLegislativa.objects.filter(
id=expediente_materia.materia.id).first()
diff --git a/sapl/rules/map_rules.py b/sapl/rules/map_rules.py
index 2b6204ec2..aa691da5e 100644
--- a/sapl/rules/map_rules.py
+++ b/sapl/rules/map_rules.py
@@ -172,6 +172,7 @@ rules_group_sessao = {
(sessao.RegistroVotacao, __base__),
(sessao.VotoParlamentar, __base__),
(sessao.JustificativaAusencia, __base__),
+ (sessao.RetiradaPauta, __base__)
]
}
@@ -269,6 +270,7 @@ rules_group_geral = {
(sessao.JustificativaAusencia, __base__),
(sessao.Bloco, __base__),
(sessao.ResumoOrdenacao, __base__),
+ (sessao.TipoRetiradaPauta, __base__),
(lexml.LexmlProvedor, __base__),
(lexml.LexmlPublicador, __base__),
diff --git a/sapl/sessao/forms.py b/sapl/sessao/forms.py
index 2a31b60c3..386f123ca 100644
--- a/sapl/sessao/forms.py
+++ b/sapl/sessao/forms.py
@@ -23,11 +23,11 @@ from sapl.parlamentares.models import Parlamentar, Legislatura, Mandato
from sapl.utils import (RANGE_DIAS_MES, RANGE_MESES,
MateriaPesquisaOrderingFilter, autor_label,
autor_modal, timezone)
+from .models import (Bancada, Bloco, ExpedienteMateria, Orador, JustificativaAusencia,
+ OradorExpediente, OrdemDia, SessaoPlenaria,
+ SessaoPlenariaPresenca, TipoResultadoVotacao, OcorrenciaSessao,
+ RetiradaPauta, TipoRetiradaPauta)
-from .models import (Bancada, Bloco, ExpedienteMateria, JustificativaAusencia,
- Orador, OradorExpediente, OrdemDia, SessaoPlenaria,
- SessaoPlenariaPresenca, TipoJustificativa, TipoResultadoVotacao,
- OcorrenciaSessao)
def recupera_anos():
@@ -191,6 +191,100 @@ class SessaoPlenariaForm(ModelForm):
return self.cleaned_data
+class RetiradaPautaForm(ModelForm):
+
+ tipo_de_retirada = forms.ModelChoiceField(required=True,
+ empty_label='------------',
+ queryset=TipoRetiradaPauta.objects.all())
+ expediente = forms.ModelChoiceField(required=False,
+ label='Matéria do Expediente',
+ queryset=ExpedienteMateria.objects.all())
+ ordem = forms.ModelChoiceField(required=False,
+ label='Matéria da Ordem do Dia',
+ queryset=OrdemDia.objects.all())
+ materia = forms.ModelChoiceField(required=False,
+ widget=forms.HiddenInput(),
+ queryset=MateriaLegislativa.objects.all())
+
+ class Meta:
+ model = RetiradaPauta
+ fields = ['ordem',
+ 'expediente',
+ 'parlamentar',
+ 'tipo_de_retirada',
+ 'data',
+ 'observacao',
+ 'materia']
+
+ def __init__(self, *args, **kwargs):
+
+ row1 = to_row([('tipo_de_retirada', 5),
+ ('parlamentar', 4),
+ ('data', 3)])
+ row2 = to_row([('ordem', 6),
+ ('expediente', 6)])
+ row3 = to_row([('observacao',12)])
+
+ self.helper = FormHelper()
+ self.helper.layout = SaplFormLayout(
+ Fieldset(_('Retirada de Pauta'),
+ row1, row2, row3))
+
+ q = Q(sessao_plenaria=kwargs['initial']['sessao_plenaria'])
+ ordens = OrdemDia.objects.filter(q)
+ expedientes = ExpedienteMateria.objects.filter(q)
+ retiradas_ordem = [r.ordem for r in RetiradaPauta.objects.filter(q, ordem__in=ordens)]
+ retiradas_expediente = [r.expediente for r in RetiradaPauta.objects.filter(q, expediente__in=expedientes)]
+ setOrdem = set(ordens) - set(retiradas_ordem)
+ setExpediente = set(expedientes) - set(retiradas_expediente)
+
+ super(RetiradaPautaForm, self).__init__(
+ *args, **kwargs)
+
+ if self.instance.pk:
+ setOrdem = set(ordens)
+ setExpediente = set(expedientes)
+
+ presencas = SessaoPlenariaPresenca.objects.filter(
+ q).order_by('parlamentar__nome_parlamentar')
+ presentes = [p.parlamentar for p in presencas]
+
+ self.fields['expediente'].choices = [
+ (None, "------------")] + [(e.id, e.materia) for e in setExpediente]
+ self.fields['ordem'].choices = [
+ (None, "------------")] + [(o.id, o.materia) for o in setOrdem]
+ self.fields['parlamentar'].choices = [
+ (None, "------------")] + [(p.id, p) for p in presentes]
+
+ def clean(self):
+
+ super(RetiradaPautaForm, self).clean()
+
+ if not self.is_valid():
+ return self.cleaned_data
+
+ sessao_plenaria = self.instance.sessao_plenaria
+ if self.cleaned_data['data'] < sessao_plenaria.data_inicio:
+ raise ValidationError(_("Data de retirada de pauta anterior à abertura da Sessão."))
+ if sessao_plenaria.data_fim and self.cleaned_data['data'] > sessao_plenaria.data_fim:
+ raise ValidationError(_("Data de retirada de pauta posterior ao encerramento da Sessão."))
+
+ if self.cleaned_data['ordem'] and self.cleaned_data['ordem'].registrovotacao_set.exists():
+ raise ValidationError(_("Essa matéria já foi votada, portanto não pode ser retirada de pauta."))
+ elif self.cleaned_data['expediente'] and self.cleaned_data['expediente'].registrovotacao_set.exists():
+ raise ValidationError(_("Essa matéria já foi votada, portanto não pode ser retirada de pauta."))
+
+ return self.cleaned_data
+
+ def save(self, commit=False):
+ retirada = super(RetiradaPautaForm, self).save(commit=False)
+ if retirada.ordem:
+ retirada.materia = retirada.ordem.materia
+ elif retirada.expediente:
+ retirada.materia = retirada.expediente.materia
+ retirada.save()
+ return retirada
+
class BancadaForm(ModelForm):
class Meta:
diff --git a/sapl/sessao/migrations/0028_auto_20181031_0902.py b/sapl/sessao/migrations/0028_auto_20181031_0902.py
new file mode 100644
index 000000000..3ff3c0dc5
--- /dev/null
+++ b/sapl/sessao/migrations/0028_auto_20181031_0902.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.8 on 2018-10-31 12:02
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('materia', '0032_auto_20181022_1743'),
+ ('parlamentares', '0025_auto_20180924_1724'),
+ ('sessao', '0029_auto_20181024_0952'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='RetiradaPauta',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('observacao', models.TextField(blank=True, verbose_name='Observações')),
+ ('expediente', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sessao.ExpedienteMateria')),
+ ('materia', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='materia.MateriaLegislativa')),
+ ('ordem', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sessao.OrdemDia')),
+ ('parlamentar', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='parlamentares.Parlamentar', verbose_name='Requerente')),
+ ],
+ options={
+ 'verbose_name_plural': 'Retirada de Pauta',
+ 'verbose_name': 'Retirada de Pauta',
+ },
+ ),
+ migrations.CreateModel(
+ name='TipoRetiradaPauta',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('descricao', models.CharField(max_length=150, verbose_name='Descrição')),
+ ],
+ options={
+ 'verbose_name_plural': 'Tipos de Retirada de Pauta',
+ 'verbose_name': 'Tipo de Retidara de Pauta',
+ 'ordering': ['descricao'],
+ },
+ ),
+ migrations.AddField(
+ model_name='retiradapauta',
+ name='tipo_de_retirada',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='sessao.TipoRetiradaPauta', verbose_name='Motivo de Retirada de Pauta'),
+ ),
+ ]
diff --git a/sapl/sessao/migrations/0030_auto_20181113_1149.py b/sapl/sessao/migrations/0030_auto_20181113_1149.py
new file mode 100644
index 000000000..6b7c3a237
--- /dev/null
+++ b/sapl/sessao/migrations/0030_auto_20181113_1149.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.8 on 2018-11-13 13:49
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('sessao', '0028_auto_20181031_0902'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='retiradapauta',
+ name='data',
+ field=models.DateField(default=django.utils.timezone.now, verbose_name='Data'),
+ ),
+ migrations.AddField(
+ model_name='retiradapauta',
+ name='sessao_plenaria',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sessao.SessaoPlenaria', verbose_name='Sessão Plenária'),
+ ),
+ ]
diff --git a/sapl/sessao/migrations/0031_auto_20181116_1849.py b/sapl/sessao/migrations/0031_auto_20181116_1849.py
new file mode 100644
index 000000000..61ea74b9f
--- /dev/null
+++ b/sapl/sessao/migrations/0031_auto_20181116_1849.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.8 on 2018-11-16 20:49
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('sessao', '0030_auto_20181113_1149'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='retiradapauta',
+ name='materia',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='materia.MateriaLegislativa', verbose_name='Matéria'),
+ ),
+ ]
diff --git a/sapl/sessao/migrations/0032_merge_20181122_1527.py b/sapl/sessao/migrations/0032_merge_20181122_1527.py
new file mode 100644
index 000000000..9a74abe95
--- /dev/null
+++ b/sapl/sessao/migrations/0032_merge_20181122_1527.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.8 on 2018-11-22 17:27
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('sessao', '0031_auto_20181116_1849'),
+ ('sessao', '0031_auto_20181119_1103'),
+ ]
+
+ operations = [
+ ]
diff --git a/sapl/sessao/models.py b/sapl/sessao/models.py
index 6f07d9645..8cc162a05 100644
--- a/sapl/sessao/models.py
+++ b/sapl/sessao/models.py
@@ -2,6 +2,7 @@ from operator import xor
from django.core.exceptions import ValidationError
from django.db import models
+from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
import reversion
@@ -584,6 +585,18 @@ class ResumoOrdenacao(models.Model):
def __str__(self):
return 'Ordenação do Resumo de uma Sessão'
+@reversion.register()
+class TipoRetiradaPauta(models.Model):
+ descricao = models.CharField(max_length=150, verbose_name=_('Descrição'))
+
+ class Meta:
+ verbose_name = _('Tipo de Retidara de Pauta')
+ verbose_name_plural = _('Tipos de Retirada de Pauta')
+ ordering = ['descricao']
+
+ def __str__(self):
+ return self.descricao
+
@reversion.register()
class TipoJustificativa(models.Model):
@@ -662,3 +675,55 @@ class JustificativaAusencia(models.Model):
force_update=force_update,
using=using,
update_fields=update_fields)
+
+class RetiradaPauta(models.Model):
+ materia = models.ForeignKey(MateriaLegislativa,
+ on_delete=models.CASCADE,
+ verbose_name=_('Matéria'))
+ sessao_plenaria = models.ForeignKey(SessaoPlenaria,
+ on_delete=models.CASCADE,
+ verbose_name=_('Sessão Plenária'),
+ blank=True,
+ null=True)
+ ordem = models.ForeignKey(OrdemDia,
+ blank=True,
+ null=True,
+ on_delete=models.CASCADE)
+ expediente = models.ForeignKey(ExpedienteMateria,
+ blank=True,
+ null=True,
+ on_delete=models.CASCADE)
+ data = models.DateField(verbose_name=_('Data'),
+ default=timezone.now)
+ observacao = models.TextField(blank=True,
+ verbose_name=_('Observações'))
+ parlamentar = models.ForeignKey(Parlamentar,
+ on_delete=models.PROTECT,
+ verbose_name=_('Requerente'),
+ blank=True,
+ null=True)
+ tipo_de_retirada = models.ForeignKey(TipoRetiradaPauta,
+ on_delete=models.PROTECT,
+ verbose_name=_('Motivo de Retirada de Pauta'))
+
+ class Meta:
+ verbose_name = _('Retirada de Pauta')
+ verbose_name_plural = _('Retirada de Pauta')
+
+ def __str__(self):
+ return _('Ordem: %(ordem)s - Requerente: %(requerente)s - '
+ 'Matéria: %(materia)s') % {
+ 'ordem': self.ordem,
+ 'requerente': self.parlamentar,
+ 'materia': self.materia}
+
+ def clean(self):
+ """Exatamente um dos campos ordem ou expediente deve estar preenchido.
+ """
+ # TODO remover esse método quando OrdemDia e ExpedienteMateria
+ # forem reestruturados e os campos ordem e expediente forem unificados
+ if not xor(bool(self.ordem), bool(self.expediente)):
+ raise ValidationError(
+ 'ReritadaPauta deve ter exatamente um dos campos '
+ 'ordem ou expediente preenchido. Ambos estão preenchidos: '
+ '{}, {}'. format(self.ordem, self.expediente))
diff --git a/sapl/sessao/urls.py b/sapl/sessao/urls.py
index f2e2fbbcc..f9b07967d 100644
--- a/sapl/sessao/urls.py
+++ b/sapl/sessao/urls.py
@@ -10,8 +10,9 @@ from sapl.sessao.views import (AdicionarVariasMateriasExpediente,
PesquisarPautaSessaoView,
PesquisarSessaoPlenariaView,
PresencaOrdemDiaView, PresencaView,
- ResumoOrdenacaoView, ResumoView, ResumoAtaView, SessaoCrud,
+ ResumoOrdenacaoView, ResumoView, ResumoAtaView, RetiradaPautaCrud, SessaoCrud,
TipoJustificativaCrud, TipoExpedienteCrud, TipoResultadoVotacaoCrud,
+ TipoExpedienteCrud, TipoResultadoVotacaoCrud,TipoRetiradaPautaCrud,
TipoSessaoCrud, VotacaoEditView,
VotacaoExpedienteEditView,
VotacaoExpedienteView, VotacaoNominalEditView,
@@ -39,7 +40,7 @@ urlpatterns = [
OradorExpedienteCrud.get_urls() +
ExpedienteMateriaCrud.get_urls() +
JustificativaAusenciaCrud.get_urls() +
- MateriaOrdemDiaCrud.get_urls())),
+ MateriaOrdemDiaCrud.get_urls() + RetiradaPautaCrud.get_urls())),
url(r'^sessao/(?P
%s' %
+ (url,
+ retirada_descricao,
+ retirada_observacao))
+
else:
resultado = obj.registrovotacao_set.filter(
materia_id=obj.materia_id).last()
@@ -1343,8 +1356,6 @@ class ResumoView(DetailView):
context.update({'presenca_sessao': parlamentares_sessao,
'justificativa_ausencia': ausentes_sessao})
- context.update({'presenca_sessao': parlamentares_sessao})
-
# =====================================================================
# Expedientes
@@ -3106,7 +3117,7 @@ def mudar_ordem_materia_sessao(request):
elif materia == 'ordem':
materia = OrdemDia
else:
- return
+ return JsonResponse({}, safe=False)
# Testa se existe alguma matéria na posição recebida
try:
@@ -3145,7 +3156,7 @@ def mudar_ordem_materia_sessao(request):
materia_1.numero_ordem = posicao_final
materia_1.save()
- return
+ return JsonResponse({}, safe=False)
class JustificativaAusenciaCrud(MasterDetailCrud):
@@ -3225,3 +3236,38 @@ class JustificativaAusenciaCrud(MasterDetailCrud):
class DeleteView(MasterDetailCrud.DeleteView):
pass
+
+
+class RetiradaPautaCrud(MasterDetailCrud):
+ model = RetiradaPauta
+ public = [RP_LIST, RP_DETAIL, ]
+ parent_field = 'sessao_plenaria'
+
+ class BaseMixin(MasterDetailCrud.BaseMixin):
+ list_field_names = ['tipo_de_retirada', 'materia', 'observacao', 'parlamentar']
+
+ class ListView(MasterDetailCrud.ListView):
+ paginate_by = 10
+
+ class CreateView(MasterDetailCrud.CreateView):
+ form_class = RetiradaPautaForm
+ layout_key = None
+
+ def get_initial(self):
+ sessao_plenaria = SessaoPlenaria.objects.get(id=self.kwargs['pk'])
+ return {'sessao_plenaria': sessao_plenaria}
+
+ def get_success_url(self):
+ return reverse('sapl.sessao:retiradapauta_list',
+ kwargs={'pk': self.kwargs['pk']})
+
+ class UpdateView(MasterDetailCrud.UpdateView):
+ form_class = RetiradaPautaForm
+ layout_key = None
+
+ def get_initial(self):
+ sessao_plenaria = RetiradaPauta.objects.get(id=self.kwargs['pk']).sessao_plenaria
+ return {'sessao_plenaria': sessao_plenaria}
+
+ class DeleteView(MasterDetailCrud.DeleteView):
+ pass
diff --git a/sapl/templates/base.html b/sapl/templates/base.html
index 2ff5fdea4..efffd0cf8 100644
--- a/sapl/templates/base.html
+++ b/sapl/templates/base.html
@@ -184,7 +184,7 @@
Desenvolvido pelo Interlegis em software livre e aberto.
- Release: 3.1.134
+ Release: 3.1.136