Browse Source

Merge branch '3.1.x' into 2672-data-nota-explicativa

pull/2707/head
Edward 7 years ago
committed by GitHub
parent
commit
cf831a880a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      sapl/api/views.py
  2. 20
      sapl/base/migrations/0033_auto_20190415_1050.py
  3. 3
      sapl/base/models.py
  4. 46
      sapl/materia/forms.py
  5. 20
      sapl/materia/migrations/0045_auto_20190415_1050.py
  6. 58
      sapl/materia/views.py
  7. 128
      sapl/protocoloadm/forms.py
  8. 35
      sapl/protocoloadm/migrations/0018_auto_20190314_1532.py
  9. 42
      sapl/protocoloadm/models.py
  10. 32
      sapl/protocoloadm/tests/test_protocoloadm.py
  11. 7
      sapl/protocoloadm/urls.py
  12. 162
      sapl/protocoloadm/views.py
  13. 3
      sapl/rules/map_rules.py
  14. 2
      sapl/sessao/migrations/0036_auto_20190412_1106.py
  15. 33
      sapl/sessao/migrations/0037_auto_20190415_1324.py
  16. 13
      sapl/sessao/models.py
  17. 36
      sapl/sessao/views.py
  18. 4
      sapl/templates/base/layouts.yaml
  19. 6
      sapl/templates/materia/em_lote/anexada.html
  20. 13
      sapl/templates/protocoloadm/anexado_list.html
  21. 5
      sapl/templates/protocoloadm/em_lote/anexado.html
  22. 13
      sapl/templates/protocoloadm/layouts.yaml
  23. 2
      sapl/templates/protocoloadm/subnav.yaml
  24. 98
      sapl/templates/sessao/painel.html
  25. 160
      sapl/templates/sessao/votacao/votacao_bloco_expediente.html
  26. 168
      sapl/templates/sessao/votacao/votacao_bloco_ordem.html
  27. 84
      scripts/remove_multiplos_autores.py
  28. 13
      scripts/remove_protocolos_inexistentes_materias.py

16
sapl/api/views.py

@ -26,7 +26,7 @@ from sapl.materia.models import Proposicao, TipoMateriaLegislativa,\
MateriaLegislativa, Tramitacao
from sapl.parlamentares.models import Parlamentar
from sapl.protocoloadm.models import DocumentoAdministrativo,\
DocumentoAcessorioAdministrativo, TramitacaoAdministrativo
DocumentoAcessorioAdministrativo, TramitacaoAdministrativo, Anexado
from sapl.sessao.models import SessaoPlenaria, ExpedienteSessao
from sapl.utils import models_with_gr_for_model, choice_anos_com_sessaoplenaria
@ -489,6 +489,20 @@ class _TramitacaoAdministrativoViewSet(BusinessRulesNotImplementedMixin):
return qs
@customize(Anexado)
class _AnexadoViewSet(BusinessRulesNotImplementedMixin):
permission_classes = (
_DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission, )
def get_queryset(self):
qs = super().get_queryset()
if self.request.user.is_anonymous():
qs = qs.exclude(documento__restrito=True)
return qs
@customize(SessaoPlenaria)
class _SessaoPlenariaViewSet:

20
sapl/base/migrations/0033_auto_20190415_1050.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-04-15 13:50
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0032_merge_20190219_0941'),
]
operations = [
migrations.AlterField(
model_name='appconfig',
name='sequencia_numeracao',
field=models.CharField(choices=[('A', 'Sequencial por ano para cada autor'), ('B', 'Sequencial por ano indepententemente do autor'), ('L', 'Sequencial por legislatura'), ('U', 'Sequencial único')], default='A', max_length=1, verbose_name='Sequência de numeração'),
),
]

3
sapl/base/models.py

@ -18,7 +18,8 @@ TIPO_DOCUMENTO_ADMINISTRATIVO = ((DOC_ADM_OSTENSIVO, _('Ostensiva')),
RELATORIO_ATOS_ACESSADOS = (('S', _('Sim')),
('N', _('Não')))
SEQUENCIA_NUMERACAO = (('A', _('Sequencial por ano')),
SEQUENCIA_NUMERACAO = (('A', _('Sequencial por ano para cada autor')),
('B', _('Sequencial por ano indepententemente do autor')),
('L', _('Sequencial por legislatura')),
('U', _('Sequencial único')))

46
sapl/materia/forms.py

@ -790,6 +790,13 @@ class AnexadaForm(ModelForm):
cleaned_data = self.cleaned_data
data_anexacao = cleaned_data['data_anexacao']
data_desanexacao = cleaned_data['data_desanexacao'] if cleaned_data['data_desanexacao'] else data_anexacao
if data_anexacao > data_desanexacao:
self.logger.error("Data de anexação posterior à data de desanexação.")
raise ValidationError(_("Data de anexação posterior à data de desanexação."))
try:
self.logger.info("Tentando obter objeto MateriaLegislativa (numero={}, ano={}, tipo={})."
.format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo']))
@ -817,6 +824,26 @@ class AnexadaForm(ModelForm):
if is_anexada:
self.logger.error("Matéria já se encontra anexada.")
raise ValidationError(_('Matéria já se encontra anexada'))
ciclico = False
anexadas_anexada = Anexada.objects.filter(materia_principal=materia_anexada)
while anexadas_anexada and not ciclico:
anexadas = []
for anexa in anexadas_anexada:
if materia_principal == anexa.materia_anexada:
ciclico = True
else:
for a in Anexada.objects.filter(materia_principal=anexa.materia_anexada):
anexadas.append(a)
anexadas_anexada = anexadas
if ciclico:
self.logger.error("A matéria não pode ser anexada por uma de suas anexadas.")
raise ValidationError(_("A matéria não pode ser anexada por uma de suas anexadas."))
cleaned_data['materia_anexada'] = materia_anexada
@ -1639,12 +1666,17 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm):
return super().save(commit)
inst.ano = timezone.now().year
numero__max = Proposicao.objects.filter(
autor=inst.autor,
ano=timezone.now().year).aggregate(Max('numero_proposicao'))
sequencia_numeracao = AppConfig.attr('sequencia_numeracao')
if sequencia_numeracao == 'A':
numero__max = Proposicao.objects.filter(
autor=inst.autor,
ano=timezone.now().year).aggregate(Max('numero_proposicao'))
elif sequencia_numeracao == 'B':
numero__max = Proposicao.objects.filter(
ano=timezone.now().year).aggregate(Max('numero_proposicao'))
numero__max = numero__max['numero_proposicao__max']
inst.numero_proposicao = (
numero__max + 1) if numero__max else 1
numero__max + 1) if numero__max else 1
self.gerar_hash(inst, receber_recibo)
@ -1741,7 +1773,7 @@ class ConfirmarProposicaoForm(ProposicaoForm):
required=False, widget=widgets.TextInput(
attrs={'readonly': 'readonly'}))
regime_tramitacao = forms.ModelChoiceField(
regime_tramitacao = forms.ModelChoiceField(label="Regime de tramitação",
required=False, queryset=RegimeTramitacao.objects.all())
gerar_protocolo = forms.ChoiceField(
@ -1807,6 +1839,10 @@ class ConfirmarProposicaoForm(ProposicaoForm):
# esta chamada isola o __init__ de ProposicaoForm
super(ProposicaoForm, self).__init__(*args, **kwargs)
if self.instance.tipo.content_type.model_class() ==\
TipoMateriaLegislativa:
self.fields['regime_tramitacao'].required = True
fields = [
Fieldset(
_('Dados Básicos'),

20
sapl/materia/migrations/0045_auto_20190415_1050.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-04-15 13:50
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('materia', '0044_auto_20190327_1409'),
]
operations = [
migrations.AlterField(
model_name='tipomaterialegislativa',
name='sequencia_numeracao',
field=models.CharField(blank=True, choices=[('A', 'Sequencial por ano para cada autor'), ('B', 'Sequencial por ano indepententemente do autor'), ('L', 'Sequencial por legislatura'), ('U', 'Sequencial único')], max_length=1, verbose_name='Sequência de numeração'),
),
]

58
sapl/materia/views.py

@ -2070,11 +2070,39 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView):
qr = self.request.GET.copy()
context['object_list'] = context['object_list'].order_by(
'ano', 'numero')
'numero', '-ano')
principal = MateriaLegislativa.objects.get(pk=self.kwargs['pk'])
not_list = [self.kwargs['pk']] + \
[m for m in principal.materia_principal_set.all().values_list('materia_anexada_id', flat=True)]
context['object_list'] = context['object_list'].exclude(pk__in=not_list)
context['temp_object_list'] = context['object_list']
context['object_list'] = []
for obj in context['temp_object_list']:
materia_anexada = obj
ciclico = False
anexadas_anexada = Anexada.objects.filter(
materia_principal = materia_anexada
)
while anexadas_anexada and not ciclico:
anexadas = []
for anexa in anexadas_anexada:
if principal == anexa.materia_anexada:
ciclico = True
else:
for a in Anexada.objects.filter(materia_principal=anexa.materia_anexada):
anexadas.append(a)
anexadas_anexada = anexadas
if not ciclico:
context['object_list'].append(obj)
context['numero_res'] = len(context['object_list'])
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
context['show_results'] = show_results_filter_set(qr)
@ -2084,19 +2112,31 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView):
def post(self, request, *args, **kwargs):
marcadas = request.POST.getlist('materia_id')
if len(marcadas) == 0:
msg = _('Nenhuma máteria foi selecionada.')
messages.add_message(request, messages.ERROR, msg)
return self.get(request, self.kwargs)
data_anexacao = datetime.strptime(
request.POST['data_anexacao'], "%d/%m/%Y").date()
if request.POST['data_desanexacao'] == '':
data_desanexacao = None
v_data_desanexacao = data_anexacao
else:
data_desanexacao = datetime.strptime(
request.POST['data_desanexacao'], "%d/%m/%Y").date()
v_data_desanexacao = data_desanexacao
if len(marcadas) == 0:
msg = _('Nenhuma máteria foi selecionada.')
messages.add_message(request, messages.ERROR, msg)
if data_anexacao > v_data_desanexacao:
msg = _('Data de anexação posterior à data de desanexação.')
messages.add_message(request, messages.ERROR, msg)
return self.get(request, self.kwargs)
if data_anexacao > v_data_desanexacao:
msg = _('Data de anexação posterior à data de desanexação.')
messages.add_message(request, messages.ERROR, msg)
return self.get(request, self.kwargs)
principal = MateriaLegislativa.objects.get(pk=kwargs['pk'])
for materia in MateriaLegislativa.objects.filter(id__in=marcadas):
@ -2108,9 +2148,11 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView):
anexada.data_desanexacao = data_desanexacao
anexada.save()
msg = _('Materia(s) anexada(s).')
msg = _('Matéria(s) anexada(s).')
messages.add_message(request, messages.SUCCESS, msg)
return self.get(request, self.kwargs)
sucess_url = reverse('sapl_index') + 'materia/' + kwargs['pk'] + '/anexada'
return HttpResponseRedirect(sucess_url)
class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):

128
sapl/protocoloadm/forms.py

@ -29,7 +29,7 @@ from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, AnoNumeroOrderingFilter,
from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo,
DocumentoAdministrativo,
Protocolo, TipoDocumentoAdministrativo,
TramitacaoAdministrativo)
TramitacaoAdministrativo, Anexado)
TIPOS_PROTOCOLO = [('0', 'Recebido'), ('1', 'Enviado'),
@ -221,7 +221,7 @@ class DocumentoAdministrativoFilterSet(django_filters.FilterSet):
)
class AnularProcoloAdmForm(ModelForm):
class AnularProtocoloAdmForm(ModelForm):
logger = logging.getLogger(__name__)
@ -240,7 +240,7 @@ class AnularProcoloAdmForm(ModelForm):
widget=forms.Textarea)
def clean(self):
super(AnularProcoloAdmForm, self).clean()
super(AnularProtocoloAdmForm, self).clean()
cleaned_data = self.cleaned_data
@ -313,7 +313,7 @@ class AnularProcoloAdmForm(ModelForm):
form_actions(label='Anular')
)
)
super(AnularProcoloAdmForm, self).__init__(
super(AnularProtocoloAdmForm, self).__init__(
*args, **kwargs)
@ -781,6 +781,126 @@ class TramitacaoAdmEditForm(TramitacaoAdmForm):
return self.cleaned_data
class AnexadoForm(ModelForm):
logger = logging.getLogger(__name__)
tipo = forms.ModelChoiceField(
label='Tipo',
required=True,
queryset=TipoDocumentoAdministrativo.objects.all(),
empty_label='Selecione'
)
numero = forms.CharField(label='Número', required=True)
ano = forms.CharField(label='Ano', required=True)
def __init__(self, *args, **kwargs):
return super(AnexadoForm, self).__init__(*args, **kwargs)
def clean(self):
super(AnexadoForm, self).clean()
if not self.is_valid():
return self.cleaned_data
cleaned_data = self.cleaned_data
data_anexacao = cleaned_data['data_anexacao']
data_desanexacao = cleaned_data['data_desanexacao'] if cleaned_data['data_desanexacao'] else data_anexacao
if data_anexacao > data_desanexacao:
self.logger.error("Data de anexação posterior à data de desanexação.")
raise ValidationError(_("Data de anexação posterior à data de desanexação."))
try:
self.logger.info(
"Tentando obter objeto DocumentoAdministrativo (numero={}, ano={}, tipo={})."
.format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo'])
)
documento_anexado = DocumentoAdministrativo.objects.get(
numero=cleaned_data['numero'],
ano=cleaned_data['ano'],
tipo=cleaned_data['tipo']
)
except ObjectDoesNotExist:
msg = _('O {} {}/{} não existe no cadastro de documentos administrativos.'
.format(cleaned_data['tipo'], cleaned_data['numero'], cleaned_data['ano']))
self.logger.error("O documento a ser anexado não existe no cadastro"
" de documentos administrativos")
raise ValidationError(msg)
documento_principal = self.instance.documento_principal
if documento_principal == documento_anexado:
self.logger.error("O documento não pode ser anexado a si mesmo.")
raise ValidationError(_("O documento não pode ser anexado a si mesmo"))
is_anexado = Anexado.objects.filter(documento_principal=documento_principal,
documento_anexado=documento_anexado
).exclude(pk=self.instance.pk).exists()
if is_anexado:
self.logger.error("Documento já se encontra anexado.")
raise ValidationError(_('Documento já se encontra anexado'))
ciclico = False
anexados_anexado = Anexado.objects.filter(documento_principal=documento_anexado)
while(anexados_anexado and not ciclico):
anexados = []
for anexo in anexados_anexado:
if documento_principal == anexo.documento_anexado:
ciclico = True
else:
for a in Anexado.objects.filter(documento_principal=anexo.documento_anexado):
anexados.append(a)
anexados_anexado = anexados
if ciclico:
self.logger.error("O documento não pode ser anexado por um de seus anexados.")
raise ValidationError(_('O documento não pode ser anexado por um de seus anexados'))
cleaned_data['documento_anexado'] = documento_anexado
return cleaned_data
def save(self, commit=False):
anexado = super(AnexadoForm, self).save(commit)
anexado.documento_anexado = self.cleaned_data['documento_anexado']
anexado.save()
return anexado
class Meta:
model = Anexado
fields = ['tipo', 'numero', 'ano', 'data_anexacao', 'data_desanexacao']
class AnexadoEmLoteFilterSet(django_filters.FilterSet):
class Meta(FilterOverridesMetaMixin):
model = DocumentoAdministrativo
fields = ['tipo', 'data']
def __init__(self, *args, **kwargs):
super(AnexadoEmLoteFilterSet, self).__init__(*args, **kwargs)
self.filters['tipo'].label = 'Tipo de Documento*'
self.filters['data'].label = 'Data (Inicial - Final)*'
row1 = to_row([('tipo', 12)])
row2 = to_row([('data', 12)])
self.form.helper = SaplFormHelper()
self.form.helper.form_method = 'GET'
self.form.helper.layout = Layout(
Fieldset(_('Pesquisa de Documentos'),
row1, row2, form_actions(label='Pesquisar'))
)
class DocumentoAdministrativoForm(FileFieldCheckMixin, ModelForm):
logger = logging.getLogger(__name__)

35
sapl/protocoloadm/migrations/0018_auto_20190314_1532.py

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-03-14 18:32
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0017_merge_20190121_1552'),
]
operations = [
migrations.CreateModel(
name='Anexado',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('data_anexacao', models.DateField(verbose_name='Data Anexação')),
('data_desanexacao', models.DateField(blank=True, null=True, verbose_name='Data Desanexação')),
('documento_anexado', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documento_anexado_set', to='protocoloadm.DocumentoAdministrativo', verbose_name='Documento Anexado')),
('documento_principal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documento_principal_set', to='protocoloadm.DocumentoAdministrativo', verbose_name='Documento Principal')),
],
options={
'verbose_name': 'Anexado',
'verbose_name_plural': 'Anexados',
},
),
migrations.AddField(
model_name='documentoadministrativo',
name='anexados',
field=models.ManyToManyField(blank=True, related_name='anexo_de', through='protocoloadm.Anexado', to='protocoloadm.DocumentoAdministrativo'),
),
]

42
sapl/protocoloadm/models.py

@ -170,6 +170,18 @@ class DocumentoAdministrativo(models.Model):
verbose_name=_('Acesso Restrito'),
blank=True)
anexados = models.ManyToManyField(
'self',
blank=True,
through='Anexado',
symmetrical=False,
related_name='anexo_de',
through_fields=(
'documento_principal',
'documento_anexado'
)
)
class Meta:
verbose_name = _('Documento Administrativo')
verbose_name_plural = _('Documentos Administrativos')
@ -317,6 +329,36 @@ class TramitacaoAdministrativo(models.Model):
}
@reversion.register()
class Anexado(models.Model):
documento_principal = models.ForeignKey(
DocumentoAdministrativo, related_name='documento_principal_set',
on_delete = models.CASCADE,
verbose_name=_('Documento Principal')
)
documento_anexado = models.ForeignKey(
DocumentoAdministrativo, related_name='documento_anexado_set',
on_delete = models.CASCADE,
verbose_name=_('Documento Anexado')
)
data_anexacao = models.DateField(verbose_name=_('Data Anexação'))
data_desanexacao = models.DateField(
blank=True, null=True, verbose_name=_('Data Desanexação')
)
class Meta:
verbose_name = _('Anexado')
verbose_name_plural = _('Anexados')
def __str__(self):
return _('Anexado: %(documento_anexado_tipo)s %(documento_anexado_numero)s'
'/%(documento_anexado_ano)s\n') % {
'documento_anexado_tipo': self.documento_anexado.tipo,
'documento_anexado_numero': self.documento_anexado.numero,
'documento_anexado_ano': self.documento_anexado.ano
}
@reversion.register()
class AcompanhamentoDocumento(models.Model):
usuario = models.CharField(max_length=50)

32
sapl/protocoloadm/tests/test_protocoloadm.py

@ -9,7 +9,7 @@ import pytest
from sapl.base.models import AppConfig
from sapl.materia.models import UnidadeTramitacao
from sapl.protocoloadm.forms import (AnularProcoloAdmForm,
from sapl.protocoloadm.forms import (AnularProtocoloAdmForm,
DocumentoAdministrativoForm,
MateriaLegislativa, ProtocoloDocumentForm,
ProtocoloMateriaForm)
@ -51,7 +51,7 @@ def test_anular_protocolo_submit(admin_client):
@pytest.mark.django_db(transaction=False)
def test_form_anular_protocolo_inexistente():
form = AnularProcoloAdmForm({'numero': '1',
form = AnularProtocoloAdmForm({'numero': '1',
'ano': '2016',
'justificativa_anulacao': 'TESTE'})
@ -64,7 +64,7 @@ def test_form_anular_protocolo_inexistente():
@pytest.mark.django_db(transaction=False)
def test_form_anular_protocolo_valido():
mommy.make(Protocolo, numero='1', ano='2016', anulado=False)
form = AnularProcoloAdmForm({'numero': '1',
form = AnularProtocoloAdmForm({'numero': '1',
'ano': '2016',
'justificativa_anulacao': 'TESTE'})
if not form.is_valid():
@ -74,7 +74,7 @@ def test_form_anular_protocolo_valido():
@pytest.mark.django_db(transaction=False)
def test_form_anular_protocolo_anulado():
mommy.make(Protocolo, numero='1', ano='2016', anulado=True)
form = AnularProcoloAdmForm({'numero': '1',
form = AnularProtocoloAdmForm({'numero': '1',
'ano': '2016',
'justificativa_anulacao': 'TESTE'})
assert form.errors['__all__'] == \
@ -88,7 +88,7 @@ def test_form_anular_protocolo_campos_obrigatorios():
# TODO: generalizar para diminuir o tamanho deste método
# numero ausente
form = AnularProcoloAdmForm({'numero': '',
form = AnularProtocoloAdmForm({'numero': '',
'ano': '2016',
'justificativa_anulacao': 'TESTE'})
if form.is_valid():
@ -98,7 +98,7 @@ def test_form_anular_protocolo_campos_obrigatorios():
assert form.errors['numero'] == [_('Este campo é obrigatório.')]
# ano ausente
form = AnularProcoloAdmForm({'numero': '1',
form = AnularProtocoloAdmForm({'numero': '1',
'ano': '',
'justificativa_anulacao': 'TESTE'})
if form.is_valid():
@ -108,7 +108,7 @@ def test_form_anular_protocolo_campos_obrigatorios():
assert form.errors['ano'] == [_('Este campo é obrigatório.')]
# justificativa_anulacao ausente
form = AnularProcoloAdmForm({'numero': '1',
form = AnularProtocoloAdmForm({'numero': '1',
'ano': '2016',
'justificativa_anulacao': ''})
if form.is_valid():
@ -261,7 +261,7 @@ def test_create_tramitacao(admin_client):
@pytest.mark.django_db(transaction=False)
def test_anular_protocolo_dados_invalidos():
form = AnularProcoloAdmForm(data={})
form = AnularProtocoloAdmForm(data={})
assert not form.is_valid()
@ -276,10 +276,10 @@ def test_anular_protocolo_dados_invalidos():
@pytest.mark.django_db(transaction=False)
def test_anular_protocolo_form_anula_protocolo_inexistente():
form = AnularProcoloAdmForm(data={'numero': '1',
form = AnularProtocoloAdmForm(data={'numero': '1',
'ano': '2017',
'justificativa_anulacao': 'teste'
})
})
assert not form.is_valid()
@ -291,10 +291,10 @@ def test_anular_protocolo_form_anula_protocolo_inexistente():
def test_anular_protocolo_form_anula_protocolo_anulado():
mommy.make(Protocolo, numero=1, ano=2017, anulado=True)
form = AnularProcoloAdmForm(data={'numero': '1',
form = AnularProtocoloAdmForm(data={'numero': '1',
'ano': '2017',
'justificativa_anulacao': 'teste'
})
})
assert not form.is_valid()
@ -316,10 +316,10 @@ def test_anular_protocolo_form_anula_protocolo_com_doc_vinculado():
ano=2017,
numero_protocolo=1)
form = AnularProcoloAdmForm(data={'numero': '1',
form = AnularProtocoloAdmForm(data={'numero': '1',
'ano': '2017',
'justificativa_anulacao': 'teste'
})
})
assert not form.is_valid()
@ -338,10 +338,10 @@ def test_anular_protocolo_form_anula_protocolo_com_doc_vinculado():
mommy.make(DocumentoAdministrativo,
protocolo=protocolo_documento)
form = AnularProcoloAdmForm(data={'numero': '2',
form = AnularProtocoloAdmForm(data={'numero': '2',
'ano': '2017',
'justificativa_anulacao': 'teste'
})
})
assert not form.is_valid()

7
sapl/protocoloadm/urls.py

@ -21,7 +21,8 @@ from sapl.protocoloadm.views import (AcompanhamentoDocumentoView,
atualizar_numero_documento,
doc_texto_integral,
DesvincularDocumentoView,
DesvincularMateriaView)
DesvincularMateriaView,
AnexadoCrud, DocumentoAnexadoEmLoteView)
from .apps import AppConfig
@ -30,6 +31,7 @@ app_name = AppConfig.name
urlpatterns_documento_administrativo = [
url(r'^docadm/',
include(DocumentoAdministrativoCrud.get_urls() +
AnexadoCrud.get_urls() +
TramitacaoAdmCrud.get_urls() +
DocumentoAcessorioAdministrativoCrud.get_urls())),
@ -38,6 +40,9 @@ urlpatterns_documento_administrativo = [
url(r'^docadm/texto_integral/(?P<pk>\d+)$', doc_texto_integral,
name='doc_texto_integral'),
url(r'^docadm/(?P<pk>\d+)/anexado_em_lote', DocumentoAnexadoEmLoteView.as_view(),
name='anexado_em_lote'),
]
urlpatterns_protocolo = [

162
sapl/protocoloadm/views.py

@ -17,7 +17,7 @@ from django.http.response import HttpResponseRedirect
from django.shortcuts import redirect
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.views.generic import ListView, CreateView
from django.views.generic import ListView, CreateView, UpdateView
from django.views.generic.base import RedirectView, TemplateView
from django.views.generic.edit import FormView
from django_filters.views import FilterView
@ -27,7 +27,8 @@ from sapl.base.email_utils import do_envia_email_confirmacao
from sapl.base.models import Autor, CasaLegislativa
from sapl.base.signals import tramitacao_signal
from sapl.comissoes.models import Comissao
from sapl.crud.base import Crud, CrudAux, MasterDetailCrud, make_pagination
from sapl.crud.base import (Crud, CrudAux, MasterDetailCrud, make_pagination,
RP_LIST, RP_DETAIL)
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
from sapl.materia.views import gerar_pdf_impressos
from sapl.parlamentares.models import Legislatura, Parlamentar
@ -36,7 +37,7 @@ from sapl.utils import (create_barcode, get_base_url, get_client_ip,
get_mime_type_from_file_extension,
show_results_filter_set, mail_service_configured)
from .forms import (AcompanhamentoDocumentoForm, AnularProcoloAdmForm,
from .forms import (AcompanhamentoDocumentoForm, AnularProtocoloAdmForm,
DocumentoAcessorioAdministrativoForm,
DocumentoAdministrativoFilterSet,
DocumentoAdministrativoForm, FichaPesquisaAdmForm, FichaSelecionaAdmForm, ProtocoloDocumentForm,
@ -44,10 +45,11 @@ from .forms import (AcompanhamentoDocumentoForm, AnularProcoloAdmForm,
TramitacaoAdmEditForm, TramitacaoAdmForm,
DesvincularDocumentoForm, DesvincularMateriaForm,
filtra_tramitacao_adm_destino_and_status,
filtra_tramitacao_adm_destino, filtra_tramitacao_adm_status)
filtra_tramitacao_adm_destino, filtra_tramitacao_adm_status,
AnexadoForm, AnexadoEmLoteFilterSet)
from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo,
DocumentoAdministrativo, StatusTramitacaoAdministrativo,
TipoDocumentoAdministrativo, TramitacaoAdministrativo)
TipoDocumentoAdministrativo, TramitacaoAdministrativo, Anexado)
TipoDocumentoAdministrativoCrud = CrudAux.build(
@ -482,7 +484,7 @@ class ProtocoloListView(PermissionRequiredMixin, ListView):
class AnularProtocoloAdmView(PermissionRequiredMixin, CreateView):
template_name = 'protocoloadm/anular_protocoloadm.html'
form_class = AnularProcoloAdmForm
form_class = AnularProtocoloAdmForm
form_valid_message = _('Protocolo anulado com sucesso!')
permission_required = ('protocoloadm.action_anular_protocolo', )
@ -941,6 +943,154 @@ class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin,
return self.render_to_response(context)
class AnexadoCrud(MasterDetailCrud):
model = Anexado
parent_field = 'documento_principal'
help_topic = 'documento_anexado'
public = [RP_LIST, RP_DETAIL]
class BaseMixin(MasterDetailCrud.BaseMixin):
list_field_names = ['documento_anexado', 'data_anexacao']
class CreateView(MasterDetailCrud.CreateView):
form_class = AnexadoForm
class UpdateView(MasterDetailCrud.UpdateView):
form_class = AnexadoForm
def get_initial(self):
initial = super(UpdateView, self).get_initial()
initial['tipo'] = self.object.documento_anexado.tipo.id
initial['numero'] = self.object.documento_anexado.numero
initial['ano'] = self.object.documento_anexado.ano
return initial
class DetailView(MasterDetailCrud.DetailView):
@property
def layout_key(self):
return 'AnexadoDetail'
class DocumentoAnexadoEmLoteView(PermissionRequiredMixin, FilterView):
filterset_class = AnexadoEmLoteFilterSet
template_name = 'protocoloadm/em_lote/anexado.html'
permission_required = ('protocoloadm.add_anexado', )
def get_context_data(self, **kwargs):
context = super(
DocumentoAnexadoEmLoteView,self
).get_context_data(**kwargs)
context['root_pk'] = self.kwargs['pk']
context['subnav_template_name'] = 'protocoloadm/subnav.yaml'
context['title'] = _('Documentos Anexados em Lote')
# Verifica se os campos foram preenchidos
if not self.request.GET.get('tipo', " "):
msg =_('Por favor, selecione um tipo de documento.')
messages.add_message(self.request, messages.ERROR, msg)
if not self.request.GET.get('data_0', " ") or not self.request.GET.get('data_1', " "):
msg =_('Por favor, preencha as datas.')
messages.add_message(self.request, messages.ERROR, msg)
return context
if not self.request.GET.get('data_0', " ") or not self.request.GET.get('data_1', " "):
msg =_('Por favor, preencha as datas.')
messages.add_message(self.request, messages.ERROR, msg)
return context
qr = self.request.GET.copy()
context['temp_object_list'] = context['object_list'].order_by(
'numero', '-ano'
)
context['object_list'] = []
for obj in context['temp_object_list']:
if not obj.pk == int(context['root_pk']):
documento_principal = DocumentoAdministrativo.objects.get(id=context['root_pk'])
documento_anexado = obj
is_anexado = Anexado.objects.filter(documento_principal=documento_principal,
documento_anexado=documento_anexado).exists()
if not is_anexado:
ciclico = False
anexados_anexado = Anexado.objects.filter(documento_principal=documento_anexado)
while anexados_anexado and not ciclico:
anexados = []
for anexo in anexados_anexado:
if documento_principal == anexo.documento_anexado:
ciclico = True
else:
for a in Anexado.objects.filter(documento_principal=anexo.documento_anexado):
anexados.append(a)
anexados_anexado = anexados
if not ciclico:
context['object_list'].append(obj)
context['numero_res'] = len(context['object_list'])
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
context['show_results'] = show_results_filter_set(qr)
return context
def post(self, request, *args, **kwargs):
marcados = request.POST.getlist('documento_id')
data_anexacao = datetime.strptime(
request.POST['data_anexacao'], "%d/%m/%Y"
).date()
if request.POST['data_desanexacao'] == '':
data_desanexacao = None
v_data_desanexacao = data_anexacao
else:
data_desanexacao = datetime.strptime(
request.POST['data_desanexacao'], "%d/%m/%Y"
).date()
v_data_desanexacao = data_desanexacao
if len(marcados) == 0:
msg =_('Nenhum documento foi selecionado')
messages.add_message(request, messages.ERROR, msg)
if data_anexacao > v_data_desanexacao:
msg=_('Data de anexação posterior à data de desanexação.')
messages.add_message(request, messages.ERROR, msg)
return self.get(request, self.kwargs)
if data_anexacao > v_data_desanexacao:
msg =_('Data de anexação posterior à data de desanexação.')
messages.add_message(request, messages.ERROR, msg)
return self.get(request, messages.ERROR, msg)
principal = DocumentoAdministrativo.objects.get(pk = kwargs['pk'])
for documento in DocumentoAdministrativo.objects.filter(id__in = marcados):
anexado = Anexado()
anexado.documento_principal = principal
anexado.documento_anexado = documento
anexado.data_anexacao = data_anexacao
anexado.data_desanexacao = data_desanexacao
anexado.save()
msg = _('Documento(s) anexado(s).')
messages.add_message(request, messages.SUCCESS, msg)
success_url = reverse('sapl_index') + 'docadm/' + kwargs['pk'] + '/anexado'
return HttpResponseRedirect(success_url)
class TramitacaoAdmCrud(MasterDetailCrud):
model = TramitacaoAdministrativo
parent_field = 'documento'

3
sapl/rules/map_rules.py

@ -60,6 +60,7 @@ rules_group_administrativo = {
'can_access_impressos'], __perms_publicas__),
# TODO: tratar em sapl.api a questão de ostencivo e restritivo
(protocoloadm.DocumentoAdministrativo, __base__, set()),
(protocoloadm.Anexado, __base__, set()),
(protocoloadm.DocumentoAcessorioAdministrativo, __base__, set()),
(protocoloadm.TramitacaoAdministrativo, __base__, set()),
]
@ -118,6 +119,8 @@ rules_group_materia = {
(materia.Autoria, __base__, __perms_publicas__),
(materia.DespachoInicial, __base__, __perms_publicas__),
(materia.DocumentoAcessorio, __base__, __perms_publicas__),
(materia.MateriaAssunto, __base__, __perms_publicas__),
(materia.AssuntoMateria, __base__, __perms_publicas__),
(materia.MateriaLegislativa, __base__ +
['can_access_impressos'], __perms_publicas__),

2
sapl/sessao/migrations/0036_auto_20190410_0836.py → sapl/sessao/migrations/0036_auto_20190412_1106.py

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-04-10 11:36
# Generated by Django 1.11.20 on 2019-04-12 14:06
from __future__ import unicode_literals
from django.db import migrations, models

33
sapl/sessao/migrations/0037_auto_20190415_1324.py

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-04-15 16:24
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('sessao', '0036_auto_20190412_1106'),
]
operations = [
migrations.AddField(
model_name='registrovotacao',
name='data_hora',
field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='Data/Hora'),
),
migrations.AddField(
model_name='registrovotacao',
name='ip',
field=models.CharField(blank=True, default='', max_length=30, verbose_name='IP'),
),
migrations.AddField(
model_name='registrovotacao',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
),
]

13
sapl/sessao/models.py

@ -456,6 +456,19 @@ class RegistroVotacao(models.Model):
verbose_name=_('Abstenções'))
observacao = models.TextField(
blank=True, verbose_name=_('Observações'))
user = models.ForeignKey(get_settings_auth_user_model(),
on_delete=models.PROTECT,
null=True,
blank=True)
ip = models.CharField(verbose_name=_('IP'),
max_length=30,
blank=True,
default='')
data_hora = models.DateTimeField(
verbose_name=_('Data/Hora'),
auto_now_add=True,
blank=True,
null=True)
class Meta:
verbose_name = _('Votação')

36
sapl/sessao/views.py

@ -35,7 +35,7 @@ from sapl.parlamentares.models import (Filiacao, Legislatura, Mandato,
Parlamentar, SessaoLegislativa)
from sapl.sessao.apps import AppConfig
from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm
from sapl.utils import show_results_filter_set, remover_acentos
from sapl.utils import show_results_filter_set, remover_acentos, get_client_ip
from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm, BlocoForm,
ExpedienteForm, JustificativaAusenciaForm, OcorrenciaSessaoForm, ListMateriaForm,
@ -2163,6 +2163,8 @@ class VotacaoView(SessaoPermissionMixin):
votacao.ordem_id = ordem_id
votacao.tipo_resultado_votacao_id = int(
request.POST['resultado_votacao'])
votacao.user = request.user
votacao.ip = get_client_ip(request)
votacao.save()
except Exception as e:
username = request.user.username
@ -2383,6 +2385,8 @@ class VotacaoNominalAbstract(SessaoPermissionMixin):
votacao.numero_votos_nao = votos_nao
votacao.numero_abstencoes = abstencoes
votacao.observacao = request.POST.get('observacao', None)
votacao.user = request.user
votacao.ip = get_client_ip(request)
votacao.materia_id = materia_votacao.materia.id
if self.ordem:
@ -2410,6 +2414,8 @@ class VotacaoNominalAbstract(SessaoPermissionMixin):
voto_parlamentar.voto = voto
voto_parlamentar.parlamentar_id = parlamentar_id
voto_parlamentar.votacao_id = votacao.id
voto_parlamentar.user = request.user
voto_parlamentar.ip = get_client_ip(request)
voto_parlamentar.save()
resultado = form.cleaned_data['resultado_votacao']
@ -2847,10 +2853,10 @@ class VotacaoExpedienteView(SessaoPermissionMixin):
if (int(request.POST['voto_presidente']) == 0):
qtde_presentes -= 1
if (qtde_votos > qtde_presentes or qtde_votos < qtde_presentes):
if qtde_votos != qtde_presentes:
form._errors["total_votos"] = ErrorList([u""])
return self.render_to_response(context)
elif (qtde_presentes == qtde_votos):
else:
try:
votacao = RegistroVotacao()
votacao.numero_votos_sim = int(request.POST['votos_sim'])
@ -2861,6 +2867,8 @@ class VotacaoExpedienteView(SessaoPermissionMixin):
votacao.expediente_id = expediente_id
votacao.tipo_resultado_votacao_id = int(
request.POST['resultado_votacao'])
votacao.user = request.user
votacao.ip = get_client_ip(request)
votacao.save()
except Exception as e:
username = request.user.username
@ -3544,9 +3552,13 @@ class VotacaoEmBlocoExpediente(PermissionRequiredForAppCrudMixin, ListView):
def get_context_data(self, **kwargs):
context = super(VotacaoEmBlocoExpediente,
self).get_context_data(**kwargs)
context['turno_choices'] = Tramitacao.TURNO_CHOICES
context['pk'] = self.kwargs['pk']
context['root_pk'] = self.kwargs['pk']
if not verifica_sessao_iniciada(self.request, self.kwargs['pk']):
context['sessao_iniciada'] = False
return context
context['sessao_iniciada'] = True
context['turno_choices'] = Tramitacao.TURNO_CHOICES
context['title'] = SessaoPlenaria.objects.get(id=self.kwargs['pk'])
return context
@ -3566,9 +3578,13 @@ class VotacaoEmBlocoOrdemDia(PermissionRequiredForAppCrudMixin, ListView):
def get_context_data(self, **kwargs):
context = super(VotacaoEmBlocoOrdemDia,
self).get_context_data(**kwargs)
context['turno_choices'] = Tramitacao.TURNO_CHOICES
context['pk'] = self.kwargs['pk']
context['root_pk'] = self.kwargs['pk']
if not verifica_sessao_iniciada(self.request, self.kwargs['pk']):
context['sessao_iniciada'] = False
return context
context['sessao_iniciada'] = True
context['turno_choices'] = Tramitacao.TURNO_CHOICES
context['title'] = SessaoPlenaria.objects.get(id=self.kwargs['pk'])
return context
@ -3640,6 +3656,8 @@ class VotacaoEmBlocoSimbolicaView(PermissionRequiredForAppCrudMixin, TemplateVie
resultado = TipoResultadoVotacao.objects.get(
id=request.POST['resultado_votacao'])
votacao.tipo_resultado_votacao = resultado
votacao.user = request.user
votacao.ip = get_client_ip(request)
votacao.save()
except Exception as e:
username = request.user.username
@ -3671,6 +3689,8 @@ class VotacaoEmBlocoSimbolicaView(PermissionRequiredForAppCrudMixin, TemplateVie
resultado = TipoResultadoVotacao.objects.get(
id=request.POST['resultado_votacao'])
votacao.tipo_resultado_votacao = resultado
votacao.user = request.user
votacao.ip = get_client_ip(request)
votacao.save()
except Exception as e:
username = request.user.username
@ -3862,6 +3882,8 @@ class VotacaoEmBlocoNominalView(PermissionRequiredForAppCrudMixin, TemplateView)
voto_parlamentar.voto = voto
voto_parlamentar.parlamentar_id = parlamentar_id
voto_parlamentar.votacao_id = votacao.id
voto_parlamentar.user = request.user
voto_parlamentar.ip = get_client_ip(request)
voto_parlamentar.save()
ordem.resultado = form.cleaned_data['resultado_votacao'].nome
@ -3889,6 +3911,8 @@ class VotacaoEmBlocoNominalView(PermissionRequiredForAppCrudMixin, TemplateView)
votacao.materia = expediente.materia
votacao.expediente = expediente
votacao.tipo_resultado_votacao = form.cleaned_data['resultado_votacao']
votacao.user = request.user
votacao.ip = get_client_ip(request)
votacao.save()
# Salva os votos de cada parlamentar
@ -3904,6 +3928,8 @@ class VotacaoEmBlocoNominalView(PermissionRequiredForAppCrudMixin, TemplateView)
voto_parlamentar.voto = voto
voto_parlamentar.parlamentar_id = parlamentar_id
voto_parlamentar.votacao_id = votacao.id
voto_parlamentar.user = request.user
voto_parlamentar.ip = get_client_ip(request)
voto_parlamentar.save()
expediente.resultado = form.cleaned_data['resultado_votacao'].nome

4
sapl/templates/base/layouts.yaml

@ -18,8 +18,8 @@ AppConfig:
- esfera_federacao
{% trans 'Proposições e Protocolo' %}:
- sequencia_numeracao proposicao_incorporacao_obrigatoria receber_recibo_proposicao
- escolher_numero_materia_proposicao protocolo_manual
- sequencia_numeracao protocolo_manual receber_recibo_proposicao
- proposicao_incorporacao_obrigatoria escolher_numero_materia_proposicao
{% trans 'Textos Articulados' %}:
- texto_articulado_proposicao texto_articulado_materia texto_articulado_norma

6
sapl/templates/materia/em_lote/anexada.html

@ -8,11 +8,11 @@
{% endif %}
{% if show_results %}
{% if object_list.count > 0 %}
{% if object_list.count == 1 %}
{% if numero_res > 0 %}
{% if numero_res == 1 %}
<h3 style="text-align: right;">{% trans 'Pesquisa concluída com sucesso! Foi encontrada 1 matéria.'%}</h3>
{% else %}
<h3 style="text-align: right;">{% blocktrans with object_list.count as total_materias %}Foram encontradas {{total_materias}} matérias.{% endblocktrans %}</h3>
<h3 style="text-align: right;">Foram encontradas {{ numero_res }} matérias.</h3>
{% endif %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}

13
sapl/templates/protocoloadm/anexado_list.html

@ -0,0 +1,13 @@
{% extends "crud/list.html" %}
{% load i18n %}
{% load common_tags %}
{% block more_buttons %}
{% if perms|get_add_perm:view %}
<a href="{% url 'sapl.protocoloadm:anexado_em_lote' root_pk %}" class="btn btn-outline-primary">
{% trans "Adicionar Anexado em Lote" %}
</a>
{% endif %}
{% endblock more_buttons %}

5
sapl/templates/protocoloadm/em_lote/anexado.html

@ -54,7 +54,7 @@
<tr>
<td>
<input type="checkbox" name="documento_id" value="{{documento.id}}" {% if check %} checked {% endif %}/>
{{documento.tipo.sigla}} {{documento.numero}}/{{documento.ano}} - {{documento.tipo.descricao}}
<a href="{% url 'sapl_index' %}docadm/{{documento.pk}}">{{documento.tipo.sigla}} {{documento.numero}}/{{documento.ano}} - {{documento.tipo.descricao}}</a>
</td>
</tr>
{% endfor %}
@ -63,6 +63,7 @@
</fieldset>
<input type="submit" value="Salvar" class="btn btn-primary"S>
</form>
<br/>
{% else %}
<tr>
<td>
@ -75,7 +76,7 @@
{% block extra_js %}
<script language="JavaScript">
function checkAll(elem) {
let checkboxes = document.getElementsByName('materia_id');
let checkboxes = document.getElementsByName('documento_id');
for (let i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].type == 'checkbox')
checkboxes[i].checked = elem.checked;

13
sapl/templates/protocoloadm/layouts.yaml

@ -10,6 +10,8 @@ DocumentoAdministrativo:
- assunto
- interessado tramitacao
- texto_integral
- documento_anexado_set__documento_principal|m2m_urlize_for_detail
- documento_principal_set__documento_anexado|m2m_urlize_for_detail
{% trans 'Outras Informações' %}:
- numero_externo
- dias_prazo data_fim_prazo
@ -33,6 +35,17 @@ TramitacaoAdministrativo:
- data_encaminhamento data_fim_prazo
- texto
Anexado:
{% trans 'Documento Anexado' %}:
- tipo numero ano
- data_anexacao data_desanexacao
AnexadoDetail:
{% trans 'Documento Anexado' %}:
- documento_principal|fk_urlize_for_detail
- documento_anexado|fk_urlize_for_detail
- data_anexacao data_desanexacao
Protocolo:
{% trans 'Indentificação Documento' %}:
- tipo_protocolo

2
sapl/templates/protocoloadm/subnav.yaml

@ -1,6 +1,8 @@
{% load i18n common_tags %}
- title: {% trans 'Início' %}
url: documentoadministrativo_detail
- title: {% trans 'Anexado' %}
url: anexado_list
- title: {% trans 'Tramitação' %}
url: tramitacaoadministrativo_list
- title: {% trans 'Documento Acessório' %}

98
sapl/templates/sessao/painel.html

@ -116,11 +116,11 @@ $(function() {
}
startTime();
$('#discurso').prop('disabled', true);
$('#aparte').prop('disabled', true);
$('#ordem').prop('disabled', true);
$('#consideracoes').prop('disabled', true);
var audioAlertFinish = document.getElementById("audio");
$('#discurso').prop('disabled', false);
$('#aparte').prop('disabled', false);
$('#ordem').prop('disabled', false);
$('#consideracoes').prop('disabled', false);
$('#discurso').runner({
autostart: false,
@ -130,16 +130,10 @@ $(function() {
milliseconds: false
}).on('runnerFinish', function(eventObject, info){
$.get('/painel/cronometro', { tipo: 'discurso', action: 'stop' } );
audioAlertFinish.play();
$('#discursoReset').show();
$('#discurso').runner('stop');
$('#discursoStart').text('Iniciar');
$('#aparteStart').prop('disabled', false);
$('#aparteReset').prop('disabled', false);
$('#ordemStart').prop('disabled', false);
$('#ordemReset').prop('disabled', false);
$('#consideracoesStart').prop('disabled', false);
$('#consideracoesReset').prop('disabled', false);
});
@ -152,12 +146,6 @@ $(function() {
$('#discursoReset').hide();
$('#discurso').runner('start');
$('#discursoStart').text('Parar');
$('#aparteStart').prop('disabled', false);
$('#aparteReset').prop('disabled', false);
$('#ordemStart').prop('disabled', false);
$('#ordemReset').prop('disabled', false);
$('#consideracoesStart').prop('disabled', false);
$('#consideracoesReset').prop('disabled', false);
} else {
@ -166,12 +154,6 @@ $(function() {
$('#discursoReset').show();
$('#discurso').runner('stop');
$('#discursoStart').text('Iniciar');
$('#aparteStart').prop('disabled', false);
$('#aparteReset').prop('disabled', false);
$('#ordemStart').prop('disabled', false);
$('#ordemReset').prop('disabled', false);
$('#consideracoesStart').prop('disabled', false);
$('#consideracoesReset').prop('disabled', false);
}
});
@ -191,16 +173,10 @@ $(function() {
milliseconds: false
}).on('runnerFinish', function(eventObject, info){
$.get('/painel/cronometro', { tipo: 'aparte', action: 'stop' } );
audioAlertFinish.play();
$('#aparteReset').show();
$('#aparte').runner('stop');
$('#aparteStart').text('Iniciar');
$('#discursoStart').prop('disabled', false);
$('#discursoReset').prop('disabled', false);
$('#ordemStart').prop('disabled', false);
$('#ordemReset').prop('disabled', false);
$('#consideracoesStart').prop('disabled', false);
$('#consideracoesReset').prop('disabled', false);
});
@ -212,12 +188,7 @@ $(function() {
$('#aparteReset').hide();
$('#aparte').runner('start');
$('#aparteStart').text('Parar');
$('#discursoStart').prop('disabled', false);
$('#discursoReset').prop('disabled', false);
$('#ordemStart').prop('disabled', false);
$('#ordemReset').prop('disabled', false);
$('#consideracoesStart').prop('disabled', false);
$('#consideracoesReset').prop('disabled', false);
} else {
$.get('/painel/cronometro', { tipo: 'aparte', action: 'stop' } );
@ -225,12 +196,7 @@ $(function() {
$('#aparteReset').show();
$('#aparte').runner('stop');
$('#aparteStart').text('Iniciar');
$('#discursoStart').prop('disabled', false);
$('#discursoReset').prop('disabled', false);
$('#ordemStart').prop('disabled', false);
$('#ordemReset').prop('disabled', false);
$('#consideracoesStart').prop('disabled', false);
$('#consideracoesReset').prop('disabled', false);
}
});
@ -250,16 +216,11 @@ $(function() {
milliseconds: false
}).on('runnerFinish', function(eventObject, info){
$.get('/painel/cronometro', { tipo: 'ordem', action: 'stop' } );
audioAlertFinish.play();
$('#ordemReset').show();
$('#ordem').runner('stop');
$('#ordemStart').text('Iniciar');
$('#discursoStart').prop('disabled', false);
$('#discursoReset').prop('disabled', false);
$('#aparteStart').prop('disabled', false);
$('#aparteReset').prop('disabled', false);
$('#consideracoesStart').prop('disabled', false);
$('#consideracoesReset').prop('disabled', false);
});
$('#ordemStart').click(function() {
@ -270,12 +231,7 @@ $(function() {
$('#ordemReset').hide();
$('#ordem').runner('start');
$('#ordemStart').text('Parar');
$('#discursoStart').prop('disabled', false);
$('#discursoReset').prop('disabled', false);
$('#aparteStart').prop('disabled', false);
$('#aparteReset').prop('disabled', false);
$('#consideracoesStart').prop('disabled', false);
$('#consideracoesReset').prop('disabled', false);
} else {
@ -284,12 +240,7 @@ $(function() {
$('#ordemReset').show();
$('#ordem').runner('stop');
$('#ordemStart').text('Iniciar');
$('#discursoStart').prop('disabled', false);
$('#discursoReset').prop('disabled', false);
$('#aparteStart').prop('disabled', false);
$('#aparteReset').prop('disabled', false);
$('#consideracoesStart').prop('disabled', false);
$('#consideracoesReset').prop('disabled', false);
}
});
@ -309,16 +260,11 @@ $(function() {
milliseconds: false
}).on('runnerFinish', function(eventObject, info){
$.get('/painel/cronometro', { tipo: 'consideracoes', action: 'stop' } );
audioAlertFinish.play();
$('#consideracoesReset').show();
$('#consideracoes').runner('stop');
$('#consideracoesStart').text('Iniciar');
$('#discursoStart').prop('disabled', false);
$('#discursoReset').prop('disabled', false);
$('#ordemStart').prop('disabled', false);
$('#ordemReset').prop('disabled', false);
$('#aparteStart').prop('disabled', false);
$('#aparteReset').prop('disabled', false);
});
@ -330,12 +276,7 @@ $(function() {
$('#consideracoesReset').hide();
$('#consideracoes').runner('start');
$('#consideracoesStart').text('Parar');
$('#discursoStart').prop('disabled', false);
$('#discursoReset').prop('disabled', false);
$('#ordemStart').prop('disabled', false);
$('#ordemReset').prop('disabled', false);
$('#aparteStart').prop('disabled', false);
$('#aparteReset').prop('disabled', false);
} else {
$.get('/painel/cronometro', { tipo: 'consideracoes', action: 'stop' } );
@ -343,12 +284,7 @@ $(function() {
$('#consideracoesReset').show();
$('#consideracoes').runner('stop');
$('#consideracoesStart').text('Iniciar');
$('#discursoStart').prop('disabled', false);
$('#discursoReset').prop('disabled', false);
$('#ordemStart').prop('disabled', false);
$('#ordemReset').prop('disabled', false);
$('#aparteStart').prop('disabled', false);
$('#aparteReset').prop('disabled', false);
}
});

160
sapl/templates/sessao/votacao/votacao_bloco_expediente.html

@ -3,94 +3,96 @@
{% block base_content %}
<form method="POST" enctype="application/x-www-form-urlencoded" id="form" action="{% url 'sapl.sessao:votacaoblocosimb' pk %}">
{% csrf_token %}
<br><br>
<table class="table table-striped table-bordered">
<thead class="thead-default">
{% if sessao_iniciada %}
<form method="POST" enctype="application/x-www-form-urlencoded" id="form" action="{% url 'sapl.sessao:votacaoblocosimb' pk %}">
{% csrf_token %}
<br><br>
<table class="table table-striped table-bordered">
<thead class="thead-default">
<tr>
<td><h3>{% trans "Tipo de Votação" %}</h3></td>
</tr>
</thead>
<tr>
<td><h3>{% trans "Tipo de Votação" %}</h3></td>
</tr>
</thead>
<tr>
<td class="col-md-12">
<fieldset id="tipo_votacao" name="tipo">
<input type="radio" name="tipo_votacao" id="tipo_votacao_1" value="1" onchange="alteraTipoVotacao()" checked="checked"> <label for="tipo">Simbólica</label>
</br>
<input type="radio" name="tipo_votacao" id="tipo_votacao_2" value="2" onchange="alteraTipoVotacao()" > <label for="tipo">Nominal</label>
</br>
</fieldset>
</td>
</tr>
</table>
<br>
<h3 id='frase_selecione'>Selecione o(s) expediente(s) desejado(s).</h3>
<table id='tab_ordens' class="table table-striped table-bordered">
<thead class="thead-default">
<tr>
<td><h3>{% trans "Expediente" %}</h3></td>
<td class="col-md-12">
<fieldset id="tipo_votacao" name="tipo">
<input type="radio" name="tipo_votacao" id="tipo_votacao_1" value="1" onchange="alteraTipoVotacao()" checked="checked"> <label for="tipo">Simbólica</label>
</br>
<input type="radio" name="tipo_votacao" id="tipo_votacao_2" value="2" onchange="alteraTipoVotacao()" > <label for="tipo">Nominal</label>
</br>
</fieldset>
</td>
</tr>
</thead>
</table>
<br>
<h3 id='frase_selecione'>Selecione o(s) expediente(s) desejado(s).</h3>
<table id='tab_ordens' class="table table-striped table-bordered">
<thead class="thead-default">
<tr>
<td><h3>{% trans "Expediente" %}</h3></td>
</tr>
</thead>
<div class="checkbox" id="check_all">
<label for="id_check_all">
<input type="checkbox" id="id_check_all" onchange="checkAll(this)" /> Marcar/Desmarcar Todos
</label>
</div>
<div class="checkbox" id="check_all">
<label for="id_check_all">
<input type="checkbox" id="id_check_all" onchange="checkAll(this)" /> Marcar/Desmarcar Todos
</label>
</div>
{% for o in expedientes %}
{% if o.tipo_votacao == 1 or o.tipo_votacao == 2 %}
<tr class="{% if o.tipo_votacao == 1 %}Simbolica{% else %}Nominal{% endif %}" {% if o.tipo_votacao == 2 %} style="display:none;" {% endif %}>
<td>
<input type="checkbox" name="marcadas_{{o.tipo_votacao}}" id="{{o.id}}" value="{{o.id}}" {% if check %} checked {% endif %}>
<strong><a href="{% url 'sapl.materia:materialegislativa_detail' o.id %}">{{o.materia.tipo.sigla}} {{o.materia.numero}}/{{o.materia.ano}} - {{o.materia.tipo}}</strong></a></br>
{% if o.materia.numeracao_set.last %}
<strong>Processo:</strong> &nbsp; {{o.materia.numeracao_set.last}}</br>
{% endif %}
<strong>Autor:</strong>
{% for a in o.materia.autoria_set.all %}
{% if not forloop.first %}
, &nbsp;&nbsp; {{a.autor|default_if_none:""}}
{% else %}
&nbsp;{{a.autor|default_if_none:""}}
{% for o in expedientes %}
{% if o.tipo_votacao == 1 or o.tipo_votacao == 2 %}
<tr class="{% if o.tipo_votacao == 1 %}Simbolica{% else %}Nominal{% endif %}" {% if o.tipo_votacao == 2 %} style="display:none;" {% endif %}>
<td>
<input type="checkbox" name="marcadas_{{o.tipo_votacao}}" id="{{o.id}}" value="{{o.id}}" {% if check %} checked {% endif %}>
<strong><a href="{% url 'sapl.materia:materialegislativa_detail' o.id %}">{{o.materia.tipo.sigla}} {{o.materia.numero}}/{{o.materia.ano}} - {{o.materia.tipo}}</strong></a></br>
{% if o.materia.numeracao_set.last %}
<strong>Processo:</strong> &nbsp; {{o.materia.numeracao_set.last}}</br>
{% endif %}
{% endfor %}
</br>
{% if o.materia.numero_protocolo %}
<strong>Protocolo:</strong> &nbsp; {{o.materia.numero_protocolo}}</br>
{% endif %}
{% if o.materia.tramitacao_set.last %}
{% if o.materia.tramitacao_set.last.turno %}
<strong>Turno:</strong>&nbsp;
{% for t in turno_choices %}
{% if t.0 == o.materia.tramitacao_set.last.turno %}
{{ t.1 }}
{% endif %}
{% endfor %}</br>
<strong>Autor:</strong>
{% for a in o.materia.autoria_set.all %}
{% if not forloop.first %}
, &nbsp;&nbsp; {{a.autor|default_if_none:""}}
{% else %}
&nbsp;{{a.autor|default_if_none:""}}
{% endif %}
{% endfor %}
</br>
{% if o.materia.numero_protocolo %}
<strong>Protocolo:</strong> &nbsp; {{o.materia.numero_protocolo}}</br>
{% endif %}
{% if o.materia.tramitacao_set.last %}
{% if o.materia.tramitacao_set.last.turno %}
<strong>Turno:</strong>&nbsp;
{% for t in turno_choices %}
{% if t.0 == o.materia.tramitacao_set.last.turno %}
{{ t.1 }}
{% endif %}
{% endfor %}</br>
{% endif %}
{% endif %}
{% endif %}
<strong>Ementa:</strong>&nbsp;{{ o.ementa|safe }}</br>
<p></p>
</td>
</tr>
{% endif %}
{% endfor %}
</table>
<strong>Ementa:</strong>&nbsp;{{ o.ementa|safe }}</br>
<p></p>
</td>
</tr>
{% endif %}
{% endfor %}
</table>
<table class="table table-striped table-bordered" style="display:none" id="nenhum_exp">
<tr>
<td>
<h3>Nenhuma matéria do expediente aberta.</h3>
</td>
</tr>
</table>
<table class="table table-striped table-bordered" style="display:none" id="nenhum_exp">
<tr>
<td>
<h3>Nenhuma matéria do expediente aberta.</h3>
</td>
</tr>
</table>
<a href="{% url 'sapl.sessao:expedientemateria_list' pk %}" class="btn btn-warning mb-3" id="but_cancel">Voltar</a>
<input type="submit" value="Registrar votação" class="btn btn-primary mb-3 float-right" id="but_reg">
<a href="{% url 'sapl.sessao:expedientemateria_list' pk %}" class="btn btn-warning mb-3" id="but_cancel">Voltar</a>
<input type="submit" value="Registrar votação" class="btn btn-primary mb-3 float-right" id="but_reg">
<input type="hidden" id="origem" name="origem" value="expediente">
</form>
<input type="hidden" id="origem" name="origem" value="expediente">
</form>
{% endif %}
{% endblock base_content %}

168
sapl/templates/sessao/votacao/votacao_bloco_ordem.html

@ -3,94 +3,96 @@
{% block base_content %}
<form method="POST" enctype="application/x-www-form-urlencoded" id="form" action="{% url 'sapl.sessao:votacaoblocosimb' pk %}">
{% csrf_token %}
<br><br>
<table class="table table-striped table-bordered">
{% if sessao_iniciada %}
<form method="POST" enctype="application/x-www-form-urlencoded" id="form" action="{% url 'sapl.sessao:votacaoblocosimb' pk %}">
{% csrf_token %}
<br><br>
<table class="table table-striped table-bordered">
<thead class="thead-default">
<tr>
<td><h3>{% trans "Tipo de Votação" %}</h3></td>
</tr>
</thead>
<tr>
<td class="col-md-12">
<fieldset id="tipo_votacao" name="tipo">
<input type="radio" name="tipo_votacao" id="tipo_votacao_1" value="1" onchange="alteraTipoVotacao()" checked="checked"> <label for="tipo">Simbólica</label>
</br>
<input type="radio" name="tipo_votacao" id="tipo_votacao_2" value="2" onchange="alteraTipoVotacao()" > <label for="tipo">Nominal</label>
</br>
</fieldset>
</td>
</tr>
</table>
<br>
<h3 id='frase_selecione'>Selecione a(s) ordem(s) do dia desejada(s).</h3>
<table id='tab_ordens' class="table table-striped table-bordered">
<thead class="thead-default">
<tr>
<td><h3>{% trans "Tipo de Votação" %}</h3></td>
</tr>
<tr>
<td><h3>{% trans "Ordem do dia" %}</h3></td>
</tr>
</thead>
<tr>
<td class="col-md-12">
<fieldset id="tipo_votacao" name="tipo">
<input type="radio" name="tipo_votacao" id="tipo_votacao_1" value="1" onchange="alteraTipoVotacao()" checked="checked"> <label for="tipo">Simbólica</label>
</br>
<input type="radio" name="tipo_votacao" id="tipo_votacao_2" value="2" onchange="alteraTipoVotacao()" > <label for="tipo">Nominal</label>
<div class="checkbox" id="check_all">
<label for="id_check_all">
<input type="checkbox" id="id_check_all" onchange="checkAll(this)" /> Marcar/Desmarcar Todos
</label>
</div>
{% for o in ordem_dia %}
{% if o.tipo_votacao == 1 or o.tipo_votacao == 2 %}
<tr class="{% if o.tipo_votacao == 1 %}Simbolica{% else %}Nominal{% endif %}" {% if o.tipo_votacao == 2 %} style="display:none;" {% endif %}>
<td>
<input type="checkbox" name="marcadas_{{o.tipo_votacao}}" id="{{o.id}}" value="{{o.id}}" {% if check %} checked {% endif %}>
<strong><a href="{% url 'sapl.materia:materialegislativa_detail' o.id %}">{{o.materia.tipo.sigla}} {{o.materia.numero}}/{{o.materia.ano}} - {{o.materia.tipo}}</strong></a></br>
{% if o.materia.numeracao_set.last %}
<strong>Processo:</strong> &nbsp; {{o.materia.numeracao_set.last}}</br>
{% endif %}
<strong>Autor:</strong>
{% for a in o.materia.autoria_set.all %}
{% if not forloop.first %}
, &nbsp;&nbsp; {{a.autor|default_if_none:""}}
{% else %}
&nbsp;{{a.autor|default_if_none:""}}
{% endif %}
{% endfor %}
</br>
</fieldset>
</td>
</tr>
</table>
<br>
<h3 id='frase_selecione'>Selecione a(s) ordem(s) do dia desejada(s).</h3>
<table id='tab_ordens' class="table table-striped table-bordered">
<thead class="thead-default">
<tr>
<td><h3>{% trans "Ordem do dia" %}</h3></td>
</tr>
</thead>
<div class="checkbox" id="check_all">
<label for="id_check_all">
<input type="checkbox" id="id_check_all" onchange="checkAll(this)" /> Marcar/Desmarcar Todos
</label>
</div>
{% for o in ordem_dia %}
{% if o.tipo_votacao == 1 or o.tipo_votacao == 2 %}
<tr class="{% if o.tipo_votacao == 1 %}Simbolica{% else %}Nominal{% endif %}" {% if o.tipo_votacao == 2 %} style="display:none;" {% endif %}>
<td>
<input type="checkbox" name="marcadas_{{o.tipo_votacao}}" id="{{o.id}}" value="{{o.id}}" {% if check %} checked {% endif %}>
<strong><a href="{% url 'sapl.materia:materialegislativa_detail' o.id %}">{{o.materia.tipo.sigla}} {{o.materia.numero}}/{{o.materia.ano}} - {{o.materia.tipo}}</strong></a></br>
{% if o.materia.numeracao_set.last %}
<strong>Processo:</strong> &nbsp; {{o.materia.numeracao_set.last}}</br>
{% endif %}
<strong>Autor:</strong>
{% for a in o.materia.autoria_set.all %}
{% if not forloop.first %}
, &nbsp;&nbsp; {{a.autor|default_if_none:""}}
{% else %}
&nbsp;{{a.autor|default_if_none:""}}
{% if o.materia.numero_protocolo %}
<strong>Protocolo:</strong> &nbsp; {{o.materia.numero_protocolo}}</br>
{% endif %}
{% endfor %}
</br>
{% if o.materia.numero_protocolo %}
<strong>Protocolo:</strong> &nbsp; {{o.materia.numero_protocolo}}</br>
{% endif %}
{% if o.materia.tramitacao_set.last %}
{% if o.materia.tramitacao_set.last.turno %}
<strong>Turno:</strong>&nbsp;
{% for t in turno_choices %}
{% if t.0 == o.materia.tramitacao_set.last.turno %}
{{ t.1 }}
{% endif %}
{% endfor %}</br>
{% if o.materia.tramitacao_set.last %}
{% if o.materia.tramitacao_set.last.turno %}
<strong>Turno:</strong>&nbsp;
{% for t in turno_choices %}
{% if t.0 == o.materia.tramitacao_set.last.turno %}
{{ t.1 }}
{% endif %}
{% endfor %}</br>
{% endif %}
{% endif %}
{% endif %}
<strong>Ementa:</strong>&nbsp;{{ o.ementa|safe }}</br>
<p></p>
</td>
</tr>
{% endif %}
{% endfor %}
</table>
<table class="table table-striped table-bordered" style="display:none" id="nenhuma_ordem">
<tr>
<td>
<h3>Nenhuma ordem do dia aberta.</h3>
</td>
</tr>
</table>
<a href="{% url 'sapl.sessao:ordemdia_list' pk %}" class="btn btn-warning mb-3" id="but_cancel">Voltar</a>
<input type="submit" value="Registrar votação" class="btn btn-primary mb-3 float-right" id="but_reg">
<input type="hidden" id="origem" name="origem" value="ordem">
</form>
<strong>Ementa:</strong>&nbsp;{{ o.ementa|safe }}</br>
<p></p>
</td>
</tr>
{% endif %}
{% endfor %}
</table>
<table class="table table-striped table-bordered" style="display:none" id="nenhuma_ordem">
<tr>
<td>
<h3>Nenhuma ordem do dia aberta.</h3>
</td>
</tr>
</table>
<a href="{% url 'sapl.sessao:ordemdia_list' pk %}" class="btn btn-warning mb-3" id="but_cancel">Voltar</a>
<input type="submit" value="Registrar votação" class="btn btn-primary mb-3 float-right" id="but_reg">
<input type="hidden" id="origem" name="origem" value="ordem">
</form>
{% endif %}
{% endblock base_content %}

84
scripts/remove_multiplos_autores.py

@ -0,0 +1,84 @@
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Count
from sapl.base.models import Autor
from sapl.parlamentares.models import Parlamentar
def pega_autores():
return [[autor for autor in Autor.objects.filter(nome=nome)]
for nome in Autor.objects.values_list('nome', flat=True).annotate(qntd=Count('nome')).filter(qntd__gt=1)]
def pega_parlamentares_autores():
parlamentares = [[parlamentar for parlamentar in Parlamentar.objects.filter(nome_parlamentar=nome_parlamentar)]
for nome_parlamentar in Parlamentar.objects.values_list('nome_parlamentar', flat=True)
.annotate(qntd=Count('nome_parlamentar')).filter(qntd__gt=1)]
parlamentares_autores = []
for parlamentar in parlamentares:
parlamentar_autor = []
for clone in parlamentar[1:]:
try:
autor_principal = Autor.objects.get(parlamentar_set=parlamentar[0])
except ObjectDoesNotExist:
try:
autor_clonado = Autor.objects.get(parlamentar_set=clone)
except ObjectDoesNotExist:
pass
else:
autor_clonado.object_id = parlamentar[0].id
autor_clonado.save()
parlamentares_autores.append(autor_clonado)
else:
if len(parlamentar_autor) == 0:
parlamentar_autor.append(autor_principal)
try:
autor_clonado = Autor.objects.get(parlamentar_set=clone)
except ObjectDoesNotExist:
pass
else:
parlamentar_autor.append(autor_clonado)
parlamentares_autores.extend(parlamentar_autor)
return parlamentares_autores
def transfere_valeres(autores):
for autor in autores:
for clone in autor[1:]:
for autoria in clone.autoria_set.all():
autoria.autor_id = autor[0]
autoria.save()
for proposicao in clone.proposicao_set.all():
proposicao.autor_id = autor[0]
proposicao.save()
for autorianorma in clone.autorianorma_set.all():
autorianorma.autor_id = autor[0]
autorianorma.save()
for documentoadministrativo in clone.documentoadministrativo_set.all():
documentoadministrativo.autor_id = autor[0]
documentoadministrativo.save()
for protocolo in clone.protocolo_set.all():
protocolo.autor_id = autor[0]
protocolo.save()
clone.delete()
def main():
autores = pega_autores()
parlamentares_autores = pega_parlamentares_autores()
autores.append(parlamentares_autores)
transfere_valeres(autores)
if __name__ == '__main__':
main()

13
scripts/remove_protocolos_inexistentes_materias.py

@ -0,0 +1,13 @@
from sapl.materia.models import MateriaLegislativa
from sapl.protocoloadm.models import Protocolo
def main():
for materia in MateriaLegislativa.objects.filter(numero_protocolo__isnull=False):
if not Protocolo.objects.filter(ano=materia.ano, numero=materia.numero_protocolo).exists():
materia.numero_protocolo = None
materia.save()
if __name__ == '__main__':
main()
Loading…
Cancel
Save