Browse Source

Merge branch '3.1.x' into 2841-gerar-pdf-de-relatorios

pull/2895/head
Edward 6 years ago
committed by GitHub
parent
commit
4e0e9a06d0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      docker-compose.yml
  2. 6
      sapl/api/views.py
  3. 59
      sapl/base/forms.py
  4. 5
      sapl/base/receivers.py
  5. 6
      sapl/base/templatetags/common_tags.py
  6. 32
      sapl/base/tests/test_view_base.py
  7. 170
      sapl/base/views.py
  8. 2
      sapl/comissoes/views.py
  9. 9
      sapl/compilacao/views.py
  10. 39
      sapl/crud/base.py
  11. 23
      sapl/materia/migrations/0056_popula_materiaemtramitacao.py
  12. 25
      sapl/materia/migrations/0057_materiaemtramitacao.py
  13. 12
      sapl/materia/models.py
  14. 5
      sapl/materia/views.py
  15. 4
      sapl/protocoloadm/forms.py
  16. 25
      sapl/protocoloadm/migrations/0022_deduplica_protocolos.py
  17. 19
      sapl/protocoloadm/migrations/0023_auto_20190711_1755.py
  18. 16
      sapl/protocoloadm/migrations/0024_merge_20190821_1418.py
  19. 1
      sapl/protocoloadm/models.py
  20. 54
      sapl/protocoloadm/views.py
  21. 1
      sapl/rules/map_rules.py
  22. 2
      sapl/settings.py
  23. 2
      sapl/templates/base.html
  24. 17
      sapl/templates/base/RelatorioMateriasPorTramitacao_filter.html
  25. 6
      sapl/templates/base/parlamentares_duplicados.html
  26. 8
      sapl/templates/base/protocolos_duplicados.html
  27. 13
      sapl/templates/materia/proposicao_detail.html
  28. 15
      sapl/templates/protocoloadm/comprovante.html
  29. 2
      sapl/templates/protocoloadm/layouts.yaml
  30. 12
      sapl/templates/sessao/votacao/nominal.html
  31. 8
      sapl/templates/sessao/votacao/votacao.html
  32. 2
      setup.py

2
docker-compose.yml

@ -11,7 +11,7 @@ sapldb:
ports: ports:
- "5432:5432" - "5432:5432"
sapl: sapl:
image: interlegis/sapl:3.1.160-RC0 image: interlegis/sapl:3.1.160-RC1
restart: always restart: always
environment: environment:
ADMIN_PASSWORD: interlegis ADMIN_PASSWORD: interlegis

6
sapl/api/views.py

@ -15,6 +15,7 @@ from django_filters.rest_framework.filterset import FilterSet
from django_filters.utils import resolve_field from django_filters.utils import resolve_field
from rest_framework import serializers as rest_serializers from rest_framework import serializers as rest_serializers
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.fields import SerializerMethodField
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
@ -94,10 +95,15 @@ class SaplApiViewSetConstrutor():
# Define uma classe padrão para serializer caso não tenha sido # Define uma classe padrão para serializer caso não tenha sido
# criada a classe sapl.api.serializers.{model}Serializer # criada a classe sapl.api.serializers.{model}Serializer
class SaplSerializer(rest_serializers.ModelSerializer): class SaplSerializer(rest_serializers.ModelSerializer):
__str__ = SerializerMethodField()
class Meta: class Meta:
model = _model model = _model
fields = '__all__' fields = '__all__'
def get___str__(self, obj):
return str(obj)
# Define uma classe padrão para filtro caso não tenha sido # Define uma classe padrão para filtro caso não tenha sido
# criada a classe sapl.api.forms.{model}FilterSet # criada a classe sapl.api.forms.{model}FilterSet
class SaplFilterSet(SaplFilterSetMixin): class SaplFilterSet(SaplFilterSetMixin):

59
sapl/base/forms.py

@ -19,27 +19,25 @@ from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import django_filters import django_filters
from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica from sapl.audiencia.models import AudienciaPublica
from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica
from sapl.base.models import Autor, TipoAutor from sapl.base.models import Autor, TipoAutor
from sapl.comissoes.models import Reuniao, Comissao from sapl.comissoes.models import Reuniao
from sapl.comissoes.models import Reuniao, Comissao from sapl.crispy_layout_mixin import (form_actions, to_column, to_row,
from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column, SaplFormHelper, SaplFormLayout)
to_row) from sapl.materia.models import (DocumentoAcessorio, MateriaEmTramitacao,
from sapl.crispy_layout_mixin import SaplFormHelper MateriaLegislativa, UnidadeTramitacao,
from sapl.materia.models import (MateriaLegislativa, UnidadeTramitacao, StatusTramitacao, StatusTramitacao)
DocumentoAcessorio, TipoMateriaLegislativa) from sapl.norma.models import NormaJuridica
from sapl.norma.models import (NormaJuridica, NormaEstatisticas) from sapl.parlamentares.models import Partido, SessaoLegislativa
from sapl.parlamentares.models import SessaoLegislativa, Partido
from sapl.protocoloadm.models import DocumentoAdministrativo from sapl.protocoloadm.models import DocumentoAdministrativo
from sapl.sessao.models import SessaoPlenaria from sapl.sessao.models import SessaoPlenaria
from sapl.settings import MAX_IMAGE_UPLOAD_SIZE from sapl.settings import MAX_IMAGE_UPLOAD_SIZE
from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, from sapl.utils import (autor_label, autor_modal, ChoiceWithoutValidationField,
ChoiceWithoutValidationField, ImageThumbnailFileInput,
RangeWidgetOverride, autor_label, autor_modal,
models_with_gr_for_model, qs_override_django_filter,
choice_anos_com_normas, choice_anos_com_materias, choice_anos_com_normas, choice_anos_com_materias,
FilterOverridesMetaMixin, FileFieldCheckMixin, AnoNumeroOrderingFilter) FilterOverridesMetaMixin, FileFieldCheckMixin,
AnoNumeroOrderingFilter, ImageThumbnailFileInput,
models_with_gr_for_model, qs_override_django_filter,
RangeWidgetOverride, RANGE_ANOS, YES_NO_CHOICES)
from .models import AppConfig, CasaLegislativa from .models import AppConfig, CasaLegislativa
@ -619,6 +617,10 @@ class AutorForm(ModelForm):
tipo = cd['tipo'] tipo = cd['tipo']
if 'nome' in cd and \
Autor.objects.filter(nome=cd['nome']).exists():
raise ValidationError("Autor '%s' já existente!" % cd['nome'])
if not tipo.content_type: if not tipo.content_type:
if 'nome' not in cd or not cd['nome']: if 'nome' not in cd or not cd['nome']:
self.logger.error('Nome do Autor não informado.') self.logger.error('Nome do Autor não informado.')
@ -1236,9 +1238,9 @@ class RelatorioAudienciaFilterSet(django_filters.FilterSet):
) )
class RelatorioMateriasTramitacaoilterSet(django_filters.FilterSet): class RelatorioMateriasTramitacaoFilterSet(django_filters.FilterSet):
ano = django_filters.ChoiceFilter(required=True, materia__ano = django_filters.ChoiceFilter(required=True,
label='Ano da Matéria', label='Ano da Matéria',
choices=choice_anos_com_materias) choices=choice_anos_com_materias)
@ -1252,22 +1254,25 @@ class RelatorioMateriasTramitacaoilterSet(django_filters.FilterSet):
@property @property
def qs(self): def qs(self):
parent = super(RelatorioMateriasTramitacaoilterSet, self).qs parent = super(RelatorioMateriasTramitacaoFilterSet, self).qs
return parent.distinct().order_by('-ano', 'tipo', '-numero') return parent.distinct().order_by(
'-materia__ano', 'materia__tipo', '-materia__numero'
)
class Meta: class Meta:
model = MateriaLegislativa model = MateriaEmTramitacao
fields = ['ano', 'tipo', 'tramitacao__unidade_tramitacao_destino', fields = ['materia__ano', 'materia__tipo',
'tramitacao__unidade_tramitacao_destino',
'tramitacao__status'] 'tramitacao__status']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(RelatorioMateriasTramitacaoilterSet, self).__init__( super(RelatorioMateriasTramitacaoFilterSet, self).__init__(
*args, **kwargs) *args, **kwargs)
self.filters['tipo'].label = 'Tipo de Matéria' self.filters['materia__tipo'].label = 'Tipo de Matéria'
row1 = to_row([('ano', 12)]) row1 = to_row([('materia__ano', 12)])
row2 = to_row([('tipo', 12)]) row2 = to_row([('materia__tipo', 12)])
row3 = to_row([('tramitacao__unidade_tramitacao_destino', 12)]) row3 = to_row([('tramitacao__unidade_tramitacao_destino', 12)])
row4 = to_row([('tramitacao__status', 12)]) row4 = to_row([('tramitacao__status', 12)])
@ -1797,5 +1802,5 @@ class RelatorioNormasPorAutorFilterSet(django_filters.FilterSet):
HTML(autor_label), HTML(autor_label),
HTML(autor_modal), HTML(autor_modal),
row3, row3,
buttons) form_actions(label='Pesquisar'))
) )

5
sapl/base/receivers.py

@ -1,6 +1,5 @@
from django.db.models.signals import post_delete, post_save from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver from django.dispatch import receiver
from sapl.materia.models import Tramitacao from sapl.materia.models import Tramitacao
from sapl.protocoloadm.models import TramitacaoAdministrativo from sapl.protocoloadm.models import TramitacaoAdministrativo
from sapl.base.signals import tramitacao_signal from sapl.base.signals import tramitacao_signal
@ -30,12 +29,12 @@ def handle_tramitacao_signal(sender, **kwargs):
@receiver(post_delete) @receiver(post_delete)
def status_tramitacao_materia(sender, instance, **kwargs): def status_tramitacao_materia(sender, instance, **kwargs):
if isinstance(sender, TramitacaoAdministrativo): if sender == Tramitacao:
if instance.status.indicador == 'F': if instance.status.indicador == 'F':
materia = instance.materia materia = instance.materia
materia.em_tramitacao = True materia.em_tramitacao = True
materia.save() materia.save()
elif isinstance(sender, TramitacaoAdministrativo): elif sender == TramitacaoAdministrativo:
if instance.status.indicador == 'F': if instance.status.indicador == 'F':
documento = instance.documento documento = instance.documento
documento.tramitacao = True documento.tramitacao = True

6
sapl/base/templatetags/common_tags.py

@ -249,8 +249,10 @@ def facebook_url(value):
def youtube_id(value): def youtube_id(value):
from urllib.parse import urlparse, parse_qs from urllib.parse import urlparse, parse_qs
u_pars = urlparse(value) u_pars = urlparse(value)
quer_v = parse_qs(u_pars.query).get('v')[0] quer_v = parse_qs(u_pars.query).get('v')
return quer_v if quer_v:
return quer_v[0]
return ''
@register.filter @register.filter

32
sapl/base/tests/test_view_base.py

@ -24,32 +24,6 @@ from sapl.base.views import (protocolos_duplicados, protocolos_com_materias,
bancada_comissao_autor_externo, anexados_ciclicos) bancada_comissao_autor_externo, anexados_ciclicos)
@pytest.mark.django_db(transaction=False)
def test_lista_protocolos_duplicados():
mommy.make(
Protocolo,
numero=15,
ano=2031
)
mommy.make(
Protocolo,
numero=15,
ano=2031
)
mommy.make(
Protocolo,
numero=33,
ano=2033
)
lista_protocolos_duplicados = protocolos_duplicados()
assert len(lista_protocolos_duplicados) == 1
assert lista_protocolos_duplicados[0][1] == 2
assert lista_protocolos_duplicados[0][0].numero == 15
assert lista_protocolos_duplicados[0][0].ano == 2031
@pytest.mark.django_db(transaction=False) @pytest.mark.django_db(transaction=False)
def test_lista_protocolos_com_materias(): def test_lista_protocolos_com_materias():
mommy.make( mommy.make(
@ -194,13 +168,13 @@ def test_lista_parlamentares_duplicados():
sexo='M' sexo='M'
) )
lista_dict_values_parlamentares_duplicados = parlamentares_duplicados() lista_dict_parlamentares_duplicados = parlamentares_duplicados()
parlamentar_duplicado = list( parlamentar_duplicado = list(
lista_dict_values_parlamentares_duplicados[0] lista_dict_parlamentares_duplicados[0].values()
) )
parlamentar_duplicado.sort(key=str) parlamentar_duplicado.sort(key=str)
assert len(lista_dict_values_parlamentares_duplicados) == 1 assert len(lista_dict_parlamentares_duplicados) == 1
assert parlamentar_duplicado == [2, "Nome_Parlamentar_Teste"] assert parlamentar_duplicado == [2, "Nome_Parlamentar_Teste"]

170
sapl/base/views.py

@ -13,7 +13,8 @@ from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, Validat
from django.core.mail import send_mail from django.core.mail import send_mail
from django.core.urlresolvers import reverse, reverse_lazy from django.core.urlresolvers import reverse, reverse_lazy
from django.db import connection from django.db import connection
from django.db.models import Count, Q, ProtectedError from django.db.models import Count, Q, ProtectedError, Max
from django.shortcuts import render
from django.http import Http404, HttpResponseRedirect, JsonResponse from django.http import Http404, HttpResponseRedirect, JsonResponse
from django.template import TemplateDoesNotExist from django.template import TemplateDoesNotExist
from django.template.loader import get_template from django.template.loader import get_template
@ -39,23 +40,26 @@ from sapl.relatorios.views import (relatorio_materia_em_tramitacao, relatorio_ma
from sapl import settings from sapl import settings
from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica
from sapl.base.forms import AutorForm, AutorFormForAdmin, TipoAutorForm
from sapl.base.models import Autor, TipoAutor from sapl.base.models import Autor, TipoAutor
from sapl.comissoes.models import Reuniao, Comissao from sapl.base.forms import AutorForm, AutorFormForAdmin, TipoAutorForm
from sapl.comissoes.models import Comissao, Reuniao
from sapl.crud.base import CrudAux, make_pagination from sapl.crud.base import CrudAux, make_pagination
from sapl.materia.models import (Autoria, MateriaLegislativa, Proposicao, Anexada, from sapl.materia.models import (Anexada, Autoria, DocumentoAcessorio,
TipoMateriaLegislativa, StatusTramitacao, UnidadeTramitacao, MateriaEmTramitacao, MateriaLegislativa, Proposicao,
DocumentoAcessorio, TipoDocumento) StatusTramitacao, TipoDocumento,
from sapl.norma.models import (NormaJuridica, TipoNormaJuridica, NormaEstatisticas) TipoMateriaLegislativa, UnidadeTramitacao, Tramitacao)
from sapl.parlamentares.models import Parlamentar, Legislatura, Mandato, Filiacao, SessaoLegislativa from sapl.norma.models import NormaJuridica, TipoNormaJuridica
from sapl.protocoloadm.models import (Protocolo, TipoDocumentoAdministrativo, from sapl.parlamentares.models import (Filiacao, Legislatura, Mandato, Parlamentar,
SessaoLegislativa)
from sapl.protocoloadm.models import (Anexado, DocumentoAdministrativo, Protocolo,
StatusTramitacaoAdministrativo, StatusTramitacaoAdministrativo,
DocumentoAdministrativo, Anexado) TipoDocumentoAdministrativo)
from sapl.sessao.models import (PresencaOrdemDia, SessaoPlenaria, from sapl.sessao.models import (Bancada, PresencaOrdemDia, SessaoPlenaria,
SessaoPlenariaPresenca, Bancada, TipoSessaoPlenaria) SessaoPlenariaPresenca, TipoSessaoPlenaria)
from sapl.utils import (parlamentares_ativos, gerar_hash_arquivo, SEPARADOR_HASH_PROPOSICAO, from sapl.utils import (gerar_hash_arquivo, intervalos_tem_intersecao,
show_results_filter_set, mail_service_configured, mail_service_configured, parlamentares_ativos,
intervalos_tem_intersecao, remover_acentos) SEPARADOR_HASH_PROPOSICAO, show_results_filter_set)
from .forms import (AlterarSenhaForm, CasaLegislativaForm, from .forms import (AlterarSenhaForm, CasaLegislativaForm,
ConfiguracoesAppForm, RelatorioAtasFilterSet, ConfiguracoesAppForm, RelatorioAtasFilterSet,
RelatorioAudienciaFilterSet, RelatorioAudienciaFilterSet,
@ -63,7 +67,7 @@ from .forms import (AlterarSenhaForm, CasaLegislativaForm,
RelatorioHistoricoTramitacaoFilterSet, RelatorioHistoricoTramitacaoFilterSet,
RelatorioMateriasPorAnoAutorTipoFilterSet, RelatorioMateriasPorAnoAutorTipoFilterSet,
RelatorioMateriasPorAutorFilterSet, RelatorioMateriasPorAutorFilterSet,
RelatorioMateriasTramitacaoilterSet, RelatorioMateriasTramitacaoFilterSet,
RelatorioPresencaSessaoFilterSet, RelatorioPresencaSessaoFilterSet,
RelatorioReuniaoFilterSet, UsuarioCreateForm, RelatorioReuniaoFilterSet, UsuarioCreateForm,
UsuarioEditForm, RelatorioNormasMesFilterSet, UsuarioEditForm, RelatorioNormasMesFilterSet,
@ -75,19 +79,6 @@ from .forms import (AlterarSenhaForm, CasaLegislativaForm,
from .models import AppConfig, CasaLegislativa from .models import AppConfig, CasaLegislativa
def filtra_url_materias_em_tramitacao(qr, qs, campo_url, local_ou_status):
id_materias = []
filtro_url = qr[campo_url]
if local_ou_status == 'local':
id_materias = [item.id for item in qs if item.tramitacao_set.order_by(
'-id').first().unidade_tramitacao_destino_id == int(filtro_url)]
elif local_ou_status == 'status':
id_materias = [item.id for item in qs if item.tramitacao_set.order_by(
'-id').first().status_id == int(filtro_url)]
return qs.filter(em_tramitacao=True, id__in=id_materias)
def get_casalegislativa(): def get_casalegislativa():
return CasaLegislativa.objects.first() return CasaLegislativa.objects.first()
@ -731,62 +722,109 @@ class RelatorioAudienciaView(RelatorioMixin, FilterView):
class RelatorioMateriasTramitacaoView(RelatorioMixin, FilterView): class RelatorioMateriasTramitacaoView(RelatorioMixin, FilterView):
model = MateriaLegislativa model = MateriaEmTramitacao
filterset_class = RelatorioMateriasTramitacaoilterSet filterset_class = RelatorioMateriasTramitacaoFilterSet
template_name = 'base/RelatorioMateriasPorTramitacao_filter.html' template_name = 'base/RelatorioMateriasPorTramitacao_filter.html'
relatorio = relatorio_materia_em_tramitacao relatorio = relatorio_materia_em_tramitacao
paginate_by = 100
total_resultados_tipos = {}
def get_filterset_kwargs(self, filterset_class):
data = super().get_filterset_kwargs(filterset_class)
if data['data']:
qs = data['queryset']
ano_materia = data['data']['materia__ano']
tipo_materia = data['data']['materia__tipo']
unidade_tramitacao_destino = data['data']['tramitacao__unidade_tramitacao_destino']
status_tramitacao = data['data']['tramitacao__status']
kwargs = {}
if ano_materia:
kwargs['materia__ano'] = ano_materia
if tipo_materia:
kwargs['materia__tipo'] = tipo_materia
if unidade_tramitacao_destino:
kwargs['tramitacao__unidade_tramitacao_destino'] = unidade_tramitacao_destino
if status_tramitacao:
kwargs['tramitacao__status'] = status_tramitacao
qs = qs.filter(**kwargs)
data['queryset'] = qs
qtdes = { tipo:0 for tipo in TipoMateriaLegislativa.objects.all() }
for i in qs:
qtdes[i.materia.tipo] += 1
# remove as entradas de valor igual a zero
qtdes = {k:v for k,v in qtdes.items() if v > 0}
self.total_resultados_tipos = qtdes
return data
def get_queryset(self):
qs = super().get_queryset()
qs = qs.select_related('materia__tipo').filter(
materia__em_tramitacao=True
).exclude(
tramitacao__status__indicador='F'
).order_by('-materia__ano', '-materia__numero')
return qs
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(RelatorioMateriasTramitacaoView, context = super(
self).get_context_data(**kwargs) RelatorioMateriasTramitacaoView, self
).get_context_data(**kwargs)
context['title'] = _('Matérias em Tramitação') context['title'] = _('Matérias em Tramitação')
if not self.filterset.form.is_valid(): if not self.filterset.form.is_valid():
return context return context
qr = self.request.GET.copy() qr = self.request.GET.copy()
qs = context['object_list']
qs = qs.filter(em_tramitacao=True)
if qr.get('tramitacao__unidade_tramitacao_destino'):
qs = filtra_url_materias_em_tramitacao(
qr, qs, 'tramitacao__unidade_tramitacao_destino', 'local')
if qr.get('tramitacao__status'):
qs = filtra_url_materias_em_tramitacao(
qr, qs, 'tramitacao__status', 'status')
li = [li1 for li1 in qs if li1.tramitacao_set.last() and li1.tramitacao_set.last().status.indicador != 'F'] context['qtdes'] = self.total_resultados_tipos
context['object_list'] = li context['ano'] = (self.request.GET['materia__ano'])
qtdes = {} if self.request.GET['materia__tipo']:
for tipo in TipoMateriaLegislativa.objects.all(): tipo = self.request.GET['materia__tipo']
li = context['object_list']
qtde = sum(1 for i in li if i.tipo_id==tipo.id)
if qtde > 0:
qtdes[tipo] = qtde
context['qtdes'] = qtdes
context['ano'] = (self.request.GET['ano'])
if self.request.GET['tipo']:
tipo = self.request.GET['tipo']
context['tipo'] = ( context['tipo'] = (
str(TipoMateriaLegislativa.objects.get(id=tipo))) str(TipoMateriaLegislativa.objects.get(id=tipo))
)
else: else:
context['tipo'] = '' context['tipo'] = ''
if self.request.GET['tramitacao__status']: if self.request.GET['tramitacao__status']:
tramitacao_status = self.request.GET['tramitacao__status'] tramitacao_status = self.request.GET['tramitacao__status']
context['tramitacao__status'] = ( context['tramitacao__status'] = (
str(StatusTramitacao.objects.get(id=tramitacao_status))) str(StatusTramitacao.objects.get(id=tramitacao_status))
)
else: else:
context['tramitacao__status'] = '' context['tramitacao__status'] = ''
if self.request.GET['tramitacao__unidade_tramitacao_destino']: if self.request.GET['tramitacao__unidade_tramitacao_destino']:
context['tramitacao__unidade_tramitacao_destino'] = (str(UnidadeTramitacao.objects.get( context['tramitacao__unidade_tramitacao_destino'] = (
id=self.request.GET['tramitacao__unidade_tramitacao_destino']))) str(UnidadeTramitacao.objects.get(
id=self.request.GET['tramitacao__unidade_tramitacao_destino']
))
)
else: else:
context['tramitacao__unidade_tramitacao_destino'] = '' context['tramitacao__unidade_tramitacao_destino'] = ''
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
context['show_results'] = show_results_filter_set(qr) context['show_results'] = show_results_filter_set(qr)
paginator = context['paginator']
page_obj = context['page_obj']
context['page_range'] = make_pagination(
page_obj.number, paginator.num_pages
)
context['NO_ENTRIES_MSG'] = 'Nenhum encontrado.'
return context return context
@ -1471,7 +1509,7 @@ class ListarParlMandatosIntersecaoView(PermissionRequiredMixin, ListView):
def parlamentares_duplicados(): def parlamentares_duplicados():
return [parlamentar.values() for parlamentar in Parlamentar.objects.values( return [parlamentar for parlamentar in Parlamentar.objects.values(
'nome_parlamentar').order_by('nome_parlamentar').annotate(count=Count( 'nome_parlamentar').order_by('nome_parlamentar').annotate(count=Count(
'nome_parlamentar')).filter(count__gt=1)] 'nome_parlamentar')).filter(count__gt=1)]
@ -1656,14 +1694,10 @@ class ListarProtocolosComMateriasView(PermissionRequiredMixin, ListView):
def protocolos_duplicados(): def protocolos_duplicados():
protocolos = {} return [
for p in Protocolo.objects.order_by('-ano', 'numero'): protocolo for protocolo in Protocolo.objects.values(
key = "{}/{}".format(p.numero, p.ano) 'numero', 'ano').order_by('-ano', 'numero').annotate(total=Count('numero')).filter(total__gt=1)
val = protocolos.get(key, list()) ]
val.append(p)
protocolos[key] = val
return [(v[0], len(v)) for (k, v) in protocolos.items() if len(v) > 1]
class ListarProtocolosDuplicadosView(PermissionRequiredMixin, ListView): class ListarProtocolosDuplicadosView(PermissionRequiredMixin, ListView):

2
sapl/comissoes/views.py

@ -167,7 +167,7 @@ class ComissaoCrud(Crud):
def lista_materias_comissao(comissao_pk): def lista_materias_comissao(comissao_pk):
ts = Tramitacao.objects.order_by( ts = Tramitacao.objects.order_by(
'materia', '-data_tramitacao', '-id').annotate( 'materia_id', '-data_tramitacao', '-id').annotate(
comissao=F('unidade_tramitacao_destino__comissao')).distinct( comissao=F('unidade_tramitacao_destino__comissao')).distinct(
'materia').values_list('materia', 'comissao') 'materia').values_list('materia', 'comissao')

9
sapl/compilacao/views.py

@ -195,8 +195,13 @@ class IntegracaoTaView(TemplateView):
return redirect(to=reverse_lazy('sapl.compilacao:ta_text_edit', return redirect(to=reverse_lazy('sapl.compilacao:ta_text_edit',
kwargs={'ta_id': ta.pk})) kwargs={'ta_id': ta.pk}))
else: else:
return redirect(to=reverse_lazy('sapl.compilacao:ta_text', return redirect(
kwargs={'ta_id': ta.pk})) to='%s?%s' % (
reverse_lazy('sapl.compilacao:ta_text',
kwargs={'ta_id': ta.pk}),
request.META['QUERY_STRING']
)
)
class Meta: class Meta:
abstract = True abstract = True

39
sapl/crud/base.py

@ -4,13 +4,14 @@ from braces.views import FormMessagesMixin
from crispy_forms.bootstrap import FieldWithButtons, StrictButton from crispy_forms.bootstrap import FieldWithButtons, StrictButton
from crispy_forms.layout import Field, Layout from crispy_forms.layout import Field, Layout
from django import forms from django import forms
from django.conf import settings
from django.conf.urls import url from django.conf.urls import url
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.db.models.fields.related import ForeignKey from django.db.models.fields.related import ForeignKey, ManyToManyField
from django.http.response import Http404 from django.http.response import Http404
from django.shortcuts import redirect from django.shortcuts import redirect
from django.utils.decorators import classonlymethod from django.utils.decorators import classonlymethod
@ -27,10 +28,12 @@ from sapl.crispy_layout_mixin import CrispyLayoutFormMixin, get_field_display
from sapl.crispy_layout_mixin import SaplFormHelper from sapl.crispy_layout_mixin import SaplFormHelper
from sapl.rules.map_rules import (RP_ADD, RP_CHANGE, RP_DELETE, RP_DETAIL, from sapl.rules.map_rules import (RP_ADD, RP_CHANGE, RP_DELETE, RP_DETAIL,
RP_LIST) RP_LIST)
from sapl.settings import BASE_DIR
from sapl.utils import normalize from sapl.utils import normalize
logger = logging.getLogger(settings.BASE_DIR.name)
ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \ ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \
'list', 'create', 'detail', 'update', 'delete' 'list', 'create', 'detail', 'update', 'delete'
@ -122,7 +125,6 @@ class SearchMixin(models.Model):
except Exception as e: except Exception as e:
username = self.request.user.username username = self.request.user.username
self.logger.error("user=" + username + ". " + str(e)) self.logger.error("user=" + username + ". " + str(e))
pass
else: else:
_self = self _self = self
for field in fields: for field in fields:
@ -206,6 +208,7 @@ class PermissionRequiredContainerCrudMixin(PermissionRequiredMixin):
if not self.model.objects.filter(**params).exists(): if not self.model.objects.filter(**params).exists():
raise Http404() raise Http404()
elif self.container_field: elif self.container_field:
container = self.container_field.split('__') container = self.container_field.split('__')
@ -230,14 +233,14 @@ class PermissionRequiredContainerCrudMixin(PermissionRequiredMixin):
return super(PermissionRequiredMixin, self).dispatch( return super(PermissionRequiredMixin, self).dispatch(
request, *args, **kwargs) request, *args, **kwargs)
@cached_property @property
def container_field(self): def container_field(self):
if hasattr(self, 'crud') and not hasattr(self.crud, 'container_field'): if hasattr(self, 'crud') and not hasattr(self.crud, 'container_field'):
self.crud.container_field = '' self.crud.container_field = ''
if hasattr(self, 'crud'): if hasattr(self, 'crud'):
return self.crud.container_field return self.crud.container_field
@cached_property @property
def container_field_set(self): def container_field_set(self):
if hasattr(self, 'crud') and\ if hasattr(self, 'crud') and\
not hasattr(self.crud, 'container_field_set'): not hasattr(self.crud, 'container_field_set'):
@ -245,7 +248,7 @@ class PermissionRequiredContainerCrudMixin(PermissionRequiredMixin):
if hasattr(self, 'crud'): if hasattr(self, 'crud'):
return self.crud.container_field_set return self.crud.container_field_set
@cached_property @property
def is_contained(self): def is_contained(self):
return self.container_field_set or self.container_field return self.container_field_set or self.container_field
@ -600,7 +603,7 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
# print(ordering) # print(ordering)
except Exception as e: except Exception as e:
print(string_concat(_( logger.error(string_concat(_(
'ERRO: construção da tupla de ordenação.'), str(e))) 'ERRO: construção da tupla de ordenação.'), str(e)))
# print(queryset.query) # print(queryset.query)
@ -1108,12 +1111,14 @@ class MasterDetailCrud(Crud):
permission_required = RP_LIST, permission_required = RP_LIST,
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get(self, request, *args, **kwargs):
return Crud.ListView.get(self, request, *args, **kwargs)
@classmethod @classmethod
def get_url_regex(cls): def get_url_regex(cls):
return r'^(?P<pk>\d+)/%s$' % cls.model._meta.model_name return r'^(?P<pk>\d+)/%s$' % cls.model._meta.model_name
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
obj = self.crud if hasattr(self, 'crud') else self obj = self.crud if hasattr(self, 'crud') else self
context = CrudListView.get_context_data(self, **kwargs) context = CrudListView.get_context_data(self, **kwargs)
@ -1133,7 +1138,12 @@ class MasterDetailCrud(Crud):
else: else:
parent_model = getattr( parent_model = getattr(
self.model, obj.parent_field).field.related_model self.model, obj.parent_field)
if isinstance(parent_model.field, (
ForeignKey, ManyToManyField)):
parent_model = parent_model.field.related_model
else:
parent_model = parent_model.rel.related_model
params = {'pk': kwargs['root_pk']} params = {'pk': kwargs['root_pk']}
@ -1165,6 +1175,9 @@ class MasterDetailCrud(Crud):
return qs.filter(**kwargs) return qs.filter(**kwargs)
def dispatch(self, request, *args, **kwargs):
return PermissionRequiredMixin.dispatch(self, request, *args, **kwargs)
class CreateView(Crud.CreateView): class CreateView(Crud.CreateView):
permission_required = RP_ADD, permission_required = RP_ADD,
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -1229,8 +1242,12 @@ class MasterDetailCrud(Crud):
parent_object = getattr(parent_object, field) parent_object = getattr(parent_object, field)
else: else:
parent_model = getattr( parent_model = getattr(self.model, obj.parent_field)
parent_model, obj.parent_field).field.related_model if isinstance(parent_model.field, ForeignKey):
parent_model = parent_model.field.related_model
else:
parent_model = parent_model.rel.related_model
parent_object = parent_model.objects.get(**params) parent_object = parent_model.objects.get(**params)
context['root_pk'] = parent_object.pk context['root_pk'] = parent_object.pk

23
sapl/materia/migrations/0056_popula_materiaemtramitacao.py

@ -0,0 +1,23 @@
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('materia', '0055_auto_20190816_0943'),
]
operations = [
migrations.RunSQL("""
create or replace view materia_materiaemtramitacao as
select m.id as id,
m.id as materia_id,
t.id as tramitacao_id
from materia_materialegislativa m
inner join materia_tramitacao t on (m.id = t.materia_id)
where t.id = (select max(id) from materia_tramitacao where materia_id = m.id)
order by m.id DESC
"""),
]

25
sapl/materia/migrations/0057_materiaemtramitacao.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-08-27 20:13
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('materia', '0056_popula_materiaemtramitacao'),
]
operations = [
migrations.CreateModel(
name='MateriaEmTramitacao',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
options={
'db_table': 'materia_materiaemtramitacao',
'managed': False,
},
),
]

12
sapl/materia/models.py

@ -1084,3 +1084,15 @@ class Tramitacao(models.Model):
'materia': self.materia, 'materia': self.materia,
'status': self.status, 'status': self.status,
'data': self.data_tramitacao.strftime("%d/%m/%Y")} 'data': self.data_tramitacao.strftime("%d/%m/%Y")}
class MateriaEmTramitacao(models.Model):
materia = models.ForeignKey(MateriaLegislativa, on_delete=models.DO_NOTHING)
tramitacao = models.ForeignKey(Tramitacao, on_delete=models.DO_NOTHING)
class Meta:
managed = False
db_table = "materia_materiaemtramitacao"
def __str__(self):
return '{}/{}'.format(self.materia, self.tramitacao)

5
sapl/materia/views.py

@ -782,7 +782,8 @@ class ProposicaoCrud(Crud):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
action = request.GET.get('action', '') action = request.GET.get('action', '')
username = request.user.username user = request.user
username = user.username
if not action: if not action:
return Crud.DetailView.get(self, request, *args, **kwargs) return Crud.DetailView.get(self, request, *args, **kwargs)
@ -790,7 +791,7 @@ class ProposicaoCrud(Crud):
p = Proposicao.objects.get(id=kwargs['pk']) p = Proposicao.objects.get(id=kwargs['pk'])
msg_error = '' msg_error = ''
if p: if p and p.autor.user == user:
if action == 'send': if action == 'send':
if p.data_envio and p.data_recebimento: if p.data_envio and p.data_recebimento:
msg_error = _('Proposição já foi enviada e recebida.') msg_error = _('Proposição já foi enviada e recebida.')

4
sapl/protocoloadm/forms.py

@ -302,9 +302,7 @@ class AnularProtocoloAdmForm(ModelForm):
class Meta: class Meta:
model = Protocolo model = Protocolo
fields = ['numero', fields = ['justificativa_anulacao',
'ano',
'justificativa_anulacao',
'anulado', 'anulado',
'user_anulacao', 'user_anulacao',
'ip_anulacao', 'ip_anulacao',

25
sapl/protocoloadm/migrations/0022_deduplica_protocolos.py

@ -0,0 +1,25 @@
from __future__ import unicode_literals
from django.db import migrations
def deduplica_protocolos(apps, schema_editor):
from sapl.base.views import protocolos_duplicados
Protocolo = apps.get_model('protocoloadm', 'Protocolo')
protocolos = protocolos_duplicados()
for protocolo in protocolos:
protocolo_principal = Protocolo.objects.filter(numero=protocolo['numero'], ano=protocolo['ano']).order_by('-id')[0]
Protocolo.objects.filter(numero=protocolo['numero'], ano=protocolo['ano']).exclude(id=protocolo_principal.id).delete()
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0021_merge_20190429_1531'),
]
operations = [
migrations.RunPython(deduplica_protocolos)
]

19
sapl/protocoloadm/migrations/0023_auto_20190711_1755.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-07-11 20:55
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0022_deduplica_protocolos'),
]
operations = [
migrations.AlterUniqueTogether(
name='protocolo',
unique_together=set([('numero', 'ano')]),
),
]

16
sapl/protocoloadm/migrations/0024_merge_20190821_1418.py

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-08-21 17:18
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0023_auto_20190711_1755'),
('protocoloadm', '0023_merge_20190802_1112'),
]
operations = [
]

1
sapl/protocoloadm/models.py

@ -120,6 +120,7 @@ class Protocolo(models.Model):
permissions = ( permissions = (
('action_anular_protocolo', _('Permissão para Anular Protocolo')), ('action_anular_protocolo', _('Permissão para Anular Protocolo')),
) )
unique_together = ('numero', 'ano',)
def __str__(self): def __str__(self):
return _('%(numero)s/%(ano)s') % { return _('%(numero)s/%(ano)s') % {

54
sapl/protocoloadm/views.py

@ -34,10 +34,10 @@ from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa, Unid
from sapl.materia.views import gerar_pdf_impressos from sapl.materia.views import gerar_pdf_impressos
from sapl.parlamentares.models import Legislatura, Parlamentar from sapl.parlamentares.models import Legislatura, Parlamentar
from sapl.protocoloadm.models import Protocolo from sapl.protocoloadm.models import Protocolo
from sapl.relatorios.views import relatorio_doc_administrativos
from sapl.utils import (create_barcode, get_base_url, get_client_ip, from sapl.utils import (create_barcode, get_base_url, get_client_ip,
get_mime_type_from_file_extension, lista_anexados, get_mime_type_from_file_extension, lista_anexados,
show_results_filter_set, mail_service_configured) show_results_filter_set, mail_service_configured)
from sapl.relatorios.views import relatorio_doc_administrativos
from .forms import (AcompanhamentoDocumentoForm, AnularProtocoloAdmForm, from .forms import (AcompanhamentoDocumentoForm, AnularProtocoloAdmForm,
DocumentoAcessorioAdministrativoForm, DocumentoAcessorioAdministrativoForm,
@ -373,13 +373,13 @@ class DocumentoAdministrativoCrud(Crud):
return redirect('/') return redirect('/')
return super(Crud.DetailView, self).get(args, kwargs) return super(Crud.DetailView, self).get(args, kwargs)
def get_context_data(self, **kwargs): def urlize(self, obj, fieldname):
context = super().get_context_data(**kwargs) a = '<a href="%s">%s</a>' % (
self.layout_display[0]['rows'][-1][0]['text'] = ( reverse(
'<a href="%s"></a>' % reverse(
'sapl.protocoloadm:doc_texto_integral', 'sapl.protocoloadm:doc_texto_integral',
kwargs={'pk': self.object.pk})) kwargs={'pk': obj.pk}),
return context obj.texto_integral.name.split('/')[-1])
return obj.texto_integral.field.verbose_name, a
class DeleteView(Crud.DeleteView): class DeleteView(Crud.DeleteView):
@ -700,6 +700,13 @@ class ComprovanteProtocoloView(PermissionRequiredMixin, TemplateView):
autenticacao = str(protocolo.tipo_processo) + \ autenticacao = str(protocolo.tipo_processo) + \
data + str(protocolo.numero).zfill(6) data + str(protocolo.numero).zfill(6)
if protocolo.tipo_materia:
materia = MateriaLegislativa.objects.filter(
numero_protocolo=protocolo.numero,
ano=protocolo.ano).first()
if materia:
context['materia'] = materia.numero
context.update({"protocolo": protocolo, context.update({"protocolo": protocolo,
"barcode": barcode, "barcode": barcode,
"autenticacao": autenticacao}) "autenticacao": autenticacao})
@ -950,6 +957,7 @@ class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin,
else: else:
return self.render_to_response(context) return self.render_to_response(context)
class AnexadoCrud(MasterDetailCrud): class AnexadoCrud(MasterDetailCrud):
model = Anexado model = Anexado
parent_field = 'documento_principal' parent_field = 'documento_principal'
@ -1019,13 +1027,15 @@ class DocumentoAnexadoEmLoteView(PermissionRequiredMixin, FilterView):
context['object_list'] = [] context['object_list'] = []
for obj in context['temp_object_list']: for obj in context['temp_object_list']:
if not obj.pk == int(context['root_pk']): if not obj.pk == int(context['root_pk']):
documento_principal = DocumentoAdministrativo.objects.get(id=context['root_pk']) documento_principal = DocumentoAdministrativo.objects.get(
id=context['root_pk'])
documento_anexado = obj documento_anexado = obj
is_anexado = Anexado.objects.filter(documento_principal=documento_principal, is_anexado = Anexado.objects.filter(documento_principal=documento_principal,
documento_anexado=documento_anexado).exists() documento_anexado=documento_anexado).exists()
if not is_anexado: if not is_anexado:
ciclico = False ciclico = False
anexados_anexado = Anexado.objects.filter(documento_principal=documento_anexado) anexados_anexado = Anexado.objects.filter(
documento_principal=documento_anexado)
while anexados_anexado and not ciclico: while anexados_anexado and not ciclico:
anexados = [] anexados = []
@ -1094,7 +1104,8 @@ class DocumentoAnexadoEmLoteView(PermissionRequiredMixin, FilterView):
msg = _('Documento(s) anexado(s).') msg = _('Documento(s) anexado(s).')
messages.add_message(request, messages.SUCCESS, msg) messages.add_message(request, messages.SUCCESS, msg)
success_url = reverse('sapl.protocoloadm:anexado_list', kwargs={'pk': kwargs['pk']}) success_url = reverse('sapl.protocoloadm:anexado_list', kwargs={
'pk': kwargs['pk']})
return HttpResponseRedirect(success_url) return HttpResponseRedirect(success_url)
@ -1142,7 +1153,8 @@ class TramitacaoAdmCrud(MasterDetailCrud):
'-timestamp', '-timestamp',
'-id').first() '-id').first()
#TODO: Esta checagem foi inserida na issue #2027, mas é mesmo necessária? # TODO: Esta checagem foi inserida na issue #2027, mas é mesmo
# necessária?
if ultima_tramitacao: if ultima_tramitacao:
if ultima_tramitacao.unidade_tramitacao_destino: if ultima_tramitacao.unidade_tramitacao_destino:
context['form'].fields[ context['form'].fields[
@ -1230,7 +1242,6 @@ class TramitacaoAdmCrud(MasterDetailCrud):
context['user'] = self.request.user context['user'] = self.request.user
return context return context
class DeleteView(MasterDetailCrud.DeleteView): class DeleteView(MasterDetailCrud.DeleteView):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -1271,7 +1282,8 @@ class TramitacaoAdmCrud(MasterDetailCrud):
if da.tramitacaoadministrativo_set.count() == 0: if da.tramitacaoadministrativo_set.count() == 0:
da.tramitacao = False da.tramitacao = False
da.save() da.save()
TramitacaoAdministrativo.objects.filter(id__in=tramitacoes_deletar).delete() TramitacaoAdministrativo.objects.filter(
id__in=tramitacoes_deletar).delete()
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@ -1464,7 +1476,6 @@ class PrimeiraTramitacaoEmLoteAdmView(PermissionRequiredMixin, FilterView):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(PrimeiraTramitacaoEmLoteAdmView, context = super(PrimeiraTramitacaoEmLoteAdmView,
self).get_context_data(**kwargs) self).get_context_data(**kwargs)
@ -1517,23 +1528,24 @@ class PrimeiraTramitacaoEmLoteAdmView(PermissionRequiredMixin, FilterView):
form.save() form.save()
msg = _('Tramitação completa.') msg = _('Tramitação completa.')
self.logger.info('user=' + user.username + '. Tramitação completa.') self.logger.info('user=' + user.username +
'. Tramitação completa.')
messages.add_message(request, messages.SUCCESS, msg) messages.add_message(request, messages.SUCCESS, msg)
return self.get_success_url() return self.get_success_url()
return self.form_invalid(form) return self.form_invalid(form)
def get_success_url(self): def get_success_url(self):
return HttpResponseRedirect(reverse('sapl.protocoloadm:primeira_tramitacao_em_lote_docadm')) return HttpResponseRedirect(reverse('sapl.protocoloadm:primeira_tramitacao_em_lote_docadm'))
def form_invalid(self, form, *args, **kwargs): def form_invalid(self, form, *args, **kwargs):
for key, erros in form.errors.items(): for key, erros in form.errors.items():
if not key == '__all__': if not key == '__all__':
[messages.add_message(self.request, messages.ERROR, form.fields[key].label + ": " + e) for e in erros] [messages.add_message(
self.request, messages.ERROR, form.fields[key].label + ": " + e) for e in erros]
else: else:
[messages.add_message(self.request, messages.ERROR, e) for e in erros] [messages.add_message(self.request, messages.ERROR, e)
for e in erros]
return self.get(self.request, kwargs, {'form': form}) return self.get(self.request, kwargs, {'form': form})
@ -1562,21 +1574,18 @@ class TramitacaoEmLoteAdmView(PrimeiraTramitacaoEmLoteAdmView):
return context return context
def pega_ultima_tramitacao(self): def pega_ultima_tramitacao(self):
return TramitacaoAdministrativo.objects.values( return TramitacaoAdministrativo.objects.values(
'documento_id').annotate(data_encaminhamento=Max( 'documento_id').annotate(data_encaminhamento=Max(
'data_encaminhamento'), 'data_encaminhamento'),
id=Max('id')).values_list('id', flat=True) id=Max('id')).values_list('id', flat=True)
def filtra_tramitacao_status(self, status): def filtra_tramitacao_status(self, status):
lista = self.pega_ultima_tramitacao() lista = self.pega_ultima_tramitacao()
return TramitacaoAdministrativo.objects.filter( return TramitacaoAdministrativo.objects.filter(
id__in=lista, id__in=lista,
status=status).distinct().values_list('documento_id', flat=True) status=status).distinct().values_list('documento_id', flat=True)
def filtra_tramitacao_destino(self, destino): def filtra_tramitacao_destino(self, destino):
lista = self.pega_ultima_tramitacao() lista = self.pega_ultima_tramitacao()
return TramitacaoAdministrativo.objects.filter( return TramitacaoAdministrativo.objects.filter(
@ -1584,7 +1593,6 @@ class TramitacaoEmLoteAdmView(PrimeiraTramitacaoEmLoteAdmView):
unidade_tramitacao_destino=destino).distinct().values_list( unidade_tramitacao_destino=destino).distinct().values_list(
'documento_id', flat=True) 'documento_id', flat=True)
def filtra_tramitacao_destino_and_status(self, status, destino): def filtra_tramitacao_destino_and_status(self, status, destino):
lista = self.pega_ultima_tramitacao() lista = self.pega_ultima_tramitacao()
return TramitacaoAdministrativo.objects.filter( return TramitacaoAdministrativo.objects.filter(

1
sapl/rules/map_rules.py

@ -127,6 +127,7 @@ rules_group_materia = {
['can_access_impressos'], __perms_publicas__), ['can_access_impressos'], __perms_publicas__),
(materia.Numeracao, __base__, __perms_publicas__), (materia.Numeracao, __base__, __perms_publicas__),
(materia.Tramitacao, __base__, __perms_publicas__), (materia.Tramitacao, __base__, __perms_publicas__),
(materia.MateriaEmTramitacao, __base__, __perms_publicas__),
(norma.LegislacaoCitada, __base__, __perms_publicas__), (norma.LegislacaoCitada, __base__, __perms_publicas__),
(norma.AutoriaNorma, __base__, __perms_publicas__), (norma.AutoriaNorma, __base__, __perms_publicas__),
(compilacao.Dispositivo, __base__ + [ (compilacao.Dispositivo, __base__ + [

2
sapl/settings.py

@ -41,7 +41,7 @@ ALLOWED_HOSTS = ['*']
LOGIN_REDIRECT_URL = '/' LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/login/?next=' LOGIN_URL = '/login/?next='
SAPL_VERSION = '3.1.160-RC0' SAPL_VERSION = '3.1.160-RC1'
if DEBUG: if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

2
sapl/templates/base.html

@ -179,7 +179,7 @@
<small> <small>
Desenvolvido pelo <a href="http://www.interlegis.leg.br/">Interlegis</a> em software livre e aberto. Desenvolvido pelo <a href="http://www.interlegis.leg.br/">Interlegis</a> em software livre e aberto.
</small> </small>
<span>Release: 3.1.160-RC0</span> <span>Release: 3.1.160-RC1</span>
</p> </p>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">

17
sapl/templates/base/RelatorioMateriasPorTramitacao_filter.html

@ -44,17 +44,20 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for materia in object_list %} {% for materia_em_tramitacao in object_list %}
<tr> <tr>
<td><a href="{% url 'sapl.materia:materialegislativa_detail' materia.pk %}"> <td>
{{materia.tipo.descricao}} {{materia.numero}}/{{materia.ano}} <a href="{% url 'sapl.materia:materialegislativa_detail' materia_em_tramitacao.materia.pk %}">
</a></td> {{ materia_em_tramitacao.materia.tipo.descricao }} {{ materia_em_tramitacao.materia.numero }}/{{ materia_em_tramitacao.materia.ano }}
<td>{{materia.tramitacao_set.last.unidade_tramitacao_destino}}</td> </a>
<td>{{materia.tramitacao_set.last.status}}</td> </td>
<td>{{ materia_em_tramitacao.materia.tramitacao_set.last.unidade_tramitacao_destino }}</td>
<td>{{ materia_em_tramitacao.materia.tramitacao_set.last.status }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
{% include 'paginacao.html' %}
<br/>
{% endblock base_content %} {% endblock base_content %}

6
sapl/templates/base/parlamentares_duplicados.html

@ -15,12 +15,12 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for quantidade, parlamentar in parlamentares_duplicados %} {% for parlamentar in parlamentares_duplicados %}
<tr> <tr>
<td> <td>
<a href="{% url 'sapl.parlamentares:pesquisar_parlamentar' %}?nome_parlamentar={{ parlamentar }}">{{ parlamentar }}</a> <a href="{% url 'sapl.parlamentares:pesquisar_parlamentar' %}?nome_parlamentar={{ parlamentar.nome_parlamentar }}">{{ parlamentar.nome_parlamentar }}</a>
</td> </td>
<td>{{ quantidade }}</td> <td>{{ parlamentar.count }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

8
sapl/templates/base/protocolos_duplicados.html

@ -15,12 +15,14 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for protocolo, quantidade in protocolos_duplicados %} {% for protocolo in protocolos_duplicados %}
<tr> <tr>
<td> <td>
<a href="{% url 'sapl.protocoloadm:protocolo' %}?numero={{ protocolo.numero }}&ano={{ protocolo.ano }}">{{ protocolo }}</a> <a href="{% url 'sapl.protocoloadm:protocolo' %}?numero={{ protocolo.numero }}&ano={{ protocolo.ano }}">
{{ protocolo.numero }}/{{ protocolo.ano }}
</a>
</td> </td>
<td>{{ quantidade }}</td> <td>{{ protocolo.total }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

13
sapl/templates/materia/proposicao_detail.html

@ -3,6 +3,7 @@
{% load tz %} {% load tz %}
{% block sub_actions %} {% block sub_actions %}
{{block.super}} {{block.super}}
{% if user == object.autor.user %}
<div class="actions btn-group btn-group-sm {%block sub_actions_pull%}{% endblock%}" role="group"> <div class="actions btn-group btn-group-sm {%block sub_actions_pull%}{% endblock%}" role="group">
{% if object.texto_articulado.exists %} {% if object.texto_articulado.exists %}
<a class="btn btn-success" href="{% url 'sapl.materia:proposicao_ta' object.pk%}">{% trans "Texto Eletrônico" %}</a> <a class="btn btn-success" href="{% url 'sapl.materia:proposicao_ta' object.pk%}">{% trans "Texto Eletrônico" %}</a>
@ -11,10 +12,11 @@
<a class="btn btn-success" href="{% url 'sapl.materia:proposicao_texto' object.pk %}">{% trans "Texto Original" %}</a> <a class="btn btn-success" href="{% url 'sapl.materia:proposicao_texto' object.pk %}">{% trans "Texto Original" %}</a>
{% endif %} {% endif %}
</div> </div>
{% endif %}
{% endblock sub_actions%} {% endblock sub_actions%}
{% block editions %} {% block editions %}
{% if object.data_envio %}
{% if user == object.autor.user %} {% if user == object.autor.user %}
{% if object.data_envio %}
{% block editions_actions_return %} {% block editions_actions_return %}
<div class="actions btn-group" role="group"> <div class="actions btn-group" role="group">
<a class="btn btn-outline-primary" onclick="window.open('{% url 'sapl.materia:recibo-proposicao' object.pk %}','Recibo','width=1100, height=600, scrollbars=yes')">{% trans "Recibo de Envio" %}</a> <a class="btn btn-outline-primary" onclick="window.open('{% url 'sapl.materia:recibo-proposicao' object.pk %}','Recibo','width=1100, height=600, scrollbars=yes')">{% trans "Recibo de Envio" %}</a>
@ -23,7 +25,6 @@
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}
{% endif %}
{% else %} {% else %}
{% block editions_actions_send %} {% block editions_actions_send %}
<div class="actions btn-group" role="group"> <div class="actions btn-group" role="group">
@ -35,8 +36,10 @@
</div> </div>
{% endblock %} {% endblock %}
{% endif %} {% endif %}
{% endif %}
{% endblock editions %} {% endblock editions %}
{% block detail_content %} {% block detail_content %}
{% if user == object.autor.user %}
<h2 class="legend">{% model_verbose_name 'sapl.materia.models.Proposicao' %}</h2> <h2 class="legend">{% model_verbose_name 'sapl.materia.models.Proposicao' %}</h2>
<div class="row"> <div class="row">
<div class="col-sm-3"> <div class="col-sm-3">
@ -203,4 +206,10 @@
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}
{% else %}
<br><br>
<div class="alert alert-danger alert-dismissible" role="alert">
<h3>Você não tem acesso a essa Proposição porque não é de sua autoria.</h3>
</div>
{% endif %}
{% endblock detail_content %} {% endblock detail_content %}

15
sapl/templates/protocoloadm/comprovante.html

@ -18,6 +18,9 @@
.hide-print { .hide-print {
display : none; display : none;
} }
.downsize {
font-size: 12px;
}
} }
@page { @page {
size: auto; /* auto is the initial value */ size: auto; /* auto is the initial value */
@ -74,7 +77,7 @@
{% if protocolo.tipo_processo == 1 %} {% if protocolo.tipo_processo == 1 %}
<tr> <tr>
<th>Ementa</th> <th>Ementa</th>
<td>{{ protocolo.assunto_ementa }}</td> <td class="downsize">{{ protocolo.assunto_ementa }}</td>
</tr> </tr>
<tr> <tr>
<th>Autor</th> <th>Autor</th>
@ -102,10 +105,16 @@
</tr> </tr>
<tr> <tr>
<th>Número Páginas</th> <th>Número Páginas</th>
<td>{{ protocolo.numero_paginas }}</td> <td>{{ protocolo.numero_paginas|default_if_none:"0" }}</td>
</tr> </tr>
{% if materia %}
<tr>
<th>Número da Matéria</th>
<td>{{ materia }}</td>
</tr>
{% endif %}
<tr> <tr>
<th>Comprovante emitido por</th> <th>Emitido por</th>
<td>{{ request.user.username }}</td> <td>{{ request.user.username }}</td>
</tr> </tr>
</table> </table>

2
sapl/templates/protocoloadm/layouts.yaml

@ -9,7 +9,7 @@ DocumentoAdministrativo:
- data protocolo - data protocolo
- assunto - assunto
- interessado tramitacao - interessado tramitacao
- texto_integral - texto_integral|urlize
- documento_anexado_set__documento_principal|m2m_urlize_for_detail - documento_anexado_set__documento_principal|m2m_urlize_for_detail
- documento_principal_set__documento_anexado|m2m_urlize_for_detail - documento_principal_set__documento_anexado|m2m_urlize_for_detail
{% trans 'Outras Informações' %}: {% trans 'Outras Informações' %}:

12
sapl/templates/sessao/votacao/nominal.html

@ -21,7 +21,6 @@
{% else %} {% else %}
<fieldset class="form-group"> <fieldset class="form-group">
<legend>Votos</legend> <legend>Votos</legend>
<div class="row"> <div class="row">
{% for parlamentar in parlamentares %} {% for parlamentar in parlamentares %}
@ -39,7 +38,6 @@
</div> </div>
<legend>Situação da Votação:</legend> <legend>Situação da Votação:</legend>
<div id="soma_votos"></div> <div id="soma_votos"></div>
</fieldset> </fieldset>
@ -59,11 +57,17 @@
</div> </div>
<br /><br /> <br /><br />
<input type="submit" id="salvar-votacao" name="salvar-votacao" value="Fechar Votação" class="btn btn-primary" /> <div class="row">
<div class="col-md-12">
<div class="form-group row justify-content-between">
<input type="submit" id="cancelar-votacao" name="cancelar-votacao" value="Cancelar Votação" class="btn btn-warning" /> <input type="submit" id="cancelar-votacao" name="cancelar-votacao" value="Cancelar Votação" class="btn btn-warning" />
<input type="submit" id="salvar-votacao" name="salvar-votacao" value="Fechar Votação" class="btn btn-primary" />
</div>
</div>
</div>
{% endif %}
</fieldset> </fieldset>
</form> </form>
{% endif %}
{% endblock detail_content %} {% endblock detail_content %}
{% block extra_js %} {% block extra_js %}

8
sapl/templates/sessao/votacao/votacao.html

@ -71,8 +71,14 @@
</div> </div>
<br /><br /> <br /><br />
<input type="submit" id="salvar-votacao" name="salvar-votacao" value="Salvar" class="btn btn-primary" /> <div class="row">
<div class="col-md-12">
<div class="form-group row justify-content-between">
<input type="submit" id="cancelar-votacao" name="cancelar-votacao" value="Cancelar Votação" class="btn btn-warning" /> <input type="submit" id="cancelar-votacao" name="cancelar-votacao" value="Cancelar Votação" class="btn btn-warning" />
<input type="submit" id="salvar-votacao" name="salvar-votacao" value="Salvar" class="btn btn-primary" />
</div>
</div>
</div>
</fieldset> </fieldset>
</form> </form>
{% endblock detail_content %} {% endblock detail_content %}

2
setup.py

@ -43,7 +43,7 @@ install_requires = [
] ]
setup( setup(
name='interlegis-sapl', name='interlegis-sapl',
version='3.1.160-RC0', version='3.1.160-RC1',
packages=find_packages(), packages=find_packages(),
include_package_data=True, include_package_data=True,
license='GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007', license='GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007',

Loading…
Cancel
Save