diff --git a/sapl/materia/views.py b/sapl/materia/views.py index afde0ef64..4119107a5 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -1317,12 +1317,19 @@ class TramitacaoCrud(MasterDetailCrud): return HttpResponseRedirect(url) else: tramitacoes_deletar = [tramitacao.id] + if materia.tramitacao_set.count() == 0: + materia.em_tramitacao = False + materia.save() mat_anexadas = lista_anexados(materia) for ma in mat_anexadas: tram_anexada = ma.tramitacao_set.last() if compara_tramitacoes_mat(tram_anexada, tramitacao): 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() + return HttpResponseRedirect(url) class DetailView(MasterDetailCrud.DetailView): diff --git a/sapl/protocoloadm/forms.py b/sapl/protocoloadm/forms.py index a8630539c..b7c92bcc6 100644 --- a/sapl/protocoloadm/forms.py +++ b/sapl/protocoloadm/forms.py @@ -1407,3 +1407,252 @@ class FichaSelecionaAdmForm(forms.Form): 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 = ''' + +
+ Selecione os documentos para tramitação: + +
+
+ +
+
+ + + + + {% for documento in object_list %} + + + + {% endfor %} + +
Documento
+ + + {{documento.tipo.sigla}} {{documento.tipo.descricao}} {{documento.numero}}/{{documento.ano}} + +
+
+ ''' + + 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')))) \ No newline at end of file diff --git a/sapl/protocoloadm/tests/test_protocoloadm.py b/sapl/protocoloadm/tests/test_protocoloadm.py index ce322a7ba..ff7bf06d2 100644 --- a/sapl/protocoloadm/tests/test_protocoloadm.py +++ b/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.utils import timezone from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _ from model_mommy import mommy +from urllib.parse import urlencode import pytest from sapl.base.models import AppConfig @@ -15,7 +16,8 @@ from sapl.protocoloadm.forms import (AnularProtocoloAdmForm, MateriaLegislativa, ProtocoloDocumentForm, ProtocoloMateriaForm, TramitacaoAdmForm, TramitacaoAdmEditForm, - compara_tramitacoes_doc) + compara_tramitacoes_doc, + TramitacaoEmLoteAdmForm) from sapl.protocoloadm.models import (DocumentoAdministrativo, Protocolo, StatusTramitacaoAdministrativo, TipoDocumentoAdministrativo, @@ -703,3 +705,338 @@ def test_tramitacoes_documentos_anexados(admin_client): 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_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 diff --git a/sapl/protocoloadm/urls.py b/sapl/protocoloadm/urls.py index e5925204d..0fca1b76b 100644 --- a/sapl/protocoloadm/urls.py +++ b/sapl/protocoloadm/urls.py @@ -22,7 +22,9 @@ from sapl.protocoloadm.views import (AcompanhamentoDocumentoView, doc_texto_integral, DesvincularDocumentoView, DesvincularMateriaView, - AnexadoCrud, DocumentoAnexadoEmLoteView) + AnexadoCrud, DocumentoAnexadoEmLoteView, + PrimeiraTramitacaoEmLoteAdmView, + TramitacaoEmLoteAdmView) from .apps import AppConfig @@ -98,6 +100,12 @@ urlpatterns_protocolo = [ url(r'^protocoloadm/recuperar-materia', 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'), ] diff --git a/sapl/protocoloadm/views.py b/sapl/protocoloadm/views.py index eec04836b..6081be6ef 100755 --- a/sapl/protocoloadm/views.py +++ b/sapl/protocoloadm/views.py @@ -29,7 +29,7 @@ from sapl.base.signals import tramitacao_signal from sapl.comissoes.models import Comissao from sapl.crud.base import (Crud, CrudAux, MasterDetailCrud, make_pagination, 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.parlamentares.models import Legislatura, Parlamentar 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, filtra_tramitacao_adm_status, AnexadoForm, AnexadoEmLoteFilterSet, + PrimeiraTramitacaoEmLoteAdmFilterSet, + TramitacaoEmLoteAdmForm, + TramitacaoEmLoteAdmFilterSet, compara_tramitacoes_doc) from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo, DocumentoAdministrativo, StatusTramitacaoAdministrativo, @@ -1251,12 +1254,19 @@ class TramitacaoAdmCrud(MasterDetailCrud): return HttpResponseRedirect(url) else: tramitacoes_deletar = [tramitacao.id] + if documento.tramitacaoadministrativo_set.count() == 0: + documento.tramitacao = False + documento.save() docs_anexados = lista_anexados(documento, False) for da in docs_anexados: tram_anexada = da.tramitacaoadministrativo_set.last() if compara_tramitacoes_doc(tram_anexada, tramitacao): 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() + return HttpResponseRedirect(url) @@ -1437,3 +1447,142 @@ class FichaSelecionaAdmView(PermissionRequiredMixin, FormView): return gerar_pdf_impressos(self.request, context, '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) \ No newline at end of file diff --git a/sapl/templates/navbar.yaml b/sapl/templates/navbar.yaml index de556d882..e06638095 100644 --- a/sapl/templates/navbar.yaml +++ b/sapl/templates/navbar.yaml @@ -29,8 +29,13 @@ url: sapl.materia:receber-proposicao - title: {% trans 'Documentos Administrativos' %} - {% if 'documentos_administrativos'|get_config_attr == 'R' %}check_permission: protocoloadm.list_documentoadministrativo{%endif%} - url: sapl.protocoloadm:pesq_doc_adm + {% 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 + - title: {% trans 'Tramitação em Lote' %} + url: sapl.protocoloadm:primeira_tramitacao_em_lote_docadm + check_permission: sapl.documento:add_tramitacao - title: {% trans 'Atividade Legislativa' %} children: diff --git a/sapl/templates/protocoloadm/em_lote/subnav_em_lote.yaml b/sapl/templates/protocoloadm/em_lote/subnav_em_lote.yaml new file mode 100644 index 000000000..cf5beb12b --- /dev/null +++ b/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 diff --git a/sapl/templates/protocoloadm/em_lote/tramitacaoadm.html b/sapl/templates/protocoloadm/em_lote/tramitacaoadm.html new file mode 100644 index 000000000..94796621f --- /dev/null +++ b/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 %} +

{% trans 'Pesquisa concluída com sucesso! Foi encontrado 1 documento.'%}

+ {% else %} +

Foram encontrados {{object_list|length}} documentos.

+ {% endif %} + {% crispy form %} + {% else %} +

Nenhum documento encontrado.

+ {% endif %} + + {% endif%} +{% endblock detail_content %} + +{% block extra_js %} + +{% endblock %}