Browse Source

#2764 - Cria tramitação em Lote para Documento Administrativo (#2773)

* Fix #2764 - Tramitacao em lote para Doc Adm

* Adiciona validação do formulario

* Tramitacao em Lote

* Corrige permissões

* Adiciona testes para Tramtacao em lote Adm
pull/2811/head
Cesar Augusto de Carvalho 6 years ago
committed by Cesar Carvalho
parent
commit
f06f8add9f
  1. 7
      sapl/materia/views.py
  2. 249
      sapl/protocoloadm/forms.py
  3. 341
      sapl/protocoloadm/tests/test_protocoloadm.py
  4. 10
      sapl/protocoloadm/urls.py
  5. 151
      sapl/protocoloadm/views.py
  6. 5
      sapl/templates/navbar.yaml
  7. 5
      sapl/templates/protocoloadm/em_lote/subnav_em_lote.yaml
  8. 46
      sapl/templates/protocoloadm/em_lote/tramitacaoadm.html

7
sapl/materia/views.py

@ -1317,12 +1317,19 @@ class TramitacaoCrud(MasterDetailCrud):
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
else: else:
tramitacoes_deletar = [tramitacao.id] tramitacoes_deletar = [tramitacao.id]
if materia.tramitacao_set.count() == 0:
materia.em_tramitacao = False
materia.save()
mat_anexadas = lista_anexados(materia) mat_anexadas = lista_anexados(materia)
for ma in mat_anexadas: for ma in mat_anexadas:
tram_anexada = ma.tramitacao_set.last() tram_anexada = ma.tramitacao_set.last()
if compara_tramitacoes_mat(tram_anexada, tramitacao): if compara_tramitacoes_mat(tram_anexada, tramitacao):
tramitacoes_deletar.append(tram_anexada.id) tramitacoes_deletar.append(tram_anexada.id)
if ma.tramitacao_set.count() == 0:
ma.em_tramitacao = False
ma.save()
Tramitacao.objects.filter(id__in=tramitacoes_deletar).delete() Tramitacao.objects.filter(id__in=tramitacoes_deletar).delete()
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
class DetailView(MasterDetailCrud.DetailView): class DetailView(MasterDetailCrud.DetailView):

249
sapl/protocoloadm/forms.py

@ -1407,3 +1407,252 @@ class FichaSelecionaAdmForm(forms.Form):
form_actions(label='Gerar Impresso') form_actions(label='Gerar Impresso')
) )
) )
class PrimeiraTramitacaoEmLoteAdmFilterSet(django_filters.FilterSet):
class Meta(FilterOverridesMetaMixin):
model = DocumentoAdministrativo
fields = ['tipo', 'data']
def __init__(self, *args, **kwargs):
super(PrimeiraTramitacaoEmLoteAdmFilterSet, self).__init__(
*args, **kwargs)
self.filters['tipo'].label = 'Tipo de Documento'
self.filters['data'].label = 'Data (Inicial - Final)'
self.form.fields['tipo'].required = True
self.form.fields['data'].required = False
row1 = to_row([('tipo', 12)])
row2 = to_row([('data', 12)])
self.form.helper = SaplFormHelper()
self.form.helper.form_method = 'GET'
self.form.helper.layout = Layout(
Fieldset(_('Primeira Tramitação'),
row1, row2, form_actions(label='Pesquisar')))
class TramitacaoEmLoteAdmForm(ModelForm):
logger = logging.getLogger(__name__)
class Meta:
model = TramitacaoAdministrativo
fields = ['data_tramitacao',
'unidade_tramitacao_local',
'status',
'urgente',
'unidade_tramitacao_destino',
'data_encaminhamento',
'data_fim_prazo',
'texto',
'user',
'ip']
widgets = {'user': forms.HiddenInput(),
'ip': forms.HiddenInput()}
def __init__(self, *args, **kwargs):
super(TramitacaoEmLoteAdmForm, self).__init__(*args, **kwargs)
self.fields['data_tramitacao'].initial = timezone.now().date()
ust = UnidadeTramitacao.objects.select_related().all()
unidade_tramitacao_destino = [('', '---------')] + [(ut.pk, ut)
for ut in ust if ut.comissao and ut.comissao.ativa]
unidade_tramitacao_destino.extend(
[(ut.pk, ut) for ut in ust if ut.orgao])
unidade_tramitacao_destino.extend(
[(ut.pk, ut) for ut in ust if ut.parlamentar])
self.fields['unidade_tramitacao_destino'].choices = unidade_tramitacao_destino
self.fields['urgente'].label = "Urgente? *"
row1 = to_row([
('data_tramitacao', 4),
('data_encaminhamento', 4),
('data_fim_prazo', 4)
])
row2 = to_row([
('unidade_tramitacao_local', 6),
('unidade_tramitacao_destino', 6),
])
row3 = to_row([
('status', 6),
('urgente', 6)
])
row4 = to_row([
('texto', 12)
])
documentos_checkbox_HTML = '''
<br\><br\><br\>
<fieldset>
<legend style="font-size: 24px;">Selecione os documentos para tramitação:</legend>
<table class="table table-striped table-hover">
<div class="controls">
<div class="checkbox">
<label for="id_check_all">
<input type="checkbox" id="id_check_all" onchange="checkAll(this)" /> Marcar/Desmarcar Todos
</label>
</div>
</div>
<thead>
<tr><th>Documento</th></tr>
</thead>
<tbody>
{% for documento in object_list %}
<tr>
<td>
<input type="checkbox" name="documentos" value="{{documento.id}}" {% if check %} checked {% endif %}/>
<a href="{% url 'sapl.protocoloadm:documentoadministrativo_detail' documento.id %}">
{{documento.tipo.sigla}} {{documento.tipo.descricao}} {{documento.numero}}/{{documento.ano}}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
'''
self.helper = SaplFormHelper()
self.helper.layout = Layout(
Fieldset(
'Detalhes da tramitação:',
row1, row2, row3, row4,
HTML(documentos_checkbox_HTML),
form_actions(label='Salvar')
)
)
def clean(self):
cleaned_data = super(TramitacaoEmLoteAdmForm, self).clean()
if not self.is_valid():
return self.cleaned_data
if 'data_encaminhamento' in cleaned_data:
data_enc_form = cleaned_data['data_encaminhamento']
if 'data_fim_prazo' in cleaned_data:
data_prazo_form = cleaned_data['data_fim_prazo']
if 'data_tramitacao' in cleaned_data:
data_tram_form = cleaned_data['data_tramitacao']
if not self.instance.data_tramitacao:
if cleaned_data['data_tramitacao'] > timezone.now().date():
self.logger.error('A data de tramitação ({}) deve ser '
'menor ou igual a data de hoje ({})!'
.format(cleaned_data['data_tramitacao'], timezone.now().date()))
msg = _(
'A data de tramitação deve ser ' +
'menor ou igual a data de hoje!')
raise ValidationError(msg)
if data_enc_form:
if data_enc_form < data_tram_form:
self.logger.error('A data de encaminhamento ({}) deve ser '
'maior que a data de tramitação ({})!'
.format(data_enc_form, data_tram_form))
msg = _('A data de encaminhamento deve ser ' +
'maior que a data de tramitação!')
raise ValidationError(msg)
if data_prazo_form:
if data_prazo_form < data_tram_form:
self.logger.error('A data fim de prazo ({}) deve ser '
'maior que a data de tramitação ({})!'
.format(data_prazo_form, data_tram_form))
msg = _('A data fim de prazo deve ser ' +
'maior que a data de tramitação!')
raise ValidationError(msg)
if cleaned_data['unidade_tramitacao_local'] == cleaned_data['unidade_tramitacao_destino']:
msg = _('Unidade tramitação local deve ser diferente da unidade tramitação destino.')
self.logger.error('Unidade tramitação local ({}) deve ser diferente da unidade tramitação destino'
.format(cleaned_data['unidade_tramitacao_local']))
raise ValidationError(msg)
return cleaned_data
@transaction.atomic
def save(self, commit=True):
cd = self.cleaned_data
documentos = self.initial['documentos']
user = self.initial['user'] if 'user' in self.initial else None
ip = self.initial['ip'] if 'ip' in self.initial else ''
for doc_id in documentos:
doc = DocumentoAdministrativo.objects.get(id=doc_id)
tramitacao = TramitacaoAdministrativo.objects.create(
status=cd['status'],
documento=doc,
data_tramitacao=cd['data_tramitacao'],
unidade_tramitacao_local=cd['unidade_tramitacao_local'],
unidade_tramitacao_destino=cd['unidade_tramitacao_destino'],
data_encaminhamento=cd['data_encaminhamento'],
urgente=cd['urgente'],
texto=cd['texto'],
data_fim_prazo=cd['data_fim_prazo'],
user=user,
ip=ip
)
doc.tramitacao = False if tramitacao.status.indicador == "F" else True
doc.save()
lista_tramitacao = []
anexados = lista_anexados(doc, False)
for da in anexados:
if not da.tramitacaoadministrativo_set.all() \
or da.tramitacaoadministrativo_set.last() \
.unidade_tramitacao_destino == tramitacao.unidade_tramitacao_local:
da.tramitacao = False if tramitacao.status.indicador == "F" else True
da.save()
lista_tramitacao.append(TramitacaoAdministrativo(
status=tramitacao.status,
documento=da,
data_tramitacao=tramitacao.data_tramitacao,
unidade_tramitacao_local=tramitacao.unidade_tramitacao_local,
data_encaminhamento=tramitacao.data_encaminhamento,
unidade_tramitacao_destino=tramitacao.unidade_tramitacao_destino,
urgente=tramitacao.urgente,
texto=tramitacao.texto,
data_fim_prazo=tramitacao.data_fim_prazo,
user=tramitacao.user,
ip=tramitacao.ip
))
TramitacaoAdministrativo.objects.bulk_create(lista_tramitacao)
return tramitacao
class TramitacaoEmLoteAdmFilterSet(django_filters.FilterSet):
class Meta(FilterOverridesMetaMixin):
model = DocumentoAdministrativo
fields = ['tipo', 'data', 'tramitacaoadministrativo__status',
'tramitacaoadministrativo__unidade_tramitacao_destino']
def __init__(self, *args, **kwargs):
super(TramitacaoEmLoteAdmFilterSet, self).__init__(
*args, **kwargs)
self.filters['tipo'].label = _('Tipo de Documento')
self.filters['data'].label = _('Data (Inicial - Final)')
self.filters['tramitacaoadministrativo__unidade_tramitacao_destino'
].label = _('Unidade Destino (Último Destino)')
self.filters['tramitacaoadministrativo__status'].label = _('Status')
self.form.fields['tipo'].required = True
self.form.fields['data'].required = False
self.form.fields['tramitacaoadministrativo__status'].required = True
self.form.fields[
'tramitacaoadministrativo__unidade_tramitacao_destino'].required = True
row1 = to_row([
('tipo', 4),
('tramitacaoadministrativo__unidade_tramitacao_destino', 4),
('tramitacaoadministrativo__status', 4)])
row2 = to_row([('data', 12)])
self.form.helper = SaplFormHelper()
self.form.helper.form_method = 'GET'
self.form.helper.layout = Layout(
Fieldset(_('Tramitação em Lote'),
row1, row2, form_actions(label=_('Pesquisar'))))

341
sapl/protocoloadm/tests/test_protocoloadm.py

@ -1,10 +1,11 @@
from datetime import date, timedelta, datetime from datetime import date, timedelta
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_mommy import mommy from model_mommy import mommy
from urllib.parse import urlencode
import pytest import pytest
from sapl.base.models import AppConfig from sapl.base.models import AppConfig
@ -15,7 +16,8 @@ from sapl.protocoloadm.forms import (AnularProtocoloAdmForm,
MateriaLegislativa, ProtocoloDocumentForm, MateriaLegislativa, ProtocoloDocumentForm,
ProtocoloMateriaForm, TramitacaoAdmForm, ProtocoloMateriaForm, TramitacaoAdmForm,
TramitacaoAdmEditForm, TramitacaoAdmEditForm,
compara_tramitacoes_doc) compara_tramitacoes_doc,
TramitacaoEmLoteAdmForm)
from sapl.protocoloadm.models import (DocumentoAdministrativo, Protocolo, from sapl.protocoloadm.models import (DocumentoAdministrativo, Protocolo,
StatusTramitacaoAdministrativo, StatusTramitacaoAdministrativo,
TipoDocumentoAdministrativo, TipoDocumentoAdministrativo,
@ -703,3 +705,338 @@ def test_tramitacoes_documentos_anexados(admin_client):
response = admin_client.post(url, {'confirmar':'confirmar'} ,follow=True) response = admin_client.post(url, {'confirmar':'confirmar'} ,follow=True)
assert TramitacaoAdministrativo.objects.filter(id=tramitacao_anexada.pk).count() == 0 assert TramitacaoAdministrativo.objects.filter(id=tramitacao_anexada.pk).count() == 0
assert TramitacaoAdministrativo.objects.filter(id=tramitacao_anexada_anexada.pk).count() == 0 assert TramitacaoAdministrativo.objects.filter(id=tramitacao_anexada_anexada.pk).count() == 0
@pytest.mark.django_db(transaction=False)
def test_tramitacao_lote_documentos_form(admin_client):
tipo_documento = mommy.make(
TipoDocumentoAdministrativo,
descricao="Tipo_Teste"
)
documento = mommy.make(
DocumentoAdministrativo,
numero=20,
ano=2018,
data="2019-05-16",
tipo=tipo_documento
)
unidade_tramitacao_local_1 = make_unidade_tramitacao(descricao="Teste 1")
unidade_tramitacao_destino_1 = make_unidade_tramitacao(descricao="Teste 2")
status = mommy.make(
StatusTramitacaoAdministrativo,
indicador='R')
# Form sem campos obrigatórios
documentos = []
form = TramitacaoEmLoteAdmForm(initial={'documentos': documentos}, data={})
errors = form.errors
assert errors['data_tramitacao'] == ['Este campo é obrigatório.']
assert errors['unidade_tramitacao_local'] == ['Este campo é obrigatório.']
assert errors['status'] == ['Este campo é obrigatório.']
assert errors['unidade_tramitacao_destino'] == ['Este campo é obrigatório.']
assert errors['texto'] == ['Este campo é obrigatório.']
assert not form.is_valid()
# Tramitar apenas um documento sem anexados
documentos = [documento.id]
now = timezone.now().date()
form = TramitacaoEmLoteAdmForm(initial={'documentos': documentos}, data={})
form.data = {'data_tramitacao': now + timedelta(days=5),
'unidade_tramitacao_local': unidade_tramitacao_local_1.id,
'unidade_tramitacao_destino': unidade_tramitacao_destino_1.id,
'status': status.id,
'urgente': False,
'texto': 'aaaa'}
assert form.errors['__all__'] == \
["A data de tramitação deve ser menor ou igual a data de hoje!"]
assert not form.is_valid()
form = TramitacaoEmLoteAdmForm(initial={'documentos': documentos}, data={})
form.data = {'data_tramitacao': '2019-05-14',
'data_encaminhamento' : '2019-05-09',
'unidade_tramitacao_local': unidade_tramitacao_local_1.id,
'unidade_tramitacao_destino': unidade_tramitacao_destino_1.id,
'status': status.id,
'urgente': False,
'texto': 'aaaa'}
assert form.errors['__all__'] == \
["A data de encaminhamento deve ser maior que a data de tramitação!"]
assert not form.is_valid()
form = TramitacaoEmLoteAdmForm(initial={'documentos': documentos}, data={})
form.data = {'data_tramitacao': '2019-05-14',
'data_encaminhamento' : '2019-05-15',
'data_fim_prazo': '2019-05-09',
'unidade_tramitacao_local': unidade_tramitacao_local_1.id,
'unidade_tramitacao_destino': unidade_tramitacao_destino_1.id,
'status': status.id,
'urgente': False,
'texto': 'aaaa'}
assert form.errors['__all__'] == \
["A data fim de prazo deve ser maior que a data de tramitação!"]
assert not form.is_valid()
form = TramitacaoEmLoteAdmForm(initial={'documentos': documentos}, data={})
form.data = {'data_tramitacao': '2019-05-14',
'data_encaminhamento' : '2019-05-15',
'data_fim_prazo': '2019-05-18',
'unidade_tramitacao_local': unidade_tramitacao_local_1.id,
'unidade_tramitacao_destino': unidade_tramitacao_local_1.id,
'status': status.id,
'urgente': False,
'texto': 'aaaa'}
assert form.errors['__all__'] == \
["Unidade tramitação local deve ser diferente da unidade tramitação destino."]
assert not form.is_valid()
form = TramitacaoEmLoteAdmForm(initial={'documentos': documentos}, data={})
form.data = {'data_tramitacao': '2019-05-14',
'data_encaminhamento' : '2019-05-15',
'data_fim_prazo': '2019-05-18',
'unidade_tramitacao_local': unidade_tramitacao_local_1.id,
'unidade_tramitacao_destino': unidade_tramitacao_destino_1.id,
'status': status.id,
'urgente': False,
'texto': 'aaaa'}
assert form.is_valid()
@pytest.mark.django_db(transaction=False)
def test_tramitacao_lote_documentos_views(admin_client):
tipo_documento = mommy.make(
TipoDocumentoAdministrativo,
descricao="Tipo_Teste"
)
documento_principal = mommy.make(
DocumentoAdministrativo,
numero=20,
ano=2018,
data="2018-01-04",
tipo=tipo_documento
)
documento_anexado = mommy.make(
DocumentoAdministrativo,
numero=21,
ano=2019,
data="2019-05-04",
tipo=tipo_documento
)
documento_anexado_anexado = mommy.make(
DocumentoAdministrativo,
numero=22,
ano=2020,
data="2020-01-05",
tipo=tipo_documento
)
documento_sem_anexados = mommy.make(
DocumentoAdministrativo,
numero=23,
ano=2020,
data="2021-01-05",
tipo=tipo_documento
)
mommy.make(
Anexado,
documento_principal=documento_principal,
documento_anexado=documento_anexado,
data_anexacao="2019-05-11"
)
mommy.make(
Anexado,
documento_principal=documento_anexado,
documento_anexado=documento_anexado_anexado,
data_anexacao="2020-11-05"
)
unidade_tramitacao_local_1 = make_unidade_tramitacao(descricao="Teste 1")
unidade_tramitacao_destino_1 = make_unidade_tramitacao(descricao="Teste 2")
unidade_tramitacao_destino_2 = make_unidade_tramitacao(descricao="Teste 3")
unidade_tramitacao_destino_3 = make_unidade_tramitacao(descricao="Teste 4")
status = mommy.make(
StatusTramitacaoAdministrativo,
indicador='R')
url = reverse('sapl.protocoloadm:primeira_tramitacao_em_lote_docadm')
url = url + '?' + urlencode({'tipo':tipo_documento.id, 'data_0':'', 'data_1':''})
response = admin_client.post(url, {'salvar':'salvar'}, follow=True)
assert response.status_code == 200
msgs = [m.message for m in response.context['messages']]
assert len(msgs) == 1
assert msgs[0] == 'Escolha algum Documento para ser tramitado.'
documentos = [documento_sem_anexados.id, documento_anexado_anexado.id]
response = admin_client.post(url, {'documentos': documentos,'salvar':'salvar'}, follow=True)
msgs = [m.message for m in response.context['messages']]
assert 'Data Tramitação: Este campo é obrigatório.' in msgs
assert 'Unidade Local: Este campo é obrigatório.' in msgs
assert 'Status: Este campo é obrigatório.' in msgs
assert 'Unidade Destino: Este campo é obrigatório.' in msgs
assert 'Texto da Ação: Este campo é obrigatório.' in msgs
# Primeira tramitação em lote
response = admin_client.post(url,
{'documentos': documentos,
'data_tramitacao': date(2019, 5, 15),
'unidade_tramitacao_local': unidade_tramitacao_local_1.id,
'unidade_tramitacao_destino': unidade_tramitacao_destino_1.id,
'status': status.id,
'urgente': False,
'texto': 'aaaa',
'salvar':'salvar'},
follow=True)
assert response.status_code == 200
assert TramitacaoAdministrativo.objects.all().count() == 2
assert documento_sem_anexados.tramitacaoadministrativo_set.all().count() == 1
assert documento_anexado_anexado.tramitacaoadministrativo_set.all().count() == 1
# Segunda tramitação em lote
url_lote = reverse('sapl.protocoloadm:tramitacao_em_lote_docadm')
url_lote = url_lote + '?' + urlencode(
{'tipo':tipo_documento.id,
'tramitacaoadministrativo__unidade_tramitacao_destino':unidade_tramitacao_destino_1.id,
'tramitacaoadministrativo__status': status.id,
'data_0':'',
'data_1':''})
response = admin_client.post(url_lote, {'salvar':'salvar'}, follow=True)
assert response.status_code == 200
assert response.context_data['object_list'].count() == 2
msgs = [m.message for m in response.context['messages']]
assert len(msgs) == 1
assert msgs[0] == 'Escolha algum Documento para ser tramitado.'
response = admin_client.post(url_lote, {'documentos':documentos, 'salvar':'salvar'}, follow=True)
assert response.status_code == 200
msgs = [m.message for m in response.context['messages']]
assert 'Data Tramitação: Este campo é obrigatório.' in msgs
assert 'Unidade Local: Este campo é obrigatório.' in msgs
assert 'Status: Este campo é obrigatório.' in msgs
assert 'Unidade Destino: Este campo é obrigatório.' in msgs
assert 'Texto da Ação: Este campo é obrigatório.' in msgs
response = admin_client.post(url_lote,
{'documentos': documentos,
'data_tramitacao': date(2019, 5, 15),
'unidade_tramitacao_local': unidade_tramitacao_destino_1.id,
'unidade_tramitacao_destino': unidade_tramitacao_destino_1.id,
'status': status.id,
'urgente': False,
'texto': 'aaaa',
'salvar':'salvar'},
follow=True)
assert response.status_code == 200
msgs = [m.message for m in response.context['messages']]
assert 'Unidade tramitação local deve ser diferente da unidade tramitação destino.' in msgs
response = admin_client.post(url_lote,
{'documentos': documentos,
'data_tramitacao': date(2019, 5, 15),
'unidade_tramitacao_local': unidade_tramitacao_destino_1.id,
'unidade_tramitacao_destino': unidade_tramitacao_destino_2.id,
'status': status.id,
'urgente': False,
'texto': 'aaaa',
'salvar':'salvar'},
follow=True)
assert response.status_code == 200
msgs = [m.message for m in response.context['messages']]
assert 'Tramitação completa.' in msgs
assert TramitacaoAdministrativo.objects.all().count() == 4
assert documento_sem_anexados.tramitacaoadministrativo_set.all().count() == 2
assert documento_anexado_anexado.tramitacaoadministrativo_set.all().count() == 2
# Tramitar documentos com anexados
# O documento anexado ao anexado não deve tramitar junto porque já está com tramitação diferente
documentos = [documento_principal.id]
response = admin_client.post(url,
{'documentos': documentos,
'data_tramitacao': date(2019, 5, 15),
'unidade_tramitacao_local': unidade_tramitacao_local_1.id,
'unidade_tramitacao_destino': unidade_tramitacao_destino_1.id,
'status': status.id,
'urgente': False,
'texto': 'aaaa',
'salvar':'salvar'},
follow=True)
assert response.status_code == 200
assert TramitacaoAdministrativo.objects.all().count() == 6
assert documento_principal.tramitacaoadministrativo_set.all().count() == 1
assert documento_anexado.tramitacaoadministrativo_set.all().count() == 1
# Segunda tramitação com documentos anexados
response = admin_client.post(url_lote,
{'documentos': documentos,
'data_tramitacao': date(2019, 5, 15),
'unidade_tramitacao_local': unidade_tramitacao_destino_1.id,
'unidade_tramitacao_destino': unidade_tramitacao_destino_2.id,
'status': status.id,
'urgente': False,
'texto': 'aaaa',
'salvar':'salvar'},
follow=True)
assert response.status_code == 200
msgs = [m.message for m in response.context['messages']]
assert 'Tramitação completa.' in msgs
assert TramitacaoAdministrativo.objects.all().count() == 8
assert documento_principal.tramitacaoadministrativo_set.all().count() == 2
assert documento_anexado.tramitacaoadministrativo_set.all().count() == 2
# Terceira tramitação em lote
# Agora, o documento anexado ao anexado deve tramitar junto com o documento principal,
# pois suas tramitações convergiram
response = admin_client.post(url_lote,
{'documentos': documentos,
'data_tramitacao': date(2019, 5, 15),
'unidade_tramitacao_local': unidade_tramitacao_destino_2.id,
'unidade_tramitacao_destino': unidade_tramitacao_destino_3.id,
'status': status.id,
'urgente': False,
'texto': 'aaaa',
'salvar':'salvar'},
follow=True)
assert response.status_code == 200
msgs = [m.message for m in response.context['messages']]
assert 'Tramitação completa.' in msgs
assert TramitacaoAdministrativo.objects.all().count() == 11
assert documento_principal.tramitacaoadministrativo_set.all().count() == 3
assert documento_anexado.tramitacaoadministrativo_set.all().count() == 3
assert documento_anexado_anexado.tramitacaoadministrativo_set.all().count() == 3

10
sapl/protocoloadm/urls.py

@ -22,7 +22,9 @@ from sapl.protocoloadm.views import (AcompanhamentoDocumentoView,
doc_texto_integral, doc_texto_integral,
DesvincularDocumentoView, DesvincularDocumentoView,
DesvincularMateriaView, DesvincularMateriaView,
AnexadoCrud, DocumentoAnexadoEmLoteView) AnexadoCrud, DocumentoAnexadoEmLoteView,
PrimeiraTramitacaoEmLoteAdmView,
TramitacaoEmLoteAdmView)
from .apps import AppConfig from .apps import AppConfig
@ -98,6 +100,12 @@ urlpatterns_protocolo = [
url(r'^protocoloadm/recuperar-materia', url(r'^protocoloadm/recuperar-materia',
recuperar_materia_protocolo, name='recuperar_materia_protocolo'), recuperar_materia_protocolo, name='recuperar_materia_protocolo'),
url(r'^protocoloadm/primeira-tramitacao-em-lote',
PrimeiraTramitacaoEmLoteAdmView.as_view(),
name='primeira_tramitacao_em_lote_docadm'),
url(r'^protocoloadm/tramitacao-em-lote', TramitacaoEmLoteAdmView.as_view(),
name='tramitacao_em_lote_docadm'),
] ]

151
sapl/protocoloadm/views.py

@ -29,7 +29,7 @@ from sapl.base.signals import tramitacao_signal
from sapl.comissoes.models import Comissao from sapl.comissoes.models import Comissao
from sapl.crud.base import (Crud, CrudAux, MasterDetailCrud, make_pagination, from sapl.crud.base import (Crud, CrudAux, MasterDetailCrud, make_pagination,
RP_LIST, RP_DETAIL) RP_LIST, RP_DETAIL)
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa, UnidadeTramitacao
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
@ -47,6 +47,9 @@ from .forms import (AcompanhamentoDocumentoForm, AnularProtocoloAdmForm,
filtra_tramitacao_adm_destino_and_status, filtra_tramitacao_adm_destino_and_status,
filtra_tramitacao_adm_destino, filtra_tramitacao_adm_status, filtra_tramitacao_adm_destino, filtra_tramitacao_adm_status,
AnexadoForm, AnexadoEmLoteFilterSet, AnexadoForm, AnexadoEmLoteFilterSet,
PrimeiraTramitacaoEmLoteAdmFilterSet,
TramitacaoEmLoteAdmForm,
TramitacaoEmLoteAdmFilterSet,
compara_tramitacoes_doc) compara_tramitacoes_doc)
from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo, from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo,
DocumentoAdministrativo, StatusTramitacaoAdministrativo, DocumentoAdministrativo, StatusTramitacaoAdministrativo,
@ -1251,12 +1254,19 @@ class TramitacaoAdmCrud(MasterDetailCrud):
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
else: else:
tramitacoes_deletar = [tramitacao.id] tramitacoes_deletar = [tramitacao.id]
if documento.tramitacaoadministrativo_set.count() == 0:
documento.tramitacao = False
documento.save()
docs_anexados = lista_anexados(documento, False) docs_anexados = lista_anexados(documento, False)
for da in docs_anexados: for da in docs_anexados:
tram_anexada = da.tramitacaoadministrativo_set.last() tram_anexada = da.tramitacaoadministrativo_set.last()
if compara_tramitacoes_doc(tram_anexada, tramitacao): if compara_tramitacoes_doc(tram_anexada, tramitacao):
tramitacoes_deletar.append(tram_anexada.id) tramitacoes_deletar.append(tram_anexada.id)
if da.tramitacaoadministrativo_set.count() == 0:
da.tramitacao = False
da.save()
TramitacaoAdministrativo.objects.filter(id__in=tramitacoes_deletar).delete() TramitacaoAdministrativo.objects.filter(id__in=tramitacoes_deletar).delete()
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@ -1437,3 +1447,142 @@ class FichaSelecionaAdmView(PermissionRequiredMixin, FormView):
return gerar_pdf_impressos(self.request, context, return gerar_pdf_impressos(self.request, context,
'materia/impressos/ficha_adm_pdf.html') 'materia/impressos/ficha_adm_pdf.html')
class PrimeiraTramitacaoEmLoteAdmView(PermissionRequiredMixin, FilterView):
filterset_class = PrimeiraTramitacaoEmLoteAdmFilterSet
template_name = 'protocoloadm/em_lote/tramitacaoadm.html'
permission_required = ('materia.add_tramitacao', )
primeira_tramitacao = True
logger = logging.getLogger(__name__)
def get_context_data(self, **kwargs):
context = super(PrimeiraTramitacaoEmLoteAdmView,
self).get_context_data(**kwargs)
context['subnav_template_name'] = 'protocoloadm/em_lote/subnav_em_lote.yaml'
context['primeira_tramitacao'] = self.primeira_tramitacao
# Verifica se os campos foram preenchidos
if not self.filterset.form.is_valid():
return context
context['object_list'] = context['object_list'].order_by(
'ano', 'numero')
qr = self.request.GET.copy()
form = TramitacaoEmLoteAdmForm()
context['form'] = form
if self.primeira_tramitacao:
context['title'] = _('Primeira Tramitação em Lote')
# Pega somente documentos que não possuem tramitação
context['object_list'] = [obj for obj in context['object_list']
if obj.tramitacaoadministrativo_set.all().count() == 0]
else:
context['title'] = _('Tramitação em Lote')
context['form'].fields['unidade_tramitacao_local'].initial = UnidadeTramitacao.objects.get(
id=qr['tramitacaoadministrativo__unidade_tramitacao_destino'])
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
context['show_results'] = show_results_filter_set(qr)
return context
def post(self, request, *args, **kwargs):
user = request.user
ip = get_client_ip(request)
documentos_ids = request.POST.getlist('documentos')
if not documentos_ids:
msg = _("Escolha algum Documento para ser tramitado.")
messages.add_message(request, messages.ERROR, msg)
return self.get(request, self.kwargs)
form = TramitacaoEmLoteAdmForm(request.POST, initial=
{'documentos': documentos_ids,
'user': user, 'ip':ip})
if form.is_valid():
# cd = form.clean()
form.save()
msg = _('Tramitação completa.')
self.logger.info('user=' + user.username + '. Tramitação completa.')
messages.add_message(request, messages.SUCCESS, msg)
return self.get_success_url()
return self.form_invalid(form)
def get_success_url(self):
return HttpResponseRedirect(reverse('sapl.protocoloadm:primeira_tramitacao_em_lote_docadm'))
def form_invalid(self, form, *args, **kwargs):
for key, erros in form.errors.items():
if not key=='__all__':
[messages.add_message(self.request, messages.ERROR, form.fields[key].label + ": " + e) for e in erros]
else:
[messages.add_message(self.request, messages.ERROR, e) for e in erros]
return self.get(self.request, kwargs, {'form':form})
class TramitacaoEmLoteAdmView(PrimeiraTramitacaoEmLoteAdmView):
filterset_class = TramitacaoEmLoteAdmFilterSet
primeira_tramitacao = False
def get_context_data(self, **kwargs):
context = super(TramitacaoEmLoteAdmView,
self).get_context_data(**kwargs)
qr = self.request.GET.copy()
context['primeira_tramitacao'] = False
if ('tramitacao__status' in qr and
'tramitacao__unidade_tramitacao_destino' in qr and
qr['tramitacao__status'] and
qr['tramitacao__unidade_tramitacao_destino']):
lista = self.filtra_tramitacao_destino_and_status(
qr['tramitacao__status'],
qr['tramitacao__unidade_tramitacao_destino'])
context['object_list'] = context['object_list'].filter(
id__in=lista).distinct()
return context
def pega_ultima_tramitacao(self):
return TramitacaoAdministrativo.objects.values(
'documento_id').annotate(data_encaminhamento=Max(
'data_encaminhamento'),
id=Max('id')).values_list('id', flat=True)
def filtra_tramitacao_status(self, status):
lista = self.pega_ultima_tramitacao()
return TramitacaoAdministrativo.objects.filter(
id__in=lista,
status=status).distinct().values_list('documento_id', flat=True)
def filtra_tramitacao_destino(self, destino):
lista = self.pega_ultima_tramitacao()
return TramitacaoAdministrativo.objects.filter(
id__in=lista,
unidade_tramitacao_destino=destino).distinct().values_list(
'documento_id', flat=True)
def filtra_tramitacao_destino_and_status(self, status, destino):
lista = self.pega_ultima_tramitacao()
return TramitacaoAdministrativo.objects.filter(
id__in=lista,
status=status,
unidade_tramitacao_destino=destino).distinct().values_list(
'documento_id', flat=True)

5
sapl/templates/navbar.yaml

@ -30,7 +30,12 @@
- title: {% trans 'Documentos Administrativos' %} - title: {% trans 'Documentos Administrativos' %}
{% if 'documentos_administrativos'|get_config_attr == 'R' %}check_permission: protocoloadm.list_documentoadministrativo {% endif %} {% if 'documentos_administrativos'|get_config_attr == 'R' %}check_permission: protocoloadm.list_documentoadministrativo {% endif %}
children:
- title: {% trans 'Pesquisar Documentos Administrativos' %}
url: sapl.protocoloadm:pesq_doc_adm url: sapl.protocoloadm:pesq_doc_adm
- title: {% trans 'Tramitação em Lote' %}
url: sapl.protocoloadm:primeira_tramitacao_em_lote_docadm
check_permission: sapl.documento:add_tramitacao
- title: {% trans 'Atividade Legislativa' %} - title: {% trans 'Atividade Legislativa' %}
children: children:

5
sapl/templates/protocoloadm/em_lote/subnav_em_lote.yaml

@ -0,0 +1,5 @@
{% load i18n common_tags %}
- title: {% trans 'Primeira Tramitação' %}
url: primeira_tramitacao_em_lote_docadm
- title: {% trans 'Tramitação em Lote' %}
url: tramitacao_em_lote_docadm

46
sapl/templates/protocoloadm/em_lote/tramitacaoadm.html

@ -0,0 +1,46 @@
{% extends "crud/detail.html" %}
{% load i18n crispy_forms_tags %}
{% block detail_content %}
{% if not show_results %}
{% crispy filter.form %}
{% else %}
{% if object_list|length > 0 %}
{% if object_list|length == 1 %}
<h3 style="text-align: right;">{% trans 'Pesquisa concluída com sucesso! Foi encontrado 1 documento.'%}</h3>
{% else %}
<h3 style="text-align: right;">Foram encontrados {{object_list|length}} documentos.</h3>
{% endif %}
{% crispy form %}
{% else %}
<tr><td><h3 style="text-align: right;">Nenhum documento encontrado.</h3></td></tr>
{% endif %}
{% endif%}
{% endblock detail_content %}
{% block extra_js %}
<script language="JavaScript">
function checkAll(elem) {
let checkboxes = document.getElementsByName('documentos');
for (let i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].type == 'checkbox')
checkboxes[i].checked = elem.checked;
}
}
$(document).ready(function(){
var primeira_tramitacao = {{primeira_tramitacao|yesno:"true,false"}}
if (primeira_tramitacao == false){
$('#id_unidade_tramitacao_local').prop('disabled', true);
}
// Reabilita o campo, no momento do Submit, para que seu dado seja enviado
$('input[type=submit]').click(function() {
$('#id_unidade_tramitacao_local').attr('disabled', false);
$('#id_unidade_tramitacao_local').parents('form').submit();
});
});
</script>
{% endblock %}
Loading…
Cancel
Save