Browse Source

Tramitacao customizada ordemdia expediente (#3584)

* Campo tramitação para expediente e ordem do dia das pautas de sessoes plenarias

* Migration para tramitacao customizada de ordem dia e expediente da pauta de sessao

* ajuste automático feito pela IDE para pep8

* Ajuste de usabilidade

- encaminha além da descrição do status, também a data de tramitação, unidade local e unidade de destino de cada tramitação
- recebe os dados acima na interface, monta options com data, e status.
- qualquer alteração no select, monta um alert, assim como para matéria selecionada, com os dados cima da tramitação

Co-authored-by: joao <joao@mezzoplanejamento.com.br>
Co-authored-by: LeandroJatai <leandroroberto.br@gmail.com>
pull/3455/merge
joaohortsenado 2 years ago
committed by GitHub
parent
commit
e1a3654d9e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 58
      sapl/sessao/forms.py
  2. 25
      sapl/sessao/migrations/0064_auto_20220713_2335.py
  3. 7
      sapl/sessao/models.py
  4. 4
      sapl/sessao/urls.py
  5. 78
      sapl/sessao/views.py
  6. 59
      sapl/templates/sessao/expedientemateria_form.html
  7. 4
      sapl/templates/sessao/layouts.yaml

58
sapl/sessao/forms.py

@ -1,7 +1,8 @@
from datetime import datetime
import re
from crispy_forms.layout import Button, Fieldset, HTML, Layout
from datetime import datetime
from django import forms
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist, ValidationError
@ -12,6 +13,7 @@ from django.forms.widgets import CheckboxSelectMultiple
from django.utils.translation import ugettext_lazy as _
import django_filters
import sapl.utils
from sapl.base.models import Autor, TipoAutor
from sapl.crispy_layout_mixin import (form_actions, to_row,
SaplFormHelper, SaplFormLayout)
@ -33,13 +35,16 @@ from .models import (Bancada, ExpedienteMateria,
ORDENACAO_RESUMO, PresencaOrdemDia,
RegistroLeitura, ResumoOrdenacao, RetiradaPauta,
SessaoPlenaria, SessaoPlenariaPresenca,
TipoResultadoVotacao, TipoRetiradaPauta)
TipoResultadoVotacao, TipoRetiradaPauta, Tramitacao)
MES_CHOICES = RANGE_MESES
DIA_CHOICES = RANGE_DIAS_MES
def tramitacao_select_validation():
return True
class SessaoPlenariaForm(FileFieldCheckMixin, ModelForm):
class Meta:
@ -109,7 +114,7 @@ class SessaoPlenariaForm(FileFieldCheckMixin, ModelForm):
if upload_pauta:
validar_arquivo(upload_pauta, "Pauta da Sessão")
if upload_ata:
validar_arquivo(upload_ata, "Ata da Sessão")
@ -117,12 +122,12 @@ class SessaoPlenariaForm(FileFieldCheckMixin, ModelForm):
validar_arquivo(upload_anexo, "Anexo da Sessão")
hora_inicio = self.cleaned_data['hora_inicio']
if not re.match(TIME_PATTERN, hora_inicio):
if not re.match(sapl.utils.TIME_PATTERN, hora_inicio):
raise ValidationError(f'Formato ou valores de horário de '
f'abertura errados: {hora_inicio}')
hora_fim = self.cleaned_data['hora_fim']
if hora_fim and not re.match(TIME_PATTERN, hora_fim):
if hora_fim and not re.match(sapl.utils.TIME_PATTERN, hora_fim):
raise ValidationError(f'Formato ou valores de horário de '
f'encerramento errados: {hora_fim}.')
@ -294,6 +299,12 @@ class BancadaForm(ModelForm):
return bancada
class DependentChoiceField(forms.ChoiceField):
def validate(self, value):
return True
class ExpedienteMateriaForm(ModelForm):
_model = ExpedienteMateria
@ -306,6 +317,10 @@ class ExpedienteMateriaForm(ModelForm):
empty_label='Selecione',
widget=forms.Select(attrs={'autocomplete': 'off'}))
tramitacao_select = DependentChoiceField(
label=_('Situação Atual'),
widget=forms.Select())
numero_materia = forms.CharField(
label='Número Matéria', required=True,
widget=forms.TextInput(attrs={'autocomplete': 'off'}))
@ -326,7 +341,7 @@ class ExpedienteMateriaForm(ModelForm):
class Meta:
model = ExpedienteMateria
fields = ['data_ordem', 'numero_ordem', 'tipo_materia', 'observacao',
'numero_materia', 'ano_materia', 'tipo_votacao']
'numero_materia', 'ano_materia', 'tramitacao_select', 'tipo_votacao']
def clean_numero_ordem(self):
sessao = self.instance.sessao_plenaria
@ -363,11 +378,28 @@ class ExpedienteMateriaForm(ModelForm):
else:
cleaned_data['materia'] = materia
try:
id_t = self.cleaned_data['tramitacao_select'] if self.cleaned_data['tramitacao_select'] != '' else -1
tramitacao = materia.tramitacao_set.get(pk=self.cleaned_data['tramitacao_select'] if self.cleaned_data['tramitacao_select'] != '' else -1)
except ObjectDoesNotExist:
if self.cleaned_data['tramitacao_select'] != '':
raise ValidationError(
_('Tramitação selecionada não existe para a Matéria: %(value)s'),
code='invalid',
params={'value': self.cleaned_data['tramitacao_select']},
)
else:
cleaned_data['tramitacao'] = False
else:
cleaned_data['tramitacao'] = tramitacao
return cleaned_data
def save(self, commit=False):
expediente = super(ExpedienteMateriaForm, self).save(commit)
expediente.materia = self.cleaned_data['materia']
if self.cleaned_data['tramitacao'] is not False:
expediente.tramitacao = self.cleaned_data['tramitacao']
expediente.save()
return expediente
@ -996,7 +1028,7 @@ class OrdemExpedienteLeituraForm(forms.ModelForm):
'ordem',
'expediente',
'observacao',
'user',
'user',
'ip']
widgets = {'materia': forms.HiddenInput(),
'ordem': forms.HiddenInput(),
@ -1008,14 +1040,14 @@ class OrdemExpedienteLeituraForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
instance = self.initial['instance']
if instance:
self.instance = instance.first()
self.fields['observacao'].initial = self.instance.observacao
row1 = to_row(
[('observacao', 12)])
[('observacao', 12)])
actions = [HTML('<a href="{{ view.cancel_url }}"'
' class="btn btn-warning">Cancelar Leitura</a>')]
@ -1024,11 +1056,11 @@ class OrdemExpedienteLeituraForm(forms.ModelForm):
self.helper.form_method = 'POST'
self.helper.layout = Layout(
Fieldset(_('Leitura de Matéria'),
HTML('''
HTML('''
<b>Matéria:</b> {{materia}}<br>
<b>Ementa:</b> {{materia.ementa}} <br>
'''),
row1,
form_actions(more=actions),
)
)
)
)

25
sapl/sessao/migrations/0064_auto_20220713_2335.py

@ -0,0 +1,25 @@
# Generated by Django 2.2.28 on 2022-07-14 02:35
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('materia', '0081_auto_20220321_0934'),
('sessao', '0063_merge_20220609_0838'),
]
operations = [
migrations.AddField(
model_name='expedientemateria',
name='tramitacao',
field=models.ForeignKey(blank=True, default='', null=True, on_delete=django.db.models.deletion.PROTECT, to='materia.Tramitacao', verbose_name='Situação Atual'),
),
migrations.AddField(
model_name='ordemdia',
name='tramitacao',
field=models.ForeignKey(blank=True, default='', null=True, on_delete=django.db.models.deletion.PROTECT, to='materia.Tramitacao', verbose_name='Situação Atual'),
),
]

7
sapl/sessao/models.py

@ -10,6 +10,7 @@ import reversion
from sapl.base.models import Autor
from sapl.materia.models import MateriaLegislativa
from sapl.materia.models import Tramitacao
from sapl.parlamentares.models import (CargoMesa, Legislatura, Parlamentar,
Partido, SessaoLegislativa)
from sapl.utils import (YES_NO_CHOICES, SaplGenericRelation,
@ -360,6 +361,12 @@ class AbstractOrdemDia(models.Model):
materia = models.ForeignKey(MateriaLegislativa,
on_delete=models.PROTECT,
verbose_name=_('Matéria'))
tramitacao = models.ForeignKey(Tramitacao,
on_delete=models.PROTECT,
verbose_name=_('Situação Atual'),
blank=True,
default='',
null=True)
data_ordem = models.DateField(verbose_name=_('Data da Sessão'))
observacao = models.TextField(
blank=True, verbose_name=_('Observação'))

4
sapl/sessao/urls.py

@ -36,7 +36,8 @@ from sapl.sessao.views import (AdicionarVariasMateriasExpediente,
OrdemDiaLeituraView,
retirar_leitura,
TransferenciaMateriasExpediente, TransferenciaMateriasOrdemDia,
filtra_materias_copia_sessao_ajax, verifica_materia_sessao_plenaria_ajax)
filtra_materias_copia_sessao_ajax, verifica_materia_sessao_plenaria_ajax,
recuperar_tramitacao)
from .apps import AppConfig
@ -68,6 +69,7 @@ urlpatterns = [
name='remove_parlamentar_composicao'),
url(r'^sessao/recuperar-materia/', recuperar_materia),
url(r'^sessao/recuperar-tramitacao/', recuperar_tramitacao),
url(r'^sessao/recuperar-numero-sessao/',
recuperar_numero_sessao_view,
name='recuperar_numero_sessao_view'

78
sapl/sessao/views.py

@ -1,12 +1,8 @@
from collections import OrderedDict
from datetime import datetime
from re import sub
import pytz
from sapl.settings import TIME_ZONE
import json
import logging
from re import sub
from django.conf import settings
from django.contrib import messages
@ -28,6 +24,7 @@ from django.views.generic.base import RedirectView
from django.views.generic.detail import DetailView
from django.views.generic.edit import FormMixin
from django_filters.views import FilterView
import pytz
from sapl.base.models import AppConfig as AppsAppConfig
from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux,
@ -41,6 +38,7 @@ from sapl.parlamentares.models import (Filiacao, Legislatura, Mandato,
Parlamentar, SessaoLegislativa)
from sapl.sessao.apps import AppConfig
from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm, OrdemExpedienteLeituraForm
from sapl.settings import TIME_ZONE
from sapl.utils import show_results_filter_set, remover_acentos, get_client_ip
from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm,
@ -754,7 +752,7 @@ class MateriaOrdemDiaCrud(MasterDetailCrud):
class BaseMixin(MasterDetailCrud.BaseMixin):
list_field_names = ['numero_ordem', 'materia',
('materia__ementa', '', 'observacao'),
('materia__ementa', '', 'tramitacao', 'observacao'),
'resultado']
class CreateView(MasterDetailCrud.CreateView):
@ -790,6 +788,7 @@ class MateriaOrdemDiaCrud(MasterDetailCrud):
context["tipo_materia_salvo"] = self.object.materia.tipo.id
context["numero_materia_salvo"] = self.object.materia.numero
context["ano_materia_salvo"] = self.object.materia.ano
context["tramitacao_salvo"] = None if not self.object.tramitacao else self.object.tramitacao.id
return context
@ -800,6 +799,7 @@ class MateriaOrdemDiaCrud(MasterDetailCrud):
initial['numero_materia'] = self.object.materia.numero
initial['ano_materia'] = self.object.materia.ano
initial['numero_ordem'] = self.object.numero_ordem
initial['tramitacao'] = None if not self.object.tramitacao else self.object.tramitacao.id
return initial
@ -840,6 +840,33 @@ def recuperar_materia(request):
return response
def recuperar_tramitacao(request):
tipo = request.GET['tipo_materia']
numero = request.GET['numero_materia']
ano = request.GET['ano_materia']
try:
materia = MateriaLegislativa.objects.get(tipo_id=tipo,
ano=ano,
numero=numero)
tramitacao = {}
for obj in materia.tramitacao_set.all():
tramitacao[obj.id] = {
'status': obj.status.descricao,
'texto': obj.texto,
'data_tramitacao': obj.data_tramitacao.strftime('%d/%m/%Y'),
'unidade_tramitacao_local': str(obj.unidade_tramitacao_local),
'unidade_tramitacao_destino': str(obj.unidade_tramitacao_destino)
}
response = JsonResponse(tramitacao)
except ObjectDoesNotExist:
response = JsonResponse({'id': 0})
return response
class ExpedienteMateriaCrud(MasterDetailCrud):
model = ExpedienteMateria
parent_field = 'sessao_plenaria'
@ -904,6 +931,7 @@ class ExpedienteMateriaCrud(MasterDetailCrud):
context["tipo_materia_salvo"] = self.object.materia.tipo.id
context["numero_materia_salvo"] = self.object.materia.numero
context["ano_materia_salvo"] = self.object.materia.ano
context["tramitacao_salvo"] = self.object.tramitacao.id if self.object.tramitacao is not None else ''
return context
@ -914,6 +942,7 @@ class ExpedienteMateriaCrud(MasterDetailCrud):
initial['numero_materia'] = self.object.materia.numero
initial['ano_materia'] = self.object.materia.ano
initial['numero_ordem'] = self.object.numero_ordem
initial['tramitacao'] = self.object.tramitacao.id if self.object.tramitacao is not None else ''
return initial
@ -2081,15 +2110,17 @@ def get_assinaturas(sessao_plenaria):
return context
def get_assinaturas_presidente(sessao_plenaria):
mesa_dia = get_mesa_diretora(sessao_plenaria)['mesa']
presidente_dia = [m['parlamentar'] for m in mesa_dia if m['cargo'].descricao == 'Presidente']
presidente_dia = presidente_dia[0] if presidente_dia else ''
presidente_dia = [m['parlamentar']
for m in mesa_dia if m['cargo'].descricao == 'Presidente']
presidente_dia = presidente_dia[0] if presidente_dia else ''
context = {}
assinatura_presidente = [
{'parlamentar': presidente_dia, 'cargo': "Presidente"}]
{'parlamentar': presidente_dia, 'cargo': "Presidente"}]
context.update({'assinatura_mesa': assinatura_presidente})
return context
@ -3703,7 +3734,8 @@ class PautaSessaoView(TemplateView):
template_name = "sessao/pauta_inexistente.html"
def get(self, request, *args, **kwargs):
sessao = SessaoPlenaria.objects.filter(publicar_pauta = True).order_by("-data_inicio").first()
sessao = SessaoPlenaria.objects.filter(
publicar_pauta=True).order_by("-data_inicio").first()
if not sessao:
return self.render_to_response({})
@ -3755,10 +3787,12 @@ class PautaSessaoDetailView(DetailView):
sessao_plenaria = SessaoPlenaria.objects.get(id=self.object.id)
data_sessao = sessao_plenaria.data_inicio.strftime("%Y-%m-%d ")
data_hora_sessao = datetime.strptime(data_sessao + sessao_plenaria.hora_inicio, "%Y-%m-%d %H:%M")
data_hora_sessao_utc = pytz.timezone(TIME_ZONE).localize(data_hora_sessao).astimezone(pytz.utc)
ultima_tramitacao = m.materia.tramitacao_set.filter(timestamp__lt = data_hora_sessao_utc).order_by(
'-data_tramitacao', '-id').first()
data_hora_sessao = datetime.strptime(
data_sessao + sessao_plenaria.hora_inicio, "%Y-%m-%d %H:%M")
data_hora_sessao_utc = pytz.timezone(TIME_ZONE).localize(
data_hora_sessao).astimezone(pytz.utc)
ultima_tramitacao = m.materia.tramitacao_set.filter(timestamp__lt=data_hora_sessao_utc).order_by(
'-data_tramitacao', '-id').first() if m.tramitacao is None else m.tramitacao
numeracao = m.materia.numeracao_set.first()
materias_expediente.append({
@ -3810,10 +3844,12 @@ class PautaSessaoDetailView(DetailView):
sessao_plenaria = SessaoPlenaria.objects.get(id=self.object.id)
data_sessao = sessao_plenaria.data_inicio.strftime("%Y-%m-%d ")
data_hora_sessao = datetime.strptime(data_sessao + sessao_plenaria.hora_inicio, "%Y-%m-%d %H:%M")
data_hora_sessao_utc = pytz.timezone(TIME_ZONE).localize(data_hora_sessao).astimezone(pytz.utc)
data_hora_sessao = datetime.strptime(
data_sessao + sessao_plenaria.hora_inicio, "%Y-%m-%d %H:%M")
data_hora_sessao_utc = pytz.timezone(TIME_ZONE).localize(
data_hora_sessao).astimezone(pytz.utc)
ultima_tramitacao = o.materia.tramitacao_set.filter(timestamp__lt=data_hora_sessao_utc).order_by(
'-data_tramitacao', '-id').first()
'-data_tramitacao', '-id').first() if o.tramitacao is None else o.tramitacao
numeracao = o.materia.numeracao_set.first()
materias_ordem.append({
@ -3919,7 +3955,7 @@ class PesquisarPautaSessaoView(PesquisarSessaoPlenariaView):
def get_filterset_kwargs(self, filterset_class):
kwargs = super().get_filterset_kwargs(filterset_class)
qs = kwargs.get('queryset')
qs = qs.filter(publicar_pauta = True)
qs = qs.filter(publicar_pauta=True)
kwargs['queryset'] = qs
return kwargs
@ -3948,14 +3984,14 @@ def verifica_materia_sessao_plenaria_ajax(request):
sessao_plenaria=pk_sessao_plenaria, materia=id_materia_selecionada
).exists()
is_materia_presente_any_sessao = ExpedienteMateria.objects.filter(
materia=id_materia_selecionada
materia=id_materia_selecionada
).exists()
elif tipo_materia_sessao == MATERIAS_ORDEMDIA:
is_materia_presente = OrdemDia.objects.filter(
sessao_plenaria=pk_sessao_plenaria, materia=id_materia_selecionada
).exists()
is_materia_presente_any_sessao = OrdemDia.objects.filter(
materia=id_materia_selecionada
materia=id_materia_selecionada
).exists()
return JsonResponse({'is_materia_presente': is_materia_presente, 'is_materia_presente_any_sessao': is_materia_presente_any_sessao})

59
sapl/templates/sessao/expedientemateria_form.html

@ -17,7 +17,7 @@
{ tipo_materia: tipo_materia, numero_materia: numero_materia, ano_materia: ano_materia },
function(data, status) {
if ($(".ementa-materia").length === 0){
$("#div_id_tipo_materia").closest('.row').after($('<div class="row"/>').append($('<div class="col-xs-12"/>').append(
$("#div_id_tipo_materia").closest('.row').after($('<div class="row"/>').append($('<div class="col-12"/>').append(
$('<div class="alert alert-info ementa-materia"/>').html(data.ementa)
)))
}
@ -54,11 +54,64 @@
}
}
function recuperar_tramitacao() {
let tipo_materia = $("#id_tipo_materia").val()
let numero_materia = $("#id_numero_materia").val()
let ano_materia = $("#id_ano_materia").val()
let tramitacao_salvo = "{{ tramitacao_salvo }}"
if (tipo_materia && numero_materia && ano_materia) {
$.get("/sessao/recuperar-tramitacao",
{ tipo_materia: tipo_materia, numero_materia: numero_materia, ano_materia: ano_materia },
function(data, status) {
if (status == 'success') {
$('#id_tramitacao_select').off('change')
$('#id_tramitacao_select').find('option').remove()
$('#id_tramitacao_select').append('<option value="-1">Selecione uma opção..</option>');
for (const property in data) {
console.log(tramitacao_salvo + "===" + property)
const option = $(`<option value="${property}">${data[property].data_tramitacao} - ${data[property].status}</option>`)
option[0].data = data[property]
$('#id_tramitacao_select').append(option)
if (property == tramitacao_salvo) {
$("#id_tramitacao_select option[value='"+ property +"']").attr("selected", "selected");
}
}
$('#id_tramitacao_select').on('change', function(event) {
const option = event.currentTarget.selectedOptions[0]
if (option.data === undefined)
return
const html_alert = `Data da Tramitação: ${option.data.data_tramitacao}<br>
De: <strong>${option.data.unidade_tramitacao_local}</strong> - Para: <strong>${option.data.unidade_tramitacao_destino}</strong><br>
Status Atual: ${option.data.status}<br>
Texto da Ação: ${option.data.texto}`
if ($(".tramitacao-materia").length === 0){
$("#id_tramitacao_select").closest('.row').after($('<div class="row"/>').append($('<div class="col-12"/>').append(
$('<div class="alert alert-info w-100 tramitacao-materia"/>').html(html_alert)
)))
}
else {
$('.tramitacao-materia').html(html_alert)
}
}).trigger('change')
}
});
}
}
var fields = ["#id_tipo_materia", "#id_numero_materia", "#id_ano_materia"];
for (i = 0; i < fields.length; i++){
$(fields[i]).change(recuperar_materia)
$(fields[i]).change(function() {
recuperar_materia();
recuperar_tramitacao();
});
}
recuperar_materia()
recuperar_materia();
recuperar_tramitacao();
var modal_estilos = 'display: block; width: 85%; max-width: 600px; background: #fff; padding: 15px; border-radius: 5px;'
+'-webkit-box-shadow: 0px 6px 14px -2px rgba(0, 0, 0, 0.75); -moz-box-shadow: 0px 6px 14px -2px rgba(0, 0, 0, 0.75);'

4
sapl/templates/sessao/layouts.yaml

@ -60,6 +60,7 @@ ExpedienteMateria:
- tipo_materia numero_materia ano_materia
- tipo_votacao
- apenas_leitura
- tramitacao_select
- observacao
OrdemDia:
@ -68,6 +69,7 @@ OrdemDia:
- tipo_materia numero_materia ano_materia
- tipo_votacao
- apenas_leitura
- tramitacao_select
- observacao
ExpedienteMateriaDetail:
@ -75,6 +77,7 @@ ExpedienteMateriaDetail:
- materia
- ementa
- tipo_votacao
- tramitacao
- observacao
OrdemDiaDetail:
@ -82,6 +85,7 @@ OrdemDiaDetail:
- materia
- ementa
- tipo_votacao
- tramitacao
- observacao
Bancada:

Loading…
Cancel
Save