Browse Source

Merge pull request #527 from interlegis/524-Proposição

Fix #524 proposição
pull/516/head
Edward 9 years ago
committed by GitHub
parent
commit
5d3cb556a9
  1. 2
      sapl/crispy_layout_mixin.py
  2. 58
      sapl/materia/forms.py
  3. 42
      sapl/materia/migrations/0039_auto_20160808_1753.py
  4. 35
      sapl/materia/migrations/0040_auto_20160810_1524.py
  5. 19
      sapl/materia/migrations/0041_remove_proposicao_data_incorporação.py
  6. 20
      sapl/materia/migrations/0042_proposicao_data_devolução.py
  7. 20
      sapl/materia/migrations/0043_auto_20160810_1738.py
  8. 28
      sapl/materia/models.py
  9. 35
      sapl/materia/tests/test_materia.py
  10. 33
      sapl/materia/urls.py
  11. 225
      sapl/materia/views.py
  12. 29
      sapl/protocoloadm/forms.py
  13. 30
      sapl/protocoloadm/urls.py
  14. 70
      sapl/protocoloadm/views.py
  15. 1
      sapl/templates/base.html
  16. 8
      sapl/templates/crud/confirm_delete.html
  17. 2
      sapl/templates/crud/detail.html
  18. 2
      sapl/templates/crud/form.html
  19. 32
      sapl/templates/materia/confirmar_proposicao.html
  20. 43
      sapl/templates/materia/prop_devolvidas_list.html
  21. 35
      sapl/templates/materia/prop_pendentes_list.html
  22. 43
      sapl/templates/materia/prop_recebidas_list.html
  23. 10
      sapl/templates/materia/proposicao_confirm_delete.html
  24. 12
      sapl/templates/materia/proposicao_detail.html
  25. 8
      sapl/templates/materia/proposicao_form.html
  26. 9
      sapl/templates/materia/receber_proposicao.html
  27. 70
      sapl/templates/materia/recibo_proposicao.html
  28. 6
      sapl/templates/materia/subnav_prop.html
  29. 12
      sapl/utils.py

2
sapl/crispy_layout_mixin.py

@ -1,12 +1,12 @@
from math import ceil
import rtyaml
from crispy_forms.bootstrap import FormActions
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Div, Fieldset, Layout, Submit
from django import template
from django.utils import formats
from django.utils.translation import ugettext as _
import rtyaml
def heads_and_tails(list_of_lists):

58
sapl/materia/forms.py

@ -31,6 +31,31 @@ def em_tramitacao():
(False, 'Não')]
class ConfirmarProposicaoForm(ModelForm):
class Meta:
model = Proposicao
exclude = ['texto_original', 'descricao', 'tipo']
class ReceberProposicaoForm(ModelForm):
cod_hash = forms.CharField(label='Código do Documento', required=True)
class Meta:
model = Proposicao
exclude = ['texto_original', 'descricao', 'tipo']
def __init__(self, *args, **kwargs):
row1 = to_row([('cod_hash', 12)])
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(
_('Incorporar Proposição'), row1,
form_actions(save_label='Buscar Proposição')
)
)
super(ReceberProposicaoForm, self).__init__(*args, **kwargs)
class UnidadeTramitacaoForm(ModelForm):
class Meta:
@ -82,20 +107,33 @@ class ProposicaoForm(ModelForm):
cleaned_data = self.cleaned_data
if 'tipo' in cleaned_data:
if cleaned_data['tipo'].descricao == 'Parecer':
try:
materia = MateriaLegislativa.objects.get(
tipo_id=cleaned_data['tipo_materia'],
ano=cleaned_data['ano_materia'],
numero=cleaned_data['numero_materia'])
except ObjectDoesNotExist:
msg = _('Matéria adicionada não existe!')
raise ValidationError(msg)
if self.instance.materia:
cleaned_data['materia'] = self.instance.materia
cleaned_data['autor'] = (
self.instance.materia.autoria_set.first().autor)
else:
cleaned_data['materia'] = materia
cleaned_data['autor'] = materia.autoria_set.first().autor
try:
materia = MateriaLegislativa.objects.get(
tipo_id=cleaned_data['tipo_materia'],
ano=cleaned_data['ano_materia'],
numero=cleaned_data['numero_materia'])
except ObjectDoesNotExist:
msg = _('Matéria adicionada não existe!')
raise ValidationError(msg)
else:
cleaned_data['materia'] = materia
cleaned_data['autor'] = materia.autoria_set.first(
).autor
return cleaned_data
def save(self, commit=False):
proposicao = super(ProposicaoForm, self).save(commit)
if 'materia' in self.cleaned_data:
proposicao.materia = self.cleaned_data['materia']
proposicao.save()
return proposicao
class Meta:
model = Proposicao
fields = ['tipo', 'data_envio', 'descricao', 'texto_original']

42
sapl/materia/migrations/0039_auto_20160808_1753.py

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.5 on 2016-08-08 20:53
from __future__ import unicode_literals
from django.db import migrations, models
import sapl.materia.models
import sapl.utils
class Migration(migrations.Migration):
dependencies = [
('materia', '0038_auto_20160612_1506'),
]
operations = [
migrations.RemoveField(
model_name='proposicao',
name='data_devolucao',
),
migrations.AddField(
model_name='proposicao',
name='data_incorporação',
field=models.DateTimeField(blank=True, null=True, verbose_name='Data de Incorporação'),
),
migrations.AlterField(
model_name='proposicao',
name='data_recebimento',
field=models.DateTimeField(blank=True, null=True, verbose_name='Data de Recebimento'),
),
migrations.AlterField(
model_name='proposicao',
name='status',
field=models.CharField(blank=True, choices=[('E', 'Enviada'), ('R', 'Recebida'), ('I', 'Incorporada')], max_length=1, verbose_name='Status Proposição'),
),
migrations.AlterField(
model_name='proposicao',
name='texto_original',
field=models.FileField(default='', upload_to=sapl.materia.models.texto_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Texto Original'),
preserve_default=False,
),
]

35
sapl/materia/migrations/0040_auto_20160810_1524.py

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.5 on 2016-08-10 18:24
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('materia', '0039_auto_20160808_1753'),
]
operations = [
migrations.RemoveField(
model_name='proposicao',
name='documento',
),
migrations.AddField(
model_name='proposicao',
name='documento_gerado',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='materia.DocumentoAcessorio'),
),
migrations.AddField(
model_name='proposicao',
name='materia_gerada',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='materia_gerada', to='materia.MateriaLegislativa'),
),
migrations.AlterField(
model_name='proposicao',
name='materia',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='materia_vinculada', to='materia.MateriaLegislativa', verbose_name='Matéria'),
),
]

19
sapl/materia/migrations/0041_remove_proposicao_data_incorporação.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.5 on 2016-08-10 20:02
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('materia', '0040_auto_20160810_1524'),
]
operations = [
migrations.RemoveField(
model_name='proposicao',
name='data_incorporação',
),
]

20
sapl/materia/migrations/0042_proposicao_data_devolução.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.5 on 2016-08-10 20:37
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('materia', '0041_remove_proposicao_data_incorporação'),
]
operations = [
migrations.AddField(
model_name='proposicao',
name='data_devolução',
field=models.DateTimeField(blank=True, null=True, verbose_name='Data de Devolução'),
),
]

20
sapl/materia/migrations/0043_auto_20160810_1738.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.5 on 2016-08-10 20:38
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('materia', '0042_proposicao_data_devolução'),
]
operations = [
migrations.RenameField(
model_name='proposicao',
old_name='data_devolução',
new_name='data_devolucao',
),
]

28
sapl/materia/models.py

@ -443,14 +443,16 @@ class TipoProposicao(models.Model):
class Proposicao(models.Model):
autor = models.ForeignKey(Autor, null=True, blank=True)
tipo = models.ForeignKey(TipoProposicao, verbose_name=_('Tipo'))
# XXX data_envio was not null, but actual data said otherwise!!!
data_envio = models.DateTimeField(
null=True, blank=True, verbose_name=_('Data de Envio'))
blank=True, null=True, verbose_name=_('Data de Envio'))
data_recebimento = models.DateTimeField(
blank=True, null=True, verbose_name=_('Data de Incorporação'))
descricao = models.TextField(max_length=100, verbose_name=_('Descrição'))
blank=True, null=True, verbose_name=_('Data de Recebimento'))
data_devolucao = models.DateTimeField(
blank=True, null=True, verbose_name=_('Data de devolução'))
blank=True, null=True, verbose_name=_('Data de Devolução'))
descricao = models.TextField(max_length=100, verbose_name=_('Descrição'))
justificativa_devolucao = models.CharField(
max_length=200,
blank=True,
@ -461,17 +463,23 @@ class Proposicao(models.Model):
status = models.CharField(blank=True,
max_length=1,
choices=(('E', 'Enviada'),
('D', 'Devolvida'),
('R', 'Recebida'),
('I', 'Incorporada')),
verbose_name=_('Status Proposição'))
# mutually exclusive (depend on tipo.materia_ou_documento)
materia = models.ForeignKey(
MateriaLegislativa, blank=True, null=True, verbose_name=_('Matéria'))
documento = models.ForeignKey(
DocumentoAcessorio, blank=True, null=True, verbose_name=_('Documento'))
MateriaLegislativa, blank=True, null=True, verbose_name=_('Matéria'),
related_name=_('materia_vinculada'))
# Ao ser recebida, irá gerar uma nova matéria ou um documento acessorio
# de uma já existente
materia_gerada = models.ForeignKey(
MateriaLegislativa, blank=True, null=True,
related_name=_('materia_gerada'))
documento_gerado = models.ForeignKey(
DocumentoAcessorio, blank=True, null=True)
texto_original = models.FileField(
blank=True,
null=True,
upload_to=texto_upload_path,
verbose_name=_('Texto Original'),
validators=[restringe_tipos_de_arquivo_txt])

35
sapl/materia/tests/test_materia.py

@ -5,10 +5,10 @@ from model_mommy import mommy
from sapl.comissoes.models import Comissao, TipoComissao
from sapl.materia.models import (Anexada, Autor, Autoria, DespachoInicial,
DocumentoAcessorio, MateriaLegislativa,
Numeracao, Proposicao, RegimeTramitacao,
StatusTramitacao, TipoAutor, TipoDocumento,
TipoMateriaLegislativa, TipoProposicao,
Tramitacao, UnidadeTramitacao)
Numeracao, RegimeTramitacao, StatusTramitacao,
TipoAutor, TipoDocumento,
TipoMateriaLegislativa, Tramitacao,
UnidadeTramitacao)
from sapl.norma.models import (LegislacaoCitada, NormaJuridica,
TipoNormaJuridica)
@ -414,30 +414,3 @@ def test_form_errors_relatoria(client):
['Este campo é obrigatório.'])
assert (response.context_data['form'].errors['parlamentar'] ==
['Este campo é obrigatório.'])
@pytest.mark.django_db(transaction=False)
def test_proposicao_submit(client):
response = client.post(reverse('sapl.materia:proposicao_create'),
{'tipo': mommy.make(TipoProposicao, pk=3).pk,
'descricao': 'Teste proposição',
'salvar': 'salvar'},
follow=True)
assert response.status_code == 200
proposicao = Proposicao.objects.first()
assert proposicao.descricao == 'Teste proposição'
assert proposicao.tipo.pk == 3
@pytest.mark.django_db(transaction=False)
def test_form_errors_proposicao(client):
response = client.post(reverse('sapl.materia:proposicao_create'),
{'salvar': 'salvar'},
follow=True)
assert (response.context_data['form'].errors['tipo'] ==
['Este campo é obrigatório.'])
assert (response.context_data['form'].errors['descricao'] ==
['Este campo é obrigatório.'])

33
sapl/materia/urls.py

@ -3,17 +3,20 @@ from django.conf.urls import include, url
from sapl.materia.views import (AcompanhamentoConfirmarView,
AcompanhamentoExcluirView,
AcompanhamentoMateriaView, AnexadaCrud,
AutorCrud, AutoriaCrud, DespachoInicialCrud,
DocumentoAcessorioCrud, LegislacaoCitadaCrud,
MateriaLegislativaCrud,
AutorCrud, AutoriaCrud, ConfirmarProposicao,
DespachoInicialCrud, DocumentoAcessorioCrud,
LegislacaoCitadaCrud, MateriaLegislativaCrud,
MateriaLegislativaPesquisaView, MateriaTaView,
NumeracaoCrud, OrgaoCrud, OrigemCrud,
ProposicaoCrud, ProposicaoTaView,
RegimeTramitacaoCrud, RelatoriaCrud,
StatusTramitacaoCrud, TipoAutorCrud,
TipoDocumentoCrud, TipoFimRelatoriaCrud,
TipoMateriaCrud, TipoProposicaoCrud,
TramitacaoCrud, UnidadeTramitacaoCrud)
ProposicaoCrud, ProposicaoDevolvida,
ProposicaoPendente, ProposicaoRecebida,
ProposicaoTaView, ReceberProposicao,
ReciboProposicaoView, RegimeTramitacaoCrud,
RelatoriaCrud, StatusTramitacaoCrud,
TipoAutorCrud, TipoDocumentoCrud,
TipoFimRelatoriaCrud, TipoMateriaCrud,
TipoProposicaoCrud, TramitacaoCrud,
UnidadeTramitacaoCrud)
from .apps import AppConfig
@ -31,6 +34,18 @@ urlpatterns = [
DocumentoAcessorioCrud.get_urls())),
url(r'^proposicao/', include(ProposicaoCrud.get_urls())),
url(r'^proposicao/recibo/(?P<pk>\d+)', ReciboProposicaoView.as_view(),
name='recibo-proposicao'),
url(r'^proposicao/receber/', ReceberProposicao.as_view(),
name='receber-proposicao'),
url(r'^proposicao/pendente/', ProposicaoPendente.as_view(),
name='proposicao-pendente'),
url(r'^proposicao/recebida/', ProposicaoRecebida.as_view(),
name='proposicao-recebida'),
url(r'^proposicao/devolvida/', ProposicaoDevolvida.as_view(),
name='proposicao-devolvida'),
url(r'^proposicao/confirmar/(?P<pk>\d+)', ConfirmarProposicao.as_view(),
name='proposicao-confirmar'),
# Integração com Compilação
url(r'^materia/(?P<pk>[0-9]+)/ta$',

225
sapl/materia/views.py

@ -11,7 +11,7 @@ from django.core.urlresolvers import reverse
from django.http.response import HttpResponseRedirect
from django.template import Context, loader
from django.utils.translation import ugettext_lazy as _
from django.views.generic import CreateView, TemplateView, UpdateView
from django.views.generic import CreateView, ListView, TemplateView, UpdateView
from django_filters.views import FilterView
from sapl.base.models import CasaLegislativa
@ -21,14 +21,15 @@ from sapl.crud.base import (Crud, CrudBaseMixin, CrudCreateView, CrudListView,
CrudUpdateView, make_pagination)
from sapl.crud.masterdetail import MasterDetailCrud
from sapl.norma.models import LegislacaoCitada
from sapl.utils import autor_label, autor_modal, get_base_url
from sapl.utils import (autor_label, autor_modal, gerar_hash_arquivo,
get_base_url)
from .forms import (AcompanhamentoMateriaForm, AnexadaForm, AutoriaForm,
DespachoInicialForm, DocumentoAcessorioForm,
LegislacaoCitadaForm, MateriaLegislativaFilterSet,
NumeracaoForm, ProposicaoForm, RelatoriaForm,
TramitacaoForm, UnidadeTramitacaoForm,
filtra_tramitacao_destino,
ConfirmarProposicaoForm, DespachoInicialForm,
DocumentoAcessorioForm, LegislacaoCitadaForm,
MateriaLegislativaFilterSet, NumeracaoForm, ProposicaoForm,
ReceberProposicaoForm, RelatoriaForm, TramitacaoForm,
UnidadeTramitacaoForm, filtra_tramitacao_destino,
filtra_tramitacao_destino_and_status,
filtra_tramitacao_status)
from .models import (AcompanhamentoMateria, Anexada, Autor, Autoria,
@ -52,6 +53,43 @@ TipoProposicaoCrud = Crud.build(TipoProposicao, 'tipo_proposicao')
StatusTramitacaoCrud = Crud.build(StatusTramitacao, 'status_tramitacao')
def criar_materia_proposicao(proposicao):
tipo_materia = TipoMateriaLegislativa.objects.get(
descricao=proposicao.tipo.descricao)
numero = MateriaLegislativa.objects.filter(
ano=datetime.now().year).order_by('numero').last().numero + 1
regime = RegimeTramitacao.objects.get(descricao='Normal')
return MateriaLegislativa.objects.create(
tipo=tipo_materia,
ano=datetime.now().year,
numero=numero,
data_apresentacao=datetime.now(),
regime_tramitacao=regime,
em_tramitacao=True,
ementa=proposicao.descricao,
texto_original=proposicao.texto_original
)
def criar_doc_proposicao(proposicao):
tipo_doc = TipoDocumento.objects.get(
descricao=proposicao.tipo.descricao)
if proposicao.autor is None:
autor = 'Desconhecido'
else:
autor = proposicao.autor
return DocumentoAcessorio.objects.create(
materia=proposicao.materia,
tipo=tipo_doc,
arquivo=proposicao.texto_original,
nome=proposicao.descricao,
data=proposicao.data_envio,
autor=autor
)
class UnidadeTramitacaoCrud(Crud):
model = UnidadeTramitacao
help_path = 'unidade_tramitacao'
@ -63,12 +101,149 @@ class UnidadeTramitacaoCrud(Crud):
form_class = UnidadeTramitacaoForm
class ProposicaoDevolvida(ListView):
template_name = 'materia/prop_devolvidas_list.html'
model = Proposicao
ordering = ['data_envio']
paginate_by = 10
def get_queryset(self):
return Proposicao.objects.filter(
data_envio__isnull=False,
data_recebimento__isnull=True,
data_devolucao__isnull=False)
def get_context_data(self, **kwargs):
context = super(ProposicaoDevolvida, self).get_context_data(**kwargs)
paginator = context['paginator']
page_obj = context['page_obj']
context['page_range'] = make_pagination(
page_obj.number, paginator.num_pages)
context['NO_ENTRIES_MSG'] = 'Nenhuma proposição devolvida.'
return context
class ProposicaoPendente(ListView):
template_name = 'materia/prop_pendentes_list.html'
model = Proposicao
ordering = ['data_envio', 'autor', 'tipo', 'descricao']
paginate_by = 10
def get_queryset(self):
return Proposicao.objects.filter(
data_envio__isnull=False,
data_recebimento__isnull=True,
data_devolucao__isnull=True)
def get_context_data(self, **kwargs):
context = super(ProposicaoPendente, self).get_context_data(**kwargs)
paginator = context['paginator']
page_obj = context['page_obj']
context['page_range'] = make_pagination(
page_obj.number, paginator.num_pages)
context['NO_ENTRIES_MSG'] = 'Nenhuma proposição pendente.'
return context
class ProposicaoRecebida(ListView):
template_name = 'materia/prop_recebidas_list.html'
model = Proposicao
ordering = ['data_envio']
paginate_by = 10
def get_queryset(self):
return Proposicao.objects.filter(
data_envio__isnull=False,
data_recebimento__isnull=False,
data_devolucao__isnull=True)
def get_context_data(self, **kwargs):
context = super(ProposicaoRecebida, self).get_context_data(**kwargs)
paginator = context['paginator']
page_obj = context['page_obj']
context['page_range'] = make_pagination(
page_obj.number, paginator.num_pages)
context['NO_ENTRIES_MSG'] = 'Nenhuma proposição recebida.'
return context
class ReceberProposicao(CreateView):
template_name = "materia/receber_proposicao.html"
form_class = ReceberProposicaoForm
def get_context_data(self, **kwargs):
context = super(ReceberProposicao, self).get_context_data(**kwargs)
context.update({'form': self.get_form()})
return context
def post(self, request, *args, **kwargs):
form = ReceberProposicaoForm(request.POST)
if form.is_valid():
proposicoes = Proposicao.objects.filter(data_envio__isnull=False)
for proposicao in proposicoes:
hasher = gerar_hash_arquivo(proposicao.texto_original.path,
str(proposicao.pk))
if hasher == form.cleaned_data['cod_hash']:
return HttpResponseRedirect(
reverse('sapl.materia:proposicao-confirmar',
kwargs={'pk': proposicao.pk}))
msg = 'Proposição não encontrada!'
return self.render_to_response({'form': form, 'msg': msg})
else:
return self.render_to_response({'form': form})
def get_success_url(self):
return reverse('sapl.materia:receber-proposicao')
class ConfirmarProposicao(CreateView):
template_name = "materia/confirmar_proposicao.html"
form_class = ConfirmarProposicaoForm
def get_context_data(self, **kwargs):
context = super(ConfirmarProposicao, self).get_context_data(**kwargs)
proposicao = Proposicao.objects.get(pk=self.kwargs['pk'])
context.update({'form': self.get_form(), 'proposicao': proposicao})
return context
def post(self, request, *args, **kwargs):
form = ConfirmarProposicaoForm(request.POST)
proposicao = Proposicao.objects.get(pk=self.kwargs['pk'])
if form.is_valid():
if 'incorporar' in request.POST:
proposicao.data_recebimento = datetime.now()
if proposicao.tipo.descricao == 'Parecer':
documento = criar_doc_proposicao(proposicao)
proposicao.documento_gerado = documento
proposicao.save()
return HttpResponseRedirect(
reverse('sapl.materia:documentoacessorio_update',
kwargs={'pk': documento.pk}))
else:
materia = criar_materia_proposicao(proposicao)
proposicao.materia_gerada = materia
proposicao.save()
return HttpResponseRedirect(
reverse('sapl.materia:materialegislativa_update',
kwargs={'pk': materia.pk}))
else:
proposicao.data_devolucao = datetime.now()
proposicao.save()
return HttpResponseRedirect(
reverse('sapl.materia:proposicao-devolvida'))
class ProposicaoCrud(Crud):
model = Proposicao
help_path = ''
class BaseMixin(CrudBaseMixin):
list_field_names = ['data_envio', 'descricao', 'tipo']
list_field_names = ['data_envio', 'descricao',
'tipo', 'data_recebimento']
class CreateView(CrudCreateView):
form_class = ProposicaoForm
@ -80,18 +255,36 @@ class ProposicaoCrud(Crud):
class UpdateView(CrudUpdateView):
form_class = ProposicaoForm
def get_context_data(self, **kwargs):
context = super(UpdateView, self).get_context_data(**kwargs)
if self.object.materia:
context['form'].fields['tipo_materia'].initial = (
self.object.materia.tipo.id)
context['form'].fields['numero_materia'].initial = (
self.object.materia.numero)
context['form'].fields['ano_materia'].initial = (
self.object.materia.ano)
return context
@property
def layout_key(self):
return 'ProposicaoCreate'
class ListView(CrudListView):
ordering = ['-data_envio', 'descricao']
ordering = ['-data_envio', '-descricao']
def get_rows(self, object_list):
for obj in object_list:
if obj.data_envio is None:
obj.data_envio = 'Em elaboração...'
else:
obj.data_envio = obj.data_envio.strftime("%d/%m/%Y %H:%M")
if obj.data_recebimento is None:
obj.data_recebimento = 'Não recebida'
else:
obj.data_recebimento = obj.data_recebimento.strftime(
"%d/%m/%Y %H:%M")
return [self._as_row(obj) for obj in object_list]
@ -112,6 +305,20 @@ class ProposicaoCrud(Crud):
kwargs={'pk': proposicao.pk}))
class ReciboProposicaoView(TemplateView):
template_name = "materia/recibo_proposicao.html"
def get_context_data(self, **kwargs):
context = super(ReciboProposicaoView, self).get_context_data(
**kwargs)
proposicao = Proposicao.objects.get(pk=self.kwargs['pk'])
context.update({'proposicao': proposicao,
'hash': gerar_hash_arquivo(
proposicao.texto_original.path,
self.kwargs['pk'])})
return context
class RelatoriaCrud(MasterDetailCrud):
model = Relatoria
parent_field = 'materia'

29
sapl/protocoloadm/forms.py

@ -439,35 +439,6 @@ class ProtocoloMateriaForm(ModelForm):
*args, **kwargs)
class ProposicaoSimpleForm(forms.Form):
tipo = forms.CharField(label='Tipo',
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
materia = forms.CharField(label='Matéria',
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
data_envio = forms.DateField(label=_('Data Envio'),
widget=forms.DateInput(
format='%d/%m/%Y',
attrs={'readonly': 'readonly'}))
data_recebimento = forms.DateField(label=_('Data Recebimento'),
widget=forms.DateInput(
format='%d/%m/%Y',
attrs={'readonly': 'readonly'}))
descricao = forms.CharField(label='Descrição',
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
numero_proposicao = forms.CharField(label='Número',
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
# ano = forms.CharField(label='Ano',
# widget = forms.TextInput(
# attrs={'readonly':'readonly'}))
class DocumentoAcessorioAdministrativoForm(ModelForm):
class Meta:

30
sapl/protocoloadm/urls.py

@ -9,11 +9,6 @@ from sapl.protocoloadm.views import (AnularProtocoloAdmView,
DocumentoAcessorioAdministrativoView,
DocumentoAdministrativoCrud,
PesquisarDocumentoAdministrativoView,
ProposicaoDetailView,
ProposicaoReceberView, ProposicaoView,
ProposicoesIncorporadasView,
ProposicoesNaoIncorporadasView,
ProposicoesNaoRecebidasView,
ProtocoloDocumentoCrud,
ProtocoloDocumentoView, ProtocoloListView,
ProtocoloMateriaCrud,
@ -27,8 +22,7 @@ from sapl.protocoloadm.views import (AnularProtocoloAdmView,
TramitacaoAdmEditView,
TramitacaoAdmIncluirView,
TramitacaoAdministrativoCrud,
TramitacaoAdmView, get_nome_autor,
pesquisa_autores)
TramitacaoAdmView)
from .apps import AppConfig
@ -86,26 +80,4 @@ urlpatterns = [
ComprovanteProtocoloView.as_view(), name='comprovante_protocolo'),
url(r'^protocoloadm/(?P<pk>\d+)/(?P<ano>\d+)/criar_documento$',
CriarDocumentoProtocolo.as_view(), name='criar_documento'),
# TODO: move to Proposicoes app
url(r'^proposicao$',
ProposicaoView.as_view(), name='proposicao'),
url(r'^proposicao/proposicao-receber',
ProposicaoReceberView.as_view(), name='proposicao_receber'),
url(r'^proposicao/proposicao-naorecebidas',
ProposicoesNaoRecebidasView.as_view(),
name='proposicao_naorecebidas'),
url(r'^proposicao/proposicao-naoincorporadas',
ProposicoesNaoIncorporadasView.as_view(),
name='proposicao_naoincorporadas'),
url(r'^proposicao/proposicao-incorporadas',
ProposicoesIncorporadasView.as_view(),
name='proposicao_incorporadas'),
url(r'^proposicao/(?P<pk>\d+)/proposicao',
ProposicaoDetailView.as_view(), name='proposicao_view'),
url(r'^proposicao/pesquisar_autor',
pesquisa_autores, name='pesquisar_autor'),
url(r'^proposicao/get_nome_autor',
get_nome_autor, name='get_nome_autor')
]

70
sapl/protocoloadm/views.py

@ -13,14 +13,14 @@ from django.views.generic.base import TemplateView
from django_filters.views import FilterView
from sapl.crud.base import Crud, CrudBaseMixin, CrudListView, make_pagination
from sapl.materia.models import Proposicao, TipoMateriaLegislativa
from sapl.materia.models import TipoMateriaLegislativa
from sapl.utils import create_barcode, get_client_ip
from .forms import (AnularProcoloAdmForm, DocumentoAcessorioAdministrativoForm,
DocumentoAdministrativoFilterSet,
DocumentoAdministrativoForm, ProposicaoSimpleForm,
ProtocoloDocumentForm, ProtocoloFilterSet,
ProtocoloMateriaForm, TramitacaoAdmForm)
DocumentoAdministrativoForm, ProtocoloDocumentForm,
ProtocoloFilterSet, ProtocoloMateriaForm,
TramitacaoAdmForm)
from .models import (Autor, DocumentoAcessorioAdministrativo,
DocumentoAdministrativo, Protocolo,
StatusTramitacaoAdministrativo,
@ -306,68 +306,6 @@ class ProtocoloMateriaView(CreateView):
return redirect(self.get_success_url())
# TODO: move to Proposicao app
class ProposicaoReceberView(TemplateView):
template_name = "protocoloadm/proposicao_receber.html"
class ProposicoesNaoRecebidasView(ListView):
template_name = "protocoloadm/proposicao_naorecebidas.html"
model = Proposicao
paginate_by = 10
def get_queryset(self):
return Proposicao.objects.filter(data_envio__isnull=False, status='E')
class ProposicoesNaoIncorporadasView(ListView):
template_name = "protocoloadm/proposicao_naoincorporadas.html"
model = Proposicao
paginate_by = 10
def get_queryset(self):
return Proposicao.objects.filter(data_envio__isnull=False,
data_devolucao__isnull=False,
status='D')
class ProposicoesIncorporadasView(ListView):
template_name = "protocoloadm/proposicao_incorporadas.html"
model = Proposicao
paginate_by = 10
def get_queryset(self):
return Proposicao.objects.filter(data_envio__isnull=False,
data_recebimento__isnull=False,
status='I')
class ProposicaoView(TemplateView):
template_name = "protocoloadm/proposicoes.html"
class ProposicaoDetailView(DetailView):
template_name = "protocoloadm/proposicao_view.html"
model = Proposicao
def get(self, request, *args, **kwargs):
proposicao = Proposicao.objects.get(id=kwargs['pk'])
data = { # 'ano': proposicao.ano, # TODO: FIX
'tipo': proposicao.tipo.descricao, # TODO: FIX
'materia': proposicao.materia,
'numero_proposicao': proposicao.numero_proposicao,
'data_envio': proposicao.data_envio,
'data_recebimento': proposicao.data_recebimento,
'descricao': proposicao.descricao}
form = ProposicaoSimpleForm(initial=data)
return self.render_to_response({'form': form})
def get_context_data(self, **kwargs):
context = super(ProposicaoView, self).get_context_data(**kwargs)
context['form'] = ProposicaoSimpleForm
return context
class PesquisarDocumentoAdministrativoView(FilterView):
model = DocumentoAdministrativo
filterset_class = DocumentoAdministrativoFilterSet

1
sapl/templates/base.html

@ -57,6 +57,7 @@
<ul class="dropdown-menu">
<li class="nav__sub-item"><a class="nav__sub-link" href="{% url 'sapl.protocoloadm:protocolo' %}">Pesquisar Protocolo</a></li>
<li class="nav__sub-item"><a class="nav__sub-link" href="{% url 'sapl.protocoloadm:pesq_doc_adm' %}">Pesquisar Documento Administrativo</a></li>
<li class="nav__sub-item"><a class="nav__sub-link" href="{% url 'sapl.materia:receber-proposicao' %}">Receber Proposições</a></li>
<!-- <li class="nav__sub-item"><a class="nav__sub-link" href="/materia">Protocolo Legislativo</a></li> -->
{# <li class="nav__sub-item"><a class="nav__sub-link" href="">Protocolo Geral</a></li> #}
{# <li class="nav__sub-item"><a class="nav__sub-link" href="{% url 'sapl.protocoloadm:proposicao' %}">Proposições</a></li> #}

8
sapl/templates/crud/confirm_delete.html

@ -5,9 +5,11 @@
<form action="" method="post">{% csrf_token %}
<div class="panel panel-danger">
<div class="panel-heading text-center">
{% blocktrans %}
Confirma exclusão de "{{ object }}"?
{% endblocktrans %}
{% block msg %}
{% blocktrans %}
Confirma exclusão de "{{ object }}"?
{% endblocktrans %}
{% endblock msg %}
</div>
<div class="panel-body text-center">
<a href="{{ view.cancel_url }}" class="btn btn-inverse">{% trans 'Cancelar' %}</a>

2
sapl/templates/crud/detail.html

@ -11,7 +11,7 @@
</div>
{% endblock actions %}
</div>
{% block extra_msg %}{% endblock extra_msg %}
{% block detail_content %}
{% for fieldset in view.layout_display %}
<h2 class="legend">{{ fieldset.legend }}</h2>

2
sapl/templates/crud/form.html

@ -1,6 +1,6 @@
{% extends "base.html" %}
{% load i18n crispy_forms_tags %}
{% block base_content %}
{% block extra_msg %}{% endblock %}
{% crispy form %}
{% endblock %}

32
sapl/templates/materia/confirmar_proposicao.html

@ -0,0 +1,32 @@
{% extends "base.html" %}
{% load i18n crispy_forms_tags %}
{% block base_content %}
<style>
table {
border-collapse: collapse;
}
table, th, td {
border: 2px solid black;
}
</style>
<fieldset>
<legend>Confirmar recebimento de Proposição</legend>
<table class="table table-striped">
<tr><td><b>Tipo: </b>{{proposicao.tipo}}</td></tr>
<tr><td><b>Autor: </b>{{proposicao.autor}}</td></tr>
<tr><td><b>Descrição: </b>{{proposicao.descricao}}</td></tr>
<tr><td><b>Data de Envio: </b>{{proposicao.data_envio|date:'d/m/Y H:i:s'}}</td></tr>
</table>
<form method="POST">
{% csrf_token %}
<div align="center">
<input type="submit" value="Devolver ao autor" name="devolver" class="btn btn-danger">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<input type="submit" value="Incorporar" name="incorporar" class="btn btn-primary">
</div>
</form>
</fieldset>
{% endblock %}

43
sapl/templates/materia/prop_devolvidas_list.html

@ -0,0 +1,43 @@
{% extends "base.html" %}
{% load i18n %}
{% block sections_nav %} {% include 'materia/subnav_prop.html'%} {% endblock sections_nav %}
{% block base_content %}
<fieldset>
<legend>Proposições Não Incorporadas</legend>
{% if not object_list %}
<p>{{ NO_ENTRIES_MSG }}</p>
{% else %}
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Data do Devolução</th>
<th>Tipo</th>
<th>Descrição</th>
<th>Autor</th>
<th>Vínculo</th>
</tr>
</thead>
<tbody>
{% for prop in object_list %}
<tr>
<td><a href="{% url 'sapl.materia:proposicao_detail' prop.pk %}">{{ prop.data_devolucao|date:"d/m/Y H:i:s" }}</a></td>
<td>{{ prop.tipo.descricao }}</td>
<td>{{ prop.descricao }}</td>
<td>{{ prop.autor }}</td>
<td>
{% if prop.materia_gerada %}
<a href="{% url 'sapl.materia:materialegislativa_detail' prop.materia_gerada.pk %}">{{ prop.materia_gerada.tipo.sigla }} {{ prop.materia_gerada.numero }}/{{ prop.materia_gerada.ano }}</a>
{% elif prop.documento_gerado %}
<a href="{% url 'sapl.materia:documentoacessorio_detail' prop.documento_gerado.pk %}">{{ prop.documento_gerado.materia.tipo.sigla }} {{ prop.documento_gerado.materia.numero }}/{{ prop.documento_gerado.materia.ano }}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</fieldset>
{% include 'paginacao.html'%}
{% endblock %}

35
sapl/templates/materia/prop_pendentes_list.html

@ -0,0 +1,35 @@
{% extends "base.html" %}
{% load i18n %}
{% block sections_nav %} {% include 'materia/subnav_prop.html'%} {% endblock sections_nav %}
{% block base_content %}
<fieldset>
<legend>Proposições Não Recebidas</legend>
{% if not object_list %}
<p>{{ NO_ENTRIES_MSG }}</p>
{% else %}
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Data de Envio</th>
<th>Tipo</th>
<th>Descrição</th>
<th>Autor</th>
</tr>
</thead>
<tbody>
{% for prop in object_list %}
<tr>
<td><a href="{% url 'sapl.materia:proposicao_detail' prop.pk %}">{{ prop.data_envio|date:"d/m/Y H:i:s" }}</a></td>
<td>{{ prop.tipo.descricao }}</td>
<td>{{ prop.descricao }}</td>
<td>{{ prop.autor }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</fieldset>
{% include 'paginacao.html'%}
{% endblock %}

43
sapl/templates/materia/prop_recebidas_list.html

@ -0,0 +1,43 @@
{% extends "base.html" %}
{% load i18n %}
{% block sections_nav %} {% include 'materia/subnav_prop.html'%} {% endblock sections_nav %}
{% block base_content %}
<fieldset>
<legend>Proposições Incorporadas</legend>
{% if not object_list %}
<p>{{ NO_ENTRIES_MSG }}</p>
{% else %}
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Data do Recebimento</th>
<th>Tipo</th>
<th>Descrição</th>
<th>Autor</th>
<th>Vínculo</th>
</tr>
</thead>
<tbody>
{% for prop in object_list %}
<tr>
<td><a href="{% url 'sapl.materia:proposicao_detail' prop.pk %}">{{ prop.data_recebimento|date:"d/m/Y H:i:s" }}</a></td>
<td>{{ prop.tipo.descricao }}</td>
<td>{{ prop.descricao }}</td>
<td>{{ prop.autor }}</td>
<td>
{% if prop.materia_gerada %}
<a href="{% url 'sapl.materia:materialegislativa_detail' prop.materia_gerada.pk %}">{{ prop.materia_gerada.tipo.sigla }} {{ prop.materia_gerada.numero }}/{{ prop.materia_gerada.ano }}</a>
{% elif prop.documento_gerado %}
<a href="{% url 'sapl.materia:documentoacessorio_detail' prop.documento_gerado.pk %}">{{ prop.documento_gerado.materia.tipo.sigla }} {{ prop.documento_gerado.materia.numero }}/{{ prop.documento_gerado.materia.ano }}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</fieldset>
{% include 'paginacao.html'%}
{% endblock %}

10
sapl/templates/materia/proposicao_confirm_delete.html

@ -0,0 +1,10 @@
{% extends "crud/confirm_delete.html" %}
{% load i18n %}
{% block msg %}
{% if proposicao.data_envio %}
Confirma o retorno de "{{ object }}"?
{% else %}
Confirma exclusão de "{{ object }}"?
{% endif %}
{% endblock msg %}

12
sapl/templates/materia/proposicao_detail.html

@ -3,12 +3,16 @@
{% block actions %}
<div class="actions btn-group pull-right" role="group">
{% if proposicao.data_envio %}
<a href="{{ view.update_url }}" class="btn btn-default">{% trans 'Editar Proposição' %}</a>
{% if proposicao.data_envio and not proposicao.data_recebimento %}
<a href="{{ view.delete_url }}" class="btn btn-default">{% trans 'Retornar Proposição Enviada' %}</a>
{% else %}
<a href="{{ view.update_url }}" class="btn btn-default">{% trans 'Enviar/Editar Proposição' %}</a>
{% elif not proposicao.data_envio %}
<a href="{{ view.update_url }}" class="btn btn-default">{% trans 'Enviar Proposição' %}</a>
<a href="{{ view.delete_url }}" class="btn btn-default">{% trans 'Excluir Proposição' %}</a>
{% endif %}
</div>
{% endblock actions %}
{% block extra_msg %}
{% if proposicao.data_envio and not proposicao.data_recebimento %}
<b><p align="center"><a href="" onclick="window.open('{% url 'sapl.materia:recibo-proposicao' object.pk %}','Recibo','width=1100, height=600, scrollbars=yes')">[Imprimir Recibo]</a></p></b>
{% endif %}
{% endblock extra_msg %}

8
sapl/templates/materia/proposicao_form.html

@ -7,18 +7,12 @@
$(document).ready(function(){
if($("#id_data_envio").val() != ''){
$("#submit-id-excluir").val('Retornar proposição enviada');
$("#submit-id-salvar").val('Salvar proposição');
}else{
$("#submit-id-excluir").val('Excluir proposição');
$("#submit-id-salvar").val('Enviar proposição');
}
});
function disable_fields() {
$("#id_tipo_materia").val("");
$("#id_numero_materia").val("");
$("#id_ano_materia").val("");
$("#id_tipo_materia").attr("disabled", "disabled");
$("#id_numero_materia").attr("disabled", "disabled");
$("#id_ano_materia").attr("disabled", "disabled");
@ -34,7 +28,7 @@
$(function () {
disable_fields();
$("#id_tipo").change(function() {
if ($("#id_tipo").val() == 9) { // parecer
if ($('#id_tipo option:selected').text() == 'Parecer') { // parecer
enable_fields();
}else {
disable_fields();

9
sapl/templates/materia/receber_proposicao.html

@ -0,0 +1,9 @@
{% extends "crud/form.html" %}
{% load i18n %}
{% block sections_nav %} {% include 'materia/subnav_prop.html'%} {% endblock sections_nav %}
{% load crispy_forms_tags %}
{% block extra_msg %}
<p align="center"><font size="4" color="red"><b>{{msg}}</b></font></p>
{% endblock %}

70
sapl/templates/materia/recibo_proposicao.html

@ -0,0 +1,70 @@
{% load i18n %}
{% load crispy_forms_tags %}
{% load static %}
{% block detail_content %}
<style>
table {
width: 100%;
}
th, td {
padding: 5px;
}
</style>
<div align="center">
<input type="submit" value="Imprimir" onclick="window.print();" class="btn btn-success"/>
</div>
<br />
<table>
<tr>
<td>
<img height="100" width="100"
src="{% if logotipo %}{{ MEDIA_URL }}{{ logotipo }}{% else %}{% static 'img/logo.png' %}{% endif %}"
alt="Logotipo"
class="img-responsive visible-lg-inline-block vcenter">
<div>
</td>
<td>
{% if nome %}
<b>{{ nome }} {% trans 'de' %} {{ municipio }} - {{ uf }}</b>
{% else %}
<b>{% trans 'Sem Nome Cadastrado' %}</b>
{% endif %}
<br />
{% trans 'Sistema de Apoio ao Processo Legislativo' %}
</td>
</tr>
<tr> <td colspan="2" align="center"><b>RECIBO DE ENVIO DE PROPOSIÇÃO</b></td> </tr>
</table>
<br /><br />
<table frame="box">
<tr>
<td>Código do Documento: <b>{{hash}}</b></td>
<td>Tipo de Proposição: <b>{{proposicao.tipo.descricao}}</b></td>
</tr>
<tr>
<td>Autor: <b>{{proposicao.autor}}</b></td>
<td>Data de Envio: <b>{{proposicao.data_envio|date:"d/m/Y H:i:s"}}</b></td>
</tr>
<tr>
<td>Descrição: <b>{{proposicao.descricao}}</b></td>
</tr>
</table>
<br /><br />
<table>
<tr>
<td align="center">
Declaro que o conteúdo do texto impresso em anexo é idêntico ao conteúdo enviado eletronicamente por meio do sistema SAPL para esta proposição.
</td>
</tr>
<tr> <td align="center"><br /><br /><br /><b>________________________________________________________________</b></td> </tr>
<tr> <td align="center">{{proposicao.autor}}</td> </tr>
</table>
{% endblock detail_content %}

6
sapl/templates/materia/subnav_prop.html

@ -0,0 +1,6 @@
<ul class="nav nav-pills navbar-right">
<li class=""><a href="{% url 'sapl.materia:receber-proposicao' %}">Receber Proposição</a></li>
<li class=""><a href="{% url 'sapl.materia:proposicao-pendente' %}">Proposições Não Recebidas</a></li>
<li class=""><a href="{% url 'sapl.materia:proposicao-devolvida' %}">Proposições Não Incorporadas</a></li>
<li class=""><a href="{% url 'sapl.materia:proposicao-recebida' %}">Proposições Incorporadas</a></li>
</ul>

12
sapl/utils.py

@ -1,3 +1,4 @@
import hashlib
from datetime import date
from functools import wraps
@ -218,3 +219,14 @@ def intervalos_tem_intersecao(a_inicio, a_fim, b_inicio, b_fim):
maior_inicio = max(a_inicio, b_inicio)
menor_fim = min(a_fim, b_fim)
return maior_inicio <= menor_fim
def gerar_hash_arquivo(arquivo, pk, block_size=2**20):
md5 = hashlib.md5()
arq = open(arquivo, 'rb')
while True:
data = arq.read(block_size)
if not data:
break
md5.update(data)
return 'P' + md5.hexdigest() + '/' + pk

Loading…
Cancel
Save