diff --git a/sigi/apps/casas/models.py b/sigi/apps/casas/models.py index e9ec425..35deb89 100644 --- a/sigi/apps/casas/models.py +++ b/sigi/apps/casas/models.py @@ -175,6 +175,15 @@ class Orgao(models.Model): def contato_interlegis(self): return self.funcionario_set.filter(setor="contato_interlegis").first() + def get_sigla(self): + return self.sigla or "".join( + [ + w[0] + for w in self.nome.split() + if w.lower() not in ["da", "de", "do"] + ] + ) + def __str__(self): return self.nome diff --git a/sigi/apps/convenios/migrations/0031_alter_projeto_modelo_minuta_and_more.py b/sigi/apps/convenios/migrations/0031_alter_projeto_modelo_minuta_and_more.py new file mode 100644 index 0000000..6b13fcb --- /dev/null +++ b/sigi/apps/convenios/migrations/0031_alter_projeto_modelo_minuta_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 4.0.5 on 2022-07-04 12:36 + +import django.core.validators +from django.db import migrations, models +import tinymce.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('convenios', '0030_alter_projeto_modelo_minuta_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='projeto', + name='modelo_minuta', + field=models.FileField(blank=True, help_text='\nUtilize os seguintes placeholders\n\n', upload_to='convenios/minutas/', validators=[django.core.validators.FileExtensionValidator(['docx'])], verbose_name='Modelo de minuta'), + ), + migrations.AlterField( + model_name='projeto', + name='texto_oficio', + field=tinymce.models.HTMLField(blank=True, help_text='\nUtilize os seguintes placeholders\n\n', verbose_name='texto do ofício'), + ), + ] diff --git a/sigi/apps/convenios/models.py b/sigi/apps/convenios/models.py index abd8143..5d660ea 100644 --- a/sigi/apps/convenios/models.py +++ b/sigi/apps/convenios/models.py @@ -4,11 +4,16 @@ from django.db import models from django.db.models import Q, fields from django.core.mail import send_mail from django.core.validators import FileExtensionValidator +from django.template import Template, Context +from django.template.exceptions import TemplateSyntaxError from django.urls import reverse from django.utils import timezone from django.utils.formats import date_format from django.utils.translation import gettext as _ +from django_weasyprint.utils import django_url_fetcher +from docx import Document from tinymce.models import HTMLField +from weasyprint import HTML from sigi.apps.contatos.models import Municipio, UnidadeFederativa from sigi.apps.eventos.models import Evento from sigi.apps.parlamentares.models import Parlamentar @@ -22,7 +27,6 @@ class Projeto(models.Model): OFICIO_HELP = editor_help( "texto_oficio", [ - ("evento", Evento), ("casa", Orgao), ("presidente", Parlamentar), ("contato", Funcionario), @@ -35,7 +39,6 @@ class Projeto(models.Model): MINUTA_HELP = editor_help( "modelo_minuta", [ - ("evento", Evento), ("casa", Orgao), ("presidente", Parlamentar), ("contato", Funcionario), @@ -71,6 +74,84 @@ class Projeto(models.Model): class Meta: ordering = ("nome",) + def gerar_oficio(self, file_object, casa, presidente, contato, path): + texto = self.texto_oficio + template_string = ( + '{% extends "convenios/oficio_padrao.html" %}' + "{% load pdf %}" + f"{{% block text_body %}}{texto}{{% endblock %}}" + ) + context = Context( + { + "casa": casa, + "presidente": presidente, + "contato": contato, + "data": timezone.localdate(), + "doravante": casa.tipo.nome.split(" ")[0], + } + ) + string = Template(template_string).render(context) + pdf = HTML( + string=string, + url_fetcher=django_url_fetcher, + encoding="utf-8", + base_url=path, + ) + if file_object.closed: + file_object.open(mode="wb") + pdf.write_pdf(target=file_object) + file_object.flush() + + def gerar_minuta(self, file_path, casa, presidente, contato): + doc = Document(self.modelo_minuta.path) + + if casa.tipo.sigla == "CM": + ente = ( + f"Município de {casa.municipio.nome}, " + f"{casa.municipio.uf.sigla}" + ) + else: + ente = f"Estado de {casa.municipio.uf.nome}" + + doc_context = Context( + { + "casa": casa, + "presidente": presidente, + "contato": contato, + "data": timezone.localdate(), + "ente": ente, + "doravante": casa.tipo.nome.split(" ")[0], + } + ) + + self.processa_paragrafos(doc.paragraphs, doc_context) + for table in doc.tables: + for row in table.rows: + for cell in row.cells: + self.processa_paragrafos( + cell.paragraphs, + doc_context, + ) + + doc.save(file_path) + + def processa_paragrafos(self, paragrafos, context): + for paragrafo in paragrafos: + run_final = None + for run in paragrafo.runs: + if run_final is None: + run_final = run + else: + run_final.text += run.text + run.text = "" + if run_final.text.count("{{") != run_final.text.count("}}"): + continue + try: + run_final.text = Template(run_final.text).render(context) + run_final = None + except TemplateSyntaxError: + pass + class StatusConvenio(models.Model): nome = models.CharField(max_length=100) diff --git a/sigi/apps/eventos/templates/eventos/oficio_padrao.html b/sigi/apps/convenios/templates/convenios/oficio_padrao.html similarity index 100% rename from sigi/apps/eventos/templates/eventos/oficio_padrao.html rename to sigi/apps/convenios/templates/convenios/oficio_padrao.html diff --git a/sigi/apps/eventos/views.py b/sigi/apps/eventos/views.py index 620f310..90f8eab 100644 --- a/sigi/apps/eventos/views.py +++ b/sigi/apps/eventos/views.py @@ -10,7 +10,6 @@ from django.contrib.auth.decorators import login_required from django.http import HttpResponse from django.shortcuts import redirect, render, get_object_or_404 from django.template import Template, Context -from django.template.exceptions import TemplateSyntaxError from django.utils import timezone from django.utils.text import slugify from django.utils.translation import ( @@ -22,7 +21,6 @@ from django.utils.translation import ( from django.urls import reverse from django_weasyprint.utils import django_url_fetcher from django_weasyprint.views import WeasyTemplateResponse -from docx import Document from weasyprint import HTML from sigi.apps.casas.models import Funcionario, Orgao from sigi.apps.convenios.models import Projeto @@ -208,23 +206,6 @@ def evento(request, id): def convida_casa(request, evento_id, casa_id): - def processa_paragrafos(paragrafos, context): - for paragrafo in paragrafos: - run_final = None - for run in paragrafo.runs: - if run_final is None: - run_final = run - else: - run_final.text += run.text - run.text = "" - if run_final.text.count("{{") != run_final.text.count("}}"): - continue - try: - run_final.text = Template(run_final.text).render(context) - run_final = None - except TemplateSyntaxError: - pass - if not request.user.servidor: messages.error( request, _("Você não é servidor, não pode registrar convites") @@ -294,52 +275,39 @@ def convida_casa(request, evento_id, casa_id): query_str = "" projeto = get_object_or_404(Projeto, id=proj_id) if projeto.texto_oficio: - oficio = gerar_anexo( + oficio = Anexo( + evento=evento, + descricao=f"Ofício de solicitação de {projeto.sigla}", + ) + sigla_casa = casa.sigla or "".join( + [ + w[0].upper() + for w in casa.nome.replace("de", "").split() + ] + ) + oficio.arquivo.name = ( + f"{Anexo.arquivo.field.upload_to}/" + f"oficio_{projeto.sigla}_{sigla_casa}.pdf" + ) + projeto.gerar_oficio( + oficio.arquivo, casa, presidente, contato, - path=request.build_absolute_uri("/"), - nome=f"Ofício de solicitação de {projeto.sigla}", - modelo="oficio_padrao.html", - texto=projeto.texto_oficio, + request.build_absolute_uri("/"), ) - oficio.evento = evento oficio.save() query_str += f"anexo_id={oficio.id}&" if projeto.modelo_minuta: - doc = Document(projeto.modelo_minuta.path) - if casa.tipo.sigla == "CM": - ente = ( - f"Município de {casa.municipio.nome}, " - f"{casa.municipio.uf.sigla}" - ) - else: - ente = f"Estado de {casa.municipio.uf.nome}" - doc_context = Context( - { - "evento": evento, - "casa": casa, - "presidente": presidente, - "contato": contato, - "data": timezone.localdate(), - "ente": ente, - "doravante": casa.tipo.nome.split(" ")[0], - } - ) - processa_paragrafos(doc.paragraphs, doc_context) - for table in doc.tables: - for row in table.rows: - for cell in row.cells: - processa_paragrafos( - cell.paragraphs, - doc_context, - ) nome = f"Minuta de {projeto.sigla} da {casa.nome}"[:70] minuta = Anexo(descricao=nome, evento=evento) minuta.arquivo.name = ( - f"{Anexo.arquivo.field.upload_to}/{slugify(nome)}.docx" + f"{Anexo.arquivo.field.upload_to}/" + f"minuta_{projeto.sigla}_{sigla_casa}.docx" + ) + projeto.gerar_minuta( + minuta.arquivo.path, casa, presidente, contato ) - doc.save(minuta.arquivo.path) minuta.save() query_str += f"anexo_id={minuta.id}" @@ -393,39 +361,6 @@ def presidente_form(request, presidente_id): ) -def gerar_anexo(casa, presidente, contato, path, modelo, nome, texto): - template_string = ( - f'{{% extends "eventos/{modelo}" %}}' - "{% load pdf %}" - f"{{% block text_body %}}{texto}{{% endblock %}}" - ) - context = Context( - { - "evento": evento, - "casa": casa, - "presidente": presidente, - "contato": contato, - "data": timezone.localdate(), - "doravante": casa.tipo.nome.split(" ")[0], - } - ) - string = Template(template_string).render(context) - pdf = HTML( - string=string, - url_fetcher=django_url_fetcher, - encoding="utf-8", - base_url=path, - ) - nome = (nome + f" da {casa.nome}")[:70] - anexo = Anexo(descricao=nome) - anexo.arquivo.name = slugify(nome) + ".pdf" - f = anexo.arquivo.open("wb") - pdf.write_pdf(target=f) - f.flush() - f.close() - return anexo - - @login_required def alocacao_equipe(request): ano_pesquisa = int(request.GET.get("ano", timezone.localdate().year)) diff --git a/sigi/apps/home/templates/home/openmap.html b/sigi/apps/home/templates/home/openmap.html index ea919f0..f0ead55 100644 --- a/sigi/apps/home/templates/home/openmap.html +++ b/sigi/apps/home/templates/home/openmap.html @@ -34,6 +34,7 @@ {% block usertools %}
{% trans "Entrar" %} + {% trans "Solicitar convênio" %}
{% endblock %} @@ -49,9 +50,23 @@ {% endblock %} {% block content %} -
- -
+ + +
+ +
{% endblock %} {% block sidebar %}{% endblock %} @@ -65,6 +80,10 @@ var options = { color: 'blue', fillColor: 'red', fillOpacity: 0.4, radius: 500 }; var unfiltred_options = { color: 'red', fillColor: 'red', fillOpacity: 0, radius: 1000 }; $(document).ready(function () { + var aviso = M.Modal.init($('#aviso')); + aviso[0].open(); + + $("input[type=checkbox]").change(filtra); mymap = L.map('map', { zoomSnap: 0.01 }).setView(map_center, 4.5); diff --git a/sigi/apps/ocorrencias/admin_urls.py b/sigi/apps/ocorrencias/admin_urls.py new file mode 100644 index 0000000..b4a54bd --- /dev/null +++ b/sigi/apps/ocorrencias/admin_urls.py @@ -0,0 +1,12 @@ +from django.urls import path +from sigi.apps.ocorrencias import views + +urlpatterns = [ + path("painel/", views.painel_ocorrencias, name="painel-ocorrencias"), + path("convenio/painel/", views.painel_convenio, name="painel-convenio"), + path( + "convenio/painel//", + views.painel_convenio, + name="painel-convenio", + ), +] diff --git a/sigi/apps/ocorrencias/forms.py b/sigi/apps/ocorrencias/forms.py index 51bddec..ec2ac57 100644 --- a/sigi/apps/ocorrencias/forms.py +++ b/sigi/apps/ocorrencias/forms.py @@ -1,14 +1,18 @@ from django import forms -from sigi.apps.ocorrencias.models import Ocorrencia, Comentario, Anexo -from sigi.apps.servidores.models import Servico -from django.utils.encoding import force_str -from django.utils.html import format_html +from django.contrib import admin +from django.contrib.admin.widgets import AutocompleteSelect +from django.core.validators import FileExtensionValidator from django.forms.utils import flatatt from django.urls import reverse_lazy +from django.utils.encoding import force_str +from django.utils.html import format_html from django.utils.safestring import mark_safe +from django.utils.translation import ngettext, gettext as _ from material.admin.widgets import MaterialAdminTextareaWidget -from django.contrib.admin.widgets import AutocompleteSelect -from django.contrib import admin +from sigi.apps.casas.models import Funcionario, Orgao +from sigi.apps.ocorrencias.models import Ocorrencia, Comentario, Anexo +from sigi.apps.servidores.models import Servico +from sigi.apps.parlamentares.models import Parlamentar class AjaxSelect(forms.TextInput): @@ -96,3 +100,98 @@ class OcorrenciaForm(forms.ModelForm): # attrs={'size':100} # ), # } + + +class OcorrenciaChangeForm(forms.ModelForm): + class Meta: + model = Ocorrencia + fields = ["prioridade", "processo_sigad"] + + +class CasaForm(forms.ModelForm): + class Meta: + model = Orgao + fields = [ + "cnpj", + "data_instalacao", + "horario_funcionamento", + "logradouro", + "bairro", + "cep", + "telefone_geral", + "email", + "pagina_web", + "foto", + "brasao", + ] + + +class PresidenteForm(forms.ModelForm): + parlamentar = forms.ModelChoiceField(queryset=Parlamentar.objects.none()) + + class Meta: + model = Parlamentar + fields = [ + "parlamentar", + "data_nascimento", + "cpf", + "identidade", + "telefones", + "email", + "redes_sociais", + ] + widgets = { + "redes_sociais": MaterialAdminTextareaWidget, + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields[ + "parlamentar" + ].queryset = self.instance.casa_legislativa.parlamentar_set.all() + + +class ContatoForm(forms.ModelForm): + class Meta: + model = Funcionario + fields = [ + "nome", + "sexo", + "cpf", + "identidade", + "nota", + "email", + "redes_sociais", + ] + widgets = { + "nota": MaterialAdminTextareaWidget, + "redes_sociais": MaterialAdminTextareaWidget, + } + + +class DocumentoForm(forms.ModelForm): + arquivo = forms.FileField( + label=_("Solicitação assinada"), + help_text=_("Utilize o formato PDF apenas"), + validators=[ + FileExtensionValidator( + ["pdf"], _("Somente arquivos em formato PDF"), "pdf_only" + ) + ], + ) + + class Meta: + model = Anexo + fields = ["arquivo"] + + +class ComentarioForm(forms.ModelForm): + class Meta: + model = Comentario + fields = ["descricao"] + + +class ComentarioInternoForm(forms.ModelForm): + class Meta: + model = Comentario + fields = ["descricao", "interno", "novo_status"] diff --git a/sigi/apps/ocorrencias/migrations/0009_alter_comentario_options_categoria_projeto_and_more.py b/sigi/apps/ocorrencias/migrations/0009_alter_comentario_options_categoria_projeto_and_more.py new file mode 100644 index 0000000..7a10f0c --- /dev/null +++ b/sigi/apps/ocorrencias/migrations/0009_alter_comentario_options_categoria_projeto_and_more.py @@ -0,0 +1,60 @@ +# Generated by Django 4.0.5 on 2022-07-07 21:51 + +import django.core.serializers.json +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('convenios', '0031_alter_projeto_modelo_minuta_and_more'), + ('ocorrencias', '0008_remove_categoria_setor_responsavel'), + ] + + operations = [ + migrations.AlterModelOptions( + name='comentario', + options={'ordering': ['-data_criacao'], 'verbose_name': 'comentário', 'verbose_name_plural': 'comentários'}, + ), + migrations.AddField( + model_name='categoria', + name='projeto', + field=models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(('texto_oficio', ''), _negated=True), models.Q(('modelo_minuta', ''), _negated=True)), null=True, on_delete=django.db.models.deletion.PROTECT, to='convenios.projeto', verbose_name='projeto'), + ), + migrations.AddField( + model_name='categoria', + name='tipo', + field=models.CharField(choices=[('C', 'Solicitação de convênio (ACT)'), ('O', 'Outras')], default='O', max_length=1, verbose_name='Tipo de solicitação'), + ), + migrations.AddField( + model_name='comentario', + name='interno', + field=models.BooleanField(default=False, verbose_name='Comentário interno'), + ), + migrations.AddField( + model_name='ocorrencia', + name='casa_brasao', + field=models.ImageField(blank=True, null=True, upload_to='ocorrencias/img/', verbose_name='brasão da casa'), + ), + migrations.AddField( + model_name='ocorrencia', + name='casa_foto', + field=models.ImageField(blank=True, editable=False, null=True, upload_to='ocorrencias/img/', verbose_name='foto da Casa'), + ), + migrations.AddField( + model_name='ocorrencia', + name='infos', + field=models.JSONField(blank=True, editable=False, encoder=django.core.serializers.json.DjangoJSONEncoder, null=True, verbose_name='dados estruturados'), + ), + migrations.AddField( + model_name='ocorrencia', + name='processo_sigad', + field=models.CharField(blank=True, max_length=20, verbose_name='Nº processo SIGAD'), + ), + migrations.AddField( + model_name='tipocontato', + name='ind_site', + field=models.BooleanField(default=False, verbose_name='Contato pelo SIGI'), + ), + ] diff --git a/sigi/apps/ocorrencias/models.py b/sigi/apps/ocorrencias/models.py index 3ea0928..f37f7ed 100644 --- a/sigi/apps/ocorrencias/models.py +++ b/sigi/apps/ocorrencias/models.py @@ -1,13 +1,37 @@ +from collections import OrderedDict from django.db import models from django.conf import settings from django.utils.translation import gettext as _ from django.core.exceptions import ValidationError from django.utils.safestring import mark_safe +from django.core.serializers.json import DjangoJSONEncoder + +from sigi.apps.convenios.models import Projeto class Categoria(models.Model): + TIPO_CHOICES = ( + ("C", _("Solicitação de convênio (ACT)")), + ("O", _("Outras")), + ) nome = models.CharField(_("Categoria"), max_length=50) descricao = models.TextField(_("descrição"), blank=True, null=True) + tipo = models.CharField( + _("Tipo de solicitação"), + max_length=1, + choices=TIPO_CHOICES, + default="O", + ) + projeto = models.ForeignKey( + Projeto, + verbose_name=_("projeto"), + blank=True, + null=True, + on_delete=models.PROTECT, + limit_choices_to=( + ~models.Q(texto_oficio="") & ~models.Q(modelo_minuta="") + ), + ) class Meta: verbose_name = _("Categoria") @@ -17,9 +41,15 @@ class Categoria(models.Model): def __str__(self): return self.nome + def save(self, *args, **kwargs): + if self.tipo == "C": + Categoria.objects.filter(tipo="C").update(tipo="O") + return super().save(*args, **kwargs) + class TipoContato(models.Model): descricao = models.CharField(_("Descrição"), max_length=50) + ind_site = models.BooleanField(_("Contato pelo SIGI"), default=False) class Meta: verbose_name = _("Tipo de contato") @@ -28,6 +58,11 @@ class TipoContato(models.Model): def __str__(self): return self.descricao + def save(self, *args, **kwargs): + if self.ind_site: + TipoContato.objects.filter(ind_site=True).update(ind_site=False) + return super().save(*args, **kwargs) + class Ocorrencia(models.Model): STATUS_ABERTO = 1 @@ -52,6 +87,13 @@ class Ocorrencia(models.Model): (5, _("Baixíssimo")), ) + INFO_KEYS = ( + ("casa_legislativa", _("Casa legislativa")), + ("presidente", _("Presidente")), + ("contato", _("Contato Interlegis")), + ("documento", _("Documento assinado")), + ) + casa_legislativa = models.ForeignKey( "casas.Orgao", on_delete=models.CASCADE, @@ -93,6 +135,30 @@ class Ocorrencia(models.Model): null=True, help_text=_("Número do ticket no osTicket"), ) + processo_sigad = models.CharField( + _("Nº processo SIGAD"), max_length=20, blank=True + ) + infos = models.JSONField( + _("dados estruturados"), + blank=True, + null=True, + encoder=DjangoJSONEncoder, + editable=False, + ) + casa_foto = models.ImageField( + _("foto da Casa"), + upload_to="ocorrencias/img/", + blank=True, + null=True, + editable=False, + ) + casa_brasao = models.ImageField( + _("brasão da casa"), + upload_to="ocorrencias/img/", + blank=True, + null=True, + editable=True, + ) class Meta: verbose_name = _("ocorrência") @@ -125,6 +191,12 @@ class Ocorrencia(models.Model): def get_ticket_url(self): return mark_safe(settings.OSTICKET_URL % self.ticket) + def get_infos_details(self): + infos = self.infos or {} + return OrderedDict( + {key: [key in infos, label] for key, label in Ocorrencia.INFO_KEYS} + ) + class Comentario(models.Model): ocorrencia = models.ForeignKey( @@ -148,6 +220,12 @@ class Comentario(models.Model): blank=True, null=True, ) + interno = models.BooleanField(_("Comentário interno"), default=False) + + class Meta: + verbose_name = _("comentário") + verbose_name_plural = _("comentários") + ordering = ["-data_criacao"] def save(self, *args, **kwargs): if self.novo_status and (self.novo_status != self.ocorrencia.status): diff --git a/sigi/apps/ocorrencias/static/ocorrencias/css/ocorrencia.css b/sigi/apps/ocorrencias/static/ocorrencias/css/ocorrencia.css new file mode 100644 index 0000000..80af5b7 --- /dev/null +++ b/sigi/apps/ocorrencias/static/ocorrencias/css/ocorrencia.css @@ -0,0 +1,69 @@ +#content { + display: block; +} + +#container.indent { + padding-left: unset; +} + +.main-content { + margin-top: 24px; +} + +.card .card-content p { + margin: 0 0 1em 0; +} + +.search-result { + padding: 3em; +} + +.search-result-item { + display: list-item; + list-style-type: none; + padding: 3px 3px 3px 20px; + font-weight: bold; +} + +.tab-panel { + background-color: #fff; + height: 100%; +} + +.tab-content { + padding: 10px 24px; + height: 100%; + overflow-y: auto; + scrollbar-width: thin; +} + +.tab-content::-webkit-scrollbar { + width: 3px; + background-color: #fff; +} + +.tab-content::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 2px #fff; + background-color: #fff; + border-radius: 10px; +} + +.tab-content::-webkit-scrollbar-thumb { + border-radius: 10px; + -webkit-box-shadow: inset 0 0 2px var(--main-hover-color); + background-color: var(--main-hover-color); +} + +span.title { + color: var(--main-bg-color); + border-color: var(--main-bg-color); + display: block; + line-height: 32px; + margin-bottom: 8px; + font-size: 24px; + font-weight: 300; +} + +.dropdown-content.select-dropdown { + top: 0px !important; +} \ No newline at end of file diff --git a/sigi/apps/ocorrencias/templates/ocorrencias/convenio/atualiza_casa.html b/sigi/apps/ocorrencias/templates/ocorrencias/convenio/atualiza_casa.html new file mode 100644 index 0000000..5e46cb7 --- /dev/null +++ b/sigi/apps/ocorrencias/templates/ocorrencias/convenio/atualiza_casa.html @@ -0,0 +1,53 @@ +{% extends "ocorrencias/convenio/base_convenio.html" %} +{% load static i18n %} + +{% block content %} +
+
+
+
+
+ {% trans "Solicitar convênio" %} + {% blocktrans %} +

Para que uma Casa Legislativa possa utilizar, gratuitamente, os + serviços do Interlegis / Senado Federal, é necessário formalizar + um convênio, na forma de um Acordo de Cooperação Técnica (ACT), + na forma da lei Lei Nº 14.133/2021 + e da lei Lei Nº 8.666/1993. +

+

Para solicitar o ACT, serão necessárias as seguintes informações:

+
    +
  • Dados cadastrais da Casa Legislativa, como CNPJ, endereço, e-mail, telefone.
  • +
  • Dados cadastrais do Presidente, como nome, CPF, identidade, e-mail, telefone, redes sociais.
  • +
  • Designação de um servidor como Contato Interlegis.
  • +
+ {% endblocktrans %} +
+
+
+
+
+
+
+
+ {% trans "Identifique sua Casa Legislativa" %} +

{% trans "Informe o nome do município ou Estado da sua Casa Legislativa" %}:

+
+ search + +
+
+
+
+
+
+{% endblock %} + +{% block footer %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/sigi/apps/ocorrencias/templates/ocorrencias/convenio/base_convenio.html b/sigi/apps/ocorrencias/templates/ocorrencias/convenio/base_convenio.html new file mode 100644 index 0000000..c62f864 --- /dev/null +++ b/sigi/apps/ocorrencias/templates/ocorrencias/convenio/base_convenio.html @@ -0,0 +1,48 @@ +{% extends "admin/index.html" %} +{% load static i18n %} + +{% block extrastyle %} +{{ block.super }} + + + +{% endblock %} + +{% block extrahead %} +{{ block.super }} + + + + + + + +{% endblock %} + +{% block usertools %} + +{% endblock %} + +{% block side_nav %} +{% endblock %} + +{% block content %} +
+ +
+{% endblock %} + +{% block sidebar %}{% endblock %} + +{% block footer %} +{{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/sigi/apps/ocorrencias/templates/ocorrencias/convenio/ocorrencia.html b/sigi/apps/ocorrencias/templates/ocorrencias/convenio/ocorrencia.html new file mode 100644 index 0000000..6b51ea7 --- /dev/null +++ b/sigi/apps/ocorrencias/templates/ocorrencias/convenio/ocorrencia.html @@ -0,0 +1,216 @@ +{% extends "ocorrencias/convenio/base_convenio.html" %} +{% load static i18n %} + +{% block content %} +
+
+
+ {{ ocorrencia.assunto }} +

{% blocktrans with name=ocorrencia.casa_legislativa.nome %}A solicitação da {{ name }} está na nossa base de dados {% endblocktrans %}

+
+
+
+
+ +
+ {% if 'casa_legislativa' not in infos.aplicados %} +
+
+
+
+ {% csrf_token %} +
+
+ + {% trans "Atualizar as informações cadastrais da Casa" %} + + {{ casa_form }} +
+ +
+
+
+
+
+
+
+ {% endif %} + {% if 'presidente' not in infos.aplicados %} +
+
+
+
+ {% csrf_token %} +
+
+ + {% trans "Selecionar e atualizar o presidente" %} + + {{ presidente_form }} +
+ +
+
+
+
+
+
+
+ {% endif %} + {% if 'contato' not in infos.aplicados %} +
+
+
+
+ {% csrf_token %} +
+
+ {% trans "Contato Interlegis" %} +

Designe um servidor da Casa Legislativa para ser o contato técnico junto ao Interlegis.

+

Este servidor será o responsável por acompanhar a formalização do ACT, prestar informações e esclarecimentos, e solicitar a instalação de produtos e serviços ao Interlegis.

+ {{ contato_form }} +
+ +
+
+
+
+
+
+
+ {% endif %} +
+
+
+
+ {% csrf_token %} +
+
+ {% trans "Documentos de formalização" %} +

Os documentos para formalização do convênio foram gerados e estão disponíveis abaixo.

+

Você precisa realizar os seguintes passos para que possamos dar prosseguimento ao processo:

+
    +
  • Baixe (dowload) o ofício de solicitação do convênio.
  • +
  • Providencie que o ofício de solicitação seja assinado pelo Presidente da Casa. Se for possível, assine o PDF com uma assinatura digital. Se o Presidente não possui uma assinatura digital válida, pode-se imprimir o documento, assinar com caneta azul, e escanear o documento assinado.
  • +
  • Suba (upload) o ofício assinado no campo Arquivo, abaixo, em formato PDF
  • +
  • O documento Minuta de ACT é a minuta do convênio que tramitará no Senado e está disponível para consulta e avaliação da consultoria jurídica da sua Casa Legislativa. Após assinatura do ACt pela diretoria do Senado, a cópia definitiva será enviada à sua Casa para colher também a assinatura do Presidente.

    +
+

Assim que recebermos o ofício devidamente assinado, iniciaremos o procedimento burocrático para formalização do convênio. Você poderá acompanhar a evolução deste processo nesta tela, na aba RESUMO

+ + + + + + + + + {% for anexo in ocorrencia.anexo_set.all %} + + + + + {% endfor %} + +
Arquivos do processo
DescriçãoArquivo
{{ anexo.descricao }}{{ anexo.arquivo.name }}
+ {{ documento_form }} +
+ +
+
+
+
+
+
+
+
+
+
+
+
+ {% csrf_token %} +
+
+ + + + + + + + + + + +
{% trans "Casa legislativa" %}{{ ocorrencia.casa_legislativa.nome }}{% trans "Descrição" %}{{ ocorrencia.descricao }}{% trans "Solicitado em" %}{{ ocorrencia.data_criacao|date:"DATE_FORMAT" }}
{% trans "Situação" %}{{ ocorrencia.get_status_display }}{% trans "Prioridade" %}{{ ocorrencia.get_prioridade_display }}{% trans "Última atualização" %}{{ ocorrencia.data_modificacao|date:"DATE_FORMAT" }}
+ + + + + + + {% for comentario in ocorrencia.comentarios.all %} + {% if not comentario.interno %} + + + + + + + {% endif %} + {% empty %} + + {% endfor %} + +
{% trans "Progresso do processo" %}
{% trans "Data" %}{% trans "Descrição" %}{% trans "Novo status" %}{% trans "Registrado por" %}
{{ comentario.data_criacao|date:"SHORT_DATE_FORMAT" }}{{ comentario.descricao }}{{ comentario.get_novo_status_display|default:"-" }}{{ comentario.usuario }}
{% trans "Aguardando andamento no Senado" %}
+ {{ comentario_form }} +
+ +
+
+
+
+
+
+
+
+{% endblock %} + +{% block footer %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/sigi/apps/ocorrencias/templates/ocorrencias/convenio/painel_convenio.html b/sigi/apps/ocorrencias/templates/ocorrencias/convenio/painel_convenio.html new file mode 100644 index 0000000..18e85ce --- /dev/null +++ b/sigi/apps/ocorrencias/templates/ocorrencias/convenio/painel_convenio.html @@ -0,0 +1,40 @@ +{% extends "admin/base_block.html" %} +{% load i18n %} + +{% block content %} +

{% trans "Solicitações de convenio" %}

+ + + + + + + + + + + + {% for ocorrencia in ocorrencias %} + + + + + + + + {% endfor %} + +
{% trans "Data abertura" %}{% trans "Casa solicitante" %}{% trans "Assunto" %}{% trans "Comentários" %} + {% trans "Situação" %}{% trans "Detalhes" %}
{{ ocorrencia.data_criacao|date:"SHORT_DATE_FORMAT" }}{{ ocorrencia.casa_legislativa }}{{ ocorrencia.assunto }}{{ ocorrencia.comentarios.count }} +
    + {% for key, value in ocorrencia.get_infos_details.items %} +
  • + {% if value.0 %}done{% else %}clear{% endif %} + {{ value.1 }} +
  • + {% endfor %} +
+
+ details +
+{% endblock %} \ No newline at end of file diff --git a/sigi/apps/ocorrencias/templates/ocorrencias/convenio/painel_convenio_detail.html b/sigi/apps/ocorrencias/templates/ocorrencias/convenio/painel_convenio_detail.html new file mode 100644 index 0000000..703d2f2 --- /dev/null +++ b/sigi/apps/ocorrencias/templates/ocorrencias/convenio/painel_convenio_detail.html @@ -0,0 +1,245 @@ +{% extends "admin/base_block.html" %} +{% load i18n model_fields %} + +{% block content %} +
+
+
+ {% csrf_token %} +
+
+ {% trans "Resumo da ocorrência" %} + + + {% for campo in campos_ocorrencia %} + + + + + {% endfor %} + +
{{ ocorrencia|verbose_name:campo|capfirst }}{{ ocorrencia|field_value:campo|default:"-" }}
+ {{ ocorrencia_form }} +
+
+ + {% trans "Voltar ao painel" %} +
+
+
+
+
+ + {% if infos.aplicados %} + {% if 'casa_legislativa' in infos.aplicados and 'presidente' in infos.aplicados and 'contato' in infos.aplicados %} +

{% trans "Todas as alterações foram aplicadas" %}

+ {% else %} + + {% endif %} + {% endif %} + + {% if 'casa_legislativa' not in infos.aplicados %} +
+
+
+
+ {% trans "Casa legislativa" %} + + + + + + + + + + {% for key, value in infos.casa_legislativa.items %} + + + + + + {% endfor %} + + + + + + + + + +
{% trans "Campo" %}{% trans "Valor original" %}{% trans "Valor alterado" %}
{{ casa|verbose_name:key|capfirst }}{{ casa|field_value:key|default:"-" }}{{ value|default:"-" }}
Foto{% if casa.foto %}{% else %}{% trans "Sem imagem" %}{% endif %}{% if ocorrencia.casa_foto %}{% else %}{% trans "Sem alteração" %}{% endif %} +
Brasão{% if casa.brasao %}{% else %}{% trans "Sem imagem" %}{% endif %}{% if ocorrencia.casa_brasao %}{% else %}{% trans "Sem alteração" %}{% endif %} +
+
+ +
+
+
+ {% endif %} + + {% if 'presidente' not in infos.aplicados %} +
+
+
+
+ {% trans "Presidente" %} + {% if casa.presidente %} +

{% trans "Presidente anterior" %}: {{ casa.presidente.nome_completo }}

+ {% else %} +

{% trans "A Casa não tinha Presidente definido anteriormente" %}

+ {% endif %} +

{% trans "Presidente selecionado" %}: {{ novo_presidente.nome_completo }}

+ + + + + + + + + + {% for key, value in infos.presidente.items %} + + + + + + {% endfor %} + +
{% trans "Campo" %}{% trans "Valor original" %}{% trans "Valor alterado" %}
{{ novo_presidente|verbose_name:key|capfirst }}{{ novo_presidente|field_value:key|default:"-" }}{{ value|default:"-" }}
+
+ +
+
+
+ {% endif %} + + {% if 'contato' not in infos.aplicados %} +
+
+
+
+ {% trans "Contato Interlegis" %} + + + + + + + + + + {% for key, value in infos.contato.items %} + + + + + + {% endfor %} + +
{% trans "Campo" %}{% trans "Valor original" %}{% trans "Valor alterado" %}
{{ contato|verbose_name:key|capfirst }}{{ contato|field_value:key|default:"-" }}{{ value|default:"-" }}
+
+ +
+
+
+ {% endif %} + +
+
+
+
+ {% trans "Anexos (documentos)" %} + + + + + + + + + + {% for anexo in ocorrencia.anexo_set.all %} + + + + + + {% endfor %} + +
{% trans "Data de inclusão" %}{% trans "Descrição" %}{% trans "Arquivo" %}
{{ anexo.data_pub|date:"SHORT_DATE_FORMAT" }}{{ anexo.descricao|default:"-" }}{{ anexo.arquivo.name }}
+
+
+
+
+ +
+
+
+
+ {% trans "Comentários" %} + + + + + + + + + + + + {% for comentario in ocorrencia.comentarios.all %} + + + + + + + + {% endfor %} + +
{% trans "Data" %}{% trans "Descrição" %}{% trans "Servidor" %}{% trans "Novo status" %}{% trans "Visibilidade" %}
{{ comentario.data_criacao|date:"SHORT_DATETIME_FORMAT" }}{{ comentario.descricao|default:"-" }}{{ comentario.usuario.nome_completo|default:"-" }}{{ comentario.get_novo_status_display|default:"-" }} + {% if comentario.interno %} + visibility_off Interno + {% else %} + visibility Público + {% endif %} +
+
+
+
+ {% csrf_token %} + {{ comentario_form }} + +
+
+
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/sigi/apps/ocorrencias/templates/ocorrencias/convenio/seleciona_casa.html b/sigi/apps/ocorrencias/templates/ocorrencias/convenio/seleciona_casa.html new file mode 100644 index 0000000..5e46cb7 --- /dev/null +++ b/sigi/apps/ocorrencias/templates/ocorrencias/convenio/seleciona_casa.html @@ -0,0 +1,53 @@ +{% extends "ocorrencias/convenio/base_convenio.html" %} +{% load static i18n %} + +{% block content %} +
+
+
+
+
+ {% trans "Solicitar convênio" %} + {% blocktrans %} +

Para que uma Casa Legislativa possa utilizar, gratuitamente, os + serviços do Interlegis / Senado Federal, é necessário formalizar + um convênio, na forma de um Acordo de Cooperação Técnica (ACT), + na forma da lei Lei Nº 14.133/2021 + e da lei Lei Nº 8.666/1993. +

+

Para solicitar o ACT, serão necessárias as seguintes informações:

+
    +
  • Dados cadastrais da Casa Legislativa, como CNPJ, endereço, e-mail, telefone.
  • +
  • Dados cadastrais do Presidente, como nome, CPF, identidade, e-mail, telefone, redes sociais.
  • +
  • Designação de um servidor como Contato Interlegis.
  • +
+ {% endblocktrans %} +
+
+
+
+
+
+
+
+ {% trans "Identifique sua Casa Legislativa" %} +

{% trans "Informe o nome do município ou Estado da sua Casa Legislativa" %}:

+
+ search + +
+
+
+
+
+
+{% endblock %} + +{% block footer %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/sigi/apps/ocorrencias/urls.py b/sigi/apps/ocorrencias/urls.py index 25fcefc..0c0b9a9 100644 --- a/sigi/apps/ocorrencias/urls.py +++ b/sigi/apps/ocorrencias/urls.py @@ -1,8 +1,13 @@ from django.urls import path -from sigi.apps.ocorrencias.views import painel_ocorrencias +from sigi.apps.ocorrencias import views urlpatterns = [ - path("painel/", painel_ocorrencias, name="painel-ocorrencias"), + path("convenio/", views.seleciona_casa, name="ocorrencias-seleciona-casa"), + path( + "ocorrencia/", + views.ocorrencia, + name="ocorrencias_ocorrencia", + ), ] diff --git a/sigi/apps/ocorrencias/views.py b/sigi/apps/ocorrencias/views.py index 77473ad..3d5a8d5 100644 --- a/sigi/apps/ocorrencias/views.py +++ b/sigi/apps/ocorrencias/views.py @@ -1,21 +1,39 @@ -# -*- coding: utf-8 -*- -from django.http import JsonResponse, Http404 +import copy from django.db.models import Q, Count -from django.utils.translation import ngettext, gettext as _ -from django.shortcuts import get_object_or_404, render, HttpResponse +from django.contrib import messages +from django.contrib.admin.sites import site from django.contrib.auth.decorators import login_required -from django.views.decorators.http import require_POST +from django.http import JsonResponse, Http404 +from django.shortcuts import get_object_or_404, redirect, render, HttpResponse from django.template.loader import render_to_string from django.template import RequestContext +from django.urls import reverse +from django.utils import timezone +from django.utils.translation import ngettext, gettext as _ +from django.views.decorators.http import require_POST +from sigi.apps import ocorrencias +from sigi.apps.parlamentares.models import Parlamentar from sigi.apps.utils import to_ascii -from sigi.apps.casas.models import Orgao +from sigi.apps.casas.models import Funcionario, Orgao from sigi.apps.contatos.models import UnidadeFederativa from sigi.apps.servidores.models import Servidor, Servico -from sigi.apps.ocorrencias.models import Ocorrencia, Anexo +from sigi.apps.ocorrencias.models import ( + Categoria, + Comentario, + Ocorrencia, + Anexo, + TipoContato, +) from sigi.apps.ocorrencias.forms import ( AnexoForm, ComentarioForm, + ComentarioInternoForm, + ContatoForm, + DocumentoForm, + OcorrenciaChangeForm, OcorrenciaForm, + CasaForm, + PresidenteForm, ) from django.utils.html import escape @@ -327,3 +345,351 @@ def inclui_ocorrencia(request): ) return JsonResponse(data) + + +def seleciona_casa(request): + context = site.each_context(request) or {} + casa_id = request.GET.get("casa_id", None) + + if casa_id: + casa = get_object_or_404(Orgao, pk=casa_id) + categoria = get_object_or_404(Categoria, tipo="C") + tipo_contato = get_object_or_404(TipoContato, ind_site=True) + + if request.user.is_anonymous or not ( + request.user.is_staff and request.user.servidor is not None + ): + servidor = get_object_or_404(Servidor, sigi=True) + else: + servidor = request.user.servidor + + if casa.convenio_set.filter(projeto__sigla="ACT").exists(): + # TODO: Fluxo para Casa já conveniada + messages.info(request, "Já conveniada") + return redirect("/") + ocorrencia = casa.ocorrencia_set.filter( + categoria=categoria, + status__in=[Ocorrencia.STATUS_ABERTO, Ocorrencia.STATUS_REABERTO], + ).first() + if not ocorrencia: + ocorrencia = Ocorrencia( + casa_legislativa=casa, + categoria=categoria, + tipo_contato=tipo_contato, + assunto=_("Solicitação de Adesão ao Programa Interlegis"), + descricao=_( + f"A {casa.nome} solicita adesão ao Programa Interlegis" + ), + servidor_registro=servidor, + ) + ocorrencia.save() + return redirect(reverse("ocorrencias_ocorrencia", args=[ocorrencia.id])) + + return render(request, "ocorrencias/convenio/seleciona_casa.html", context) + + +def bound_copy(instance, bound_data, removes=None): + data = bound_data.copy() + if removes: + for remove_key in removes: + data.pop(remove_key) + for key, value in data.items(): + setattr(instance, key, value) + + +def ocorrencia(request, ocorrencia_id): + ANEXO_DESCRICAO = _("Solicitação de convenio assinada") + + def set_instances(): + casa.foto = ocorrencia.casa_foto if ocorrencia.casa_foto else casa.foto + casa.brasao = ( + ocorrencia.casa_brasao if ocorrencia.casa_brasao else casa.brasao + ) + if "casa_legislativa" in infos: + bound_copy(casa, infos["casa_legislativa"]) + if "presidente" in infos: + bound_copy(presidente, infos["presidente"], ["id"]) + if "contato" in infos: + bound_copy(contato, infos["contato"]) + + ocorrencia = get_object_or_404(Ocorrencia, pk=ocorrencia_id) + infos = ocorrencia.infos or {} + casa = ocorrencia.casa_legislativa + presidente = casa.presidente or ( + Parlamentar.objects.get(id=infos["presidente"]["id"]) + if "presidente" in infos + else Parlamentar(casa_legislativa=casa) + ) + contato = casa.contato_interlegis or Funcionario( + casa_legislativa=casa, setor="contato_interlegis" + ) + documento = ( + ocorrencia.anexo_set.get(id=infos["documento"]["id"]) + if "documento" in infos + else Anexo(ocorrencia=ocorrencia, descricao=ANEXO_DESCRICAO) + ) + + set_instances() + + contato_form = ContatoForm(instance=contato, prefix="contato") + documento_form = DocumentoForm(instance=documento) + + if request.method == "POST": + if "salva_casa" in request.POST: + casa_form = CasaForm( + request.POST, request.FILES, instance=casa, prefix="casa" + ) + if casa_form.is_valid(): + cleaned = casa_form.cleaned_data.copy() + foto = cleaned.pop("foto") + brasao = cleaned.pop("brasao") + if "foto" in casa_form.changed_data: + if foto == False: + if ocorrencia.casa_foto: + ocorrencia.casa_foto.delete(save=True) + else: + ocorrencia.casa_foto = foto + if "brasao" in casa_form.changed_data: + if brasao == False: + if ocorrencia.casa_brasao: + ocorrencia.casa_brasao.delete(save=True) + else: + ocorrencia.casa_brasao = brasao + + infos["casa_legislativa"] = cleaned + ocorrencia.infos = infos + ocorrencia.save() + else: + messages.error( + request, + _("Corrija os erros no cadastro da Casa Legislativa"), + ) + elif "salva_presidente" in request.POST: + presidente_form = PresidenteForm( + request.POST, instance=presidente, prefix="presidente" + ) + if presidente_form.is_valid(): + cleaned = presidente_form.cleaned_data.copy() + presidente = cleaned.pop("parlamentar") + cleaned["id"] = presidente.id + infos["presidente"] = cleaned + ocorrencia.infos = infos + ocorrencia.save() + elif "salva_contato" in request.POST: + contato_form = ContatoForm( + request.POST, instance=contato, prefix="contato" + ) + if contato_form.is_valid(): + infos["contato"] = contato_form.cleaned_data.copy() + ocorrencia.infos = infos + ocorrencia.save() + elif "salva_documento" in request.POST: + documento_form = DocumentoForm( + request.POST, request.FILES, instance=documento + ) + if documento_form.is_valid(): + documento = documento_form.save() + infos["documento"] = {"id": documento.id} + ocorrencia.infos = infos + ocorrencia.save() + elif "salva_comentario" in request.POST: + comentario_form = ComentarioForm(request.POST) + if comentario_form.is_valid(): + comentario = comentario_form.save(commit=False) + comentario.ocorrencia = ocorrencia + comentario.usuario = ocorrencia.servidor_registro + if ocorrencia.status not in [ + ocorrencia.STATUS_ABERTO, + ocorrencia.STATUS_REABERTO, + ]: + comentario.novo_status = ocorrencia.STATUS_REABERTO + comentario.save() + + set_instances() + + if {"casa_legislativa", "presidente", "contato"}.issubset( + infos + ) and "documento" not in infos: + ocorrencia.anexo_set.all().delete() + documento = Anexo(ocorrencia=ocorrencia, descricao=ANEXO_DESCRICAO) + documento_form = DocumentoForm(instance=documento) + projeto = ocorrencia.categoria.projeto + oficio = Anexo( + ocorrencia=ocorrencia, + descricao=f"Solicitação de {projeto.sigla}", + ) + oficio.arquivo.name = ( + f"{Anexo.arquivo.field.upload_to}/" + f"solicitacao_{projeto.sigla}_{casa.get_sigla()}.pdf" + ) + projeto.gerar_oficio( + oficio.arquivo, + casa, + presidente, + contato, + request.build_absolute_uri("/"), + ) + oficio.save() + minuta = Anexo( + ocorrencia=ocorrencia, descricao=f"Minuta de {projeto.sigla}" + ) + minuta.arquivo.name = ( + f"{Anexo.arquivo.field.upload_to}/" + f"minuta_{projeto.sigla}_{casa.get_sigla()}.docx" + ) + projeto.gerar_minuta(minuta.arquivo.path, casa, presidente, contato) + minuta.save() + + if presidente.id: + bounds = { + f"presidente-{key}": value + for key, value in infos["presidente"].items() + if key != "id" + } + bounds["presidente-parlamentar"] = presidente + presidente_form = PresidenteForm( + bounds, instance=presidente, prefix="presidente" + ) + else: + presidente_form = PresidenteForm( + instance=presidente, prefix="presidente" + ) + + context = site.each_context(request) or {} + context.update( + { + "ocorrencia": ocorrencia, + "casa_form": CasaForm(instance=casa, prefix="casa"), + "presidente_form": presidente_form, + "contato_form": contato_form, + "documento_form": documento_form, + "comentario_form": ComentarioForm(), + "infos": infos, + } + ) + return render(request, "ocorrencias/convenio/ocorrencia.html", context) + + +@login_required +def painel_convenio(request, ocorrencia_id=None): + context = site.each_context(request) or {} + + if ocorrencia_id: + ocorrencia = get_object_or_404(Ocorrencia, id=ocorrencia_id) + + if request.method == "POST": + if "salva_ocorrencia" in request.POST: + ocorrencia_form = OcorrenciaChangeForm( + request.POST, instance=ocorrencia + ) + if ocorrencia_form.is_valid(): + ocorrencia = ocorrencia_form.save() + if "processo_sigad" in ocorrencia_form.changed_data: + Comentario( + ocorrencia=ocorrencia, + descricao=_( + f"criado processo administrativo nº {ocorrencia.processo_sigad}" + ), + usuario=request.user.servidor, + ).save() + + if "salva_comentario" in request.POST: + comentario_form = ComentarioInternoForm(request.POST) + if comentario_form.is_valid(): + comentario = comentario_form.save(commit=False) + comentario.ocorrencia = ocorrencia + comentario.usuario = request.user.servidor + comentario.save() + + casa = ocorrencia.casa_legislativa + novo_presidente = ( + get_object_or_404( + Parlamentar, id=ocorrencia.infos["presidente"]["id"] + ) + if ocorrencia.infos["presidente"] + else None + ) + contato = casa.contato_interlegis or Funcionario() + + if not "aplicados" in ocorrencia.infos: + ocorrencia.infos["aplicados"] = [] + + apply = request.GET.get("apply", None) + + if ( + apply == "casa" + and "casa_legislativa" in ocorrencia.infos + and not "casa_legislativa" in ocorrencia.infos["aplicados"] + ): + casa.foto = ( + ocorrencia.casa_foto if ocorrencia.casa_foto else casa.foto + ) + casa.brasao = ( + ocorrencia.casa_brasao + if ocorrencia.casa_brasao + else casa.brasao + ) + bound_copy(casa, ocorrencia.infos["casa_legislativa"]) + casa.save() + ocorrencia.infos["aplicados"].append("casa_legislativa") + ocorrencia.save() + + if ( + apply == "presidente" + and "presidente" in ocorrencia.infos + and "presidente" not in ocorrencia.infos["aplicados"] + ): + bound_copy(novo_presidente, ocorrencia.infos["presidente"], ["id"]) + novo_presidente.save() + ocorrencia.infos["aplicados"].append("presidente") + ocorrencia.save() + + if ( + apply == "contato" + and "contato" in ocorrencia.infos + and "contato" not in ocorrencia.infos["aplicados"] + ): + bound_copy(contato, ocorrencia.infos["contato"]) + contato.save() + ocorrencia.infos["aplicados"].append("contato") + ocorrencia.save() + + infos = copy.deepcopy(ocorrencia.infos) + + if infos["presidente"]: + del infos["presidente"]["id"] + + context.update( + { + "ocorrencia": ocorrencia, + "campos_ocorrencia": [ + "assunto", + "casa_legislativa", + "categoria", + "descricao", + "data_criacao", + "data_modificacao", + ], + "infos": infos, + "casa": casa, + "novo_presidente": novo_presidente, + "contato": contato, + "comentario_form": ComentarioInternoForm(), + "ocorrencia_form": OcorrenciaChangeForm(instance=ocorrencia), + } + ) + + return render( + request, "ocorrencias/convenio/painel_convenio_detail.html", context + ) + + base_query = Ocorrencia.objects.filter( + status__in=[Ocorrencia.STATUS_ABERTO, Ocorrencia.STATUS_REABERTO], + categoria__tipo="C", + ) + ocorrencias = base_query.filter(infos__has_any_keys=Ocorrencia.INFO_KEYS) + ocorrencias = ocorrencias.union( + base_query.exclude(infos__has_any_keys=Ocorrencia.INFO_KEYS) + ) + context["ocorrencias"] = ocorrencias + return render(request, "ocorrencias/convenio/painel_convenio.html", context) diff --git a/sigi/apps/parlamentares/models.py b/sigi/apps/parlamentares/models.py index dc39671..3c9b053 100644 --- a/sigi/apps/parlamentares/models.py +++ b/sigi/apps/parlamentares/models.py @@ -92,8 +92,6 @@ class Parlamentar(models.Model): verbose_name_plural = _("parlamentares") def __str__(self): - if self.nome_parlamentar: - return self.nome_parlamentar return self.nome_completo def save(self, *args, **kwargs): diff --git a/sigi/apps/parlamentares/urls.py b/sigi/apps/parlamentares/urls.py index a940169..ab760c3 100644 --- a/sigi/apps/parlamentares/urls.py +++ b/sigi/apps/parlamentares/urls.py @@ -3,8 +3,9 @@ from sigi.apps.parlamentares import views urlpatterns = [ path( - "parlamentar_json//", - views.parlamentar_json, + "parlamentarjson//", + views.parlamentares_casa, name="parlamentar-json", ), + path("parlamentardata/", views.parlamentar_data, name="parlamentar-data"), ] diff --git a/sigi/apps/parlamentares/views.py b/sigi/apps/parlamentares/views.py index 0fabb0a..62a37c5 100644 --- a/sigi/apps/parlamentares/views.py +++ b/sigi/apps/parlamentares/views.py @@ -1,6 +1,7 @@ from django.conf import settings from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator, InvalidPage, EmptyPage +from django.core.serializers import serialize from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import render, get_list_or_404 from django.template import Context, loader, RequestContext @@ -9,8 +10,7 @@ from sigi.apps.casas.models import Orgao from sigi.apps.parlamentares.models import Parlamentar -@login_required -def parlamentar_json(request, casa_id): +def parlamentares_casa(request, casa_id): return JsonResponse( { p.nome_completo: { @@ -20,3 +20,12 @@ def parlamentar_json(request, casa_id): for p in Parlamentar.objects.filter(casa_legislativa_id=casa_id) } ) + + +def parlamentar_data(request): + return HttpResponse( + serialize( + "json", Parlamentar.objects.filter(id=request.GET.get("id", None)) + ), + content_type="application/json", + ) diff --git a/sigi/apps/servidores/migrations/0010_servidor_sigi.py b/sigi/apps/servidores/migrations/0010_servidor_sigi.py new file mode 100644 index 0000000..5327a91 --- /dev/null +++ b/sigi/apps/servidores/migrations/0010_servidor_sigi.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.5 on 2022-06-30 21:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('servidores', '0009_servidor_cargo'), + ] + + operations = [ + migrations.AddField( + model_name='servidor', + name='sigi', + field=models.BooleanField(default=False, editable=False, verbose_name='Servidor SIGI'), + ), + ] diff --git a/sigi/apps/servidores/models.py b/sigi/apps/servidores/models.py index e5f32f7..d096065 100644 --- a/sigi/apps/servidores/models.py +++ b/sigi/apps/servidores/models.py @@ -57,6 +57,9 @@ class Servidor(models.Model): _("órgão de origem, "), max_length=100, blank=True ) qualificacoes = models.TextField(_("qualificações"), blank=True) + sigi = models.BooleanField( + _("Servidor SIGI"), default=False, editable=False + ) class Meta: ordering = ("nome_completo",) diff --git a/sigi/menu_conf.yaml b/sigi/menu_conf.yaml index e6d44d7..62c3484 100644 --- a/sigi/menu_conf.yaml +++ b/sigi/menu_conf.yaml @@ -63,6 +63,8 @@ main_menu: - title: Registro de ocorrências view_name: admin:ocorrencias_ocorrencia_changelist querystr: minhas=S&status__in=1,2 + - title: Solicitações de convênio + view_name: painel-convenio - title: Eventos icon: school children: diff --git a/sigi/static/css/base_block.css b/sigi/static/css/base_block.css new file mode 100644 index 0000000..a5b81ff --- /dev/null +++ b/sigi/static/css/base_block.css @@ -0,0 +1,3 @@ +#content { + display: block; +} \ No newline at end of file diff --git a/sigi/templates/admin/base_block.html b/sigi/templates/admin/base_block.html new file mode 100644 index 0000000..a15db36 --- /dev/null +++ b/sigi/templates/admin/base_block.html @@ -0,0 +1,7 @@ +{% extends "admin/base_site.html" %} +{% load static %} + +{% block extrastyle %} + {{ block.super }} + +{% endblock %} diff --git a/sigi/urls.py b/sigi/urls.py index 4f84def..0afa9c6 100644 --- a/sigi/urls.py +++ b/sigi/urls.py @@ -21,7 +21,8 @@ from django.conf.urls.static import static urlpatterns = [ path("admin/casas/", include("sigi.apps.casas.urls")), path("admin/convenios/", include("sigi.apps.convenios.urls")), - path("admin/ocorrencias/", include("sigi.apps.ocorrencias.urls")), + path("admin/ocorrencias/", include("sigi.apps.ocorrencias.admin_urls")), + path("ocorrencias/", include("sigi.apps.ocorrencias.urls")), path("eventos/", include("sigi.apps.eventos.urls")), path("parlamentares/", include("sigi.apps.parlamentares.urls")), path("admin/", admin.site.urls),