diff --git a/sigi/apps/convenios/migrations/0040_alter_convenio_options_convenio_id_gescon_and_more.py b/sigi/apps/convenios/migrations/0040_alter_convenio_options_convenio_id_gescon_and_more.py
new file mode 100644
index 0000000..b1480a4
--- /dev/null
+++ b/sigi/apps/convenios/migrations/0040_alter_convenio_options_convenio_id_gescon_and_more.py
@@ -0,0 +1,39 @@
+# Generated by Django 6.0.4 on 2026-08-31 13:18
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("casas", "0027_alter_orgao_email"),
+ ("convenios", "0039_projeto_extinto"),
+ ("servidores", "0015_limpa_servidores_users"),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name="convenio",
+ options={
+ "get_latest_by": "id",
+ "ordering": ("id",),
+ "verbose_name": "convênio",
+ "verbose_name_plural": "convênios",
+ },
+ ),
+ migrations.AddField(
+ model_name="convenio",
+ name="id_gescon",
+ field=models.IntegerField(
+ blank=True, null=True, verbose_name="ID do convênio no Gescon"
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="convenio",
+ constraint=models.UniqueConstraint(
+ condition=models.Q(("id_gescon__isnull", False)),
+ fields=("id_gescon",),
+ name="unique_id_gescon_if_not_null",
+ ),
+ ),
+ ]
diff --git a/sigi/apps/convenios/models.py b/sigi/apps/convenios/models.py
index 0c662f9..bb2f664 100644
--- a/sigi/apps/convenios/models.py
+++ b/sigi/apps/convenios/models.py
@@ -2,6 +2,7 @@ import re
import requests
from hashlib import md5
from pathlib import Path
+from url_normalize import url_normalize
from django.db import models
from django.db.models import Q, F
from django.contrib.sites.shortcuts import get_current_site
@@ -231,6 +232,9 @@ class Convenio(models.Model):
default="",
editable=False,
)
+ id_gescon = models.IntegerField(
+ _("ID do convênio no Gescon"), blank=True, null=True
+ )
data_sigi = models.DateField(
_("data de cadastro no SIGI"), blank=True, null=True, auto_now_add=True
)
@@ -445,6 +449,14 @@ class Convenio(models.Model):
get_latest_by = "id"
ordering = ("id",)
verbose_name = _("convênio")
+ verbose_name_plural = _("convênios")
+ constraints = [
+ models.UniqueConstraint(
+ fields=["id_gescon"],
+ condition=models.Q(id_gescon__isnull=False),
+ name="unique_id_gescon_if_not_null",
+ )
+ ]
def __str__(self):
SDF = "SHORT_DATE_FORMAT"
@@ -556,6 +568,14 @@ class Tramitacao(models.Model):
class Gescon(models.Model):
+ _report_user = False
+ _dominio = ""
+ _novos = 0
+ _erros = 0
+ _alertas = 0
+ _atualizados = 0
+ _todos_orgaos = None
+
url_gescon = models.URLField(
_("Webservice Gescon"),
default=(
@@ -670,6 +690,8 @@ class Gescon(models.Model):
importados/atualizados)
"""
+ self._dominio = get_current_site(None).domain
+
self.ultima_importacao = ""
if self.checksums is None:
self.checksums = {}
@@ -707,96 +729,57 @@ class Gescon(models.Model):
)
return True
- palavras = self.palavras.splitlines()
- excludentes = self.palavras_excluir.splitlines()
- orgaos = self.orgaos_gestores.split()
subespecies = {tuple(s.split("=")) for s in self.subespecies.split()}
- todos_orgaos = [
+ self._todos_orgaos = [
(o, f"{to_ascii(o.nome)} - {o.uf_sigla}".lower())
for o in Orgao.objects.all()
.order_by()
.annotate(uf_sigla=F("municipio__uf__sigla"))
]
- requests.packages.urllib3.disable_warnings()
- report_user = False
- dominio = get_current_site(None).domain
+ self._report_user = False
for sigla_gescon, sigla_sigi in subespecies:
- self.add_message(_(f"\n**Importando subespécie {sigla_gescon}**"))
- url = self.url_gescon.format(s=sigla_gescon)
-
- projeto = Projeto.objects.get(sigla=sigla_sigi)
-
- try:
- response = requests.get(url, verify=False)
- except Exception as e:
- self.add_message(_(f"\tErro ao acessar {url}: {e.message}"))
- report_user = True
- continue
-
- if response.status_code != 200:
- self.add_message(
- f"\tErro na leitura dos dados de {url}: "
- f"[{response.status_code}] {response.reason}"
+ self.add_message(
+ _("\n**Importando subespécie {sigla_gescon}**").format(
+ sigla_gescon=sigla_gescon
)
- continue
-
- if not response.ok:
+ )
+ try:
+ projeto = Projeto.objects.get(sigla=sigla_sigi)
+ except Projeto.DoesNotExist:
self.add_message(
- _(f"\tErro ao acessar {url}: {response.reason}")
+ _(
+ "Projeto com a sigla {sigla_sigi} não existe no SIGI"
+ ).format(sigla_sigi=sigla_sigi)
)
- report_user = True
continue
-
- if "application/json" not in response.headers.get("Content-Type"):
+ except Projeto.MultipleObjectsReturned:
self.add_message(
_(
- f"\tResultado da consulta à {url} não "
- "retornou dados em formato json"
- )
+ "A sigla {sigla_sigi} foi cadastrada com duplicidade "
+ "no SIGI"
+ ).format(sigla_sigi=sigla_sigi)
)
- report_user = True
continue
- md5sum = md5(response.text.encode(response.encoding)).hexdigest()
- if (
- sigla_gescon in self.checksums
- and self.checksums[sigla_gescon] == md5sum
- ):
+ nossos = self._get_contratos_gescon(sigla_gescon)
+ if not nossos:
self.add_message(
- f"\tDados da subespécie {sigla_gescon} inalterados no "
- "Gescon. Processamento desnecessário."
+ _(
+ "Nenhum contrato do tipo {sigla_gescon} pertence ao "
+ "ILB / Interlegis"
+ ).format(sigla_gescon=sigla_gescon)
)
continue
- contratos = response.json()
+ # Desmarca erros de importações passadas
Convenio.objects.filter(projeto=projeto).update(erro_gescon=False)
- # Pegar só os contratos que possuem alguma das palavras-chave
-
- nossos = [
- c
- for c in contratos
- if (
- any(palavra in c["objeto"] for palavra in palavras)
- or any(
- orgao in c["orgaosGestoresTitulares"]
- for orgao in orgaos
- if c["orgaosGestoresTitulares"] is not None
- )
- )
- and not any(palavra in c["objeto"] for palavra in excludentes)
- ]
-
- self.add_message(
- _(f"\t{len(nossos)} contratos encontrados no Gescon")
- )
-
- novos = 0
- erros = 0
- alertas = 0
- atualizados = 0
+ self._novos = 0
+ self._erros = 0
+ self._alertas = 0
+ self._atualizados = 0
for contrato in nossos:
if contrato["numero"] is None:
@@ -807,7 +790,7 @@ class Gescon(models.Model):
"importado."
).format(id=contrato["id"], sub=contrato["subEspecie"])
)
- report_user = True
+ self._report_user = True
continue
numero = re.sub(
r"(\d{4})(\d{4})", r"\1/\2", contrato["numero"].zfill(8)
@@ -838,235 +821,345 @@ class Gescon(models.Model):
else:
nome = None
- # Buscar o Convenio pelo NUP #
- convenios = Convenio.objects.filter(
- projeto=projeto, num_processo_sf=sigad
+ convenio = self._get_convenio(
+ contrato, projeto, sigad, numero, cnpj_masked
)
- if convenios.count() == 0:
- # Encontrou 0: Pode ser que só exista com o código Gescon
- 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,
- )
- 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 "
- f"do SIGI: {urls}"
- )
- )
- erros += 1
- # 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
- convenio.data_sigad = contrato["assinatura"]
- convenio.observacao = contrato["objeto"]
- convenio.data_retorno_assinatura = contrato[
- "inicioVigencia"
- ]
- convenio.data_termino_vigencia = contrato["terminoVigencia"]
- 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 ""
+
+ if convenio:
+ # Encontrou um único convênio, basta atualizar seus dados
+ self._update_convenio(
+ convenio, contrato, projeto, sigad, numero
)
- 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
+ self._update_orgao(convenio.casa_legislativa, cnpj_masked)
+ self._atualizados += 1
+ else:
+ # Não encontrou o convênio. Um novo convênio precisa ser
+ # criado. Primeiro, é preciso identificar qual órgão
+ # consta no contrato do Gescon
- # 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):
- self.add_message(
- _(
- f"\t* O contrato {numero} no Gescon não informa "
- "nem o CNPJ nem o nome do órgão, então não é "
- "possível importar para o SIGI."
- )
+ orgao = self._get_orgao(
+ contrato, cnpj, cnpj_masked, nome, numero, sigad
)
- erros += 1
- continue
- # Vamos tentar primeiro com o CNPJ
- if cnpj is not None:
- try:
- orgao = Orgao.objects.get(cnpj=cnpj_masked)
- except Orgao.MultipleObjectsReturned:
- # 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.
- orgao = Orgao.get_semelhantes(
- to_ascii(contrato["nomeFornecedor"]).lower(),
- [
- (
- o,
- f"{to_ascii(o.nome)} - {o.uf_sigla}".lower(),
- )
- for o in Orgao.objects.filter(cnpj=cnpj_masked)
- .order_by()
- .annotate(uf_sigla=F("municipio__uf__sigla"))
- ],
- min_ratio=0,
- )[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
- if nome is None:
- # Também não tem nome... então temos que reportar erro
+
+ if orgao is None:
+ # Não encontrou o órgão... bora reportar o erro
self.add_message(
_(
- f"\t* O contrato {numero} no Gescon "
- f"com NUP sigad {sigad}, fornecedor "
- f"{cnpj_masked} não pode ser imortado porque "
- "não é possível identificar o órgão no SIGI. "
- "Cadastre um órgão com o CNPJ desse "
- "fornecedor, que na próxima importação este "
- "contrato será importado."
+ "\t* Órgão não encontrado no SIGI ou mais de "
+ "um órgão encontrado com o mesmo CNPJ ou nome. "
+ "Favor regularizar o cadastro: \n"
+ "\t * CNPJ: {cnpj}\n"
+ "\t * Nome: {nome}"
+ ).format(
+ cnpj=contrato["cnpjCpfFornecedor"],
+ nome=contrato["nomeFornecedor"],
)
)
- erros += 1
- continue
- # Tentar primeiro com o nome igual veio do GESCON
- semelhantes = Orgao.get_semelhantes(
- to_ascii(contrato["nomeFornecedor"]).lower(),
- todos_orgaos,
- )
- if not semelhantes:
- # Não achou, então vamos tentar com o nome limpado
- semelhantes = Orgao.get_semelhantes(
- to_ascii(nome).lower(),
- todos_orgaos,
- )
- if len(semelhantes) > 0:
- # Encontrou algo semelhante.... bora usar.
- orgao = semelhantes[0][0]
+ self._erros += 1
else:
- # Não encontrou nada parecido. Bora reportar como erro
- self.add_message(
- _(
- f"\t* O contrato {numero} no Gescon "
- f"com NUP Sigad {sigad}, indica o "
- f"fornecedor com CNPJ {cnpj_masked} "
- f"e com o nome {contrato['nomeFornecedor']}, "
- "que não tem correspondência no SIGI. "
- "Este convênio precisa ser cadastrado "
- "manualmente no SIGI para este erro "
- "parar de acontecer."
- )
+ # Bora criar o convênio
+ self._create_convenio(
+ orgao, contrato, projeto, sigad, numero
)
- erros += 1
- continue
- # Não encontrou o órgão... bora reportar o erro
- if orgao is None:
- # Em teoria, nunca vai cair aqui... mas...
- self.add_message(
- _(
- f"\t* Órgão não encontrado no SIGI ou mais de um "
- f"órgão encontrado com o mesmo CNPJ ou nome. Favor"
- f" regularizar o cadastro: "
- f"CNPJ: {contrato['cnpjCpfFornecedor']}, "
- f"Nome: {contrato['nomeFornecedor']}"
- )
- )
- erros += 1
- continue
- else:
- # Bora criar o convênio
- convenio = Convenio(
- casa_legislativa=orgao,
- projeto=projeto,
- num_processo_sf=sigad,
- num_convenio=numero,
- data_sigi=timezone.localdate(),
- data_sigad=contrato["assinatura"],
- observacao=contrato["objeto"],
- data_retorno_assinatura=contrato["inicioVigencia"],
- data_termino_vigencia=contrato["terminoVigencia"],
- data_pub_diario=contrato["publicacao"],
- atualizacao_gescon=timezone.localtime(),
- 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
+ self._update_orgao(orgao, cnpj_masked)
+ self._novos += 1
- if novos or erros or alertas or atualizados:
+ if self._novos or self._erros or self._alertas or self._atualizados:
report_user = True
self.add_message(
_(
- f"\n\n\t{novos} novos convenios adicionados ao SIGI, "
- f"{atualizados} atualizados, sendo {alertas} com alertas, e "
- f"{erros} reportados com erro."
+ "\n\n\t{novos} novos convenios adicionados ao SIGI, "
+ "{atualizados} atualizados, sendo {alertas} com alertas, e "
+ "{erros} reportados com erro."
+ ).format(
+ novos=self._novos,
+ atualizados=self._atualizados,
+ alertas=self._alertas,
+ erros=self._erros,
)
)
- self.checksums[sigla_gescon] = md5sum
self.save()
return report_user
+ def _get_contratos_gescon(self, sigla_gescon):
+ requests.packages.urllib3.disable_warnings()
+
+ url = self.url_gescon.format(s=sigla_gescon)
+ palavras = self.palavras.splitlines()
+ orgaos = self.orgaos_gestores.splitlines()
+ excludentes = self.palavras_excluir.splitlines()
+
+ try:
+ response = requests.get(url, verify=False)
+ except Exception as e:
+ self.add_message(_(f"\tErro ao acessar {url}: {e.message}"))
+ self._report_user = True
+ return None
+
+ if response.status_code != 200:
+ self.add_message(
+ _(
+ "\tErro na leitura dos dados de {url}: "
+ "[{status_code}] {reason}"
+ ).format(
+ url=url,
+ status_code=response.status_code,
+ reason=response.reason,
+ )
+ )
+ self._report_user = True
+ return None
+
+ if "application/json" not in response.headers.get("Content-Type", ""):
+ self.add_message(
+ _(
+ "\tResultado da consulta à {url} não retornou dados "
+ "em formato json: {content_type}"
+ ).format(
+ url=url,
+ content_type=response.headers.get("Content-Type", ""),
+ )
+ )
+ self._report_user = True
+ return None
+
+ md5sum = md5(response.text.encode(response.encoding)).hexdigest()
+ if (
+ sigla_gescon in self.checksums
+ and self.checksums[sigla_gescon] == md5sum
+ ):
+ self.add_message(
+ _(
+ "\tDados da subespécie {sigla_gescon} inalterados no "
+ "Gescon. Processamento desnecessário."
+ ).format(sigla_gescon=sigla_gescon)
+ )
+ return None
+ self.checksums[sigla_gescon] = md5sum
+
+ contratos = response.json()
+
+ # Pegar só os contratos que possuem alguma das palavras-chave
+ nossos = [
+ c
+ for c in contratos
+ if (
+ any(palavra in c["objeto"] for palavra in palavras)
+ or any(
+ orgao in c["orgaosGestoresTitulares"]
+ for orgao in orgaos
+ if c["orgaosGestoresTitulares"] is not None
+ )
+ )
+ and not any(palavra in c["objeto"] for palavra in excludentes)
+ ]
+
+ self.add_message(
+ _("\t{qty} contratos encontrados no Gescon").format(qty=len(nossos))
+ )
+
+ return nossos
+
+ def _get_convenio(self, contrato, projeto, sigad, numero, cnpj_masked):
+ # Buscar o convenio pelo ID do Gescon
+ try:
+ convenio = Convenio.objects.get(id_gescon=contrato["id"])
+ return convenio
+ except Convenio.DoesNotExist:
+ # Não existe, mas vamos buscar com outras chaves candidatas
+ pass
+
+ # Buscar o Convenio pelo NUP #
+ convenios = Convenio.objects.filter(
+ projeto=projeto, num_processo_sf=sigad
+ )
+ num_convenios = convenios.count()
+ if num_convenios == 0:
+ # Nenhum convênio. Vamos continuar buscando em todos convenios
+ # desse tipo de projeto
+ convenios = Convenio.objects.filter(projeto=projeto)
+ elif num_convenios == 1:
+ # Encontrou um único, então deve ser ele
+ return convenios.get()
+
+ # Se chegou aqui, é porque encontrou vários ou nenhum.
+ # Vamos tentar diferenciar pelas outras chaves candidatas
+
+ convenios = convenios.filter(
+ Q(Q(num_convenio=numero) | Q(num_processo_sf=numero))
+ )
+ num_convenios = convenios.count()
+
+ if num_convenios == 0:
+ # Nenhum encontrado. Já finaliza como não encontrado
+ return None
+ elif num_convenios == 1:
+ # Achou um. Deve ser ele
+ return convenios.get()
+
+ # Encontrou N: Marcamos todos como erro e reportamos
+ urls = []
+ for c in convenios:
+ uri = reverse("admin:convenios_convenio_change", args=[c.id])
+ url = url_normalize(f"{self._dominio}{uri}")
+ urls.append(f'{c.id}')
+ urls = ", ".join(urls)
+ convenios.update(
+ erro_gescon=True,
+ observacao_gescon=_(
+ "Este convênio possui o mesmo número dos convenios {urls}"
+ ).format(urls=urls),
+ )
+ self.add_message(
+ _(
+ "\t* O contrato {numero} no Gescon pode "
+ "ser relacionado aos seguintes convênios "
+ "do SIGI: {urls}"
+ ).format(numero=numero, urls=urls)
+ )
+ self._erros += 1
+
+ # 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:
+ # Achou exatamente o único que deveria existir.
+ return convenios.get()
+ # Continua ambíguo. Não dá pra fazer nada.
+ return None
+
+ def _get_orgao(self, contrato, cnpj, cnpj_masked, nome, numero, sigad):
+ if (cnpj is None) and (nome is None):
+ self.add_message(
+ _(
+ "\t* O contrato {numero} no Gescon não informa nem o CNPJ "
+ "nem o nome do órgão, então não é possível importar "
+ "para o SIGI."
+ ).format(numero=numero)
+ )
+ self._erros += 1
+ return None
+ # Vamos tentar primeiro com o CNPJ
+ if cnpj is not None:
+ try:
+ orgao = Orgao.objects.get(cnpj=cnpj_masked)
+ return orgao
+ except Orgao.MultipleObjectsReturned:
+ # 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.
+ orgaos = Orgao.get_semelhantes(
+ to_ascii(contrato["nomeFornecedor"]).lower(),
+ [
+ (
+ o,
+ f"{to_ascii(o.nome)} - {o.uf_sigla}".lower(),
+ )
+ for o in Orgao.objects.filter(cnpj=cnpj_masked)
+ .order_by()
+ .annotate(uf_sigla=F("municipio__uf__sigla"))
+ ],
+ min_ratio=0,
+ )
+ if orgaos:
+ # Retorna o mais semelhante
+ return orgaos[0][0]
+ except Orgao.DoesNotExist:
+ # Não encontrou nenhum. Vamos seguir sem órgao e tentar
+ # encontrar pelo nome logo abaixo
+ pass
+ # Não achou pelo CNPJ. Bora ver se acha por similaridade do nome
+ if nome is None:
+ # Também não tem nome... então temos que reportar erro
+ self.add_message(
+ _(
+ "\t* O contrato {numero} no Gescon com NUP sigad {sigad}, "
+ "fornecedor {cnpj_masked} não pode ser imortado porque "
+ "não é possível identificar o órgão no SIGI. Cadastre um "
+ "órgão com o CNPJ desse fornecedor, que na próxima "
+ "importação este contrato será importado."
+ ).format(numero=numero, sigad=sigad, cnpj_masked=cnpj_masked)
+ )
+ self._erros += 1
+ return None
+ # Tentar primeiro com o nome igual veio do GESCON
+ semelhantes = Orgao.get_semelhantes(
+ to_ascii(contrato["nomeFornecedor"]).lower(),
+ self._todos_orgaos,
+ )
+ if not semelhantes:
+ # Não achou, então vamos tentar com o nome limpado
+ semelhantes = Orgao.get_semelhantes(
+ to_ascii(nome).lower(),
+ self._todos_orgaos,
+ )
+ if len(semelhantes) > 0:
+ # Encontrou algo semelhante.... bora usar.
+ return semelhantes[0][0]
+ # Não encontrou nada parecido. Bora reportar como erro
+ self.add_message(
+ _(
+ "\t* O contrato {numero} no Gescon com NUP Sigad {sigad}, "
+ "indica o fornecedor com CNPJ {cnpj_masked} e com o nome "
+ "{nome}, que não tem correspondência no SIGI. Este convênio "
+ "precisa ser cadastrado manualmente no SIGI para este erro "
+ "parar de acontecer."
+ ).format(
+ numero=numero,
+ sigad=sigad,
+ cnpj_masked=cnpj_masked,
+ nome=contrato["nomeFornecedor"],
+ )
+ )
+ self._erros += 1
+ return None
+
+ def _update_convenio(self, convenio, contrato, projeto, sigad, numero):
+ convenio.projeto = projeto
+ convenio.num_processo_sf = sigad
+ convenio.num_convenio = numero
+ convenio.data_sigad = contrato["assinatura"]
+ convenio.observacao = contrato["objeto"]
+ convenio.data_retorno_assinatura = contrato["inicioVigencia"]
+ convenio.data_termino_vigencia = contrato["terminoVigencia"]
+ 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.id_gescon = contrato["id"]
+ convenio.save()
+
+ def _create_convenio(self, orgao, contrato, projeto, sigad, numero):
+ convenio = Convenio(
+ casa_legislativa=orgao,
+ projeto=projeto,
+ num_processo_sf=sigad,
+ num_convenio=numero,
+ data_sigi=timezone.localdate(),
+ data_sigad=contrato["assinatura"],
+ observacao=contrato["objeto"],
+ data_retorno_assinatura=contrato["inicioVigencia"],
+ data_termino_vigencia=contrato["terminoVigencia"],
+ data_pub_diario=contrato["publicacao"],
+ atualizacao_gescon=timezone.localtime(),
+ observacao_gescon=_("Importado integralmente do Gescon"),
+ id_contrato_gescon=(contrato["codTextoContrato"] or ""),
+ id_gescon=contrato["id"],
+ )
+ convenio.save()
+
+ def _update_orgao(self, orgao, cnpj_masked):
+ # Corrigir o CNPJ do órgão se estiver diferente do
+ # Gescon. O gescon é um pouquinho mais confiável,
+ # por enquanto.
+ if cnpj_masked and orgao.cnpj != cnpj_masked:
+ orgao.cnpj = cnpj_masked
+ orgao.save()
+
@classmethod
def load(cls):
obj, created = cls.objects.get_or_create(pk=1)