diff --git a/sapl/materia/views.py b/sapl/materia/views.py index 059ec38b2..7659e684b 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..be2349e07 100644 --- a/sapl/protocoloadm/forms.py +++ b/sapl/protocoloadm/forms.py @@ -1407,3 +1407,214 @@ 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 PrimeiraTramitacaoEmLoteAdmForm(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(PrimeiraTramitacaoEmLoteAdmForm, 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(PrimeiraTramitacaoEmLoteAdmForm, 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.is_valid(): + return cleaned_data + + if not self.instance.data_tramitacao: + + if self.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(self.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) + + return self.cleaned_data + + @transaction.atomic + def save(self, commit=True): + cd = self.cleaned_data + documentos = self.initial['documentos'] + user = self.initial['user'] + ip = self.initial['ip'] + 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 = [] + for da in doc.anexados.all(): + 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 \ No newline at end of file diff --git a/sapl/protocoloadm/urls.py b/sapl/protocoloadm/urls.py index e5925204d..4fa10976f 100644 --- a/sapl/protocoloadm/urls.py +++ b/sapl/protocoloadm/urls.py @@ -22,7 +22,8 @@ from sapl.protocoloadm.views import (AcompanhamentoDocumentoView, doc_texto_integral, DesvincularDocumentoView, DesvincularMateriaView, - AnexadoCrud, DocumentoAnexadoEmLoteView) + AnexadoCrud, DocumentoAnexadoEmLoteView, + PrimeiraTramitacaoEmLoteAdmView) from .apps import AppConfig @@ -98,6 +99,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 6296758ee..3de044e9b 100755 --- a/sapl/protocoloadm/views.py +++ b/sapl/protocoloadm/views.py @@ -47,6 +47,8 @@ from .forms import (AcompanhamentoDocumentoForm, AnularProtocoloAdmForm, filtra_tramitacao_adm_destino_and_status, filtra_tramitacao_adm_destino, filtra_tramitacao_adm_status, AnexadoForm, AnexadoEmLoteFilterSet, + PrimeiraTramitacaoEmLoteAdmFilterSet, + PrimeiraTramitacaoEmLoteAdmForm, compara_tramitacoes_doc) from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo, DocumentoAdministrativo, StatusTramitacaoAdministrativo, @@ -1251,12 +1253,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 +1446,181 @@ 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') + + 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['unidade_local'] = [UnidadeTramitacao.objects.get( + # id=qr['tramitacao__unidade_tramitacao_destino'])] + + qr = self.request.GET.copy() + + form = PrimeiraTramitacaoEmLoteAdmForm() + context['form'] = form + + 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') + form = PrimeiraTramitacaoEmLoteAdmForm(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(request, self.kwargs) + + # marcadas = request.POST.getlist('materia_id') + + # tz = timezone.get_current_timezone() + + # username = request.user.username + + # if len(marcadas) == 0: + # msg = _('Nenhuma máteria foi selecionada.') + # messages.add_message(request, messages.ERROR, msg) + # return self.get(request, self.kwargs) + # obrigatorios = {'data_tramitacao': 'Data da Tramitação', + # 'unidade_tramitacao_local': 'Unidade Local', + # 'unidade_tramitacao_destino': 'Unidade Destino', + # 'status': 'Status', + # 'urgente': 'Urgente', + # 'texto': 'Texto da Ação'} + # for field, nome in obrigatorios.items(): + # if not request.POST[field]: + # msg = _('Campo {} deve ser preenchido.'.format(nome)) + # messages.add_message(request, messages.ERROR, msg) + # return self.get(request, self.kwargs) + + # if not request.POST['data_encaminhamento']: + # data_encaminhamento = None + # else: + # try: + # data_encaminhamento = tz.localize(datetime.strptime( + # request.POST['data_encaminhamento'], "%d/%m/%Y")) + # except ValueError: + # msg = _('Formato da data de encaminhamento incorreto.') + # messages.add_message(request, messages.ERROR, msg) + # return self.get(request, self.kwargs) + + # if request.POST['data_fim_prazo'] == '': + # data_fim_prazo = None + # else: + # try: + # data_fim_prazo = tz.localize(datetime.strptime( + # request.POST['data_fim_prazo'], "%d/%m/%Y")) + # except ValueError: + # msg = _('Formato da data fim do prazo incorreto.') + # messages.add_message(request, messages.ERROR, msg) + # return self.get(request, self.kwargs) + + # # issue https://github.com/interlegis/sapl/issues/1123 + # # TODO: usar Form + # urgente = request.POST['urgente'] == 'True' + # flag_error = False + + # materias_principais = [m for m in MateriaLegislativa.objects.filter(id__in=marcadas)] + # materias_anexadas = [m.anexadas.all() for m in MateriaLegislativa.objects.filter(id__in=marcadas) if m.anexadas.all()] + # import ipdb; ipdb.set_trace() + # materias_anexadas = list(itertools.chain.from_iterable(materias_anexadas)) + # tramitacao_local = int(request.POST['unidade_tramitacao_local']) + # materias_anexadas = list(filter(lambda ma : not ma.tramitacao_set.all() or \ + # ma.tramitacao_set.last().unidade_tramitacao_destino.id == tramitacao_local, + # materias_anexadas)) + # materias = set(materias_principais + materias_anexadas) + + # for materia in materias: + # try: + # data_tramitacao = tz.localize(datetime.strptime( + # request.POST['data_tramitacao'], "%d/%m/%Y")) + # except ValueError: + # msg = _('Formato da data da tramitação incorreto.') + # messages.add_message(request, messages.ERROR, msg) + # return self.get(request, self.kwargs) + + # user = request.user + # ip = get_client_ip(request) + # t = Tramitacao( + # materia=materia, + # data_tramitacao=data_tramitacao, + # data_encaminhamento=data_encaminhamento, + # data_fim_prazo=data_fim_prazo, + # unidade_tramitacao_local_id=request.POST[ + # 'unidade_tramitacao_local'], + # unidade_tramitacao_destino_id=request.POST[ + # 'unidade_tramitacao_destino'], + # urgente=urgente, + # status_id=request.POST['status'], + # turno=request.POST['turno'], + # texto=request.POST['texto'], + # user=user, + # ip=ip + # ) + # t.save() + # try: + # self.logger.debug("user=" + username + + # ". Tentando enviar tramitação.") + # tramitacao_signal.send(sender=Tramitacao, + # post=t, + # request=self.request) + + # except Exception as e: + # self.logger.error('user=' + username + '. Tramitação criada , mas e-mail de acompanhamento ' + # 'de matéria não enviado. Há problemas na configuração ' + # 'do e-mail. ' + str(e)) + # flag_error = True + # if flag_error: + # msg = _('Tramitação criada, mas e-mail de acompanhamento ' + # 'de matéria não enviado. A não configuração do servidor de e-mail ' + # 'impede o envio de aviso de tramitação') + # messages.add_message(self.request, messages.WARNING, msg) + + # status = StatusTramitacao.objects.get(id=request.POST['status']) + + # for materia in materias: + # if status.indicador == 'F': + # materia.em_tramitacao = False + # elif self.primeira_tramitacao: + # materia.em_tramitacao = True + # materia.save() \ No newline at end of file diff --git a/sapl/templates/navbar.yaml b/sapl/templates/navbar.yaml index de556d882..2e0f3de0c 100644 --- a/sapl/templates/navbar.yaml +++ b/sapl/templates/navbar.yaml @@ -29,8 +29,12 @@ 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 - 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..30fba817b --- /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: primeira_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 %}