diff --git a/sigi/apps/casas/admin_urls.py b/sigi/apps/casas/admin_urls.py index 455b634..1f6489e 100644 --- a/sigi/apps/casas/admin_urls.py +++ b/sigi/apps/casas/admin_urls.py @@ -1,7 +1,9 @@ -from django.urls import path, include +from django.urls import path from sigi.apps.casas import views urlpatterns = [ path("carteira/", views.painel_relacionamento, name="casas_carteira"), path("gerentes/", views.GerentesListView.as_view(), name="casas_gerentes"), + path("cnpjduplicado/", views.cnpj_duplicado, name="casas_cnpj_duplicado"), + path("cnpjerrado/", views.cnpj_errado, name="casas_cnpj_errado"), ] diff --git a/sigi/apps/casas/forms.py b/sigi/apps/casas/forms.py index 1fb3c39..4e2e051 100644 --- a/sigi/apps/casas/forms.py +++ b/sigi/apps/casas/forms.py @@ -105,3 +105,11 @@ class FuncionarioForm(forms.ModelForm): widgets = { "redes_sociais": MaterialAdminTextareaWidget, } + + +class CnpjErradoForm(forms.Form): + has_convenio = forms.BooleanField( + label=_("Mostrar apenas órgãos com convênio"), + required=False, + initial=False, + ) diff --git a/sigi/apps/casas/templates/casas/cnpj_duplicado.html b/sigi/apps/casas/templates/casas/cnpj_duplicado.html new file mode 100644 index 0000000..58ef6f8 --- /dev/null +++ b/sigi/apps/casas/templates/casas/cnpj_duplicado.html @@ -0,0 +1,44 @@ +{% extends "admin/base_site.html" %} +{% load static i18n %} + +{% block extrastyle %} +{{ block.super }} + + +{% endblock %} + +{% block coltype %}colMS{% endblock %} + +{% block content_title %} +
{% blocktranslate with count=orgaos.count %}{{ count }} órgãos com CNPJ duplicado{% endblocktranslate %}
+{% endblock %} + +{% block content %} +
+ + print + + +
+ {% include "casas/snippets/cnpj_duplicado_snippet.html" with mode="html" %} +{% endblock %} + +{% block footer %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/sigi/apps/casas/templates/casas/cnpj_duplicado_pdf.html b/sigi/apps/casas/templates/casas/cnpj_duplicado_pdf.html new file mode 100644 index 0000000..e9f7280 --- /dev/null +++ b/sigi/apps/casas/templates/casas/cnpj_duplicado_pdf.html @@ -0,0 +1,19 @@ +{% extends "pdf/base_report.html" %} +{% load static i18n %} + +{% block page_size %}A4 landscape{% endblock %} + +{% block extra_style %} + {{ block.super }} + .bordered td { + border-top: 1px solid #000; + } +{% endblock %} + +{% block report_name %} + {% blocktranslate with count=orgaos.count %}{{ count }} órgãos com CNPJ duplicado{% endblocktranslate %} +{% endblock report_name %} + +{% block main_content %} + {% include "casas/snippets/cnpj_duplicado_snippet.html" %} +{% endblock %} diff --git a/sigi/apps/casas/templates/casas/cnpj_errado.html b/sigi/apps/casas/templates/casas/cnpj_errado.html new file mode 100644 index 0000000..6bf16f8 --- /dev/null +++ b/sigi/apps/casas/templates/casas/cnpj_errado.html @@ -0,0 +1,57 @@ +{% extends "admin/base_site.html" %} +{% load static i18n %} + +{% block extrastyle %} +{{ block.super }} + + +{% endblock %} + +{% block coltype %}colMS{% endblock %} + +{% block content_title %} +
{% blocktranslate with count=orgaos|length %}{{ count }} órgãos com CNPJ digitado errado{% endblocktranslate %}
+{% endblock %} + +{% block content %} +
+
+
+ +
+
+ +
+
+
+ + print + + +
+
+ {% include "casas/snippets/cnpj_errado_snippet.html" with mode="html" %} +{% endblock %} + +{% block footer %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/sigi/apps/casas/templates/casas/cnpj_errado_pdf.html b/sigi/apps/casas/templates/casas/cnpj_errado_pdf.html new file mode 100644 index 0000000..c0be206 --- /dev/null +++ b/sigi/apps/casas/templates/casas/cnpj_errado_pdf.html @@ -0,0 +1,19 @@ +{% extends "pdf/base_report.html" %} +{% load static i18n %} + +{% block page_size %}A4 landscape{% endblock %} + +{% block extra_style %} + {{ block.super }} + .bordered td { + border-top: 1px solid #000; + } +{% endblock %} + +{% block report_name %} + {% blocktranslate with count=orgaos|length %}{{ count }} órgãos com CNPJ digitado errado{% endblocktranslate %} +{% endblock report_name %} + +{% block main_content %} + {% include "casas/snippets/cnpj_errado_snippet.html" %} +{% endblock %} diff --git a/sigi/apps/casas/templates/casas/snippets/cnpj_duplicado_snippet.html b/sigi/apps/casas/templates/casas/snippets/cnpj_duplicado_snippet.html new file mode 100644 index 0000000..90e7c83 --- /dev/null +++ b/sigi/apps/casas/templates/casas/snippets/cnpj_duplicado_snippet.html @@ -0,0 +1,37 @@ +{% load i18n %} +
+
+ + + + + + + + + + + + + + {% for orgao in orgaos %} + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
{% translate "ID" %}{% translate "CNPJ" %}{% translate "Tipo de órgão" %}{% translate "Sigla" %}{% translate "Nome" %}{% translate "Cidade" %}{% translate "UF" %}
{{ orgao.id|stringformat:"s" }}{{ orgao.cnpj }}{{ orgao.tipo.nome }}{{ orgao.sigla }}{{ orgao.nome }}{{ orgao.municipio.nome }}{{ orgao.municipio.uf.sigla }}
+ {% translate "Nenhum órgão com CNPJ duplicado" %} +
+
+
diff --git a/sigi/apps/casas/templates/casas/snippets/cnpj_errado_snippet.html b/sigi/apps/casas/templates/casas/snippets/cnpj_errado_snippet.html new file mode 100644 index 0000000..5839e0f --- /dev/null +++ b/sigi/apps/casas/templates/casas/snippets/cnpj_errado_snippet.html @@ -0,0 +1,38 @@ +{% load i18n %} +
+
+ + + + + + + + + + + + + {% for orgao in orgaos %} + {% ifchanged orgao.tipo_nome %} + + {% endifchanged %} + + + + + + + + + {% empty %} + + + + {% endfor %} + +
{% translate "ID" %}{% translate "CNPJ" %}{% translate "Sigla" %}{% translate "Nome" %}{% translate "Cidade" %}{% translate "UF" %}
{{ orgao.tipo_nome }}
{{ orgao.id|stringformat:"s" }}{{ orgao.cnpj }}{{ orgao.sigla }}{{ orgao.nome }}{{ orgao.municipio_nome }}{{ orgao.uf_sigla }}
+ {% translate "Nenhum órgão com CNPJ digitado errado" %} +
+
+
diff --git a/sigi/apps/casas/views.py b/sigi/apps/casas/views.py index e37c700..e53c45a 100644 --- a/sigi/apps/casas/views.py +++ b/sigi/apps/casas/views.py @@ -1,7 +1,5 @@ import csv -from functools import reduce -from django.db.models import Count, Q, Prefetch -from django.contrib.admin.sites import site +from django.db.models import Count, Q, Prefetch, F from django.contrib.admin.views.decorators import staff_member_required from django.contrib.auth import get_user_model from django.contrib.auth.decorators import login_required @@ -19,10 +17,10 @@ from django.views.generic import ( DeleteView, ListView, UpdateView, - DetailView, ) +from django_weasyprint.views import WeasyTemplateResponse from rest_framework import generics, filters -from sigi.apps.casas.forms import FuncionarioForm +from sigi.apps.casas.forms import FuncionarioForm, CnpjErradoForm from sigi.apps.casas.models import Funcionario, Orgao, TipoOrgao from sigi.apps.casas.serializers import OrgaoAtendidoSerializer from sigi.apps.home.mixins import ContatoInterlegisViewMixin @@ -36,6 +34,7 @@ from sigi.apps.ocorrencias.models import Ocorrencia from sigi.apps.servicos.models import Servico, TipoServico from sigi.apps.eventos.models import Evento, TipoEvento from sigi.apps.convenios.models import Convenio +from sigi.apps.utils import valida_cnpj def resumo_carteira(casas): @@ -339,6 +338,117 @@ def painel_relacionamento(request): return render(request, "casas/painel.html", context) +@login_required +@staff_member_required +def cnpj_duplicado(request): + formato = request.GET.get("fmt", "html") + dups = ( + Orgao.objects.exclude(cnpj="") + .order_by("cnpj") + .values("cnpj") + .annotate(tot=Count("cnpj")) + .filter(tot__gt=1) + .values("cnpj") + ) + orgaos = ( + Orgao.objects.filter(cnpj__in=dups) + .order_by("cnpj", "nome", "municipio__nome", "municipio__uf") + .prefetch_related("tipo", "municipio", "municipio__uf") + ) + context = { + "orgaos": orgaos, + "title": _("Órgãos com CNPJ duplicado"), + } + if formato == "pdf": + return WeasyTemplateResponse( + filename="cnpj_duplicado.pdf", + request=request, + template="casas/cnpj_duplicado_pdf.html", + context=context, + content_type="application/pdf", + ) + elif formato == "csv": + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = ( + 'attachment; filename="cnpj_duplicado.csv"' + ) + fieldnames = [ + "id", + "cnpj", + "tipo__nome", + "sigla", + "nome", + "municipio__nome", + "municipio__uf__sigla", + ] + writer = csv.DictWriter(response, fieldnames) + writer.writeheader() + writer.writerows(orgaos.values(*fieldnames)) + return response + return render(request, "casas/cnpj_duplicado.html", context=context) + + +@login_required +@staff_member_required +def cnpj_errado(request): + formato = request.GET.get("fmt", "html") + form = CnpjErradoForm(request.GET) + if form.is_valid(): + has_convenio = form.cleaned_data.get("has_convenio", False) + else: + has_convenio = False + + todos_orgaos = ( + Orgao.objects.exclude(cnpj="") + .order_by("tipo", "cnpj", "nome") + .annotate( + tipo_nome=F("tipo__nome"), + municipio_nome=F("municipio__nome"), + uf_sigla=F("municipio__uf__sigla"), + ) + ) + if has_convenio: + todos_orgaos = todos_orgaos.exclude(convenio=None) + orgaos = [] + for orgao in todos_orgaos: + if not valida_cnpj(orgao.cnpj): + orgaos.append(orgao) + context = { + "orgaos": orgaos, + "form": form, + "title": _("Órgãos com CNPJ digitado errado"), + } + if formato == "pdf": + return WeasyTemplateResponse( + filename="cnpj_errado.pdf", + request=request, + template="casas/cnpj_errado_pdf.html", + context=context, + content_type="application/pdf", + ) + elif formato == "csv": + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = ( + 'attachment; filename="cnpj_errado.csv"' + ) + fieldnames = [ + "id", + "cnpj", + "tipo_nome", + "sigla", + "nome", + "municipio_nome", + "uf_sigla", + ] + writer = csv.DictWriter(response, fieldnames) + writer.writeheader() + writer.writerows( + [{f: getattr(o, f) for f in fieldnames} for o in orgaos] + ) + return response + return render(request, "casas/cnpj_errado.html", context=context) + + class GerentesListView(PermissionRequiredMixin, ListView): template_name = "admin/casas/gerentes_list.html" _tipos = None diff --git a/sigi/apps/convenios/admin.py b/sigi/apps/convenios/admin.py index f3802e9..2cf6cc4 100644 --- a/sigi/apps/convenios/admin.py +++ b/sigi/apps/convenios/admin.py @@ -1,6 +1,5 @@ from django.db.models import Q from django.contrib import admin -from django.http import HttpResponse, HttpResponseRedirect from django.utils import timezone from django.utils.translation import gettext as _ from django.utils.safestring import mark_safe @@ -14,12 +13,10 @@ from sigi.apps.convenios.models import ( Convenio, EquipamentoPrevisto, Anexo, - Tramitacao, Gescon, ) from sigi.apps.utils.mixins import AsciifyQParameter -from sigi.apps.servidores.models import Servidor -from sigi.apps.casas.admin import ConveniosInline, GerentesInterlegisFilter +from sigi.apps.casas.admin import GerentesInterlegisFilter from sigi.apps.utils.mixins import ( ReturnMixin, CartExportReportMixin, @@ -172,6 +169,7 @@ class ConvenioAdmin( _("Gescon"), { "fields": ( + "erro_gescon", "atualizacao_gescon", "observacao_gescon", "link_gescon", @@ -181,6 +179,7 @@ class ConvenioAdmin( ) readonly_fields = ( "data_sigi", + "erro_gescon", "atualizacao_gescon", "observacao_gescon", "link_gescon", @@ -210,6 +209,7 @@ class ConvenioAdmin( "conveniada", "equipada", "casa_legislativa__municipio__uf", + "erro_gescon", ) ordering = ( "casa_legislativa__municipio__uf__sigla", diff --git a/sigi/apps/convenios/migrations/0036_convenio_erro_gescon.py b/sigi/apps/convenios/migrations/0036_convenio_erro_gescon.py new file mode 100644 index 0000000..d10c793 --- /dev/null +++ b/sigi/apps/convenios/migrations/0036_convenio_erro_gescon.py @@ -0,0 +1,20 @@ +# Generated by Django 5.0.4 on 2024-05-15 11:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("convenios", "0035_convenio_data_extincao_convenio_motivo_extincao"), + ] + + operations = [ + migrations.AddField( + model_name="convenio", + name="erro_gescon", + field=models.BooleanField( + default=False, max_length=1, verbose_name="erro no Gescon" + ), + ), + ] diff --git a/sigi/apps/convenios/models.py b/sigi/apps/convenios/models.py index 5e76640..e0fcf38 100644 --- a/sigi/apps/convenios/models.py +++ b/sigi/apps/convenios/models.py @@ -5,6 +5,7 @@ from hashlib import md5 from pathlib import Path from django.db import models from django.db.models import Q, F +from django.contrib.sites.shortcuts import get_current_site from django.core.mail import send_mail from django.core.validators import FileExtensionValidator from django.template import Template, Context @@ -20,7 +21,7 @@ from weasyprint import HTML from sigi.apps.contatos.models import Municipio, UnidadeFederativa from sigi.apps.parlamentares.models import Parlamentar from sigi.apps.utils import to_ascii -from sigi.apps.casas.models import Funcionario, Orgao, TipoOrgao +from sigi.apps.casas.models import Funcionario, Orgao from sigi.apps.servidores.models import Servidor, Servico from sigi.apps.utils import editor_help, mask_cnpj @@ -327,6 +328,11 @@ class Convenio(models.Model): atualizacao_gescon = models.DateTimeField( _("Data de atualização pelo Gescon"), blank=True, null=True ) + erro_gescon = models.BooleanField( + _("erro no Gescon"), + max_length=1, + default=False, + ) observacao_gescon = models.TextField( _("Observações da atualização do Gescon"), blank=True ) @@ -636,7 +642,7 @@ class Gescon(models.Model): importados/atualizados) """ - def mathnames(nome, orgaos): + def mathnames(nome, orgaos, all=False): for o, nome_canonico in orgaos: ratio = SequenceMatcher( None, to_ascii(nome).lower(), nome_canonico @@ -644,9 +650,9 @@ class Gescon(models.Model): if ratio > 0.9: yield (o, ratio) - def get_semelhantes(nome, orgaos): + def get_semelhantes(nome, orgaos, all=False): return sorted( - mathnames(nome, orgaos), + mathnames(nome, orgaos, all), key=lambda m: m[1], ) @@ -700,6 +706,9 @@ class Gescon(models.Model): requests.packages.urllib3.disable_warnings() report_user = False + Convenio.objects.update(erro_gescon=False) + + dominio = get_current_site(None).domain for sigla_gescon, sigla_sigi in subespecies: self.add_message(_(f"\n**Importando subespécie {sigla_gescon}**")) @@ -784,6 +793,7 @@ class Gescon(models.Model): cnpj_masked = mask_cnpj(cnpj) else: cnpj = None + cnpj_masked = None if contrato["nomeFornecedor"]: nome = to_ascii( @@ -799,70 +809,58 @@ class Gescon(models.Model): nome = None # Buscar o Convenio pelo NUP # - try: - convenio = Convenio.objects.get( - projeto=projeto, num_processo_sf=sigad - ) - except Convenio.DoesNotExist: + convenios = Convenio.objects.filter( + projeto=projeto, num_processo_sf=sigad + ) + if convenios.count() == 0: # Encontrou 0: Pode ser que só exista com o código Gescon - try: - convenio = Convenio.objects.get( - Q(projeto=projeto) - & Q( - Q(num_convenio=numero) - | Q(num_processo_sf=numero) - ) - ) - except Convenio.DoesNotExist: - # Encontrou 0: Não existe mesmo. Precisa ser criado. - # Para não esticar muito a profundidade do código, - # vou setar convenio para None e tratar o caso lá na - # frente. - convenio = None - except Convenio.MultipleObjectsReturned: - # Encontrou N: Reportar erro - self.add_message( - _( - f"\t* O contrato {numero} no Gescon pode ser " - "relacionado aos seguintes convênios do SIGI:" - + ", ".join( - [ - reverse( - "admin:convenios_convenio_change", - args=[c.id], - ) - for c in Convenio.objects.filter( - Q(num_convenio=numero) - | Q(num_processo_sf=numero) - ) - ] - ) + convenios = Convenio.objects.filter( + Q(projeto=projeto) + & Q(Q(num_convenio=numero) | Q(num_processo_sf=numero)) + ) + if convenios.count() > 1: + # Encontrou N: Marcamos todos como erro e reportamos + urls = ", ".join( + [ + '{id}'.format( + dominio=dominio, + uri=reverse( + "admin:convenios_convenio_change", + args=[c.id], + ), + id=c.id, ) - ) - erros += 1 - continue - except Convenio.MultipleObjectsReturned: + for c in convenios + ] + ) + convenios.update( + erro_gescon=True, + observacao_gescon=_( + "Este convênio possui o mesmo número dos " + f"convenios {urls}" + ), + ) self.add_message( _( - f"\t* O contrato {numero} no Gescon pode ser " - "relacionado aos seguintes convênios do SIGI: " - + ", ".join( - [ - reverse( - "admin:convenios_convenio_change", - args=[c.id], - ) - for c in Convenio.objects.filter( - num_processo_sf=sigad - ) - ] - ) + f"\t* O contrato {numero} no Gescon pode " + "ser relacionado aos seguintes convênios " + f"do SIGI: {urls}" ) ) erros += 1 - continue - if convenio is not None: - # Encontrou 1: Basta atualizar + # Porém, talvez seja possível ser desambiguado pelo CNPJ do + # fornecedor + if cnpj_masked is not None: + convenios = convenios.filter( + casa_legislativa__cnpj=cnpj_masked + ) + if convenios.count() != 1: + # Continua ambíguo. Não dá pra fazer nada. + continue + if convenios.count() == 1: + # Achou exatamente o único que deveria existir. Basta + # atualizar os dados + convenio = convenios.get() convenio.projeto = projeto convenio.num_processo_sf = sigad convenio.num_convenio = numero @@ -876,10 +874,25 @@ class Gescon(models.Model): ] convenio.data_pub_diario = contrato["publicacao"] convenio.atualizacao_gescon = timezone.localtime() + convenio.erro_gescon = False + convenio.observacao_gescon = "" + convenio.id_contrato_gescon = ( + contrato["codTextoContrato"] or "" + ) + convenio.save() atualizados += 1 + # Corrigir o CNPJ do órgão se estiver diferente do Gescon + # O gescon é um pouquinho mais confiável, por enquanto. + if ( + cnpj_masked + and convenio.casa_legislativa.cnpj != cnpj_masked + ): + convenio.casa_legislativa.cnpj = cnpj_masked + convenio.casa_legislativa.save() continue - # Não encontrou o convênio. Vamos tentar criar... + # Se chegou aqui, é porque não encontrou o convênio. + # Um novo convênio precisa ser criado. # Primeiro, é preciso identificar qual órgão consta no # contrato do Gescon if (cnpj is None) and (nome is None): @@ -897,7 +910,7 @@ class Gescon(models.Model): try: orgao = Orgao.objects.get(cnpj=cnpj_masked) except Orgao.MultipleObjectsReturned: - # Pode acontecer de uma cãmara usar o mesmo CNPJ + # Pode acontecer de uma câmara usar o mesmo CNPJ # da prefeitura, e ambos terem convênio com o ILB. # Podemos tentar desambiguar pelo nome mais # semelhante. @@ -912,12 +925,12 @@ class Gescon(models.Model): .order_by() .annotate(uf_sigla=F("municipio__uf__sigla")) ], + all=True, )[0][0] except Orgao.DoesNotExist: # Encontrou 0: Vamos seguir sem órgao e tentar # encontrar pelo nome logo abaixo orgao = None - if orgao is None: # Não achou pelo CNPJ. Bora ver se acha por similaridade # do nome @@ -936,7 +949,7 @@ class Gescon(models.Model): ) erros += 1 continue - # Primeiro com o nome igual veio do GESCON + # Tentar primeiro com o nome igual veio do GESCON semelhantes = get_semelhantes( to_ascii(contrato["nomeFornecedor"]).lower(), todos_orgaos, @@ -997,9 +1010,20 @@ class Gescon(models.Model): observacao_gescon=_( "Importado integralmente do Gescon" ), + id_contrato_gescon=( + contrato["codTextoContrato"] or "" + ), ) convenio.save() novos += 1 + # Corrigir o CNPJ do órgão se estiver diferente do Gescon + # O gescon é um pouquinho mais confiável, por enquanto. + if ( + cnpj_masked + and convenio.casa_legislativa.cnpj != cnpj_masked + ): + convenio.casa_legislativa.cnpj = cnpj_masked + convenio.casa_legislativa.save() continue if novos or erros or alertas or atualizados: diff --git a/sigi/apps/convenios/templates/convenios/erros_gescon.html b/sigi/apps/convenios/templates/convenios/erros_gescon.html new file mode 100644 index 0000000..0c051d6 --- /dev/null +++ b/sigi/apps/convenios/templates/convenios/erros_gescon.html @@ -0,0 +1,41 @@ +{% extends "admin/base_site.html" %} +{% load static i18n %} + +{% block extrastyle %} +{{ block.super }} + + +{% endblock %} + +{% block coltype %}colMS{% endblock %} + +{% block content_title %} +
+ {% blocktranslate count counter=convenios.count %} + Um convênio com erro na importação do Gescon + {% plural %} + {{ counter }} convênios com erro na importação do Gescon + {% endblocktranslate %} +
+{% endblock %} + +{% block breadcrumbs %} +{% endblock %} + +{% block content %} + {% include "convenios/snippets/erros_gescon_snippet.html" with mode="html" %} +{% endblock %} + +{% block footer %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/sigi/apps/convenios/templates/convenios/erros_gescon_pdf.html b/sigi/apps/convenios/templates/convenios/erros_gescon_pdf.html new file mode 100644 index 0000000..61c350c --- /dev/null +++ b/sigi/apps/convenios/templates/convenios/erros_gescon_pdf.html @@ -0,0 +1,21 @@ +{% extends "pdf/base_report.html" %} +{% load static i18n %} + +{% block page_size %}A4 landscape{% endblock %} + +{% block report_name %} + {% blocktranslate count counter=convenios.count %} + Um convênio com erro na importação do Gescon + {% plural %} + {{ counter }} convênios com erro na importação do Gescon + {% endblocktranslate %} +{% endblock report_name %} + +{% block main_content %} + {% include "convenios/snippets/erros_gescon_snippet.html" %} +
+
+

{% translate "Resumo da última importação de dados do Gescon" %}

+ {{ ultima_importacao }} +
+{% endblock %} diff --git a/sigi/apps/convenios/templates/convenios/snippets/erros_gescon_snippet.html b/sigi/apps/convenios/templates/convenios/snippets/erros_gescon_snippet.html new file mode 100644 index 0000000..3344246 --- /dev/null +++ b/sigi/apps/convenios/templates/convenios/snippets/erros_gescon_snippet.html @@ -0,0 +1,39 @@ +{% load i18n %} +
+
+ + + + + + + + + + + + + + {% for convenio in convenios %} + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
{% translate "id SIGI" %}{% translate "NUP sigad" %}{% translate "Número" %}{% translate "Projeto" %}{% translate "Órgão conveniado" %}{% translate "UF" %}{% translate "Erro encontrado" %}
+ {{ convenio.id|stringformat:"s" }} + {{ convenio.num_processo_sf }}{{ convenio.num_convenio }}{{ convenio.projeto.nome }}{{ convenio.casa_legislativa }}{{ convenio.casa_legislativa.municipio.uf.sigla }}{{ convenio.observacao_gescon|safe }}
+ {% translate "Nenhum convênio com erro de importação do Gescon." %} +
+
+
diff --git a/sigi/apps/convenios/urls.py b/sigi/apps/convenios/urls.py index d3155ac..a7db026 100644 --- a/sigi/apps/convenios/urls.py +++ b/sigi/apps/convenios/urls.py @@ -2,6 +2,11 @@ from django.urls import path from sigi.apps.convenios import views urlpatterns = [ + path( + "errosgescon/", + views.report_erros_gescon, + name="convenios-report_erros_gescon", + ), path( "reportsRegiao//", views.report_regiao, diff --git a/sigi/apps/convenios/views.py b/sigi/apps/convenios/views.py index 4e86dc7..25c62ad 100644 --- a/sigi/apps/convenios/views.py +++ b/sigi/apps/convenios/views.py @@ -1,14 +1,8 @@ import csv - -# from django.contrib import messages -from django.contrib import admin -from django.http.response import HttpResponseForbidden - -# from django.conf import settings -# from django.core.paginator import Paginator, InvalidPage, EmptyPage -# from django.http import HttpResponse, HttpResponseRedirect +from docutils.core import publish_parts +from django.utils.safestring import mark_safe +from django.http import HttpResponse, HttpResponseForbidden from django.shortcuts import render, get_list_or_404 -from django.template import Context, loader from django.utils.translation import gettext as _ from django.contrib.admin.views.decorators import staff_member_required from django.contrib.auth.decorators import login_required @@ -17,12 +11,70 @@ 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.convenios.reports import (ConvenioReport, -# ConvenioReportSemAceite, -# ConvenioPorCMReport, -# ConvenioPorALReport, -# ConvenioReportSemAceiteAL, -# ConvenioReportSemAceiteCM) + +@login_required +@staff_member_required +def report_erros_gescon(request): + formato = request.GET.get("fmt", "html") + convenios = ( + Convenio.objects.filter(erro_gescon=True) + .order_by( + "num_processo_sf", + "projeto", + "num_convenio", + "casa_legislativa", + ) + .prefetch_related( + "casa_legislativa", "casa_legislativa__municipio__uf" + ) + ) + rst = Gescon.load().ultima_importacao + parts = publish_parts( + rst, + writer_name="html5", + settings_overrides={ + "input_encoding": "unicode", + "output_encoding": "unicode", + }, + ) + context = { + "convenios": convenios, + "ultima_importacao": mark_safe(parts["html_body"]), + "title": "Convênios com erros no Gescon", + } + if formato == "pdf": + return WeasyTemplateResponse( + filename="erros_gescon.pdf", + request=request, + template="convenios/erros_gescon_pdf.html", + context=context, + content_type="application/pdf", + ) + elif formato == "csv": + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = ( + 'attachment; filename="erros_gescon.csv"' + ) + fieldnames = [ + "id", + "num_processo_sf", + "num_convenio", + "projeto__nome", + "casa_legislativa__nome", + "casa_legislativa__municipio__uf__sigla", + "observacao_gescon", + ] + writer = csv.DictWriter(response, fieldnames) + writer.writeheader() + writer.writerows(convenios.values(*fieldnames)) + response.write("\n\nResumo da última importação do Gescon\n\n") + response.write('"' + rst.replace("\n", '"\n"') + '"') + return response + return render( + request, + "convenios/erros_gescon.html", + context=context, + ) @login_required diff --git a/sigi/menu_conf.yaml b/sigi/menu_conf.yaml index 33bcd40..123d3bb 100644 --- a/sigi/menu_conf.yaml +++ b/sigi/menu_conf.yaml @@ -29,6 +29,12 @@ main_menu: - title: Relatórios icon: print children: + - title: Erros importação Gescon + view_name: convenios-report_erros_gescon + - title: Órgãos com CNPJ duplicado + view_name: casas_cnpj_duplicado + - title: Órgãos com CNPJ errado + view_name: casas_cnpj_errado - title: Eventos por UF view_name: eventos_eventosporuf - title: Solicitações de eventos por período diff --git a/sigi/settings.py b/sigi/settings.py index 20f69cc..597cd97 100644 --- a/sigi/settings.py +++ b/sigi/settings.py @@ -66,6 +66,7 @@ INSTALLED_APPS = [ "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", + "django.contrib.sites", "django_extensions", "django_filters", ] @@ -81,6 +82,8 @@ MIDDLEWARE = [ "sigi.apps.utils.middleware.SigiAlertsMiddleware", ] +SITE_ID = 1 + if DEBUG: INSTALLED_APPS = [ "debug_toolbar",