From af9e6e3586cde73126068c6f5b7aad12249b21aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ses=C3=B3stris=20Vieira?= Date: Mon, 31 Aug 2026 16:22:11 -0300 Subject: [PATCH] =?UTF-8?q?Relat=C3=B3rio=20de=20ACTs=20duplicados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../convenios/report/duplicados.html | 114 ++++++++++++++ .../convenios/report/duplicados_pdf.html | 99 ++++++++++++ sigi/apps/convenios/urls.py | 5 + sigi/apps/convenios/views.py | 141 ++++++++++++++---- sigi/menu_conf.yaml | 2 + 5 files changed, 336 insertions(+), 25 deletions(-) create mode 100644 sigi/apps/convenios/templates/convenios/report/duplicados.html create mode 100644 sigi/apps/convenios/templates/convenios/report/duplicados_pdf.html diff --git a/sigi/apps/convenios/templates/convenios/report/duplicados.html b/sigi/apps/convenios/templates/convenios/report/duplicados.html new file mode 100644 index 0000000..87c16e6 --- /dev/null +++ b/sigi/apps/convenios/templates/convenios/report/duplicados.html @@ -0,0 +1,114 @@ +{% extends "utils/report/report.html" %} +{% load i18n djbs_extras %} + +{% block csv_link %}{% endblock %} + +{% block datacard %} +

+ {% blocktrans %} + As datas de início e término da vigência dos contratos do Gescon estão no + formato ISO-8601, que é AAAA-MM-DD, pois são recebidas asssim do webservice, + que obedece aos padrões de normalização para troca e comunicação de dados. + {% endblocktrans %} +

+

+ + {% blocktrans with total=duplicados|length %} + {{ total }} processos duplicados no SIGI + {% endblocktrans %} + +

+ {% for processo, sigad_url, convenios, contratos_gescon in duplicados %} +
+
+
{% trans "Nº do Processo:" %} {{ sigad_url }}
+
+
+
+ {% blocktrans with total=convenios|length%} + {{ total }} convênios com este mesmo número de processo no SIGI + {% endblocktrans %} +
+ + + + + + + + + + + + + + + + + + + {% for convenio, origem in convenios %} + + + + + + + + + + + + {% endfor %} + +
{% trans "Projeto" %}{% trans "Nº convênio" %}{% trans "CNPJ" %}{% trans "Casa Legislativa" %}{% trans "Observação" %}{% trans "Vigência" %}{% trans "Origem" %}{% trans "Action" %}
{% trans "Início" %}{% trans "Término" %}
{{ convenio.projeto.sigla }}{{ convenio.num_convenio }}{{ convenio.casa_legislativa.cnpj }}{{ convenio.casa_legislativa.nome }}{{ convenio.observacao|default:"" }}{{ convenio.data_retorno_assinatura }}{{ convenio.data_termino_vigencia }}{{ origem }} + + {% icon "delete" %} + + + {% icon "edit" %} + +
+ +
+ {% blocktrans count total=contratos_gescon|length%} + Um contrato com este número de processo no Gescon + {% plural %} + {{ total }} contratos com este mesmo número de processo no Gescon + {% endblocktrans %} +
+ + + + + + + + + + + + + + + + + + {% for contrato in contratos_gescon %} + + + + + + + + + + + {% endfor %} + +
{% trans "ID" %}{% trans "Subespécie" %}{% trans "Nº convênio" %}{% trans "CNPJ" %}{% trans "Nome do fornecedor" %}{% trans "Objeto" %}{% trans "Vigência" %}
{% trans "Início" %}{% trans "Término" %}
{{ contrato.id|stringformat:"d" }}{{ contrato.subEspecie }}{{ contrato.numero }}{{ contrato.cnpjCpfFornecedor }}{{ contrato.nomeFornecedor }}{{ contrato.objeto }}{{ contrato.inicioVigencia }}{{ contrato.terminoVigencia }}
+
+
+ {% endfor %} +{% endblock %} diff --git a/sigi/apps/convenios/templates/convenios/report/duplicados_pdf.html b/sigi/apps/convenios/templates/convenios/report/duplicados_pdf.html new file mode 100644 index 0000000..33ed3a4 --- /dev/null +++ b/sigi/apps/convenios/templates/convenios/report/duplicados_pdf.html @@ -0,0 +1,99 @@ +{% extends "utils/report/report_pdf.html" %} +{% load i18n %} + +{% block page_size %}A4 landscape{% endblock %} + +{% block main_content %} +

+ {% blocktrans %} + As datas de início e término da vigência dos contratos do Gescon estão no + formato ISO-8601, que é AAAA-MM-DD, pois são recebidas asssim do webservice, + que obedece aos padrões de normalização para troca e comunicação de dados. + {% endblocktrans %} +

+

+ {% blocktrans with total=duplicados|length %} + {{ total }} processos duplicados no SIGI + {% endblocktrans %} +

+ {% for processo, sigad_url, convenios, contratos_gescon in duplicados %} +
+
{% trans "Nº do Processo:" %} {{ processo }}
+
+
+ {% blocktrans with total=convenios|length%} + {{ total }} convênios com este mesmo número de processo no SIGI + {% endblocktrans %} +
+ + + + + + + + + + + + + + + + + + {% for convenio, origem in convenios %} + + + + + + + + + + + {% endfor %} + +
{% trans "Projeto" %}{% trans "Nº convênio" %}{% trans "CNPJ" %}{% trans "Casa Legislativa" %}{% trans "Observação" %}{% trans "Vigência" %}{% trans "Origem" %}
{% trans "Início" %}{% trans "Término" %}
{{ convenio.projeto.sigla }}{{ convenio.num_convenio }}{{ convenio.casa_legislativa.cnpj }}{{ convenio.casa_legislativa.nome }}{{ convenio.observacao|default:"" }}{{ convenio.data_retorno_assinatura }}{{ convenio.data_termino_vigencia }}{{ origem }}
+ +
+ {% blocktrans count total=contratos_gescon|length%} + Um contrato com este número de processo no Gescon + {% plural %} + {{ total }} contratos com este mesmo número de processo no Gescon + {% endblocktrans %} +
+ + + + + + + + + + + + + + + + + + {% for contrato in contratos_gescon %} + + + + + + + + + + + {% endfor %} + +
{% trans "ID" %}{% trans "Subespécie" %}{% trans "Nº convênio" %}{% trans "CNPJ" %}{% trans "Nome do fornecedor" %}{% trans "Objeto" %}{% trans "Vigência" %}
{% trans "Início" %}{% trans "Término" %}
{{ contrato.id|stringformat:"d" }}{{ contrato.subEspecie }}{{ contrato.numero }}{{ contrato.cnpjCpfFornecedor }}{{ contrato.nomeFornecedor }}{{ contrato.objeto }}{{ contrato.inicioVigencia }}{{ contrato.terminoVigencia }}
+ {% endfor %} +{% endblock %} \ No newline at end of file diff --git a/sigi/apps/convenios/urls.py b/sigi/apps/convenios/urls.py index 9f521d2..4053e08 100644 --- a/sigi/apps/convenios/urls.py +++ b/sigi/apps/convenios/urls.py @@ -7,6 +7,11 @@ urlpatterns = [ views.ErrosGesconReportView.as_view(), name="convenios-report_erros_gescon", ), + path( + "duplicados/", + views.report_duplicados, + name="convenios-report_duplicados", + ), path( "reportsRegiao//", views.report_regiao, diff --git a/sigi/apps/convenios/views.py b/sigi/apps/convenios/views.py index dc283d3..beef45e 100644 --- a/sigi/apps/convenios/views.py +++ b/sigi/apps/convenios/views.py @@ -1,7 +1,17 @@ import csv +import re +import requests from docutils.core import publish_parts +from django.contrib.admin.models import LogEntry, ADDITION +from django.contrib.contenttypes.models import ContentType +from django.utils import timezone +from django.utils.formats import localize from django.utils.safestring import mark_safe -from django.http import HttpResponse, HttpResponseForbidden +from django.http import ( + HttpResponse, + HttpResponseForbidden, + HttpResponseServerError, +) from django.shortcuts import render, get_list_or_404 from django.utils.translation import gettext as _, ngettext from django.contrib.admin.views.decorators import staff_member_required @@ -11,6 +21,7 @@ from django_weasyprint.views import WeasyTemplateResponse from sigi.apps.casas.models import Orgao from sigi.apps.contatos.models import UnidadeFederativa from sigi.apps.convenios.models import Convenio, Gescon, Projeto +from sigi.apps.utils import get_sigad_url from sigi.apps.utils.views import ReportListView @@ -107,6 +118,108 @@ def report_regiao(request, regiao): ) +@login_required +@staff_member_required +def report_duplicados(request): + format = request.GET.get("fmt", "html") + + ct = ContentType.objects.get_for_model(Convenio) + + # Buscar dados de ACT no Gescon + response = requests.get( + "https://adm.senado.gov.br/gestao-contratos/api/contratos/busca" + "?subespecie=AC" + ) + if response.status_code != 200: + return HttpResponseServerError( + f"Erro de comunicação {response.status_code}: {response.reason}" + ) + if "json" not in response.headers["content-type"]: + return HttpResponseServerError( + f"Gescon não retornou um json: {response.headers['content-type']}" + ) + jdata = response.json() + + # Encontrar ACTs duplicados + acts = { + c.num_processo_sf: [c] + for c in Convenio.objects.filter(projeto__sigla="ACT").exclude( + num_processo_sf="" + ) + } + for c in Convenio.objects.exclude(num_processo_sf=""): + if c.num_processo_sf in acts and not any( + [a.id == c.id for a in acts[c.num_processo_sf]] + ): + acts[c.num_processo_sf].append(c) + duplicados = [ + [k, get_sigad_url(k), [[c] for c in v]] + for k, v in acts.items() + if len(v) > 1 + ] + + for r in duplicados: + # Juntar dados do Gescon com os duplicados + processo = processo = re.sub(r"\D", "", r[0]) + contratos = list(filter(lambda d: d["processo"] in processo, jdata)) + r.append(contratos) + # Adicionar informação de origem em cada convênio + for t in r[2]: + log = LogEntry.objects.filter( + content_type=ct, object_id=t[0].id, action_flag=ADDITION + ).first() + if log: + fragment = "" + if t[0].atualizacao_gescon: + if t[0].id_gescon: + fragment = _( + "Atualizado com dados do Gescon em {date}. ID no Gescon: {id}" + ).format( + date=localize(t[0].atualizacao_gescon), + id=t[0].id_gescon, + ) + else: + fragment = _( + "Atualizado com dados do Gescon em {date}." + ).format(date=localize(t[0].atualizacao_gescon)) + t.append( + _("Cadastrado por {username} em {date}. {fragment}").format( + username=log.user.get_full_name(), + date=localize(log.action_time), + fragment=fragment, + ) + ) + else: + if t[0].id_gescon: + t.append( + _("Importado do Gescon em {date}, do ID {id}").format( + date=localize(t[0].atualizacao_gescon), + id=t[0].id_gescon, + ) + ) + else: + t.append( + _("Importado do Gescon em {date}").format( + date=localize(t[0].atualizacao_gescon) + ) + ) + + context = { + "duplicados": duplicados, + "report_title": _("Relação de Convênios duplicados no SIGI"), + } + + if format == "pdf": + return WeasyTemplateResponse( + filename=f"duplicados-{timezone.localdate():%Y-%m-%d}.pdf", + request=request, + template="convenios/report/duplicados_pdf.html", + context=context, + content_type="application/pdf", + ) + return render(request, "convenios/report/duplicados.html", context) + + def casas_estado_to_tabela(casas, convenios, regiao): estados = get_list_or_404(UnidadeFederativa, regiao=regiao) @@ -118,9 +231,7 @@ def casas_estado_to_tabela(casas, convenios, regiao): for estado in estados: linha = LinhaEstado() - convenios_est = convenios.filter( - casa_legislativa__municipio__uf=estado - ) + convenios_est = convenios.filter(casa_legislativa__municipio__uf=estado) convenios_est_publicados = convenios_est.exclude(data_pub_diario=None) convenios_est_equipados = convenios_est.exclude(data_termo_aceite=None) @@ -154,9 +265,7 @@ def casas_estado_to_tabela(casas, convenios, regiao): convenios_regiao = convenios.filter( casa_legislativa__municipio__uf__regiao=regiao ) - convenios_regiao_publicados = convenios_regiao.exclude( - data_pub_diario=None - ) + convenios_regiao_publicados = convenios_regiao.exclude(data_pub_diario=None) convenios_regiao_equipados = convenios_regiao.exclude( data_termo_aceite=None ) @@ -205,25 +314,7 @@ def importar_gescon(request): return render(request, "convenios/importar_gescon.html", context) -""" -def query_ordena(qs, o, ot): - list_display = ('num_convenio', 'casa_legislativa', - 'data_adesao', 'data_retorno_assinatura', 'data_termo_aceite', - 'projeto', - ) - - aux = list_display[(int(o) - 1)] - if ot == 'asc': - qs = qs.order_by(aux) - else: - qs = qs.order_by("-" + aux) - return qs -""" - - def normaliza_data(get, nome_param): - import re - if nome_param in get: value = get.get(nome_param, "") if value == "": diff --git a/sigi/menu_conf.yaml b/sigi/menu_conf.yaml index 0d36533..4318f17 100644 --- a/sigi/menu_conf.yaml +++ b/sigi/menu_conf.yaml @@ -31,6 +31,8 @@ main_menu: children: - title: VALIDAÇÃO - Erros importação Gescon view_name: convenios-report_erros_gescon + - title: VALIDAÇÃO - Convênios duplicados + view_name: convenios-report_duplicados - title: VALIDAÇÃO - Órgãos com CNPJ duplicado view_name: casas_cnpj_duplicado - title: VALIDAÇÃO - Órgãos com CNPJ errado