mirror of https://github.com/interlegis/sapl.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1706 lines
69 KiB
1706 lines
69 KiB
from datetime import datetime
|
|
import logging
|
|
from random import choice
|
|
from string import ascii_letters, digits
|
|
|
|
from braces.views import FormValidMessageMixin
|
|
from django.conf import settings
|
|
from django.contrib import messages
|
|
from django.contrib.auth.decorators import permission_required
|
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.core.exceptions import ObjectDoesNotExist
|
|
from django.core.urlresolvers import reverse
|
|
from django.db import transaction
|
|
from django.db.models import Max, Q
|
|
from django.http import Http404, HttpResponse, JsonResponse
|
|
from django.http.response import HttpResponseRedirect
|
|
from django.shortcuts import redirect
|
|
from django.utils import timezone
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from django.views.generic import ListView, CreateView, UpdateView
|
|
from django.views.generic.base import RedirectView, TemplateView
|
|
from django.views.generic.edit import FormView
|
|
from django_filters.views import FilterView
|
|
|
|
import sapl
|
|
from sapl.base.email_utils import do_envia_email_confirmacao
|
|
from sapl.base.models import Autor, CasaLegislativa, AppConfig
|
|
from sapl.base.signals import tramitacao_signal, post_delete_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, UnidadeTramitacao
|
|
from sapl.materia.views import gerar_pdf_impressos
|
|
from sapl.parlamentares.models import Legislatura, Parlamentar
|
|
from sapl.protocoloadm.models import Protocolo
|
|
from sapl.relatorios.views import relatorio_doc_administrativos
|
|
from sapl.utils import (create_barcode, get_base_url, get_client_ip,
|
|
get_mime_type_from_file_extension, lista_anexados,
|
|
show_results_filter_set, mail_service_configured, from_date_to_datetime_utc)
|
|
|
|
from .forms import (AcompanhamentoDocumentoForm, AnularProtocoloAdmForm,
|
|
DocumentoAcessorioAdministrativoForm,
|
|
DocumentoAdministrativoFilterSet,
|
|
DocumentoAdministrativoForm, FichaPesquisaAdmForm, FichaSelecionaAdmForm, ProtocoloDocumentForm,
|
|
ProtocoloFilterSet, ProtocoloMateriaForm,
|
|
TramitacaoAdmEditForm, TramitacaoAdmForm,
|
|
DesvincularDocumentoForm, DesvincularMateriaForm,
|
|
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,
|
|
TipoDocumentoAdministrativo, TramitacaoAdministrativo, Anexado)
|
|
|
|
|
|
TipoDocumentoAdministrativoCrud = CrudAux.build(
|
|
TipoDocumentoAdministrativo, '')
|
|
|
|
|
|
# ProtocoloDocumentoCrud = Crud.build(Protocolo, '')
|
|
# FIXME precisa de uma chave diferente para o layout
|
|
# ProtocoloMateriaCrud = Crud.build(Protocolo, '')
|
|
|
|
@permission_required('protocoloadm.add_protocolo')
|
|
def recuperar_materia_protocolo(request):
|
|
tipo = request.GET.get('tipo')
|
|
ano = request.GET.get('ano')
|
|
numero = request.GET.get('numero')
|
|
logger = logging.getLogger(__name__)
|
|
username = request.user.username
|
|
try:
|
|
logger.debug("user=" + username +
|
|
". Tentando obter matéria com tipo={}, ano={} e numero={}.".format(tipo, ano, numero))
|
|
materia = MateriaLegislativa.objects.get(
|
|
tipo=tipo, ano=ano, numero=numero)
|
|
autoria = materia.autoria_set.first()
|
|
content = {'ementa': materia.ementa.strip(),
|
|
'ano': materia.ano, 'numero': materia.numero}
|
|
if autoria:
|
|
content.update({'autor': autoria.autor.pk,
|
|
'tipo_autor': autoria.autor.tipo.pk})
|
|
response = JsonResponse(content)
|
|
except Exception as e:
|
|
logger.error("user=" + username + ". " + str(e))
|
|
response = JsonResponse({'error': e})
|
|
return response
|
|
|
|
|
|
def doc_texto_integral(request, pk):
|
|
can_see = True
|
|
|
|
if not request.user.is_authenticated():
|
|
app_config = AppConfig.objects.last()
|
|
if app_config and app_config.documentos_administrativos == 'R':
|
|
can_see = False
|
|
|
|
if can_see:
|
|
documento = DocumentoAdministrativo.objects.get(pk=pk)
|
|
if documento.texto_integral:
|
|
arquivo = documento.texto_integral
|
|
|
|
mime = get_mime_type_from_file_extension(arquivo.name)
|
|
|
|
with open(arquivo.path, 'rb') as f:
|
|
data = f.read()
|
|
|
|
response = HttpResponse(data, content_type='%s' % mime)
|
|
response['Content-Disposition'] = (
|
|
'inline; filename="%s"' % arquivo.name.split('/')[-1])
|
|
return response
|
|
raise Http404
|
|
|
|
|
|
class AcompanhamentoConfirmarView(TemplateView):
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def get_redirect_url(self, email):
|
|
username = self.request.user.username
|
|
self.logger.info(
|
|
'user=' + username + '. Este documento está sendo acompanhado pelo e-mail: {}'.format(email))
|
|
msg = _('Este documento está sendo acompanhado pelo e-mail: %s') % (
|
|
email)
|
|
messages.add_message(self.request, messages.SUCCESS, msg)
|
|
return reverse('sapl.protocoloadm:documentoadministrativo_detail',
|
|
kwargs={'pk': self.kwargs['pk']})
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
documento_id = kwargs['pk']
|
|
hash_txt = request.GET.get('hash_txt', '')
|
|
username = request.user.username
|
|
|
|
try:
|
|
self.logger.debug("user=" + username + ". Tentando obter objeto AcompanhamentoDocumento com documento_id={} e hash={}"
|
|
.format(documento_id, hash_txt))
|
|
acompanhar = AcompanhamentoDocumento.objects.get(
|
|
documento_id=documento_id,
|
|
hash=hash_txt)
|
|
except ObjectDoesNotExist as e:
|
|
self.logger.error("user=" + username + ". " + str(e))
|
|
raise Http404()
|
|
# except MultipleObjectsReturned:
|
|
# A melhor solução deve ser permitir que a exceção
|
|
# (MultipleObjectsReturned) seja lançada e vá para o log,
|
|
# pois só poderá ser causada por um erro de desenvolvimente
|
|
|
|
acompanhar.confirmado = True
|
|
acompanhar.save()
|
|
|
|
return HttpResponseRedirect(self.get_redirect_url(acompanhar.email))
|
|
|
|
|
|
class AcompanhamentoExcluirView(TemplateView):
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def get_success_url(self):
|
|
username = self.request.user.username
|
|
self.logger.info(
|
|
"user=" + username + ". Você parou de acompanhar este Documento (pk={}).".format(self.kwargs['pk']))
|
|
msg = _('Você parou de acompanhar este Documento.')
|
|
messages.add_message(self.request, messages.INFO, msg)
|
|
return reverse('sapl.protocoloadm:documentoadministrativo_detail',
|
|
kwargs={'pk': self.kwargs['pk']})
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
documento_id = kwargs['pk']
|
|
hash_txt = request.GET.get('hash_txt', '')
|
|
username = request.user.username
|
|
try:
|
|
self.logger.debug("user=" + username + ". Tentando obter AcompanhamentoDocumento com documento_id={} e hash={}."
|
|
.format(documento_id, hash_txt))
|
|
AcompanhamentoDocumento.objects.get(documento_id=documento_id,
|
|
hash=hash_txt).delete()
|
|
except ObjectDoesNotExist:
|
|
self.logger.error("user=" + username + ". AcompanhamentoDocumento com documento_id={} e hash={} não encontrado."
|
|
.format(documento_id, hash_txt))
|
|
|
|
return HttpResponseRedirect(self.get_success_url())
|
|
|
|
|
|
class AcompanhamentoDocumentoView(CreateView):
|
|
template_name = "protocoloadm/acompanhamento_documento.html"
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def get_random_chars(self):
|
|
s = ascii_letters + digits
|
|
return ''.join(choice(s) for i in range(choice([6, 7])))
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
if not mail_service_configured():
|
|
self.logger.warning(_('Servidor de email não configurado.'))
|
|
messages.error(request, _('Serviço de Acompanhamento de '
|
|
'Documentos não foi configurado'))
|
|
return redirect('/')
|
|
|
|
pk = self.kwargs['pk']
|
|
documento = DocumentoAdministrativo.objects.get(id=pk)
|
|
|
|
return self.render_to_response(
|
|
{'form': AcompanhamentoDocumentoForm(),
|
|
'documento': documento})
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
if not mail_service_configured():
|
|
self.logger.warning(_('Servidor de email não configurado.'))
|
|
messages.error(request, _('Serviço de Acompanhamento de '
|
|
'Documentos não foi configurado'))
|
|
return redirect('/')
|
|
|
|
form = AcompanhamentoDocumentoForm(request.POST)
|
|
pk = self.kwargs['pk']
|
|
documento = DocumentoAdministrativo.objects.get(id=pk)
|
|
|
|
if form.is_valid():
|
|
email = form.cleaned_data['email']
|
|
usuario = request.user
|
|
|
|
hash_txt = self.get_random_chars()
|
|
|
|
acompanhar = AcompanhamentoDocumento.objects.get_or_create(
|
|
documento=documento,
|
|
email=form.data['email'])
|
|
|
|
# Se o segundo elemento do retorno do get_or_create for True
|
|
# quer dizer que o elemento não existia
|
|
if acompanhar[1]:
|
|
acompanhar = acompanhar[0]
|
|
acompanhar.hash = hash_txt
|
|
acompanhar.usuario = usuario.username
|
|
acompanhar.confirmado = False
|
|
acompanhar.save()
|
|
|
|
base_url = get_base_url(request)
|
|
|
|
destinatario = AcompanhamentoDocumento.objects.get(
|
|
documento=documento,
|
|
email=email,
|
|
confirmado=False)
|
|
casa = CasaLegislativa.objects.first()
|
|
|
|
do_envia_email_confirmacao(base_url,
|
|
casa,
|
|
"documento",
|
|
documento,
|
|
destinatario)
|
|
self.logger.info('user={}. Foi enviado um e-mail de confirmação. Confira sua caixa '
|
|
'de mensagens e clique no link que nós enviamos para '
|
|
'confirmar o acompanhamento deste documento.'.format(usuario.username))
|
|
msg = _('Foi enviado um e-mail de confirmação. Confira sua caixa \
|
|
de mensagens e clique no link que nós enviamos para \
|
|
confirmar o acompanhamento deste documento.')
|
|
messages.add_message(request, messages.SUCCESS, msg)
|
|
|
|
# Se o elemento existir e o email não foi confirmado:
|
|
# gerar novo hash e reenviar mensagem de email
|
|
elif not acompanhar[0].confirmado:
|
|
acompanhar = acompanhar[0]
|
|
acompanhar.hash = hash_txt
|
|
acompanhar.save()
|
|
|
|
base_url = get_base_url(request)
|
|
|
|
destinatario = AcompanhamentoDocumento.objects.get(
|
|
documento=documento,
|
|
email=email,
|
|
confirmado=False
|
|
)
|
|
|
|
casa = CasaLegislativa.objects.first()
|
|
|
|
do_envia_email_confirmacao(base_url,
|
|
casa,
|
|
"documento",
|
|
documento,
|
|
destinatario)
|
|
|
|
self.logger.info('user={}. Foi enviado um e-mail de confirmação. Confira sua caixa \
|
|
de mensagens e clique no link que nós enviamos para \
|
|
confirmar o acompanhamento deste documento.'.format(usuario.username))
|
|
|
|
msg = _('Foi enviado um e-mail de confirmação. Confira sua caixa \
|
|
de mensagens e clique no link que nós enviamos para \
|
|
confirmar o acompanhamento deste documento.')
|
|
messages.add_message(request, messages.SUCCESS, msg)
|
|
|
|
# Caso esse Acompanhamento já exista
|
|
# avisa ao usuário que esse documento já está sendo acompanhado
|
|
else:
|
|
self.logger.info('user=' + request.user.username +
|
|
'. Este e-mail já está acompanhando esse documento (pk={}).'.format(pk))
|
|
msg = _('Este e-mail já está acompanhando esse documento.')
|
|
messages.add_message(request, messages.ERROR, msg)
|
|
|
|
return self.render_to_response(
|
|
{'form': form,
|
|
'documento': documento,
|
|
})
|
|
return HttpResponseRedirect(self.get_success_url())
|
|
else:
|
|
return self.render_to_response(
|
|
{'form': form,
|
|
'documento': documento})
|
|
|
|
def get_success_url(self):
|
|
return reverse('sapl.protocoloadm:documentoadministrativo_detail',
|
|
kwargs={'pk': self.kwargs['pk']})
|
|
|
|
|
|
class DocumentoAdministrativoMixin:
|
|
|
|
def has_permission(self):
|
|
app_config = AppConfig.objects.last()
|
|
if app_config and app_config.documentos_administrativos == 'O':
|
|
return True
|
|
|
|
return super().has_permission()
|
|
|
|
|
|
class DocumentoAdministrativoCrud(Crud):
|
|
model = DocumentoAdministrativo
|
|
help_topic = 'numeracao_docsacess'
|
|
|
|
class BaseMixin(Crud.BaseMixin):
|
|
list_field_names = ['tipo', 'numero', 'ano', 'data',
|
|
'protocolo__numero', 'assunto',
|
|
'interessado', 'tramitacao', 'texto_integral']
|
|
|
|
@property
|
|
def search_url(self):
|
|
namespace = self.model._meta.app_config.name
|
|
return reverse('%s:%s' % (namespace, 'pesq_doc_adm'))
|
|
|
|
list_url = ''
|
|
|
|
class ListView(RedirectView, DocumentoAdministrativoMixin, Crud.ListView):
|
|
|
|
def get_redirect_url(self, *args, **kwargs):
|
|
namespace = self.model._meta.app_config.name
|
|
return reverse('%s:%s' % (namespace, 'pesq_doc_adm'))
|
|
|
|
class CreateView(Crud.CreateView):
|
|
form_class = DocumentoAdministrativoForm
|
|
layout_key = None
|
|
|
|
def get_initial(self):
|
|
initial = super().get_initial()
|
|
|
|
initial['user'] = self.request.user
|
|
initial['ip'] = get_client_ip(self.request)
|
|
|
|
tz = timezone.get_current_timezone()
|
|
initial['ultima_edicao'] = tz.localize(datetime.now())
|
|
|
|
return initial
|
|
|
|
@property
|
|
def cancel_url(self):
|
|
return self.search_url
|
|
|
|
class UpdateView(Crud.UpdateView):
|
|
form_class = DocumentoAdministrativoForm
|
|
layout_key = None
|
|
|
|
def form_valid(self, form):
|
|
dict_objeto_antigo = DocumentoAdministrativo.objects.get(
|
|
pk=self.kwargs['pk']
|
|
).__dict__
|
|
|
|
self.object = form.save()
|
|
dict_objeto_novo = self.object.__dict__
|
|
|
|
atributos = [
|
|
'tipo_id', 'ano', 'numero', 'data', 'protocolo_id', 'assunto',
|
|
'interessado', 'tramitacao', 'restrito', 'texto_integral','numero_externo',
|
|
'dias_prazo', 'data_fim_prazo', 'observacao'
|
|
]
|
|
|
|
for atributo in atributos:
|
|
if dict_objeto_antigo[atributo] != dict_objeto_novo[atributo]:
|
|
self.object.user = self.request.user
|
|
self.object.ip = get_client_ip(self.request)
|
|
|
|
tz = timezone.get_current_timezone()
|
|
self.object.ultima_edicao = tz.localize(datetime.now())
|
|
|
|
self.object.save()
|
|
break
|
|
|
|
return super().form_valid(form)
|
|
|
|
def get_initial(self):
|
|
if self.object.protocolo:
|
|
p = self.object.protocolo
|
|
return {'ano_protocolo': p.ano,
|
|
'numero_protocolo': p.numero}
|
|
|
|
class DetailView(DocumentoAdministrativoMixin, Crud.DetailView):
|
|
|
|
def get(self, *args, **kwargs):
|
|
pk = self.kwargs['pk']
|
|
documento = DocumentoAdministrativo.objects.get(id=pk)
|
|
if documento.restrito and self.request.user.is_anonymous():
|
|
return redirect('/')
|
|
return super(Crud.DetailView, self).get(args, kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
|
|
context['user'] = self.request.user
|
|
context['documentoadministrativo'] = DocumentoAdministrativo.objects.get(
|
|
pk=self.kwargs['pk']
|
|
)
|
|
|
|
return context
|
|
|
|
def urlize(self, obj, fieldname):
|
|
a = '<a href="%s">%s</a>' % (
|
|
reverse(
|
|
'sapl.protocoloadm:doc_texto_integral',
|
|
kwargs={'pk': obj.pk}),
|
|
obj.texto_integral.name.split('/')[-1])
|
|
return obj.texto_integral.field.verbose_name, a
|
|
|
|
class DeleteView(Crud.DeleteView):
|
|
|
|
def get_success_url(self):
|
|
return self.search_url
|
|
|
|
|
|
class StatusTramitacaoAdministrativoCrud(CrudAux):
|
|
model = StatusTramitacaoAdministrativo
|
|
help_topic = 'status_tramitacao'
|
|
|
|
class BaseMixin(CrudAux.BaseMixin):
|
|
list_field_names = ['sigla', 'indicador', 'descricao']
|
|
|
|
class ListView(CrudAux.ListView):
|
|
ordering = 'sigla'
|
|
|
|
|
|
class ProtocoloPesquisaView(PermissionRequiredMixin, FilterView):
|
|
model = Protocolo
|
|
filterset_class = ProtocoloFilterSet
|
|
paginate_by = 10
|
|
permission_required = ('protocoloadm.list_protocolo',)
|
|
|
|
def get_filterset_kwargs(self, filterset_class):
|
|
super(ProtocoloPesquisaView,
|
|
self).get_filterset_kwargs(filterset_class)
|
|
|
|
kwargs = {'data': self.request.GET or None}
|
|
|
|
qs = self.get_queryset().order_by('ano', 'numero').distinct()
|
|
|
|
if 'o' in self.request.GET and not self.request.GET['o']:
|
|
qs = qs.order_by('-ano', '-numero')
|
|
|
|
kwargs.update({
|
|
'queryset': qs,
|
|
})
|
|
return kwargs
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(ProtocoloPesquisaView,
|
|
self).get_context_data(**kwargs)
|
|
|
|
paginator = context['paginator']
|
|
page_obj = context['page_obj']
|
|
|
|
context['page_range'] = make_pagination(
|
|
page_obj.number, paginator.num_pages)
|
|
|
|
context['title'] = _('Protocolo')
|
|
|
|
return context
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
super(ProtocoloPesquisaView, self).get(request)
|
|
|
|
# Se a pesquisa estiver quebrando com a paginação
|
|
# Olhe esta função abaixo
|
|
# Provavelmente você criou um novo campo no Form/FilterSet
|
|
# Então a ordem da URL está diferente
|
|
data = self.filterset.data
|
|
if data and data.get('numero') is not None:
|
|
url = "&" + str(self.request.environ['QUERY_STRING'])
|
|
if url.startswith("&page"):
|
|
ponto_comeco = url.find('numero=') - 1
|
|
url = url[ponto_comeco:]
|
|
else:
|
|
url = ''
|
|
|
|
self.filterset.form.fields['o'].label = _('Ordenação')
|
|
|
|
context = self.get_context_data(filter=self.filterset,
|
|
object_list=self.object_list,
|
|
filter_url=url,
|
|
numero_res=len(self.object_list)
|
|
)
|
|
|
|
context['show_results'] = show_results_filter_set(
|
|
self.request.GET.copy())
|
|
|
|
return self.render_to_response(context)
|
|
|
|
|
|
class ProtocoloListView(PermissionRequiredMixin, ListView):
|
|
template_name = 'protocoloadm/protocolo_list.html'
|
|
context_object_name = 'protocolos'
|
|
model = Protocolo
|
|
paginate_by = 10
|
|
permission_required = ('protocoloadm.list_protocolo',)
|
|
|
|
def get_queryset(self):
|
|
kwargs = self.request.session['kwargs']
|
|
return Protocolo.objects.filter(
|
|
**kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(ProtocoloListView, self).get_context_data(
|
|
**kwargs)
|
|
|
|
paginator = context['paginator']
|
|
page_obj = context['page_obj']
|
|
|
|
context['page_range'] = make_pagination(
|
|
page_obj.number, paginator.num_pages)
|
|
return context
|
|
|
|
|
|
class AnularProtocoloAdmView(PermissionRequiredMixin, CreateView):
|
|
template_name = 'protocoloadm/anular_protocoloadm.html'
|
|
form_class = AnularProtocoloAdmForm
|
|
form_valid_message = _('Protocolo anulado com sucesso!')
|
|
permission_required = ('protocoloadm.action_anular_protocolo', )
|
|
|
|
def get_success_url(self):
|
|
return reverse('sapl.protocoloadm:protocolo')
|
|
|
|
def get_initial(self):
|
|
initial_data = {}
|
|
initial_data['user_anulacao'] = self.request.user.username
|
|
initial_data['ip_anulacao'] = get_client_ip(self.request)
|
|
return initial_data
|
|
|
|
def form_valid(self, form):
|
|
protocolo = Protocolo.objects.get(numero=form.cleaned_data['numero'],
|
|
ano=form.cleaned_data['ano'])
|
|
protocolo.anulado = True
|
|
protocolo.justificativa_anulacao = (
|
|
form.cleaned_data['justificativa_anulacao'])
|
|
protocolo.user_anulacao = form.cleaned_data['user_anulacao']
|
|
protocolo.ip_anulacao = form.cleaned_data['ip_anulacao']
|
|
protocolo.timestamp_anulacao = timezone.now()
|
|
protocolo.save()
|
|
return redirect(self.get_success_url())
|
|
|
|
|
|
class ProtocoloDocumentoView(PermissionRequiredMixin,
|
|
FormValidMessageMixin,
|
|
CreateView):
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
template_name = "protocoloadm/protocolar_documento.html"
|
|
form_class = ProtocoloDocumentForm
|
|
form_valid_message = _('Protocolo cadastrado com sucesso!')
|
|
permission_required = ('protocoloadm.add_protocolo', )
|
|
|
|
def get_success_url(self):
|
|
return reverse('sapl.protocoloadm:protocolo_mostrar',
|
|
kwargs={'pk': self.object.id})
|
|
|
|
def get_initial(self):
|
|
initial = super().get_initial()
|
|
|
|
initial['user_data_hora_manual'] = self.request.user.username
|
|
initial['ip_data_hora_manual'] = get_client_ip(self.request)
|
|
initial['data'] = timezone.localdate(timezone.now())
|
|
initial['hora'] = timezone.localtime(timezone.now())
|
|
return initial
|
|
|
|
@transaction.atomic
|
|
def form_valid(self, form):
|
|
protocolo = form.save(commit=False)
|
|
username = self.request.user.username
|
|
|
|
self.logger.debug("user=" + username +
|
|
". Tentando obter sequência de numeração.")
|
|
numeracao = AppConfig.objects.last(
|
|
).sequencia_numeracao_protocolo
|
|
if not numeracao:
|
|
self.logger.error("user=" + username + ". É preciso definir a sequencia de "
|
|
"numeração na tabelas auxiliares! ")
|
|
msg = _('É preciso definir a sequencia de ' +
|
|
'numeração na tabelas auxiliares!')
|
|
messages.add_message(self.request, messages.ERROR, msg)
|
|
return self.render_to_response(self.get_context_data())
|
|
|
|
if numeracao == 'A':
|
|
numero = Protocolo.objects.filter(
|
|
ano=timezone.now().year).aggregate(Max('numero'))
|
|
elif numeracao == 'L':
|
|
legislatura = Legislatura.objects.filter(
|
|
data_inicio__year__lte=timezone.now().year,
|
|
data_fim__year__gte=timezone.now().year).first()
|
|
|
|
data_inicio = legislatura.data_inicio
|
|
data_fim = legislatura.data_fim
|
|
|
|
data_inicio_utc = from_date_to_datetime_utc(data_inicio)
|
|
data_fim_utc = from_date_to_datetime_utc(data_fim)
|
|
|
|
numero = Protocolo.objects.filter(
|
|
Q(data__isnull=False,
|
|
data__gte=data_inicio,
|
|
data__lte=data_fim) |
|
|
Q(timestamp__isnull=False,
|
|
timestamp__gte=data_inicio_utc,
|
|
timestamp__lte=data_fim_utc) |
|
|
Q(timestamp_data_hora_manual__isnull=False,
|
|
timestamp_data_hora_manual__gte=data_inicio_utc,
|
|
timestamp_data_hora_manual__lte=data_fim_utc,)).\
|
|
aggregate(Max('numero'))
|
|
elif numeracao == 'U':
|
|
numero = Protocolo.objects.all().aggregate(Max('numero'))
|
|
|
|
protocolo.tipo_processo = '0' # TODO validar o significado
|
|
protocolo.anulado = False
|
|
if not protocolo.numero:
|
|
protocolo.numero = (
|
|
numero['numero__max'] + 1) if numero['numero__max'] else 1
|
|
elif protocolo.numero < (numero['numero__max'] + 1) if numero['numero__max'] else 0:
|
|
msg = _('Número de protocolo deve ser maior que {}'.format(
|
|
numero['numero__max']))
|
|
self.logger.error(
|
|
"user=" + username + ". Número de protocolo deve ser maior que {}.".format(numero['numero__max']))
|
|
messages.add_message(self.request, messages.ERROR, msg)
|
|
return self.render_to_response(self.get_context_data())
|
|
protocolo.ano = timezone.now().year
|
|
protocolo.assunto_ementa = self.request.POST['assunto']
|
|
|
|
if form.cleaned_data['data_hora_manual'] == 'True':
|
|
protocolo.timestamp = None
|
|
protocolo.user_data_hora_manual = username
|
|
protocolo.ip_data_hora_manual = get_client_ip(self.request)
|
|
else:
|
|
protocolo.data = None
|
|
protocolo.hora = None
|
|
protocolo.user_data_hora_manual = ''
|
|
protocolo.ip_data_hora_manual = ''
|
|
|
|
protocolo.save()
|
|
self.object = protocolo
|
|
return redirect(self.get_success_url())
|
|
|
|
|
|
class CriarDocumentoProtocolo(PermissionRequiredMixin, CreateView):
|
|
template_name = "protocoloadm/criar_documento.html"
|
|
form_class = DocumentoAdministrativoForm
|
|
permission_required = ('protocoloadm.add_documentoadministrativo',)
|
|
|
|
def get_initial(self):
|
|
protocolo = Protocolo.objects.get(pk=self.kwargs['pk'])
|
|
return self.criar_documento(protocolo)
|
|
|
|
def get_success_url(self):
|
|
return reverse('sapl.protocoloadm:protocolo_mostrar',
|
|
kwargs={'pk': self.kwargs['pk']})
|
|
|
|
def criar_documento(self, protocolo):
|
|
curr_year = timezone.now().year
|
|
|
|
numero_max = DocumentoAdministrativo.objects.filter(
|
|
tipo=protocolo.tipo_documento, ano=curr_year
|
|
).aggregate(Max('numero'))['numero__max']
|
|
|
|
doc = {}
|
|
doc['tipo'] = protocolo.tipo_documento
|
|
doc['ano'] = curr_year
|
|
doc['data'] = timezone.now()
|
|
doc['numero_protocolo'] = protocolo.numero
|
|
doc['ano_protocolo'] = protocolo.ano
|
|
doc['protocolo'] = protocolo.id
|
|
doc['assunto'] = protocolo.assunto_ementa
|
|
doc['interessado'] = protocolo.interessado
|
|
doc['numero'] = numero_max + 1 if numero_max else 1
|
|
doc['user'] = self.request.user
|
|
doc['ip'] = get_client_ip(self.request)
|
|
|
|
tz = timezone.get_current_timezone()
|
|
doc['ultima_edicao'] = tz.localize(datetime.now())
|
|
|
|
return doc
|
|
|
|
|
|
class ProtocoloMostrarView(PermissionRequiredMixin, TemplateView):
|
|
logger = logging.getLogger(__name__)
|
|
|
|
template_name = "protocoloadm/protocolo_mostrar.html"
|
|
permission_required = ('protocoloadm.detail_protocolo', )
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = super(ProtocoloMostrarView, self).get_context_data(**kwargs)
|
|
protocolo = Protocolo.objects.get(pk=self.kwargs['pk'])
|
|
username = self.request.user.username
|
|
|
|
if protocolo.tipo_materia:
|
|
self.logger.debug(
|
|
"user=" + username +
|
|
". Tentando obter objeto MateriaLegislativa com numero_protocolo={} e ano={}."
|
|
.format(protocolo.numero, protocolo.ano))
|
|
materia = MateriaLegislativa.objects.filter(
|
|
numero_protocolo=protocolo.numero, ano=protocolo.ano)
|
|
context['materia'] = materia
|
|
if len(materia) > 1:
|
|
msg = _('Foi encontrada mais de uma matéria com o mesmo número de protocolo e ano.'
|
|
' Isso é um erro de uso!'
|
|
' Reporte isso ao suporte para que seja corrigido.')
|
|
messages.add_message(self.request, messages.ERROR, msg)
|
|
self.logger.error(
|
|
"user=" + username + ". Objeto MateriaLegislativa com numero_protocolo={} e ano={}"
|
|
" encontrado mais de um registro."
|
|
" Erro relatado ao usuário.".format(protocolo.numero, protocolo.ano))
|
|
|
|
if protocolo.tipo_documento:
|
|
context[
|
|
'documentos'] = protocolo.documentoadministrativo_set\
|
|
.all().order_by('-ano', '-numero')
|
|
|
|
context['protocolo'] = protocolo
|
|
return context
|
|
|
|
|
|
class ComprovanteProtocoloView(PermissionRequiredMixin, TemplateView):
|
|
|
|
template_name = "protocoloadm/comprovante.html"
|
|
permission_required = ('protocoloadm.detail_protocolo', )
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(ComprovanteProtocoloView, self).get_context_data(
|
|
**kwargs)
|
|
protocolo = Protocolo.objects.get(pk=self.kwargs['pk'])
|
|
# numero is string, padd with zeros left via .zfill()
|
|
base64_data = create_barcode(str(protocolo.numero).zfill(6))
|
|
barcode = 'data:image/png;base64,{0}'.format(base64_data)
|
|
|
|
autenticacao = _("** NULO **")
|
|
|
|
if not protocolo.anulado:
|
|
if protocolo.timestamp:
|
|
data = protocolo.timestamp.strftime("%Y/%m/%d")
|
|
else:
|
|
data = protocolo.data.strftime("%Y/%m/%d")
|
|
|
|
# data is not i18n sensitive 'Y-m-d' is the right format.
|
|
autenticacao = str(protocolo.tipo_processo) + \
|
|
data + str(protocolo.numero).zfill(6)
|
|
|
|
if protocolo.tipo_materia:
|
|
materia = MateriaLegislativa.objects.filter(
|
|
numero_protocolo=protocolo.numero,
|
|
ano=protocolo.ano).first()
|
|
if materia:
|
|
context['materia'] = materia.numero
|
|
|
|
context.update({"protocolo": protocolo,
|
|
"barcode": barcode,
|
|
"autenticacao": autenticacao})
|
|
return context
|
|
|
|
|
|
class ProtocoloMateriaView(PermissionRequiredMixin, CreateView):
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
template_name = "protocoloadm/protocolar_materia.html"
|
|
form_class = ProtocoloMateriaForm
|
|
form_valid_message = _('Matéria cadastrada com sucesso!')
|
|
permission_required = ('protocoloadm.add_protocolo',)
|
|
|
|
def get_success_url(self, protocolo):
|
|
return reverse('sapl.protocoloadm:materia_continuar', kwargs={
|
|
'pk': protocolo.pk})
|
|
|
|
def get_initial(self):
|
|
initial = super().get_initial()
|
|
|
|
initial['user_data_hora_manual'] = self.request.user.username
|
|
initial['ip_data_hora_manual'] = get_client_ip(self.request)
|
|
initial['data'] = timezone.localdate(timezone.now())
|
|
initial['hora'] = timezone.localtime(timezone.now())
|
|
return initial
|
|
|
|
@transaction.atomic
|
|
def form_valid(self, form):
|
|
protocolo = form.save(commit=False)
|
|
username = self.request.user.username
|
|
self.logger.debug("user=" + username +
|
|
". Tentando obter sequência de numeração.")
|
|
numeracao = AppConfig.objects.last(
|
|
).sequencia_numeracao_protocolo
|
|
if not numeracao:
|
|
self.logger.error("user=" + username + ". É preciso definir a sequencia de "
|
|
"numeração na tabelas auxiliares!")
|
|
msg = _('É preciso definir a sequencia de ' +
|
|
'numeração na tabelas auxiliares!')
|
|
messages.add_message(self.request, messages.ERROR, msg)
|
|
return self.render_to_response(self.get_context_data())
|
|
|
|
if numeracao == 'A':
|
|
numero = Protocolo.objects.filter(
|
|
ano=timezone.now().year).aggregate(Max('numero'))
|
|
elif numeracao == 'L':
|
|
legislatura = Legislatura.objects.filter(
|
|
data_inicio__year__lte=timezone.now().year,
|
|
data_fim__year__gte=timezone.now().year).first()
|
|
data_inicio = legislatura.data_inicio
|
|
data_fim = legislatura.data_fim
|
|
|
|
data_inicio_utc = from_date_to_datetime_utc(data_inicio)
|
|
data_fim_utc = from_date_to_datetime_utc(data_fim)
|
|
|
|
numero = Protocolo.objects.filter(
|
|
Q(data__isnull=False,
|
|
data__gte=data_inicio,
|
|
data__lte=data_fim) |
|
|
Q(timestamp__isnull=False,
|
|
timestamp__gte=data_inicio_utc,
|
|
timestamp__lte=data_fim_utc) |
|
|
Q(timestamp_data_hora_manual__isnull=False,
|
|
timestamp_data_hora_manual__gte=data_inicio_utc,
|
|
timestamp_data_hora_manual__lte=data_fim_utc,)).\
|
|
aggregate(Max('numero'))
|
|
|
|
elif numeracao == 'U':
|
|
numero = Protocolo.objects.all().aggregate(Max('numero'))
|
|
|
|
if numeracao is None:
|
|
numero['numero__max'] = 0
|
|
|
|
if not protocolo.numero:
|
|
protocolo.numero = (
|
|
numero['numero__max'] + 1) if numero['numero__max'] else 1
|
|
if numero['numero__max']:
|
|
if protocolo.numero < (numero['numero__max'] + 1):
|
|
self.logger.error("user=" + username + ". Número de protocolo ({}) é menor que {}"
|
|
.format(protocolo.numero, numero['numero__max']))
|
|
msg = _('Número de protocolo deve ser maior que {}'.format(
|
|
numero['numero__max']))
|
|
messages.add_message(self.request, messages.ERROR, msg)
|
|
return self.render_to_response(self.get_context_data())
|
|
protocolo.ano = timezone.now().year
|
|
|
|
protocolo.tipo_protocolo = 0
|
|
protocolo.tipo_processo = '1' # TODO validar o significado
|
|
protocolo.anulado = False
|
|
|
|
if form.cleaned_data['autor']:
|
|
protocolo.autor = form.cleaned_data['autor']
|
|
protocolo.tipo_materia = TipoMateriaLegislativa.objects.get(
|
|
id=self.request.POST['tipo_materia'])
|
|
protocolo.numero_paginas = self.request.POST['numero_paginas']
|
|
protocolo.observacao = self.request.POST['observacao']
|
|
protocolo.assunto_ementa = self.request.POST['assunto_ementa']
|
|
|
|
if form.cleaned_data['data_hora_manual'] == 'True':
|
|
protocolo.timestamp = None
|
|
protocolo.user_data_hora_manual = username
|
|
protocolo.ip_data_hora_manual = get_client_ip(self.request)
|
|
else:
|
|
protocolo.data = None
|
|
protocolo.hora = None
|
|
protocolo.user_data_hora_manual = ''
|
|
protocolo.ip_data_hora_manual = ''
|
|
protocolo.save()
|
|
data = form.cleaned_data
|
|
if data['vincular_materia'] == 'True':
|
|
materia = MateriaLegislativa.objects.get(ano=data['ano_materia'],
|
|
numero=data['numero_materia'],
|
|
tipo=data['tipo_materia'])
|
|
materia.numero_protocolo = protocolo.numero
|
|
materia.save()
|
|
|
|
return redirect(self.get_success_url(protocolo))
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(CreateView, self).get_context_data(**kwargs)
|
|
autores_ativos = self.autores_ativos()
|
|
|
|
autores = []
|
|
autores.append(['0', '------'])
|
|
for a in autores_ativos:
|
|
autores.append([a.id, a.__str__()])
|
|
|
|
context['form'].fields['autor'].choices = autores
|
|
return context
|
|
|
|
def autores_ativos(self):
|
|
lista_parlamentares = Parlamentar.objects.filter(
|
|
ativo=True).values_list('id', flat=True)
|
|
model_parlamentar = ContentType.objects.get_for_model(Parlamentar)
|
|
autor_parlamentar = Autor.objects.filter(
|
|
content_type=model_parlamentar, object_id__in=lista_parlamentares)
|
|
|
|
lista_comissoes = Comissao.objects.filter(Q(
|
|
data_extincao__isnull=True) | Q(
|
|
data_extincao__gt=timezone.now())).values_list('id', flat=True)
|
|
model_comissao = ContentType.objects.get_for_model(Comissao)
|
|
autor_comissoes = Autor.objects.filter(
|
|
content_type=model_comissao, object_id__in=lista_comissoes)
|
|
autores_outros = Autor.objects.exclude(
|
|
content_type__in=[model_parlamentar, model_comissao])
|
|
q = autor_parlamentar | autor_comissoes | autores_outros
|
|
return q
|
|
|
|
|
|
class ProtocoloMateriaTemplateView(PermissionRequiredMixin, TemplateView):
|
|
|
|
template_name = "protocoloadm/MateriaTemplate.html"
|
|
permission_required = ('protocoloadm.detail_protocolo', )
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(ProtocoloMateriaTemplateView, self).get_context_data(
|
|
**kwargs)
|
|
protocolo = Protocolo.objects.get(pk=self.kwargs['pk'])
|
|
context.update({'protocolo': protocolo})
|
|
return context
|
|
|
|
|
|
class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin,
|
|
PermissionRequiredMixin,
|
|
FilterView):
|
|
model = DocumentoAdministrativo
|
|
filterset_class = DocumentoAdministrativoFilterSet
|
|
paginate_by = 10
|
|
permission_required = ('protocoloadm.list_documentoadministrativo', )
|
|
|
|
def get_filterset_kwargs(self, filterset_class):
|
|
super(PesquisarDocumentoAdministrativoView,
|
|
self).get_filterset_kwargs(filterset_class)
|
|
|
|
kwargs = {'data': self.request.GET or None}
|
|
|
|
status_tramitacao = self.request.GET.get(
|
|
'tramitacaoadministrativo__status')
|
|
unidade_destino = self.request.GET.get(
|
|
'tramitacaoadministrativo__unidade_tramitacao_destino')
|
|
|
|
qs = self.get_queryset()
|
|
|
|
qs = qs.prefetch_related("documentoacessorioadministrativo_set",
|
|
"tramitacaoadministrativo_set",
|
|
"tipo",
|
|
"tramitacaoadministrativo_set__status",
|
|
"tramitacaoadministrativo_set__unidade_tramitacao_local",
|
|
"tramitacaoadministrativo_set__unidade_tramitacao_destino")
|
|
|
|
if status_tramitacao and unidade_destino:
|
|
lista = filtra_tramitacao_adm_destino_and_status(status_tramitacao,
|
|
unidade_destino)
|
|
qs = qs.filter(id__in=lista).distinct()
|
|
|
|
elif status_tramitacao:
|
|
lista = filtra_tramitacao_adm_status(status_tramitacao)
|
|
qs = qs.filter(id__in=lista).distinct()
|
|
|
|
elif unidade_destino:
|
|
lista = filtra_tramitacao_adm_destino(unidade_destino)
|
|
qs = qs.filter(id__in=lista).distinct()
|
|
|
|
if 'o' in self.request.GET and not self.request.GET['o']:
|
|
qs = qs.order_by('-ano', '-numero')
|
|
|
|
kwargs.update({
|
|
'queryset': qs,
|
|
})
|
|
return kwargs
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(PesquisarDocumentoAdministrativoView,
|
|
self).get_context_data(**kwargs)
|
|
|
|
if self.paginate_by:
|
|
paginator = context['paginator']
|
|
page_obj = context['page_obj']
|
|
context['page_range'] = make_pagination(
|
|
page_obj.number, paginator.num_pages)
|
|
|
|
return context
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
super(PesquisarDocumentoAdministrativoView, self).get(request)
|
|
# Se a pesquisa estiver quebrando com a paginação
|
|
# Olhe esta função abaixo
|
|
# Provavelmente você criou um novo campo no Form/FilterSet
|
|
# Então a ordem da URL está diferente
|
|
data = self.filterset.data
|
|
if data and data.get('tipo') is not None:
|
|
url = "&" + str(self.request.environ['QUERY_STRING'])
|
|
if url.startswith("&page"):
|
|
ponto_comeco = url.find('tipo=') - 1
|
|
url = url[ponto_comeco:]
|
|
else:
|
|
url = ''
|
|
self.filterset.form.fields['o'].label = _('Ordenação')
|
|
# é usada essa verificação anônima para quando os documentos administrativos
|
|
# estão no modo ostensivo, mas podem existir documentos administrativos
|
|
# restritos
|
|
if request.user.is_anonymous():
|
|
length = self.object_list.filter(restrito=False).count()
|
|
else:
|
|
length = self.object_list.count()
|
|
|
|
is_relatorio = url != '' and request.GET.get('relatorio', None)
|
|
self.paginate_by = None if is_relatorio else self.paginate_by
|
|
context = self.get_context_data(filter=self.filterset,
|
|
filter_url=url,
|
|
numero_res=length
|
|
)
|
|
context['show_results'] = show_results_filter_set(
|
|
self.request.GET.copy())
|
|
|
|
if is_relatorio:
|
|
return relatorio_doc_administrativos(request, context)
|
|
else:
|
|
return self.render_to_response(context)
|
|
|
|
|
|
class AnexadoCrud(MasterDetailCrud):
|
|
model = Anexado
|
|
parent_field = 'documento_principal'
|
|
help_topic = 'documento_anexado'
|
|
public = [RP_LIST, RP_DETAIL]
|
|
|
|
class BaseMixin(MasterDetailCrud.BaseMixin):
|
|
list_field_names = ['documento_anexado', 'data_anexacao']
|
|
|
|
class CreateView(MasterDetailCrud.CreateView):
|
|
form_class = AnexadoForm
|
|
|
|
class UpdateView(MasterDetailCrud.UpdateView):
|
|
form_class = AnexadoForm
|
|
|
|
def get_initial(self):
|
|
initial = super(UpdateView, self).get_initial()
|
|
initial['tipo'] = self.object.documento_anexado.tipo.id
|
|
initial['numero'] = self.object.documento_anexado.numero
|
|
initial['ano'] = self.object.documento_anexado.ano
|
|
return initial
|
|
|
|
class DetailView(MasterDetailCrud.DetailView):
|
|
|
|
@property
|
|
def layout_key(self):
|
|
return 'AnexadoDetail'
|
|
|
|
|
|
class DocumentoAnexadoEmLoteView(PermissionRequiredMixin, FilterView):
|
|
filterset_class = AnexadoEmLoteFilterSet
|
|
template_name = 'protocoloadm/em_lote/anexado.html'
|
|
permission_required = ('protocoloadm.add_anexado', )
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(
|
|
DocumentoAnexadoEmLoteView, self
|
|
).get_context_data(**kwargs)
|
|
|
|
context['root_pk'] = self.kwargs['pk']
|
|
|
|
context['subnav_template_name'] = 'protocoloadm/subnav.yaml'
|
|
|
|
context['title'] = _('Documentos Anexados em Lote')
|
|
|
|
# Verifica se os campos foram preenchidos
|
|
if not self.request.GET.get('tipo', " "):
|
|
msg = _('Por favor, selecione um tipo de documento.')
|
|
messages.add_message(self.request, messages.ERROR, msg)
|
|
|
|
if not self.request.GET.get('data_0', " ") or not self.request.GET.get('data_1', " "):
|
|
msg = _('Por favor, preencha as datas.')
|
|
messages.add_message(self.request, messages.ERROR, msg)
|
|
|
|
return context
|
|
|
|
if not self.request.GET.get('data_0', " ") or not self.request.GET.get('data_1', " "):
|
|
msg = _('Por favor, preencha as datas.')
|
|
messages.add_message(self.request, messages.ERROR, msg)
|
|
return context
|
|
|
|
qr = self.request.GET.copy()
|
|
context['temp_object_list'] = context['object_list'].order_by(
|
|
'numero', '-ano'
|
|
)
|
|
|
|
context['object_list'] = []
|
|
for obj in context['temp_object_list']:
|
|
if not obj.pk == int(context['root_pk']):
|
|
documento_principal = DocumentoAdministrativo.objects.get(
|
|
id=context['root_pk'])
|
|
documento_anexado = obj
|
|
is_anexado = Anexado.objects.filter(documento_principal=documento_principal,
|
|
documento_anexado=documento_anexado).exists()
|
|
if not is_anexado:
|
|
ciclico = False
|
|
anexados_anexado = Anexado.objects.filter(
|
|
documento_principal=documento_anexado)
|
|
|
|
while anexados_anexado and not ciclico:
|
|
anexados = []
|
|
|
|
for anexo in anexados_anexado:
|
|
|
|
if documento_principal == anexo.documento_anexado:
|
|
ciclico = True
|
|
else:
|
|
for a in Anexado.objects.filter(documento_principal=anexo.documento_anexado):
|
|
anexados.append(a)
|
|
|
|
anexados_anexado = anexados
|
|
|
|
if not ciclico:
|
|
context['object_list'].append(obj)
|
|
|
|
context['numero_res'] = len(context['object_list'])
|
|
|
|
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):
|
|
marcados = request.POST.getlist('documento_id')
|
|
|
|
data_anexacao = datetime.strptime(
|
|
request.POST['data_anexacao'], "%d/%m/%Y"
|
|
).date()
|
|
|
|
if request.POST['data_desanexacao'] == '':
|
|
data_desanexacao = None
|
|
v_data_desanexacao = data_anexacao
|
|
else:
|
|
data_desanexacao = datetime.strptime(
|
|
request.POST['data_desanexacao'], "%d/%m/%Y"
|
|
).date()
|
|
v_data_desanexacao = data_desanexacao
|
|
|
|
if len(marcados) == 0:
|
|
msg = _('Nenhum documento foi selecionado')
|
|
messages.add_message(request, messages.ERROR, msg)
|
|
|
|
if data_anexacao > v_data_desanexacao:
|
|
msg = _('Data de anexação posterior à data de desanexação.')
|
|
messages.add_message(request, messages.ERROR, msg)
|
|
|
|
return self.get(request, self.kwargs)
|
|
|
|
if data_anexacao > v_data_desanexacao:
|
|
msg = _('Data de anexação posterior à data de desanexação.')
|
|
messages.add_message(request, messages.ERROR, msg)
|
|
return self.get(request, messages.ERROR, msg)
|
|
|
|
principal = DocumentoAdministrativo.objects.get(pk=kwargs['pk'])
|
|
for documento in DocumentoAdministrativo.objects.filter(id__in=marcados):
|
|
anexado = Anexado()
|
|
anexado.documento_principal = principal
|
|
anexado.documento_anexado = documento
|
|
anexado.data_anexacao = data_anexacao
|
|
anexado.data_desanexacao = data_desanexacao
|
|
anexado.save()
|
|
|
|
msg = _('Documento(s) anexado(s).')
|
|
messages.add_message(request, messages.SUCCESS, msg)
|
|
|
|
success_url = reverse('sapl.protocoloadm:anexado_list', kwargs={
|
|
'pk': kwargs['pk']})
|
|
return HttpResponseRedirect(success_url)
|
|
|
|
|
|
class TramitacaoAdmCrud(MasterDetailCrud):
|
|
model = TramitacaoAdministrativo
|
|
parent_field = 'documento'
|
|
help_topic = 'unidade_tramitacao'
|
|
|
|
class BaseMixin(MasterDetailCrud.BaseMixin):
|
|
list_field_names = ['data_tramitacao', 'unidade_tramitacao_local',
|
|
'unidade_tramitacao_destino', 'status']
|
|
|
|
class CreateView(MasterDetailCrud.CreateView):
|
|
form_class = TramitacaoAdmForm
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def get_success_url(self):
|
|
return reverse('sapl.protocoloadm:tramitacaoadministrativo_list', kwargs={
|
|
'pk': self.kwargs['pk']})
|
|
|
|
def get_initial(self):
|
|
initial = super(CreateView, self).get_initial()
|
|
local = DocumentoAdministrativo.objects.get(
|
|
pk=self.kwargs['pk']).tramitacaoadministrativo_set.order_by(
|
|
'-data_tramitacao',
|
|
'-id').first()
|
|
|
|
if local:
|
|
initial['unidade_tramitacao_local'
|
|
] = local.unidade_tramitacao_destino.pk
|
|
else:
|
|
initial['unidade_tramitacao_local'] = ''
|
|
initial['data_tramitacao'] = timezone.now().date()
|
|
initial['ip'] = get_client_ip(self.request)
|
|
initial['user'] = self.request.user
|
|
|
|
tz = timezone.get_current_timezone()
|
|
initial['ultima_edicao'] = tz.localize(datetime.now())
|
|
|
|
return initial
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
username = self.request.user.username
|
|
|
|
ultima_tramitacao = TramitacaoAdministrativo.objects.filter(
|
|
documento_id=self.kwargs['pk']).order_by(
|
|
'-data_tramitacao',
|
|
'-timestamp',
|
|
'-id').first()
|
|
|
|
# TODO: Esta checagem foi inserida na issue #2027, mas é mesmo
|
|
# necessária?
|
|
if ultima_tramitacao:
|
|
if ultima_tramitacao.unidade_tramitacao_destino:
|
|
context['form'].fields[
|
|
'unidade_tramitacao_local'].choices = [
|
|
(ultima_tramitacao.unidade_tramitacao_destino.pk,
|
|
ultima_tramitacao.unidade_tramitacao_destino)]
|
|
else:
|
|
self.logger.error('user=' + username + '. Unidade de tramitação destino '
|
|
'da última tramitação não pode ser vazia!')
|
|
msg = _('Unidade de tramitação destino '
|
|
' da última tramitação não pode ser vazia!')
|
|
messages.add_message(self.request, messages.ERROR, msg)
|
|
|
|
primeira_tramitacao = not(TramitacaoAdministrativo.objects.filter(
|
|
documento_id=int(kwargs['root_pk'])).exists())
|
|
|
|
# Se não for a primeira tramitação daquela matéria, o campo
|
|
# não pode ser modificado
|
|
if not primeira_tramitacao:
|
|
context['form'].fields[
|
|
'unidade_tramitacao_local'].widget.attrs['readonly'] = True
|
|
return context
|
|
|
|
def form_valid(self, form):
|
|
self.object = form.save()
|
|
username = self.request.user.username
|
|
try:
|
|
tramitacao_signal.send(sender=TramitacaoAdministrativo,
|
|
post=self.object,
|
|
request=self.request)
|
|
except Exception as e:
|
|
self.logger.error('user=' + username + '. Tramitação criada, mas e-mail de acompanhamento de documento '
|
|
'não enviado. A não configuração do servidor de e-mail '
|
|
'impede o envio de aviso de tramitação. ' + str(e))
|
|
msg = _('Tramitação criada, mas e-mail de acompanhamento '
|
|
'de documento 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)
|
|
return HttpResponseRedirect(self.get_success_url())
|
|
return super().form_valid(form)
|
|
|
|
class UpdateView(MasterDetailCrud.UpdateView):
|
|
form_class = TramitacaoAdmEditForm
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def get_initial(self):
|
|
initial = super(UpdateView, self).get_initial()
|
|
initial['ip'] = get_client_ip(self.request)
|
|
initial['user'] = self.request.user
|
|
|
|
tz = timezone.get_current_timezone()
|
|
initial['ultima_edicao'] = tz.localize(datetime.now())
|
|
|
|
return initial
|
|
|
|
def form_valid(self, form):
|
|
self.object = form.save()
|
|
username = self.request.user.username
|
|
try:
|
|
tramitacao_signal.send(sender=TramitacaoAdministrativo,
|
|
post=self.object,
|
|
request=self.request)
|
|
except Exception as e:
|
|
self.logger.error('user=' + username + '. Tramitação criada, mas e-mail de acompanhamento de documento '
|
|
'não enviado. A não configuração do servidor de e-mail '
|
|
'impede o envio de aviso de tramitação. ' + str(e))
|
|
msg = _('Tramitação criada, mas e-mail de acompanhamento '
|
|
'de documento 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)
|
|
return HttpResponseRedirect(self.get_success_url())
|
|
return super().form_valid(form)
|
|
|
|
class ListView(DocumentoAdministrativoMixin, MasterDetailCrud.ListView):
|
|
|
|
def get_queryset(self):
|
|
qs = super(MasterDetailCrud.ListView, self).get_queryset()
|
|
kwargs = {self.crud.parent_field: self.kwargs['pk']}
|
|
return qs.filter(**kwargs).order_by('-data_tramitacao',
|
|
'-id')
|
|
|
|
class DetailView(DocumentoAdministrativoMixin,
|
|
MasterDetailCrud.DetailView):
|
|
|
|
template_name = 'protocoloadm/tramitacaoadministrativo_detail.html'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['user'] = self.request.user
|
|
return context
|
|
|
|
class DeleteView(MasterDetailCrud.DeleteView):
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def delete(self, request, *args, **kwargs):
|
|
tramitacao = TramitacaoAdministrativo.objects.get(
|
|
id=self.kwargs['pk'])
|
|
documento = tramitacao.documento
|
|
url = reverse(
|
|
'sapl.protocoloadm:tramitacaoadministrativo_list',
|
|
kwargs={'pk': documento.id})
|
|
|
|
ultima_tramitacao = \
|
|
documento.tramitacaoadministrativo_set.order_by(
|
|
'-data_tramitacao',
|
|
'-id').first()
|
|
|
|
if tramitacao.pk != ultima_tramitacao.pk:
|
|
username = request.user.username
|
|
self.logger.error("user=" + username + ". Não é possível deletar a tramitação de pk={}. "
|
|
"Somente a última tramitação (pk={}) pode ser deletada!."
|
|
.format(tramitacao.pk, ultima_tramitacao.pk))
|
|
msg = _('Somente a última tramitação pode ser deletada!')
|
|
messages.add_message(request, messages.ERROR, msg)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
tramitacoes_deletar = [tramitacao]
|
|
if documento.tramitacaoadministrativo_set.count() == 0:
|
|
documento.tramitacao = False
|
|
documento.save()
|
|
tramitar_anexados = AppConfig.attr('tramitacao_documento')
|
|
if tramitar_anexados:
|
|
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)
|
|
if da.tramitacaoadministrativo_set.count() == 0:
|
|
da.tramitacao = False
|
|
da.save()
|
|
TramitacaoAdministrativo.objects.filter(
|
|
id__in=[t.id for t in tramitacoes_deletar]).delete()
|
|
|
|
# TODO: otimizar para passar a lista de matérias
|
|
for tramitacao in tramitacoes_deletar:
|
|
post_delete_signal.send(sender=None,
|
|
instance=tramitacao,
|
|
operation='C',
|
|
request=self.request)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
class DocumentoAcessorioAdministrativoCrud(MasterDetailCrud):
|
|
model = DocumentoAcessorioAdministrativo
|
|
parent_field = 'documento'
|
|
help_topic = 'numeracao_docsacess'
|
|
|
|
class BaseMixin(MasterDetailCrud.BaseMixin):
|
|
list_field_names = ['nome', 'tipo',
|
|
'data', 'autor',
|
|
'assunto']
|
|
|
|
class CreateView(MasterDetailCrud.CreateView):
|
|
form_class = DocumentoAcessorioAdministrativoForm
|
|
|
|
class UpdateView(MasterDetailCrud.UpdateView):
|
|
form_class = DocumentoAcessorioAdministrativoForm
|
|
|
|
class ListView(DocumentoAdministrativoMixin, MasterDetailCrud.ListView):
|
|
|
|
def get_queryset(self):
|
|
qs = super(MasterDetailCrud.ListView, self).get_queryset()
|
|
kwargs = {self.crud.parent_field: self.kwargs['pk']}
|
|
return qs.filter(**kwargs).order_by('-data', '-id')
|
|
|
|
class DetailView(DocumentoAdministrativoMixin,
|
|
MasterDetailCrud.DetailView):
|
|
pass
|
|
|
|
|
|
def atualizar_numero_documento(request):
|
|
tipo = TipoDocumentoAdministrativo.objects.get(pk=request.GET['tipo'])
|
|
ano = request.GET['ano']
|
|
|
|
param = {'tipo': tipo}
|
|
param['ano'] = ano if ano else timezone.now().year
|
|
|
|
doc = DocumentoAdministrativo.objects.filter(**param).order_by(
|
|
'tipo', 'ano', 'numero').values_list('numero', 'ano').last()
|
|
|
|
if doc:
|
|
response = JsonResponse({'numero': int(doc[0]) + 1,
|
|
'ano': doc[1]})
|
|
else:
|
|
response = JsonResponse(
|
|
{'numero': 1, 'ano': ano})
|
|
|
|
return response
|
|
|
|
|
|
class DesvincularDocumentoView(PermissionRequiredMixin, CreateView):
|
|
template_name = 'protocoloadm/anular_protocoloadm.html'
|
|
form_class = DesvincularDocumentoForm
|
|
form_valid_message = _('Documento desvinculado com sucesso!')
|
|
permission_required = ('protocoloadm.action_anular_protocolo', )
|
|
|
|
def get_success_url(self):
|
|
return reverse('sapl.protocoloadm:protocolo')
|
|
|
|
def form_valid(self, form):
|
|
documento = DocumentoAdministrativo.objects.get(numero=form.cleaned_data['numero'],
|
|
ano=form.cleaned_data['ano'],
|
|
tipo=form.cleaned_data['tipo'])
|
|
documento.protocolo = None
|
|
|
|
documento.user = self.request.user
|
|
documento.ip = get_client_ip(self.request)
|
|
|
|
tz = timezone.get_current_timezone()
|
|
documento.ultima_edicao = tz.localize(datetime.now())
|
|
|
|
documento.save()
|
|
return redirect(self.get_success_url())
|
|
|
|
|
|
class DesvincularMateriaView(PermissionRequiredMixin, FormView):
|
|
template_name = 'protocoloadm/anular_protocoloadm.html'
|
|
form_class = DesvincularMateriaForm
|
|
form_valid_message = _('Matéria desvinculado com sucesso!')
|
|
permission_required = ('protocoloadm.action_anular_protocolo', )
|
|
|
|
def get_success_url(self):
|
|
return reverse('sapl.protocoloadm:protocolo')
|
|
|
|
def form_valid(self, form):
|
|
materia = MateriaLegislativa.objects.get(numero=form.cleaned_data['numero'],
|
|
ano=form.cleaned_data['ano'],
|
|
tipo=form.cleaned_data['tipo'])
|
|
materia.numero_protocolo = None
|
|
materia.save()
|
|
return redirect(self.get_success_url())
|
|
|
|
|
|
class ImpressosView(PermissionRequiredMixin, TemplateView):
|
|
template_name = 'materia/impressos/impressos.html'
|
|
permission_required = ('materia.can_access_impressos', )
|
|
|
|
|
|
class FichaPesquisaAdmView(PermissionRequiredMixin, FormView):
|
|
form_class = FichaPesquisaAdmForm
|
|
template_name = 'materia/impressos/impressos_form.html'
|
|
permission_required = ('materia.can_access_impressos', )
|
|
|
|
def form_valid(self, form):
|
|
tipo_documento = form.data['tipo_documento']
|
|
data_inicial = form.data['data_inicial']
|
|
data_final = form.data['data_final']
|
|
|
|
url = reverse('sapl.materia:impressos_ficha_seleciona_adm')
|
|
url = url + '?tipo=%s&data_inicial=%s&data_final=%s' % (
|
|
tipo_documento, data_inicial, data_final)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
class FichaSelecionaAdmView(PermissionRequiredMixin, FormView):
|
|
logger = logging.getLogger(__name__)
|
|
form_class = FichaSelecionaAdmForm
|
|
template_name = 'materia/impressos/impressos_form.html'
|
|
permission_required = ('materia.can_access_impressos', )
|
|
|
|
def get_context_data(self, **kwargs):
|
|
if ('tipo' not in self.request.GET or
|
|
'data_inicial' not in self.request.GET or
|
|
'data_final' not in self.request.GET):
|
|
return HttpResponseRedirect(reverse(
|
|
'sapl.materia:impressos_ficha_pesquisa_adm'))
|
|
|
|
context = super(FichaSelecionaAdmView, self).get_context_data(
|
|
**kwargs)
|
|
|
|
tipo = self.request.GET['tipo']
|
|
data_inicial = datetime.strptime(
|
|
self.request.GET['data_inicial'], "%d/%m/%Y").date()
|
|
data_final = datetime.strptime(
|
|
self.request.GET['data_final'], "%d/%m/%Y").date()
|
|
|
|
documento_list = DocumentoAdministrativo.objects.filter(
|
|
tipo=tipo,
|
|
data__range=(data_inicial, data_final))
|
|
context['quantidade'] = len(documento_list)
|
|
documento_list = documento_list[:100]
|
|
|
|
context['form'].fields['documento'].choices = [
|
|
(d.id, str(d)) for d in documento_list]
|
|
|
|
username = self.request.user.username
|
|
|
|
if context['quantidade'] > 100:
|
|
self.logger.info('user=' + username + '. Sua pesquisa (tipo={}, data_inicial={}, data_final={}) retornou mais do que '
|
|
'100 impressos. Por questões de '
|
|
'performance, foram retornados '
|
|
'apenas os 100 primeiros. Caso '
|
|
'queira outros, tente fazer uma '
|
|
'pesquisa mais específica'.format(tipo, data_inicial, data_final))
|
|
messages.info(self.request, _('Sua pesquisa retornou mais do que '
|
|
'100 impressos. Por questões de '
|
|
'performance, foram retornados '
|
|
'apenas os 100 primeiros. Caso '
|
|
'queira outros, tente fazer uma '
|
|
'pesquisa mais específica'))
|
|
|
|
return context
|
|
|
|
def form_valid(self, form):
|
|
context = {}
|
|
username = self.request.user.username
|
|
|
|
try:
|
|
self.logger.debug(
|
|
"user=" + username + ". Tentando obter objeto DocumentoAdministrativo com id={}".format(form.data['documento']))
|
|
documento = DocumentoAdministrativo.objects.get(
|
|
id=form.data['documento'])
|
|
except ObjectDoesNotExist:
|
|
self.logger.error(
|
|
"user=" + username + ". Este DocumentoAdministrativo não existe (id={}).".format(form.data['documento']))
|
|
mensagem = _('Este Documento Administrativo não existe.')
|
|
self.messages.add_message(self.request, messages.INFO, mensagem)
|
|
|
|
return self.render_to_response(context)
|
|
if len(documento.assunto) > 201:
|
|
documento.assunto = documento.assunto[0:200] + '[...]'
|
|
context['documento'] = documento
|
|
|
|
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 = ('protocoloadm.add_tramitacaoadministrativo', )
|
|
|
|
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)
|
|
|
|
tz = timezone.get_current_timezone()
|
|
ultima_edicao = tz.localize(datetime.now())
|
|
|
|
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,
|
|
'ultima_edicao': ultima_edicao})
|
|
|
|
if form.is_valid():
|
|
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'] = self.primeira_tramitacao
|
|
|
|
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)
|
|
|