From 4e69141cf1a3678fc9a054488cb8402f834e90e1 Mon Sep 17 00:00:00 2001 From: Alessandro Fernandes Date: Fri, 23 Sep 2022 07:31:23 -0300 Subject: [PATCH 1/9] =?UTF-8?q?fix(74103):=20Cria=20scripts=20identifica?= =?UTF-8?q?=C3=A7=C3=A3o=20de=20dev-tesouro=20perdidas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contrib/scripts_diversos/dev_tesouro.sql | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 sme_ptrf_apps/contrib/scripts_diversos/dev_tesouro.sql diff --git a/sme_ptrf_apps/contrib/scripts_diversos/dev_tesouro.sql b/sme_ptrf_apps/contrib/scripts_diversos/dev_tesouro.sql new file mode 100644 index 000000000..0978a52fb --- /dev/null +++ b/sme_ptrf_apps/contrib/scripts_diversos/dev_tesouro.sql @@ -0,0 +1,76 @@ +-- noinspection SqlNoDataSourceInspectionForFile + +-- Lista solicitações de devolução ao tesouro sem uma devolução ao tesouro vinculada +select +a.unidade_id as "codigo_eol", +a.nome, +pc.id as "pc_id", +p.referencia, +d.id as "despesa_id", d.numero_documento, +count(sal.id) +from +core_prestacaoconta as pc, +core_periodo as p, +core_associacao as a, +core_analiseprestacaoconta as apc, +core_analiselancamentoprestacaoconta as al, +despesas_despesa as d, +core_solicitacaoacertolancamento as sal, +core_tipoacertolancamento as tal +where +pc.periodo_id = p.id +and pc.associacao_id = a.id +and apc.prestacao_conta_id = pc.id +and al.analise_prestacao_conta_id = apc.id +and al.despesa_id = d.id +and sal.analise_lancamento_id = al.id +and tal.id = sal.tipo_acerto_id +and tal.categoria = 'DEVOLUCAO' +and sal.devolucao_ao_tesouro_id is null +group by +pc.id, +p.referencia, +a.unidade_id, +a.nome, +d.id, d.numero_documento, d.valor_total +order by +a.unidade_id, +a.nome, +p.referencia, +d.id, d.numero_documento, d.valor_total + + +-- Lista de Prestações de Conta com devoluções ao tesouro faltando +select +a.unidade_id as "codigo_eol", +a.nome, +pc.id as "pc_id", +p.referencia +from +core_prestacaoconta as pc, +core_periodo as p, +core_associacao as a, +core_analiseprestacaoconta as apc, +core_analiselancamentoprestacaoconta as al, +despesas_despesa as d, +core_solicitacaoacertolancamento as sal, +core_tipoacertolancamento as tal +where +pc.periodo_id = p.id +and pc.associacao_id = a.id +and apc.prestacao_conta_id = pc.id +and al.analise_prestacao_conta_id = apc.id +and al.despesa_id = d.id +and sal.analise_lancamento_id = al.id +and tal.id = sal.tipo_acerto_id +and tal.categoria = 'DEVOLUCAO' +and sal.devolucao_ao_tesouro_id is null +group by +pc.id, +p.referencia, +a.unidade_id, +a.nome +order by +a.unidade_id, +a.nome, +p.referencia From 3d81b762d420477b4d2502442f7612a8e7e74aa9 Mon Sep 17 00:00:00 2001 From: Alessandro Fernandes Date: Fri, 23 Sep 2022 17:10:20 -0300 Subject: [PATCH 2/9] =?UTF-8?q?fix(74103):=20Cria=20carga=20de=20devolu?= =?UTF-8?q?=C3=A7=C3=A3o=20ao=20tesouro?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sme_ptrf_apps/core/admin.py | 21 +- sme_ptrf_apps/core/choices/tipos_carga.py | 3 + .../migrations/0271_auto_20220923_1525.py | 23 ++ .../carga_devolucoes_tesouro_service.py | 247 ++++++++++++++++++ .../core/services/processa_cargas.py | 6 +- 5 files changed, 296 insertions(+), 4 deletions(-) create mode 100644 sme_ptrf_apps/core/migrations/0271_auto_20220923_1525.py create mode 100644 sme_ptrf_apps/core/services/carga_devolucoes_tesouro_service.py diff --git a/sme_ptrf_apps/core/admin.py b/sme_ptrf_apps/core/admin.py index cbdd85efb..1601e42cf 100644 --- a/sme_ptrf_apps/core/admin.py +++ b/sme_ptrf_apps/core/admin.py @@ -733,9 +733,24 @@ class TipoAcertoLancamentoAdmin(admin.ModelAdmin): @admin.register(SolicitacaoAcertoLancamento) class SolicitacaoAcertoLancamentoAdmin(admin.ModelAdmin): - list_display = ['uuid', 'analise_lancamento', 'tipo_acerto'] - search_fields = ['detalhamento'] - list_filter = ['tipo_acerto', ] + def get_associacao(self, obj): + return obj.analise_lancamento.analise_prestacao_conta.prestacao_conta.associacao.nome if obj and obj.analise_lancamento and obj.analise_lancamento.analise_prestacao_conta and obj.analise_lancamento.analise_prestacao_conta.prestacao_conta.associacao else '' + + get_associacao.short_description = 'Associação' + + def get_referencia(self, obj): + return obj.analise_lancamento.analise_prestacao_conta.prestacao_conta.periodo.referencia if obj and obj.analise_lancamento and obj.analise_lancamento.analise_prestacao_conta and obj.analise_lancamento.analise_prestacao_conta.prestacao_conta.periodo else '' + + get_referencia.short_description = 'Período' + + def get_despesa(self, obj): + return obj.analise_lancamento.despesa if obj and obj.analise_lancamento else '' + + get_referencia.short_description = 'Despesa' + + list_display = ['get_associacao', 'get_referencia', 'analise_lancamento', 'tipo_acerto', 'devolucao_ao_tesouro', 'get_despesa'] + search_fields = ['detalhamento', 'analise_lancamento__analise_prestacao_conta__prestacao_conta__associacao__unidade__codigo_eol' ] + list_filter = ['tipo_acerto', 'analise_lancamento__analise_prestacao_conta__prestacao_conta__periodo__referencia' ] readonly_fields = ('uuid', 'id',) diff --git a/sme_ptrf_apps/core/choices/tipos_carga.py b/sme_ptrf_apps/core/choices/tipos_carga.py index 3fd2e2b79..67c1a40a2 100644 --- a/sme_ptrf_apps/core/choices/tipos_carga.py +++ b/sme_ptrf_apps/core/choices/tipos_carga.py @@ -6,6 +6,7 @@ CARGA_USUARIOS = 'CARGA_USUARIOS' CARGA_CENSO = 'CARGA_CENSO' CARGA_REPASSE_PREVISTO_SME = 'CARGA_REPASSE_PREVISTO_SME' +CARGA_DEVOLUCAO_TESOURO = 'CARGA_DEVOLUCAO_TESOURO' CARGA_NOMES = { CARGA_REPASSE_REALIZADO: 'Repasses realizados', @@ -15,6 +16,7 @@ CARGA_USUARIOS: 'Carga de usuários', CARGA_CENSO: 'Carga de censo', CARGA_REPASSE_PREVISTO_SME: 'Repasses previstos sme', + CARGA_DEVOLUCAO_TESOURO: 'Devoluções ao Tesouro', } CARGA_CHOICES = ( @@ -25,4 +27,5 @@ (CARGA_USUARIOS, CARGA_NOMES[CARGA_USUARIOS]), (CARGA_CENSO, CARGA_NOMES[CARGA_CENSO]), (CARGA_REPASSE_PREVISTO_SME, CARGA_NOMES[CARGA_REPASSE_PREVISTO_SME]), + (CARGA_DEVOLUCAO_TESOURO, CARGA_NOMES[CARGA_DEVOLUCAO_TESOURO]), ) diff --git a/sme_ptrf_apps/core/migrations/0271_auto_20220923_1525.py b/sme_ptrf_apps/core/migrations/0271_auto_20220923_1525.py new file mode 100644 index 000000000..a62564891 --- /dev/null +++ b/sme_ptrf_apps/core/migrations/0271_auto_20220923_1525.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.10 on 2022-09-23 15:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0270_remove_prestacaoconta_devolucao_tesouro'), + ] + + operations = [ + migrations.AlterField( + model_name='arquivo', + name='tipo_carga', + field=models.CharField(choices=[('REPASSE_REALIZADO', 'Repasses realizados'), ('CARGA_PERIODO_INICIAL', 'Carga período inicial'), ('REPASSE_PREVISTO', 'Repasses previstos'), ('CARGA_ASSOCIACOES', 'Carga de Associações'), ('CARGA_USUARIOS', 'Carga de usuários'), ('CARGA_CENSO', 'Carga de censo'), ('CARGA_REPASSE_PREVISTO_SME', 'Repasses previstos sme'), ('CARGA_DEVOLUCAO_TESOURO', 'Devoluções ao Tesouro')], default='REPASSE_REALIZADO', max_length=35, verbose_name='tipo de carga'), + ), + migrations.AlterField( + model_name='modelocarga', + name='tipo_carga', + field=models.CharField(choices=[('REPASSE_REALIZADO', 'Repasses realizados'), ('CARGA_PERIODO_INICIAL', 'Carga período inicial'), ('REPASSE_PREVISTO', 'Repasses previstos'), ('CARGA_ASSOCIACOES', 'Carga de Associações'), ('CARGA_USUARIOS', 'Carga de usuários'), ('CARGA_CENSO', 'Carga de censo'), ('CARGA_REPASSE_PREVISTO_SME', 'Repasses previstos sme'), ('CARGA_DEVOLUCAO_TESOURO', 'Devoluções ao Tesouro')], default='CARGA_ASSOCIACOES', max_length=35, unique=True, verbose_name='tipo de carga'), + ), + ] diff --git a/sme_ptrf_apps/core/services/carga_devolucoes_tesouro_service.py b/sme_ptrf_apps/core/services/carga_devolucoes_tesouro_service.py new file mode 100644 index 000000000..0ccab6df4 --- /dev/null +++ b/sme_ptrf_apps/core/services/carga_devolucoes_tesouro_service.py @@ -0,0 +1,247 @@ +import csv +import logging +from datetime import datetime + +from text_unidecode import unidecode + + +from sme_ptrf_apps.despesas.models import Despesa +from sme_ptrf_apps.core.models import ( + TipoDevolucaoAoTesouro, + PrestacaoConta, + SolicitacaoAcertoLancamento, + DevolucaoAoTesouro, +) + +from ..models.arquivo import ( + DELIMITADOR_PONTO_VIRGULA, + DELIMITADOR_VIRGULA, + SUCESSO, + ERRO, + PROCESSADO_COM_ERRO) + +logger = logging.getLogger(__name__) + + +class CargaDevolcuoesTesouroException(Exception): + pass + + +class CargaDevolucoesTesouroService: + EOL_UE = 0 + NOME_ASSOCIACAO = 1 + PC_ID = 2 + PERIODO_REF = 3 + DESPESA_ID = 4 + NUMERO_DOC = 5 + DATA_DEVOLUCAO = 6 + VALOR_DEVOLUCAO = 7 + TIPO_DEVOLUCAO_NOME = 8 + + CABECALHOS = { + EOL_UE: "codigo_eol", + NOME_ASSOCIACAO: "nome", + PC_ID: "pc_id", + PERIODO_REF: "referencia", + DESPESA_ID: "despesa_id", + NUMERO_DOC: "numero_documento", + DATA_DEVOLUCAO: "data_devolucao", + VALOR_DEVOLUCAO: "valor_devolucao", + TIPO_DEVOLUCAO_NOME: "tipo_devolucao", + } + + DELIMITADORES = {',': DELIMITADOR_VIRGULA, ';': DELIMITADOR_PONTO_VIRGULA} + + logs = "" + importados = 0 + erros = 0 + + linha_index = 0 + + def __init__(self): + self.dados_devolucao = {} + + def inicializa_log(self): + self.logs = "" + self.importados = 0 + self.erros = 0 + + def loga_erro_carga_devolucao(self, mensagem_erro, linha=0): + mensagem = f'Linha:{linha} {mensagem_erro}' + logger.error(mensagem) + self.logs = f"{self.logs}\n{mensagem}" + self.erros += 1 + + def loga_sucesso_carga_devolucao(self): + mensagem = f'Devolução ao tesouro Eol:{self.dados_devolucao["prestacao_conta"].associacao.unidade.codigo_eol} PC:{self.dados_devolucao["prestacao_conta"].periodo.referencia} criado/atualizado com sucesso.' + logger.info(mensagem) + self.importados += 1 + + def carrega_e_valida_dados_devolucao(self, linha_conteudo, linha_index): + logger.info('Linha %s: %s', linha_index, linha_conteudo) + + # Define a Prestação de Contas + try: + pc_id = int(linha_conteudo[self.PC_ID].strip()) + except ValueError: + raise CargaDevolcuoesTesouroException(f'O id da PC {linha_conteudo[self.PC_ID].strip()} é inválido. Devolução não criada.') + + try: + prestacao_conta = PrestacaoConta.objects.get(id=pc_id) + except PrestacaoConta.DoesNotExist: + raise CargaDevolcuoesTesouroException(f'Não encontrada uma PC de id {pc_id}. Devolução não criada.') + + # Define a Despesa + try: + despesa_id = int(linha_conteudo[self.DESPESA_ID].strip()) + except ValueError: + raise CargaDevolcuoesTesouroException(f'O id de despesa {linha_conteudo[self.DESPESA_ID].strip()} é inválido. Devolução não criada.') + + try: + despesa = Despesa.objects.get(id=despesa_id) + except Despesa.DoesNotExist: + raise CargaDevolcuoesTesouroException(f'Não encontrada uma despesa de id {despesa_id}. Devolução não criada.') + + # Define a Data e Valor da Devolução ao tesouro + try: + data_devolucao = datetime.strptime(str(linha_conteudo[self.DATA_DEVOLUCAO]).strip(" "), '%d/%m/%Y') + except Exception: + raise CargaDevolcuoesTesouroException(f'A data de devolução {linha_conteudo[self.DATA_DEVOLUCAO].strip()} é inválida. Devolução não criada.') + + try: + valor_devolucao = float(str(linha_conteudo[self.VALOR_DEVOLUCAO].strip()).replace(',', '.')) + except Exception: + raise CargaDevolcuoesTesouroException(f'O valor da devolução {linha_conteudo[self.VALOR_DEVOLUCAO].strip()} é inválido. Devolução não criada.') + + # Define o tipo de devolução + tipo_devolucao_nome = linha_conteudo[self.TIPO_DEVOLUCAO_NOME].strip() + tipo_devolucao = TipoDevolucaoAoTesouro.objects.filter(nome=tipo_devolucao_nome).first() + if not tipo_devolucao: + raise CargaDevolcuoesTesouroException( + f'Não encontrado tipo de devolução com o nome {tipo_devolucao_nome}. Devolução não criada.') + + # Verifica se já existe uma devolução ao tesouro para a PC e Despesa + devolucao_existente = DevolucaoAoTesouro.objects.filter( + prestacao_conta=prestacao_conta, + despesa=despesa, + ).first() + + if devolucao_existente: + raise CargaDevolcuoesTesouroException( + f'Devolução ao Tesouro já existe id {devolucao_existente.id}. Devolução não criada.') + + # Define as solicitações de devolução + solicitacoes = SolicitacaoAcertoLancamento.objects.filter( + analise_lancamento__analise_prestacao_conta__prestacao_conta=prestacao_conta, + analise_lancamento__despesa=despesa, + tipo_acerto__categoria="DEVOLUCAO" + ).order_by('id').all() + + if not solicitacoes: + raise CargaDevolcuoesTesouroException( + f'Solicitação de acerto não encontrada pc: {self.dados_devolucao["prestacao_conta"].id} despesa: {self.dados_devolucao["despesa"]}. Devolução não criada.') + + self.linha_index = linha_index + + self.dados_devolucao = { + 'prestacao_conta': prestacao_conta, + 'despesa': despesa, + 'data_devolucao': data_devolucao, + 'valor_devolucao': valor_devolucao, + 'tipo_devolucao': tipo_devolucao, + 'solicitacoes_acerto_lancamento': solicitacoes, + } + + return self.dados_devolucao + + def cria_devolucao_ao_tesouro_e_atualiza_solicitacao(self): + devolucao_ao_tesouro = DevolucaoAoTesouro.objects.create( + prestacao_conta=self.dados_devolucao['prestacao_conta'], + tipo=self.dados_devolucao['tipo_devolucao'], + data=self.dados_devolucao['data_devolucao'], + despesa=self.dados_devolucao['despesa'], + valor=self.dados_devolucao['valor_devolucao'], + motivo="*recuperado", + visao_criacao="DRE", + devolucao_total=self.dados_devolucao['valor_devolucao'] == self.dados_devolucao['despesa'].valor_total + ) + + for solicitacao in self.dados_devolucao['solicitacoes_acerto_lancamento']: + solicitacao.devolucao_ao_tesouro = devolucao_ao_tesouro + solicitacao.save() + + def atualiza_status_arquivo(self, arquivo): + if self.importados > 0 and self.erros > 0: + arquivo.status = PROCESSADO_COM_ERRO + elif self.importados == 0: + arquivo.status = ERRO + else: + arquivo.status = SUCESSO + + resultado = f"{self.importados} linha(s) importada(s) com sucesso. {self.erros} erro(s) reportado(s)." + self.logs = f"{self.logs}\n{resultado}" + logger.info(resultado) + + arquivo.log = self.logs + arquivo.save() + + @classmethod + def verifica_estrutura_cabecalho(cls, cabecalho): + estrutura_correta = True + for coluna, nome in cls.CABECALHOS.items(): + titulo_coluna_arquivo = unidecode(cabecalho[coluna]) + titulo_coluna_modelo = unidecode(nome) + if titulo_coluna_arquivo != titulo_coluna_modelo: + msg_erro = (f'Título da coluna {coluna} errado. Encontrado "{cabecalho[coluna]}". ' + f'Deveria ser "{nome}". Confira o arquivo com o modelo.') + raise CargaDevolcuoesTesouroException(msg_erro) + + return estrutura_correta + + def processa_devolucoes(self, reader, arquivo): + self.inicializa_log() + + try: + for index, linha in enumerate(reader): + if index == 0: + self.verifica_estrutura_cabecalho(cabecalho=linha) + continue + + try: + self.carrega_e_valida_dados_devolucao(linha_conteudo=linha, linha_index=index) + + self.cria_devolucao_ao_tesouro_e_atualiza_solicitacao() + + self.loga_sucesso_carga_devolucao() + + except Exception as e: + self.loga_erro_carga_devolucao(f'Houve um erro na carga dessa linha:{str(e)}', index) + continue + + self.atualiza_status_arquivo(arquivo) + + except Exception as e: + self.loga_erro_carga_devolucao(str(e)) + self.atualiza_status_arquivo(arquivo) + + def carrega_devolucoes_tesouro(self, arquivo): + logger.info("Processando arquivo %s", arquivo.identificador) + arquivo.ultima_execucao = datetime.now() + + try: + with open(arquivo.conteudo.path, 'r', encoding="utf-8") as f: + sniffer = csv.Sniffer().sniff(f.readline()) + f.seek(0) + if self.DELIMITADORES[sniffer.delimiter] != arquivo.tipo_delimitador: + msg_erro = (f"Formato definido ({arquivo.tipo_delimitador}) é diferente do formato " + f"do arquivo csv ({self.DELIMITADORES[sniffer.delimiter]})") + self.loga_erro_carga_devolucao(msg_erro) + self.atualiza_status_arquivo(arquivo) + return + + reader = csv.reader(f, delimiter=sniffer.delimiter) + self.processa_devolucoes(reader, arquivo) + + except Exception as err: + self.loga_erro_carga_devolucao(f"Erro ao processar devoluções ao tesouro: {str(err)}") + self.atualiza_status_arquivo(arquivo) diff --git a/sme_ptrf_apps/core/services/processa_cargas.py b/sme_ptrf_apps/core/services/processa_cargas.py index d90e1a299..decd5a588 100644 --- a/sme_ptrf_apps/core/services/processa_cargas.py +++ b/sme_ptrf_apps/core/services/processa_cargas.py @@ -1,11 +1,13 @@ from sme_ptrf_apps.core.choices.tipos_carga import (CARGA_PERIODO_INICIAL, CARGA_REPASSE_REALIZADO, CARGA_REPASSE_PREVISTO, CARGA_ASSOCIACOES, CARGA_USUARIOS, CARGA_CENSO, - CARGA_REPASSE_PREVISTO_SME) + CARGA_REPASSE_PREVISTO_SME, CARGA_DEVOLUCAO_TESOURO + ) from sme_ptrf_apps.core.services.periodo_inicial import carrega_periodo_inicial from sme_ptrf_apps.core.services.carga_censo import carrega_censo from sme_ptrf_apps.core.services.carga_previsao_repasse import carrega_previsoes_repasses from sme_ptrf_apps.core.services.carga_associacoes_service import CargaAssociacoesService +from sme_ptrf_apps.core.services.carga_devolucoes_tesouro_service import CargaDevolucoesTesouroService from sme_ptrf_apps.receitas.services.carga_repasses_previstos import carrega_repasses_previstos from sme_ptrf_apps.receitas.services.carga_repasses_realizados import carrega_repasses_realizados from sme_ptrf_apps.users.services.carga_usuario_service import CargaUsuariosService @@ -33,3 +35,5 @@ def processa_carga(arquivo): carrega_censo(arquivo) elif arquivo.tipo_carga == CARGA_REPASSE_PREVISTO_SME: carrega_previsoes_repasses(arquivo) + elif arquivo.tipo_carga == CARGA_DEVOLUCAO_TESOURO: + CargaDevolucoesTesouroService().carrega_devolucoes_tesouro(arquivo) From ed4ba30227ecb630bd667dd1a38d01dd3778901c Mon Sep 17 00:00:00 2001 From: Alessandro Fernandes Date: Fri, 23 Sep 2022 17:18:48 -0300 Subject: [PATCH 3/9] =?UTF-8?q?fix(74103):=20Altera=20sql=20de=20verifica?= =?UTF-8?q?=C3=A7=C3=A3o=20de=20devolu=C3=A7=C3=B5es=20ausentes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contrib/scripts_diversos/dev_tesouro.sql | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sme_ptrf_apps/contrib/scripts_diversos/dev_tesouro.sql b/sme_ptrf_apps/contrib/scripts_diversos/dev_tesouro.sql index 0978a52fb..ba48cdaba 100644 --- a/sme_ptrf_apps/contrib/scripts_diversos/dev_tesouro.sql +++ b/sme_ptrf_apps/contrib/scripts_diversos/dev_tesouro.sql @@ -3,15 +3,15 @@ -- Lista solicitações de devolução ao tesouro sem uma devolução ao tesouro vinculada select a.unidade_id as "codigo_eol", -a.nome, +u.nome, pc.id as "pc_id", p.referencia, -d.id as "despesa_id", d.numero_documento, -count(sal.id) +d.id as "despesa_id", d.numero_documento from core_prestacaoconta as pc, core_periodo as p, core_associacao as a, +core_unidade as u, core_analiseprestacaoconta as apc, core_analiselancamentoprestacaoconta as al, despesas_despesa as d, @@ -19,6 +19,7 @@ core_solicitacaoacertolancamento as sal, core_tipoacertolancamento as tal where pc.periodo_id = p.id +and a.unidade_id = u.codigo_eol and pc.associacao_id = a.id and apc.prestacao_conta_id = pc.id and al.analise_prestacao_conta_id = apc.id @@ -31,13 +32,13 @@ group by pc.id, p.referencia, a.unidade_id, -a.nome, +u.nome, d.id, d.numero_documento, d.valor_total order by a.unidade_id, -a.nome, +u.nome, p.referencia, -d.id, d.numero_documento, d.valor_total +d.id, d.numero_documento -- Lista de Prestações de Conta com devoluções ao tesouro faltando From c3868e60174626f1da3e5c2d60474a789d48e963 Mon Sep 17 00:00:00 2001 From: Alessandro Fernandes Date: Mon, 26 Sep 2022 08:08:24 -0300 Subject: [PATCH 4/9] fix(74103): Melhora admin Dev.Tesouro, Ata e Solic.Ajuste.Lacto --- sme_ptrf_apps/core/admin.py | 64 ++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/sme_ptrf_apps/core/admin.py b/sme_ptrf_apps/core/admin.py index 1601e42cf..08e909a55 100644 --- a/sme_ptrf_apps/core/admin.py +++ b/sme_ptrf_apps/core/admin.py @@ -285,9 +285,9 @@ def remover_duplicacao_fechamentos(self, request, queryset): class AtaAdmin(admin.ModelAdmin): def get_eol_unidade(self, obj): - return obj.associacao.unidade.codigo_eol if obj and obj.associacao and obj.associacao.unidade else '' + return f'{obj.associacao.unidade.codigo_eol} - {obj.associacao.unidade.nome}' if obj and obj.associacao and obj.associacao.unidade else '' - get_eol_unidade.short_description = 'EOL' + get_eol_unidade.short_description = 'Unidade' def get_referencia_periodo(self, obj): return obj.periodo.referencia if obj and obj.periodo else '' @@ -295,14 +295,23 @@ def get_referencia_periodo(self, obj): get_referencia_periodo.short_description = 'Período' list_display = ( - 'get_eol_unidade', 'get_referencia_periodo', 'data_reuniao', 'tipo_ata', 'tipo_reuniao', - 'convocacao', - 'parecer_conselho', 'previa') + 'get_eol_unidade', + 'get_referencia_periodo', + 'tipo_ata', + 'parecer_conselho', + 'previa', + 'arquivo_pdf', + ) + list_filter = ( - 'parecer_conselho', 'tipo_ata', 'tipo_reuniao', 'convocacao', 'associacao', 'previa') + 'periodo', + 'tipo_ata', + 'previa', + 'parecer_conselho', + ) list_display_links = ('get_eol_unidade',) readonly_fields = ('uuid', 'id', 'criado_em', 'alterado_em') - search_fields = ('associacao__unidade__codigo_eol',) + search_fields = ('associacao__unidade__codigo_eol', 'associacao__unidade__nome') @admin.register(Arquivo) @@ -459,10 +468,10 @@ class TipoDevolucaoTesouroAdmin(admin.ModelAdmin): @admin.register(DevolucaoAoTesouro) class DevolucaoAoTesouroAdmin(admin.ModelAdmin): - def get_associacao(self, obj): - return obj.prestacao_conta.associacao.nome if obj and obj.prestacao_conta and obj.prestacao_conta.associacao else '' + def get_unidade(self, obj): + return f'{obj.prestacao_conta.associacao.unidade.codigo_eol} - {obj.prestacao_conta.associacao.unidade.nome}' if obj and obj.prestacao_conta and obj.prestacao_conta.associacao and obj.prestacao_conta.associacao.unidade else '' - get_associacao.short_description = 'Associação' + get_unidade.short_description = 'Unidade' def get_referencia_periodo(self, obj): return obj.prestacao_conta.periodo.referencia if obj and obj.prestacao_conta and obj.prestacao_conta.periodo else '' @@ -470,13 +479,13 @@ def get_referencia_periodo(self, obj): get_referencia_periodo.short_description = 'Período' list_display = ( - 'get_associacao', 'get_referencia_periodo', 'data', 'tipo', 'devolucao_total', 'valor', 'visao_criacao') + 'get_unidade', 'get_referencia_periodo', 'despesa', 'data', 'tipo', 'devolucao_total', 'valor', 'visao_criacao') list_filter = ( - 'prestacao_conta__periodo', 'prestacao_conta__associacao', 'prestacao_conta', 'tipo', 'devolucao_total', + 'prestacao_conta__periodo', 'prestacao_conta', 'tipo', 'devolucao_total', 'visao_criacao') - list_display_links = ('get_associacao',) + list_display_links = ('get_unidade',) readonly_fields = ('uuid', 'id') search_fields = ('prestacao_conta__associacao__unidade__codigo_eol', 'prestacao_conta__associacao__unidade__nome', 'prestacao_conta__associacao__nome', 'motivo') @@ -733,24 +742,27 @@ class TipoAcertoLancamentoAdmin(admin.ModelAdmin): @admin.register(SolicitacaoAcertoLancamento) class SolicitacaoAcertoLancamentoAdmin(admin.ModelAdmin): - def get_associacao(self, obj): - return obj.analise_lancamento.analise_prestacao_conta.prestacao_conta.associacao.nome if obj and obj.analise_lancamento and obj.analise_lancamento.analise_prestacao_conta and obj.analise_lancamento.analise_prestacao_conta.prestacao_conta.associacao else '' + def get_unidade(self, obj): + return f'{obj.analise_lancamento.analise_prestacao_conta.prestacao_conta.associacao.unidade.codigo_eol} - {obj.analise_lancamento.analise_prestacao_conta.prestacao_conta.associacao.unidade.nome}' if obj and obj.analise_lancamento and obj.analise_lancamento.analise_prestacao_conta and obj.analise_lancamento.analise_prestacao_conta.prestacao_conta.associacao and obj.analise_lancamento.analise_prestacao_conta.prestacao_conta.associacao.unidade else '' - get_associacao.short_description = 'Associação' - - def get_referencia(self, obj): - return obj.analise_lancamento.analise_prestacao_conta.prestacao_conta.periodo.referencia if obj and obj.analise_lancamento and obj.analise_lancamento.analise_prestacao_conta and obj.analise_lancamento.analise_prestacao_conta.prestacao_conta.periodo else '' - - get_referencia.short_description = 'Período' + get_unidade.short_description = 'Unidade' def get_despesa(self, obj): return obj.analise_lancamento.despesa if obj and obj.analise_lancamento else '' - get_referencia.short_description = 'Despesa' - - list_display = ['get_associacao', 'get_referencia', 'analise_lancamento', 'tipo_acerto', 'devolucao_ao_tesouro', 'get_despesa'] - search_fields = ['detalhamento', 'analise_lancamento__analise_prestacao_conta__prestacao_conta__associacao__unidade__codigo_eol' ] - list_filter = ['tipo_acerto', 'analise_lancamento__analise_prestacao_conta__prestacao_conta__periodo__referencia' ] + get_despesa.short_description = 'Despesa' + + list_display = ['get_unidade', 'analise_lancamento', 'tipo_acerto', 'devolucao_ao_tesouro', 'get_despesa'] + search_fields = [ + 'analise_lancamento__analise_prestacao_conta__prestacao_conta__associacao__unidade__codigo_eol', + 'analise_lancamento__analise_prestacao_conta__prestacao_conta__associacao__unidade__nome', + 'detalhamento', + ] + list_filter = [ + 'analise_lancamento__analise_prestacao_conta__prestacao_conta__periodo__referencia', + 'tipo_acerto', + 'devolucao_ao_tesouro', + ] readonly_fields = ('uuid', 'id',) From 6e5b357cd1bcd8b5a90530359fb6f3ec0a643243 Mon Sep 17 00:00:00 2001 From: Alessandro Fernandes Date: Tue, 27 Sep 2022 07:52:48 -0300 Subject: [PATCH 5/9] fix(74103): Altera carga dev-tesouro para datas vazias --- .../contrib/scripts_diversos/dev_tesouro.sql | 15 ++++++++------- .../services/carga_devolucoes_tesouro_service.py | 5 ++++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/sme_ptrf_apps/contrib/scripts_diversos/dev_tesouro.sql b/sme_ptrf_apps/contrib/scripts_diversos/dev_tesouro.sql index ba48cdaba..222547dd2 100644 --- a/sme_ptrf_apps/contrib/scripts_diversos/dev_tesouro.sql +++ b/sme_ptrf_apps/contrib/scripts_diversos/dev_tesouro.sql @@ -6,7 +6,7 @@ a.unidade_id as "codigo_eol", u.nome, pc.id as "pc_id", p.referencia, -d.id as "despesa_id", d.numero_documento +d.id as "despesa_id", d.numero_documento, d.valor_total from core_prestacaoconta as pc, core_periodo as p, @@ -27,7 +27,7 @@ and al.despesa_id = d.id and sal.analise_lancamento_id = al.id and tal.id = sal.tipo_acerto_id and tal.categoria = 'DEVOLUCAO' -and sal.devolucao_ao_tesouro_id is null +and (select count(dt.id) from core_devolucaoaotesouro as dt where dt.prestacao_conta_id = pc.id and dt.despesa_id = d.id) = 0 group by pc.id, p.referencia, @@ -40,17 +40,17 @@ u.nome, p.referencia, d.id, d.numero_documento - -- Lista de Prestações de Conta com devoluções ao tesouro faltando select a.unidade_id as "codigo_eol", -a.nome, +u.nome, pc.id as "pc_id", p.referencia from core_prestacaoconta as pc, core_periodo as p, core_associacao as a, +core_unidade as u, core_analiseprestacaoconta as apc, core_analiselancamentoprestacaoconta as al, despesas_despesa as d, @@ -58,6 +58,7 @@ core_solicitacaoacertolancamento as sal, core_tipoacertolancamento as tal where pc.periodo_id = p.id +and a.unidade_id = u.codigo_eol and pc.associacao_id = a.id and apc.prestacao_conta_id = pc.id and al.analise_prestacao_conta_id = apc.id @@ -65,13 +66,13 @@ and al.despesa_id = d.id and sal.analise_lancamento_id = al.id and tal.id = sal.tipo_acerto_id and tal.categoria = 'DEVOLUCAO' -and sal.devolucao_ao_tesouro_id is null +and (select count(dt.id) from core_devolucaoaotesouro as dt where dt.prestacao_conta_id = pc.id and dt.despesa_id = d.id) = 0 group by pc.id, p.referencia, a.unidade_id, -a.nome +u.nome order by a.unidade_id, -a.nome, +u.nome, p.referencia diff --git a/sme_ptrf_apps/core/services/carga_devolucoes_tesouro_service.py b/sme_ptrf_apps/core/services/carga_devolucoes_tesouro_service.py index 0ccab6df4..15ea6c818 100644 --- a/sme_ptrf_apps/core/services/carga_devolucoes_tesouro_service.py +++ b/sme_ptrf_apps/core/services/carga_devolucoes_tesouro_service.py @@ -104,7 +104,10 @@ def carrega_e_valida_dados_devolucao(self, linha_conteudo, linha_index): # Define a Data e Valor da Devolução ao tesouro try: - data_devolucao = datetime.strptime(str(linha_conteudo[self.DATA_DEVOLUCAO]).strip(" "), '%d/%m/%Y') + if str(linha_conteudo[self.DATA_DEVOLUCAO]).strip(" "): + data_devolucao = datetime.strptime(str(linha_conteudo[self.DATA_DEVOLUCAO]).strip(" "), '%d/%m/%Y') + else: + data_devolucao = None except Exception: raise CargaDevolcuoesTesouroException(f'A data de devolução {linha_conteudo[self.DATA_DEVOLUCAO].strip()} é inválida. Devolução não criada.') From 14a882e976c812bf594ac1472d5e2c5c70ce2f6f Mon Sep 17 00:00:00 2001 From: Alessandro Fernandes Date: Wed, 28 Sep 2022 07:34:29 -0300 Subject: [PATCH 6/9] fix(74103): Altera carga dev-tesouro para datas vazias --- sme_ptrf_apps/core/services/carga_devolucoes_tesouro_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sme_ptrf_apps/core/services/carga_devolucoes_tesouro_service.py b/sme_ptrf_apps/core/services/carga_devolucoes_tesouro_service.py index 15ea6c818..bb9111bed 100644 --- a/sme_ptrf_apps/core/services/carga_devolucoes_tesouro_service.py +++ b/sme_ptrf_apps/core/services/carga_devolucoes_tesouro_service.py @@ -142,7 +142,7 @@ def carrega_e_valida_dados_devolucao(self, linha_conteudo, linha_index): if not solicitacoes: raise CargaDevolcuoesTesouroException( - f'Solicitação de acerto não encontrada pc: {self.dados_devolucao["prestacao_conta"].id} despesa: {self.dados_devolucao["despesa"]}. Devolução não criada.') + f'Solicitação de acerto não encontrada pc: {prestacao_conta.id} despesa: {despesa}. Devolução não criada.') self.linha_index = linha_index From c6fe6d4b2cf3d42d06eeb6ce88e20232dc61d94d Mon Sep 17 00:00:00 2001 From: Alessandro Fernandes Date: Wed, 28 Sep 2022 13:53:54 -0300 Subject: [PATCH 7/9] =?UTF-8?q?fix(74103):=20Cria=20a=C3=A7=C3=A3o=20para?= =?UTF-8?q?=20vincular=20solicita=C3=A7=C3=B5es=20=C3=B3rf=C3=A3s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Permite selecionar solicitações de devolução sem uma devolução ao tesouro vinculada e vincula-las a devoluções ao tesouro existentes. --- sme_ptrf_apps/core/admin.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sme_ptrf_apps/core/admin.py b/sme_ptrf_apps/core/admin.py index 08e909a55..c4acffc9e 100644 --- a/sme_ptrf_apps/core/admin.py +++ b/sme_ptrf_apps/core/admin.py @@ -1,3 +1,5 @@ +import logging + from django.contrib import admin from rangefilter.filter import DateRangeFilter from sme_ptrf_apps.core.services.processa_cargas import processa_cargas @@ -765,6 +767,26 @@ def get_despesa(self, obj): ] readonly_fields = ('uuid', 'id',) + actions = ['buscar_e_vincular_devolucao_ao_tesouro'] + + def buscar_e_vincular_devolucao_ao_tesouro(self, request, queryset): + for solicitacao in queryset.all(): + if solicitacao.tipo_acerto.categoria != 'DEVOLUCAO' or solicitacao.devolucao_ao_tesouro: + continue + + prestacao_conta = solicitacao.analise_lancamento.analise_prestacao_conta.prestacao_conta + despesa = solicitacao.analise_lancamento.despesa + devolucao = DevolucaoAoTesouro.objects.filter(prestacao_conta=prestacao_conta, despesa=despesa).first() + + if devolucao: + solicitacao.devolucao_ao_tesouro = devolucao + solicitacao.detalhamento = solicitacao.detalhamento + "(**vinculada a dvt)" + solicitacao.save() + logging.info( + f'Vinculado solicitação {solicitacao.id}-{solicitacao} à devolução {devolucao.id}-{devolucao}') + + self.message_user(request, f"Processo realizado com sucesso!") + @admin.register(TipoDocumentoPrestacaoConta) class TipoDocumentoPrestacaoContaAdmin(admin.ModelAdmin): From c5abaac41a2f4854dc6772cd79da48a622342551 Mon Sep 17 00:00:00 2001 From: Alessandro Fernandes Date: Thu, 29 Sep 2022 06:47:29 -0300 Subject: [PATCH 8/9] fix(74103): Inclui filtro por data no admin de dev.tesouro --- sme_ptrf_apps/core/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sme_ptrf_apps/core/admin.py b/sme_ptrf_apps/core/admin.py index c4acffc9e..ab6a04fd1 100644 --- a/sme_ptrf_apps/core/admin.py +++ b/sme_ptrf_apps/core/admin.py @@ -485,7 +485,7 @@ def get_referencia_periodo(self, obj): list_filter = ( 'prestacao_conta__periodo', 'prestacao_conta', 'tipo', 'devolucao_total', - 'visao_criacao') + 'visao_criacao', 'data') list_display_links = ('get_unidade',) readonly_fields = ('uuid', 'id') From bb80d16aea6de9a4aa7164f04b1e2b05c1f51837 Mon Sep 17 00:00:00 2001 From: Alessandro Fernandes Date: Thu, 29 Sep 2022 06:52:22 -0300 Subject: [PATCH 9/9] fix(1.33.5): Hotfix --- hotfixes.md | 5 ++++- sme_ptrf_apps/__init__.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/hotfixes.md b/hotfixes.md index 82723102d..d108e2f35 100644 --- a/hotfixes.md +++ b/hotfixes.md @@ -1,6 +1,9 @@ +### 1.33.5 - 29/09/2022 - Hotfix - Soluções de bugs urgentes durante a sprint 50 +* (74103) Carga de devoluções ao tesouro e ajustes no admin + ### 1.33.4 - 20/09/2022 - Hotfix - Soluções de bugs urgentes durante a sprint 49 * (74340) Corrige seletor de períodos na conclusão de PC -* + ### 1.33.3 - 15/09/2022 - Hotfix - Soluções de bugs urgentes durante a sprint 49 * (71660) Altera conclusão de PC para tratar erros de e-mail de notificação sem abortar conclusão da PC * (72907) Melhorias diversas no relatório consolidado DRE diff --git a/sme_ptrf_apps/__init__.py b/sme_ptrf_apps/__init__.py index 69b46d09a..aa4f3f0d5 100644 --- a/sme_ptrf_apps/__init__.py +++ b/sme_ptrf_apps/__init__.py @@ -1,4 +1,4 @@ -__version__ = "1.33.4" +__version__ = "1.33.5" __version_info__ = tuple(