diff --git a/config/api_router.py b/config/api_router.py index 3f94a7462..75ccbe7b5 100644 --- a/config/api_router.py +++ b/config/api_router.py @@ -11,6 +11,7 @@ from sme_ptrf_apps import __version__ from sme_ptrf_apps.core.api.views import ( AssociacoesViewSet, + ParametrizacoesAssociacoesViewSet, AtasViewSet, DemonstrativoFinanceiroViewSet, MembroAssociacaoViewSet, @@ -26,6 +27,7 @@ TiposContaViewSet, ComentariosAnalisesPrestacoesViewSet, AcaoAssociacaoViewSet, + ParametrizacoesAcoesAssociacaoViewSet, AcoesViewSet, ArquivoViewSet, TagsViewSet, @@ -127,6 +129,7 @@ def teste_flag_view(request): router.register("receitas", ReceitaViewSet) router.register("fornecedores", FornecedoresViewSet) router.register("associacoes", AssociacoesViewSet) +router.register("parametrizacoes-associacoes", ParametrizacoesAssociacoesViewSet) router.register("repasses", RepasseViewSet, basename='repasses-pendentes') router.register("periodos", PeriodosViewSet) router.register("prestacoes-contas", PrestacoesContasViewSet) @@ -153,6 +156,7 @@ def teste_flag_view(request): router.register("motivos-aprovacao-ressalva", MotivoAprovacaoRessalvaViewSet) router.register("motivos-reprovacao", MotivoReprovacaoViewSet) router.register("acoes-associacoes", AcaoAssociacaoViewSet) +router.register("parametrizacoes-acoes-associacoes", ParametrizacoesAcoesAssociacaoViewSet) router.register("acoes", AcoesViewSet) router.register("arquivos", ArquivoViewSet) router.register("tags", TagsViewSet) diff --git a/sme_ptrf_apps/__init__.py b/sme_ptrf_apps/__init__.py index 428ee92b3..8c019afc3 100644 --- a/sme_ptrf_apps/__init__.py +++ b/sme_ptrf_apps/__init__.py @@ -1,4 +1,4 @@ -__version__ = "9.2.1" +__version__ = "9.3.0" __version_info__ = tuple( [ diff --git a/sme_ptrf_apps/core/admin.py b/sme_ptrf_apps/core/admin.py index e565fed5a..e7e152eef 100644 --- a/sme_ptrf_apps/core/admin.py +++ b/sme_ptrf_apps/core/admin.py @@ -525,7 +525,7 @@ class ProcessoAssociacaoAdmin(admin.ModelAdmin): list_display = ('associacao', 'numero_processo', 'ano', 'periodos_str') search_fields = ('uuid', 'numero_processo', 'associacao__nome') list_filter = ('ano', 'associacao', 'associacao__unidade__tipo_unidade', 'associacao__unidade__dre') - readonly_fields = ('uuid', 'id') + readonly_fields = ('uuid', 'id', 'criado_em', 'alterado_em') filter_horizontal = ('periodos',) raw_id_fields = ('associacao',) diff --git a/sme_ptrf_apps/core/api/serializers/ata_serializer.py b/sme_ptrf_apps/core/api/serializers/ata_serializer.py index 26da26433..e0d690be3 100644 --- a/sme_ptrf_apps/core/api/serializers/ata_serializer.py +++ b/sme_ptrf_apps/core/api/serializers/ata_serializer.py @@ -134,6 +134,8 @@ def create(self, validated_data): presentes_lista.append(presentes_object) ata.presentes_na_ata.set(presentes_lista) + ata.arquivo_pdf = None + ata.arquivo_pdf_nao_gerado() ata.save() return ata @@ -183,6 +185,8 @@ def update(self, instance, validated_data): update_instance_from_dict(instance, validated_data) instance.presentes_na_ata.set(presentes_lista) + instance.arquivo_pdf = None + instance.arquivo_pdf_nao_gerado() instance.save() return instance diff --git a/sme_ptrf_apps/core/api/serializers/processo_associacao_serializer.py b/sme_ptrf_apps/core/api/serializers/processo_associacao_serializer.py index efd3aa690..ed3b3da94 100644 --- a/sme_ptrf_apps/core/api/serializers/processo_associacao_serializer.py +++ b/sme_ptrf_apps/core/api/serializers/processo_associacao_serializer.py @@ -47,6 +47,21 @@ def validate(self, data): associacao = data.get('associacao') periodos = data.get('periodos', []) + numero_processo = data.get('numero_processo') + + # Verifica se já existe na associação no mesmo ano o mesmo numero + if numero_processo and associacao and ano_processo: + processos_existentes = ProcessoAssociacao.objects.filter( + associacao=associacao, numero_processo=numero_processo, ano=ano_processo + ) + + if self.instance: + processos_existentes = processos_existentes.exclude(pk=self.instance.pk) + + if processos_existentes.exists(): + raise serializers.ValidationError({ + "numero_processo": f"Este número de processo SEI já existe para o ano informado." + }) # Verifica se os períodos estão sendo reutilizados na mesma associação for periodo in periodos: diff --git a/sme_ptrf_apps/core/api/views/__init__.py b/sme_ptrf_apps/core/api/views/__init__.py index b540e88f5..20ee1885b 100644 --- a/sme_ptrf_apps/core/api/views/__init__.py +++ b/sme_ptrf_apps/core/api/views/__init__.py @@ -1,4 +1,5 @@ from .associacoes_viewset import AssociacoesViewSet +from .parametrizacoes_associacoes import ParametrizacoesAssociacoesViewSet from .atas_viewset import AtasViewSet from .conciliacoes_viewset import ConciliacoesViewSet from .demonstrativo_financeiro_viewset import DemonstrativoFinanceiroViewSet @@ -32,3 +33,4 @@ from .solicitacao_encerramento_conta_associacao_viewset import SolicitacaoEncerramentoContaAssociacaoViewset from .motivo_rejeicao_encerramento_conta_associacao_viewset import MotivoRejeicaoEncerramentoContaAssociacaoViewset from .feature_flags_view import feature_flags +from .parametrizacoes_acoes_associacoes import ParametrizacoesAcoesAssociacaoViewSet diff --git a/sme_ptrf_apps/core/api/views/parametrizacoes_acoes_associacoes.py b/sme_ptrf_apps/core/api/views/parametrizacoes_acoes_associacoes.py new file mode 100644 index 000000000..db966430d --- /dev/null +++ b/sme_ptrf_apps/core/api/views/parametrizacoes_acoes_associacoes.py @@ -0,0 +1,48 @@ +from django.db.models import Q +from django_filters import rest_framework as filters + +from rest_framework import mixins +from rest_framework.permissions import IsAuthenticated +from rest_framework.viewsets import GenericViewSet + +from sme_ptrf_apps.core.api.serializers import AcaoAssociacaoRetrieveSerializer +from sme_ptrf_apps.core.api.utils.pagination import CustomPagination +from sme_ptrf_apps.core.choices.filtro_informacoes_associacao import FiltroInformacoesAssociacao +from sme_ptrf_apps.core.models import AcaoAssociacao +from sme_ptrf_apps.users.permissoes import PermissaoApiUe + + +class ParametrizacoesAcoesAssociacaoViewSet(mixins.ListModelMixin, GenericViewSet): + permission_classes = [IsAuthenticated & PermissaoApiUe] + lookup_field = 'uuid' + queryset = AcaoAssociacao.objects.all() + serializer_class = AcaoAssociacaoRetrieveSerializer + filter_backends = (filters.DjangoFilterBackend,) + filter_fields = ('acao__uuid', 'status', 'associacao__uuid') + pagination_class = CustomPagination + + def get_queryset(self): + qs = AcaoAssociacao.objects.all().order_by('associacao__nome', 'acao__nome') + + nome = self.request.query_params.get('nome') + filtro_informacoes = self.request.query_params.get('filtro_informacoes') + filtro_informacoes_list = filtro_informacoes.split(',') if filtro_informacoes else [] + + encerradas = FiltroInformacoesAssociacao.FILTRO_INFORMACOES_ENCERRADAS + nao_encerradas = FiltroInformacoesAssociacao.FILTRO_INFORMACOES_NAO_ENCERRADAS + + if nome is not None: + qs = qs.filter(Q(associacao__nome__unaccent__icontains=nome) | Q( + associacao__unidade__nome__unaccent__icontains=nome) | Q( + associacao__unidade__codigo_eol__icontains=nome)) + + if filtro_informacoes_list: + if encerradas in filtro_informacoes_list and nao_encerradas in filtro_informacoes_list: + qs = qs + elif nao_encerradas in filtro_informacoes_list: + qs = qs.filter(associacao__data_de_encerramento__isnull=True) + + elif encerradas in filtro_informacoes_list: + qs = qs.filter(associacao__data_de_encerramento__isnull=False) + + return qs.order_by('associacao__nome', 'acao__nome') diff --git a/sme_ptrf_apps/core/api/views/parametrizacoes_associacoes.py b/sme_ptrf_apps/core/api/views/parametrizacoes_associacoes.py new file mode 100644 index 000000000..ccd7a8488 --- /dev/null +++ b/sme_ptrf_apps/core/api/views/parametrizacoes_associacoes.py @@ -0,0 +1,58 @@ +from rest_framework.permissions import IsAuthenticated + +from sme_ptrf_apps.users.permissoes import ( + PermissaoApiUe, +) + +from sme_ptrf_apps.core.models import Associacao +from django_filters import rest_framework as filters +from django.db.models import Q +from rest_framework.filters import SearchFilter + +from ..serializers.associacao_serializer import ( + AssociacaoListSerializer +) + +from sme_ptrf_apps.core.api.utils.pagination import CustomPagination +from rest_framework import mixins +from rest_framework.viewsets import GenericViewSet + +from ...choices import FiltroInformacoesAssociacao + + +class ParametrizacoesAssociacoesViewSet(mixins.ListModelMixin, GenericViewSet): + permission_classes = [IsAuthenticated & PermissaoApiUe] + lookup_field = 'uuid' + queryset = Associacao.objects.all() + serializer_class = AssociacaoListSerializer + filter_backends = (filters.DjangoFilterBackend, SearchFilter,) + filter_fields = ('unidade__dre__uuid', 'unidade__tipo_unidade') + pagination_class = CustomPagination + + def get_queryset(self): + qs = Associacao.objects.all().order_by('unidade__tipo_unidade', 'unidade__nome') + + uuid_dre = self.request.query_params.get('unidade__dre__uuid') + if uuid_dre is not None and uuid_dre != "": + qs = qs.filter(unidade__dre__uuid=uuid_dre) + + nome = self.request.query_params.get('nome') + if nome is not None: + qs = qs.filter(Q(unidade__codigo_eol=nome) | Q(nome__unaccent__icontains=nome) | Q( + unidade__nome__unaccent__icontains=nome)) + + filtro_informacoes = self.request.query_params.get('filtro_informacoes') + filtro_informacoes_list = filtro_informacoes.split(',') if filtro_informacoes else [] + + encerradas = FiltroInformacoesAssociacao.FILTRO_INFORMACOES_ENCERRADAS + nao_encerradas = FiltroInformacoesAssociacao.FILTRO_INFORMACOES_NAO_ENCERRADAS + + if filtro_informacoes_list: + if encerradas in filtro_informacoes_list and nao_encerradas in filtro_informacoes_list: + qs = qs + elif nao_encerradas in filtro_informacoes_list: + qs = qs.filter(data_de_encerramento__isnull=True) + elif encerradas in filtro_informacoes_list: + qs = qs.filter(data_de_encerramento__isnull=False) + + return qs diff --git a/sme_ptrf_apps/core/fixtures/factories/unidade_factory.py b/sme_ptrf_apps/core/fixtures/factories/unidade_factory.py index 8b3c3a25c..b8884f05b 100644 --- a/sme_ptrf_apps/core/fixtures/factories/unidade_factory.py +++ b/sme_ptrf_apps/core/fixtures/factories/unidade_factory.py @@ -15,7 +15,7 @@ class Meta: nome = Sequence(lambda n: f"DIRETORIA REGIONAL DE EDUCACAO {fake.unique.name().upper()}") tipo_unidade = "DRE" codigo_eol = Sequence(lambda n: str(fake.unique.random_int(min=100000, max=999999))) - sigla = Sequence(lambda n: fake.unique.lexify(text="??", letters="ABCDEFGHIJK")) + sigla = Sequence(lambda n: fake.unique.lexify(text="??", letters="ABCDEFGHIJKLMNOPQRSTUVWXYZ")) dre_cnpj = Sequence(lambda n: fake.unique.cnpj()) dre_diretor_regional_rf = Sequence(lambda n: str(fake.unique.random_int(min=1000000, max=9999999))) dre_diretor_regional_nome = Sequence(lambda n:fake.unique.name().upper()) diff --git a/sme_ptrf_apps/core/migrations/0384_alter_funcsmepainelparametrizacoes_options.py b/sme_ptrf_apps/core/migrations/0384_alter_funcsmepainelparametrizacoes_options.py new file mode 100644 index 000000000..c35341148 --- /dev/null +++ b/sme_ptrf_apps/core/migrations/0384_alter_funcsmepainelparametrizacoes_options.py @@ -0,0 +1,31 @@ +# Generated by Django 4.2.7 on 2024-04-04 09:01 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0383_merge_20240322_1517"), + ] + + operations = [ + migrations.AlterModelOptions( + name="funcsmepainelparametrizacoes", + options={ + "default_permissions": (), + "managed": False, + "permissions": ( + ( + "access_painel_parametrizacoes", + "[SME] Pode acessar o Painel de parametrizações.", + ), + ( + "change_painel_parametrizacoes", + "[SME] Pode atualizar o Painel de parametrizações ", + ), + ), + "verbose_name": "[SME] Painel de Parametrizacoes", + "verbose_name_plural": "[SME] Painel de Parametrizacoes", + }, + ), + ] diff --git a/sme_ptrf_apps/core/migrations/0385_delete_funcsmefornecedores.py b/sme_ptrf_apps/core/migrations/0385_delete_funcsmefornecedores.py new file mode 100644 index 000000000..1c65c7252 --- /dev/null +++ b/sme_ptrf_apps/core/migrations/0385_delete_funcsmefornecedores.py @@ -0,0 +1,15 @@ +# Generated by Django 4.2.11 on 2024-04-10 12:20 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0384_alter_funcsmepainelparametrizacoes_options"), + ] + + operations = [ + migrations.DeleteModel( + name="FuncSmeFornecedores", + ), + ] diff --git a/sme_ptrf_apps/core/migrations/0386_ata_pdf_gerado_previamente.py b/sme_ptrf_apps/core/migrations/0386_ata_pdf_gerado_previamente.py new file mode 100644 index 000000000..df01daa74 --- /dev/null +++ b/sme_ptrf_apps/core/migrations/0386_ata_pdf_gerado_previamente.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.7 on 2024-04-15 11:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0385_delete_funcsmefornecedores"), + ] + + operations = [ + migrations.AddField( + model_name="ata", + name="pdf_gerado_previamente", + field=models.BooleanField( + blank=True, + default=False, + help_text="O PDF já foi gerado previamente, foi apagado e precisa ser regerado quando editado/apagado", + verbose_name="PDF gerado previamente", + ), + ), + ] diff --git a/sme_ptrf_apps/core/migrations/0387_alter_ata_pdf_gerado_previamente.py b/sme_ptrf_apps/core/migrations/0387_alter_ata_pdf_gerado_previamente.py new file mode 100644 index 000000000..ae290915b --- /dev/null +++ b/sme_ptrf_apps/core/migrations/0387_alter_ata_pdf_gerado_previamente.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.7 on 2024-04-15 11:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0386_ata_pdf_gerado_previamente"), + ] + + operations = [ + migrations.AlterField( + model_name="ata", + name="pdf_gerado_previamente", + field=models.BooleanField( + blank=True, + default=False, + help_text="O PDF já foi gerado, foi apagado e precisa ser regerado quando a ata é editada/apagada", + verbose_name="PDF gerado previamente", + ), + ), + ] diff --git a/sme_ptrf_apps/core/migrations/0388_alter_ata_pdf_gerado_previamente.py b/sme_ptrf_apps/core/migrations/0388_alter_ata_pdf_gerado_previamente.py new file mode 100644 index 000000000..de8c06344 --- /dev/null +++ b/sme_ptrf_apps/core/migrations/0388_alter_ata_pdf_gerado_previamente.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.7 on 2024-04-15 11:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0387_alter_ata_pdf_gerado_previamente"), + ] + + operations = [ + migrations.AlterField( + model_name="ata", + name="pdf_gerado_previamente", + field=models.BooleanField( + blank=True, + default=False, + help_text="O PDF já foi gerado e precisa ser regerado quando a ata é editada/apagada", + verbose_name="PDF gerado previamente", + ), + ), + ] diff --git a/sme_ptrf_apps/core/migrations/0389_migrar_campo_pdf_gerado_previamente.py b/sme_ptrf_apps/core/migrations/0389_migrar_campo_pdf_gerado_previamente.py new file mode 100644 index 000000000..cdff097ea --- /dev/null +++ b/sme_ptrf_apps/core/migrations/0389_migrar_campo_pdf_gerado_previamente.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.7 on 2024-04-15 12:16 + +from django.db import migrations + +def migrar_campo_pdf_gerado_previamente(apps, schema_editor): + model_ata = apps.get_model('core', 'Ata') + atas_com_pdf = model_ata.objects.exclude(arquivo_pdf='') + for ata in atas_com_pdf: + ata.pdf_gerado_previamente = True + ata.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0388_alter_ata_pdf_gerado_previamente"), + ] + + operations = [ + migrations.RunPython(migrar_campo_pdf_gerado_previamente, reverse_code=migrations.RunPython.noop) + ] \ No newline at end of file diff --git a/sme_ptrf_apps/core/models/ata.py b/sme_ptrf_apps/core/models/ata.py index 5c70d5419..a8664164c 100644 --- a/sme_ptrf_apps/core/models/ata.py +++ b/sme_ptrf_apps/core/models/ata.py @@ -179,6 +179,8 @@ class Ata(ModeloBase): previa = models.BooleanField("É prévia?", default=False) justificativa_repasses_pendentes = models.TextField('Justificativa repasses pendentes', blank=True, default='') + + pdf_gerado_previamente = models.BooleanField("PDF gerado previamente", blank=True, default=False, help_text="O PDF já foi gerado e precisa ser regerado quando a ata é editada/apagada") @property def nome(self): @@ -290,6 +292,7 @@ def arquivo_pdf_iniciar(self): self.save() def arquivo_pdf_concluir(self): + self.pdf_gerado_previamente = True self.status_geracao_pdf = self.STATUS_CONCLUIDO self.save() diff --git a/sme_ptrf_apps/core/models/permissoes_sme.py b/sme_ptrf_apps/core/models/permissoes_sme.py index 0fe775a33..ee755eb67 100644 --- a/sme_ptrf_apps/core/models/permissoes_sme.py +++ b/sme_ptrf_apps/core/models/permissoes_sme.py @@ -21,6 +21,7 @@ class Meta: permissions = ( ('access_painel_parametrizacoes', '[SME] Pode acessar o Painel de parametrizações.'), + ('change_painel_parametrizacoes', '[SME] Pode atualizar o Painel de parametrizações '), ) @@ -80,21 +81,6 @@ class Meta: ) -class FuncSmeFornecedores(models.Model): - - class Meta: - managed = False # No database table creation. - default_permissions = () # disable "add", "change", "delete" and "view" default permissions - - verbose_name = "[SME] Parametrização Cadastro de Fornecedor" - verbose_name_plural = "[SME] Parametrizações Cadastro de Fornecedores" - - permissions = ( - ('access_fornecedores', '[SME] Pode acessar parametrizações cadastro de fornecedores.'), - ('change_fornecedores', '[SME] Pode atualizar parametrizações cadastro de fornecedores.'), - ) - - class FuncSmeSuporteUnidades(models.Model): class Meta: diff --git a/sme_ptrf_apps/core/models/proccessos_associacao.py b/sme_ptrf_apps/core/models/proccessos_associacao.py index db70bacdd..ec9cb971b 100644 --- a/sme_ptrf_apps/core/models/proccessos_associacao.py +++ b/sme_ptrf_apps/core/models/proccessos_associacao.py @@ -65,5 +65,35 @@ def e_o_ultimo_processo_do_ano_com_pcs_vinculada(self): associacao=self.associacao, ano=self.ano) return (self == ultimo_processo) and self.prestacoes_vinculadas.exists() + @classmethod + def vincula_periodos_aos_processos(cls): + from sme_ptrf_apps.core.models import Periodo + from datetime import datetime + + processos = cls.objects.all().order_by('criado_em') + for processo in processos: + ano = processo.ano + associacao = processo.associacao + + processos_do_ano = ProcessoAssociacao.objects.filter(ano=ano, associacao=associacao).all().order_by('id') + + if processos_do_ano: + + data_inicial = datetime.strptime(f'{ano}-01-01', "%Y-%m-%d").date() + data_final = datetime.strptime(f'{ano}-12-31', "%Y-%m-%d").date() + + periodos_deste_ano = Periodo.objects.filter( + data_inicio_realizacao_despesas__gte=data_inicial, + data_inicio_realizacao_despesas__lte=data_final + ).order_by('referencia') + + if processos_do_ano.count() == 1: + processo.periodos.set(periodos_deste_ano) + processo.save() + else: + primeiro_processo_cadastrado = processos_do_ano.first() + primeiro_processo_cadastrado.periodos.set(periodos_deste_ano) + primeiro_processo_cadastrado.save() + auditlog.register(ProcessoAssociacao) diff --git a/sme_ptrf_apps/core/services/unidade_service.py b/sme_ptrf_apps/core/services/unidade_service.py index 4910d2423..9d052665c 100644 --- a/sme_ptrf_apps/core/services/unidade_service.py +++ b/sme_ptrf_apps/core/services/unidade_service.py @@ -107,18 +107,26 @@ def consulta_unidade(codigo_eol): response = SmeIntegracaoService.get_dados_unidade_eol(codigo_eol) resultado = response.json() if resultado: + if resultado["codigo"] is None: + raise TypeError("Código EOL não encontrado") + unidade_retorno = resultado result['codigo_eol'] = codigo_eol result['nome'] = unidade_retorno.get('nome') or '' - result['tipo_unidade'] = unidade_retorno.get('siglaTipoEscola').strip() or '' + result['tipo_unidade'] = unidade_retorno.get('siglaTipoEscola').strip() if unidade_retorno["siglaTipoEscola"] is not None else '' result['email'] = unidade_retorno.get('email') or '' result['telefone'] = unidade_retorno.get('telefone') or '' result['numero'] = unidade_retorno.get('numero') or '' result['tipo_logradouro'] = unidade_retorno.get('tipoLogradouro') or '' result['logradouro'] = unidade_retorno.get('logradouro') or '' result['bairro'] = unidade_retorno.get('bairro') or '' - result['cep'] = f"{unidade_retorno['cep']:0>8}" or '' + result['cep'] = f"{unidade_retorno['cep']:0>8}" if unidade_retorno['cep'] is not None else '' logger.info("Unidade %s: %s localizada.", codigo_eol, result['nome']) + except TypeError as e: + logger.info(f"Erro ao consultar código eol: {str(e)}") + result['erro'] = 'erro' + result['mensagem'] = f"Código EOL não encontrado" + logger.info(result['mensagem']) except Exception as err: logger.info("Erro ao consultar código eol") result['erro'] = 'erro' diff --git a/sme_ptrf_apps/core/tasks/__init__.py b/sme_ptrf_apps/core/tasks/__init__.py index cd8e41ba1..9b6f1d36d 100644 --- a/sme_ptrf_apps/core/tasks/__init__.py +++ b/sme_ptrf_apps/core/tasks/__init__.py @@ -21,4 +21,4 @@ from .terminar_processo_pc import terminar_processo_pc_async from .gerar_previa_relatorio_apos_acertos_v2 import gerar_previa_relatorio_apos_acertos_v2_async from .migra_campos_presidente_secretario_atas import migrar_campos_presidente_secretario_atas_async - +from .vincula_processo_sei_a_periodos import vincular_processos_async diff --git a/sme_ptrf_apps/core/tasks/vincula_processo_sei_a_periodos.py b/sme_ptrf_apps/core/tasks/vincula_processo_sei_a_periodos.py new file mode 100644 index 000000000..c1277917f --- /dev/null +++ b/sme_ptrf_apps/core/tasks/vincula_processo_sei_a_periodos.py @@ -0,0 +1,20 @@ +import logging + +from celery import shared_task +from sme_ptrf_apps.core.models import ProcessoAssociacao + +logger = logging.getLogger(__name__) + + +@shared_task( + retry_backoff=2, + retry_kwargs={'max_retries': 8}, + time_limet=600, + soft_time_limit=300 +) +def vincular_processos_async(): + logger.info('Iniciando o vinculo de periodos a processos async.') + + ProcessoAssociacao.vincula_periodos_aos_processos() + + logger.info('Finalizado o vinculo de periodos a processos async.') diff --git a/sme_ptrf_apps/core/tests/tests_api_processos_associacoes/test_processo_associacao_create.py b/sme_ptrf_apps/core/tests/tests_api_processos_associacoes/test_processo_associacao_create.py index a8e4b8213..4f6b53df6 100644 --- a/sme_ptrf_apps/core/tests/tests_api_processos_associacoes/test_processo_associacao_create.py +++ b/sme_ptrf_apps/core/tests/tests_api_processos_associacoes/test_processo_associacao_create.py @@ -6,6 +6,7 @@ from waffle.testutils import override_flag from sme_ptrf_apps.core.models import ProcessoAssociacao +from sme_ptrf_apps.core.models.periodo import Periodo pytestmark = pytest.mark.django_db @@ -126,3 +127,56 @@ def test_create_processo_associacao_servidor_com_periodo_ja_usado( result = json.loads(response.content) assert result == {'periodos': ['O período 2019.2 já está associado a outro ProcessoAssociacao ' 'para a associação Escola Teste.']} + +def test_create_processo_associacao_com_mesmo_numero_processo_para_mesmo_ano_na_mesma_associacao( + jwt_authenticated_client_a, + periodos_de_2019_ate_2023, + associacao_factory, + processo_associacao_factory +): + periodo1 = Periodo.objects.get(referencia=2019.1) + periodo2 = Periodo.objects.get(referencia=2019.2) + associacao = associacao_factory.create() + processo_associacao_factory.create(associacao=associacao, ano=2019, numero_processo="123456") + + payload = { + 'associacao': str(associacao.uuid), + 'numero_processo': "123456", + 'ano': '2019', + 'periodos': [str(periodo1.uuid)] + } + + with override_flag('periodos-processo-sei', active=True): + response = jwt_authenticated_client_a.post( + '/api/processos-associacao/', data=json.dumps(payload), content_type='application/json') + + assert response.status_code == status.HTTP_400_BAD_REQUEST + result = json.loads(response.content) + assert result == {'numero_processo': ['Este número de processo SEI já existe para o ano informado.']} + +def test_create_processo_associacao_com_mesmo_numero_processo_para_outro_ano_na_mesma_associacao( + jwt_authenticated_client_a, + periodos_de_2019_ate_2023, + associacao_factory, + processo_associacao_factory +): + periodo1 = Periodo.objects.get(referencia=2019.1) + periodo2 = Periodo.objects.get(referencia=2020.1) + associacao = associacao_factory.create() + processo1 = processo_associacao_factory.create(associacao=associacao, ano=2019, numero_processo="123456") + + payload = { + 'associacao': str(associacao.uuid), + 'numero_processo': "123456", + 'ano': '2020', + 'periodos': [str(periodo2.uuid)] + } + + with override_flag('periodos-processo-sei', active=True): + assert ProcessoAssociacao.objects.count() == 1 + + response = jwt_authenticated_client_a.post( + '/api/processos-associacao/', data=json.dumps(payload), content_type='application/json') + + assert response.status_code == status.HTTP_201_CREATED + assert ProcessoAssociacao.objects.count() == 2 \ No newline at end of file diff --git a/sme_ptrf_apps/core/tests/tests_api_processos_associacoes/test_processo_associacao_update.py b/sme_ptrf_apps/core/tests/tests_api_processos_associacoes/test_processo_associacao_update.py index cb8e61ca3..4e6ee02df 100644 --- a/sme_ptrf_apps/core/tests/tests_api_processos_associacoes/test_processo_associacao_update.py +++ b/sme_ptrf_apps/core/tests/tests_api_processos_associacoes/test_processo_associacao_update.py @@ -1,10 +1,12 @@ import json +import uuid import pytest from rest_framework import status from waffle.testutils import override_flag from sme_ptrf_apps.core.models import ProcessoAssociacao +from sme_ptrf_apps.core.models.periodo import Periodo pytestmark = pytest.mark.django_db @@ -62,4 +64,63 @@ def test_update_processo_associacao_sem_periodos_com_flag_ligada(jwt_authenticat assert response.status_code == status.HTTP_400_BAD_REQUEST result = json.loads(response.content) assert result == {'periodos': ["É necessário informar ao menos um período quando a feature 'periodos-processo-sei' está ativa."]} + +def test_update_processo_associacao_com_mesmo_numero_processo_para_mesmo_ano_na_mesma_associacao( + jwt_authenticated_client_a, + periodos_de_2019_ate_2023, + associacao_factory, + processo_associacao_factory +): + periodo1 = Periodo.objects.get(referencia=2019.1) + periodo2 = Periodo.objects.get(referencia=2019.2) + associacao = associacao_factory.create() + processo1 = processo_associacao_factory.create(associacao=associacao, ano='2019', numero_processo="123456") + processo2 = processo_associacao_factory.create(associacao=associacao, ano='2019', numero_processo="111111") + + payload = { + 'ano': '2019', + 'numero_processo': "123456", + 'periodos': [str(periodo2.uuid)], + 'associacao': str(associacao.uuid) + } + + with override_flag('periodos-processo-sei', active=True): + response = jwt_authenticated_client_a.put( + f'/api/processos-associacao/{processo2.uuid}/', + data=json.dumps(payload), + content_type='application/json') + + assert response.status_code == status.HTTP_400_BAD_REQUEST + result = json.loads(response.content) + assert result == {'numero_processo': ['Este número de processo SEI já existe para o ano informado.']} + +def test_update_processo_associacao_com_mesmo_numero_processo_para_outro_ano_na_mesma_associacao( + jwt_authenticated_client_a, + periodos_de_2019_ate_2023, + associacao_factory, + processo_associacao_factory +): + periodo1 = Periodo.objects.get(referencia=2019.1) + periodo2 = Periodo.objects.get(referencia=2020.1) + associacao = associacao_factory.create() + processo1 = processo_associacao_factory.create(associacao=associacao, ano='2019', numero_processo="123456") + processo2 = processo_associacao_factory.create(associacao=associacao, ano='2020', numero_processo="111111") + + payload = { + 'ano': '2020', + 'numero_processo': "123456", + 'periodos': [str(periodo2.uuid)], + 'associacao': str(associacao.uuid) + } + + with override_flag('periodos-processo-sei', active=True): + response = jwt_authenticated_client_a.put( + f'/api/processos-associacao/{processo2.uuid}/', + data=json.dumps(payload), + content_type='application/json') + + assert response.status_code == status.HTTP_200_OK + result = json.loads(response.content) + assert result['numero_processo'] == '123456' + assert result['uuid'] == str(processo2.uuid).replace('urn:', '').replace('uuid:', '') diff --git a/sme_ptrf_apps/receitas/admin.py b/sme_ptrf_apps/receitas/admin.py index 01faabf17..8e3d23a05 100644 --- a/sme_ptrf_apps/receitas/admin.py +++ b/sme_ptrf_apps/receitas/admin.py @@ -75,7 +75,7 @@ class ReceitaAdmin(admin.ModelAdmin): 'categoria_receita', 'status', ) - readonly_fields = ('uuid', 'id',) + readonly_fields = ('uuid', 'id', 'criado_em', 'alterado_em') actions = ['conciliar_receita', 'desconciliar_receita', ] def conciliar_receita(self, request, queryset): @@ -100,7 +100,7 @@ class RepasseAdmin(admin.ModelAdmin): search_fields = ('associacao__nome', 'associacao__unidade__codigo_eol', 'carga_origem_linha_id') list_display = ('associacao', 'periodo', 'valor_capital', 'valor_custeio', 'valor_livre', 'tipo_conta', 'acao', 'status') - list_filter = ('periodo', 'status', 'carga_origem') + list_filter = ('periodo', 'status', 'carga_origem', 'associacao__unidade__dre') raw_id_fields = ('associacao', 'periodo', 'conta_associacao', 'acao_associacao', 'carga_origem') readonly_fields = ('uuid', 'id', 'criado_em', 'alterado_em') @@ -126,5 +126,5 @@ class DetalheTipoReceitaAdmin(admin.ModelAdmin): @admin.register(MotivoEstorno) class MotivoEstornoAdmin(admin.ModelAdmin): list_display = ('motivo', 'uuid', ) - readonly_fields = ('uuid', 'id') + readonly_fields = ('uuid', 'id', 'criado_em', 'alterado_em') search_fields = ('motivo', ) diff --git a/sme_ptrf_apps/sme/api/views/exportacoes_dados.py b/sme_ptrf_apps/sme/api/views/exportacoes_dados.py index 5604a4996..57ac4079f 100644 --- a/sme_ptrf_apps/sme/api/views/exportacoes_dados.py +++ b/sme_ptrf_apps/sme/api/views/exportacoes_dados.py @@ -79,6 +79,7 @@ def creditos(self, request): data_inicio=request.query_params.get("data_inicio"), data_final=request.query_params.get("data_final"), username=request.user.username, + dre_uuid=request.query_params.get("dre_uuid"), ) return Response( @@ -172,6 +173,7 @@ def relacao_bens(self, request): data_inicio=request.query_params.get("data_inicio"), data_final=request.query_params.get("data_final"), username=request.user.username, + dre_uuid=request.query_params.get("dre_uuid"), ) return Response( @@ -192,6 +194,7 @@ def devolucao_ao_tesouro_prestacao_conta(self, request): data_inicio=request.query_params.get("data_inicio"), data_final=request.query_params.get("data_final"), username=request.user.username, + dre_uuid=request.query_params.get("dre_uuid"), ) return Response( @@ -324,6 +327,7 @@ def repasses(self, request): data_inicio=request.query_params.get("data_inicio"), data_final=request.query_params.get("data_final"), username=request.user.username, + dre_uuid=request.query_params.get("dre_uuid"), ) return Response( diff --git a/sme_ptrf_apps/sme/services/exporta_dados_creditos_service.py b/sme_ptrf_apps/sme/services/exporta_dados_creditos_service.py index 696f6bd42..bba2658c7 100644 --- a/sme_ptrf_apps/sme/services/exporta_dados_creditos_service.py +++ b/sme_ptrf_apps/sme/services/exporta_dados_creditos_service.py @@ -149,6 +149,46 @@ def exporta_credito_csv(self) -> BinaryIO: motivos = list(instance.motivos_estorno.all()) for _, campo in self.cabecalho: + # Removendo ponto e vírgula e substituindo por vírgula + if campo == "associacao__unidade__nome": + campo = get_recursive_attr(instance, campo) + linha.append(campo.replace(";", ",") if campo else "") + continue + + if campo == 'associacao__nome': + campo = get_recursive_attr(instance, campo) + linha.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "associacao__unidade__dre__nome": + campo = get_recursive_attr(instance, campo) + linha.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "conta_associacao__tipo_conta__nome": + campo = get_recursive_attr(instance, campo) + linha.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "acao_associacao__acao__nome": + campo = get_recursive_attr(instance, campo) + linha.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "tipo_receita__nome": + campo = get_recursive_attr(instance, campo) + linha.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "detalhe_tipo_receita__nome": + campo = get_recursive_attr(instance, campo) + linha.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "detalhe_outros": + campo = get_recursive_attr(instance, campo) + linha.append(campo.replace(";", ",") if campo else "") + continue if campo == 'data': campo = getattr(instance, campo) @@ -164,7 +204,7 @@ def exporta_credito_csv(self) -> BinaryIO: motivo_string = motivo_string + '; ' + instance.outros_motivos_estorno elif(len(instance.outros_motivos_estorno)): motivo_string = instance.outros_motivos_estorno - linha.append(motivo_string) + linha.append(motivo_string.replace(";", ",")) elif isinstance(campo, tuple) and campo[1] == 'categoria_receita': linha.append(campo[0][getattr(instance, campo[1])]) @@ -173,7 +213,9 @@ def exporta_credito_csv(self) -> BinaryIO: for instance_m2m in getattr(instance, campo[1]).all(): linha.append(getattr(instance, campo[0])) linha.append(getattr(instance_m2m, self.cabecalho[1][1])) - linha.append(getattr(instance_m2m, self.cabecalho[2][1])) + + motivo_estorno_descricao = getattr(instance_m2m, self.cabecalho[2][1]) + linha.append(motivo_estorno_descricao.replace(";", ",") if motivo_estorno_descricao else "") write.writerow(linha) linha.clear() diff --git a/sme_ptrf_apps/sme/services/exporta_devolucao_tesouro_prestacoes_conta.py b/sme_ptrf_apps/sme/services/exporta_devolucao_tesouro_prestacoes_conta.py index 598c58f5a..41ed8913b 100644 --- a/sme_ptrf_apps/sme/services/exporta_devolucao_tesouro_prestacoes_conta.py +++ b/sme_ptrf_apps/sme/services/exporta_devolucao_tesouro_prestacoes_conta.py @@ -19,6 +19,7 @@ ('Código EOL', 'solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta__associacao__unidade__codigo_eol'), ('Nome Unidade', 'solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta__associacao__unidade__nome'), ('Nome Associação', 'solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta__associacao__nome'), + ('DRE', 'solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta__associacao__unidade__dre__nome'), ('Referência do Período da PC', 'solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta__periodo__referencia'), ('Status da PC', 'solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta__status'), ('ID da despesa','solicitacao_acerto_lancamento__analise_lancamento__despesa__id'), @@ -50,7 +51,7 @@ class ExportacoesDevolucaoTesouroPrestacoesContaService: - + def __init__(self, **kwargs): self.queryset = kwargs.get('queryset', None) self.data_inicio = kwargs.get('data_inicio', None) @@ -60,12 +61,35 @@ def __init__(self, **kwargs): self.cabecalho = CABECALHO[0] self.ambiente = self.get_ambiente self.objeto_arquivo_download = None + self.texto_filtro_aplicado = self.get_texto_filtro_aplicado() - @property - def get_ambiente(self): - ambiente = Ambiente.objects.first() + @property + def get_ambiente(self): + ambiente = Ambiente.objects.first() return ambiente.prefixo if ambiente else "" + def get_texto_filtro_aplicado(self): + if self.data_inicio and self.data_final: + data_inicio_formatada = datetime.strptime(f"{self.data_inicio}", '%Y-%m-%d') + data_inicio_formatada = data_inicio_formatada.strftime("%d/%m/%Y") + + data_final_formatada = datetime.strptime(f"{self.data_final}", '%Y-%m-%d') + data_final_formatada = data_final_formatada.strftime("%d/%m/%Y") + + return f"Filtro aplicado: {data_inicio_formatada} a {data_final_formatada} (data de criação do registro)" + + if self.data_inicio: + data_inicio_formatada = datetime.strptime(f"{self.data_inicio}", '%Y-%m-%d') + data_inicio_formatada = data_inicio_formatada.strftime("%d/%m/%Y") + return f"Filtro aplicado: A partir de {data_inicio_formatada} (data de criação do registro)" + + if self.data_final: + data_final_formatada = datetime.strptime(f"{self.data_final}", '%Y-%m-%d') + data_final_formatada = data_final_formatada.strftime("%d/%m/%Y") + return f"Filtro aplicado: Até {data_final_formatada} (data de criação do registro)" + + return "" + def exporta_devolucao_tesouro_pc(self): self.cria_registro_central_download() self.filtra_range_data('criado_em') @@ -83,10 +107,10 @@ def exporta_devolucao_tesouro_pc_csv(self): ) as tmp: write = csv.writer(tmp.file, delimiter=";") write.writerow([cabecalho[0] for cabecalho in self.cabecalho]) - + for linha in dados: write.writerow(linha) if linha else None - + self.cria_rodape(write) self.envia_arquivo_central_download(tmp) @@ -102,6 +126,31 @@ def monta_dados(self): for _, campo in self.cabecalho: + if campo == "solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta__associacao__unidade__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta__associacao__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta__associacao__unidade__dre__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "solicitacao_acerto_lancamento__analise_lancamento__despesa__nome_fornecedor": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "solicitacao_acerto_lancamento__analise_lancamento__despesa__documento_transacao": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + if campo == 'aplicacao_recurso' or campo == 'tipo_custeio' or campo == 'desc_material_serv' or campo == 'nome_tipo_conta' or campo == 'nome_acao' or campo == 'valor_rateio' or campo == 'valor_realizado': linha_horizontal.append('') elif campo == 'solicitacao_acerto_lancamento__analise_lancamento__despesa__data_documento': @@ -123,11 +172,11 @@ def monta_dados(self): elif campo == 'tipo_id': linha_horizontal.append(devolucao_ao_tesouro.tipo.id if devolucao_ao_tesouro is not None else '') elif campo == 'tipo_nome': - linha_horizontal.append(devolucao_ao_tesouro.tipo.nome if devolucao_ao_tesouro is not None else '') + linha_horizontal.append(devolucao_ao_tesouro.tipo.nome.replace(";", ",") if devolucao_ao_tesouro is not None else '') elif campo == 'motivo': - linha_horizontal.append(devolucao_ao_tesouro.motivo if devolucao_ao_tesouro is not None else '') + linha_horizontal.append(devolucao_ao_tesouro.motivo.replace(";", ",") if devolucao_ao_tesouro is not None else '') elif campo == 'devolucao_total': - if devolucao_ao_tesouro is not None: + if devolucao_ao_tesouro is not None: linha_horizontal.append('Sim' if devolucao_ao_tesouro.devolucao_total else 'Não') else: linha_horizontal.append('') @@ -137,7 +186,7 @@ def monta_dados(self): data_formatada = devolucao_ao_tesouro.data.strftime("%d/%m/%Y") if devolucao_ao_tesouro is not None and devolucao_ao_tesouro.data is not None else '' linha_horizontal.append(data_formatada) elif campo == 'justificativa': - linha_horizontal.append(instance.solicitacao_acerto_lancamento.justificativa if instance.solicitacao_acerto_lancamento.justificativa is not None else '') + linha_horizontal.append(instance.solicitacao_acerto_lancamento.justificativa.replace(";", ",") if instance.solicitacao_acerto_lancamento.justificativa is not None else '') else: campo = get_recursive_attr(instance, campo) linha_horizontal.append(campo) @@ -145,19 +194,19 @@ def monta_dados(self): if len(rateios) > 0: for rateio in rateios: linha_nova = linha_horizontal.copy() - linha_nova[16] = rateio.aplicacao_recurso if rateio.aplicacao_recurso else '' - linha_nova[17] = rateio.tipo_custeio.nome if rateio.tipo_custeio else '' - linha_nova[18] = rateio.especificacao_material_servico.descricao if rateio.especificacao_material_servico else '' - linha_nova[19] = rateio.conta_associacao.tipo_conta.nome if rateio.conta_associacao else '' - linha_nova[20] = rateio.acao_associacao.acao.nome if rateio.acao_associacao else '' - linha_nova[21] = str(rateio.valor_rateio).replace(".", ",") if rateio.valor_rateio else '' - linha_nova[22] = str(rateio.valor_original).replace(".", ",") if rateio.valor_original else '' - + linha_nova[17] = rateio.aplicacao_recurso if rateio.aplicacao_recurso else '' + linha_nova[18] = rateio.tipo_custeio.nome.replace(";", ",") if rateio.tipo_custeio else '' + linha_nova[19] = rateio.especificacao_material_servico.descricao.replace(";", ",") if rateio.especificacao_material_servico else '' + linha_nova[20] = rateio.conta_associacao.tipo_conta.nome.replace(";", ",") if rateio.conta_associacao else '' + linha_nova[21] = rateio.acao_associacao.acao.nome.replace(";", ",") if rateio.acao_associacao else '' + linha_nova[22] = str(rateio.valor_rateio).replace(".", ",") if rateio.valor_rateio else '' + linha_nova[23] = str(rateio.valor_original).replace(".", ",") if rateio.valor_original else '' + logger.info(f"Escrevendo linha {linha_nova} de status de prestação de conta de custeio {instance.id}.") linhas_vertical.append(linha_nova) else: logger.info(f"Escrevendo linha {linha_horizontal} de status de prestação de conta de custeio {instance.id}.") - linhas_vertical.append(linha_horizontal) + linhas_vertical.append(linha_horizontal) return linhas_vertical @@ -183,42 +232,54 @@ def filtra_range_data(self, field): **{f'{field}__lt': self.data_final} ) return self.queryset - - def cria_registro_central_download(self): - logger.info(f"Criando registro na central de download") - obj = gerar_arquivo_download( - self.user, - self.nome_arquivo ) - self.objeto_arquivo_download = obj - - def envia_arquivo_central_download(self, tmp): - try: - logger.info("Salvando arquivo download...") - self.objeto_arquivo_download.arquivo.save( - name=self.objeto_arquivo_download.identificador, - content=File(tmp) - ) - self.objeto_arquivo_download.status = ArquivoDownload.STATUS_CONCLUIDO - self.objeto_arquivo_download.save() - logger.info("Arquivo salvo com sucesso...") - except Exception as e: - self.objeto_arquivo_download.status = ArquivoDownload.STATUS_ERRO - self.objeto_arquivo_download.msg_erro = str(e) - self.objeto_arquivo_download.save() + + def cria_registro_central_download(self): + logger.info(f"Criando registro na central de download") + obj = gerar_arquivo_download( + self.user, + self.nome_arquivo, + self.texto_filtro_aplicado + ) + self.objeto_arquivo_download = obj + + def envia_arquivo_central_download(self, tmp): + try: + logger.info("Salvando arquivo download...") + self.objeto_arquivo_download.arquivo.save( + name=self.objeto_arquivo_download.identificador, + content=File(tmp) + ) + self.objeto_arquivo_download.status = ArquivoDownload.STATUS_CONCLUIDO + self.objeto_arquivo_download.save() + logger.info("Arquivo salvo com sucesso...") + except Exception as e: + self.objeto_arquivo_download.status = ArquivoDownload.STATUS_ERRO + self.objeto_arquivo_download.msg_erro = str(e) + self.objeto_arquivo_download.save() logger.error("Erro arquivo download...") def texto_rodape(self): data_hora_geracao = datetime.now().strftime("%d/%m/%Y às %H:%M:%S") - texto = f"Arquivo gerado pelo {self.ambiente} em {data_hora_geracao}" + texto = f"Arquivo gerado via {self.ambiente} pelo usuário {self.user} em {data_hora_geracao}" return texto def cria_rodape(self, write): rodape = [] + + rodape.append(" ") + write.writerow(rodape) + rodape.clear() + texto = self.texto_rodape() - write.writerow([]) + write.writerow(rodape) + rodape.clear() + rodape.append(texto) write.writerow(rodape) rodape.clear() - \ No newline at end of file + + rodape.append(self.texto_filtro_aplicado) + write.writerow(rodape) + rodape.clear() diff --git a/sme_ptrf_apps/sme/services/exporta_relacao_bens_pc.py b/sme_ptrf_apps/sme/services/exporta_relacao_bens_pc.py index b5240fa6b..6b80c7d31 100644 --- a/sme_ptrf_apps/sme/services/exporta_relacao_bens_pc.py +++ b/sme_ptrf_apps/sme/services/exporta_relacao_bens_pc.py @@ -112,6 +112,26 @@ def monta_dados(self): for _, campo in self.cabecalho: + if campo == "conta_associacao__associacao__unidade__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "conta_associacao__associacao__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "conta_associacao__associacao__unidade__dre__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "conta_associacao__tipo_conta__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + if campo == "arquivo_pdf": campo = get_recursive_attr(instance, campo) url = "" diff --git a/sme_ptrf_apps/sme/services/exporta_repasses_service.py b/sme_ptrf_apps/sme/services/exporta_repasses_service.py index f8bcac0c6..9ea9f5764 100644 --- a/sme_ptrf_apps/sme/services/exporta_repasses_service.py +++ b/sme_ptrf_apps/sme/services/exporta_repasses_service.py @@ -114,6 +114,33 @@ def monta_dados(self): linha_horizontal = [] for _, campo in self.cabecalho: + + # Removendo ponto e vírgula e substituindo por vírgula + if campo == "associacao__unidade__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "associacao__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "associacao__unidade__dre__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "conta_associacao__tipo_conta__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "acao_associacao__acao__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + if campo == "valor_custeio": valor_custeio = str(getattr(instance, campo)).replace(".", ",") linha_horizontal.append(valor_custeio) diff --git a/sme_ptrf_apps/sme/services/exporta_saldo_final_periodo_pc_service.py b/sme_ptrf_apps/sme/services/exporta_saldo_final_periodo_pc_service.py index 35a847aff..7c2e79d80 100644 --- a/sme_ptrf_apps/sme/services/exporta_saldo_final_periodo_pc_service.py +++ b/sme_ptrf_apps/sme/services/exporta_saldo_final_periodo_pc_service.py @@ -112,6 +112,33 @@ def monta_dados(self): value = str(getattr(instance, value)).replace(".", ",") for _, campo in self.cabecalho: + + # Removendo ponto e vírgula e substituindo por vírgula + if campo == "associacao__unidade__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "associacao__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "associacao__unidade__dre__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "conta_associacao__tipo_conta__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + + if campo == "acao_associacao__acao__nome": + campo = get_recursive_attr(instance, campo) + linha_horizontal.append(campo.replace(";", ",") if campo else "") + continue + if campo == "TIPO_APLICACAO": linha_horizontal.append(key) continue diff --git a/sme_ptrf_apps/sme/tasks/exportar_devolucoes_ao_tesouro.py b/sme_ptrf_apps/sme/tasks/exportar_devolucoes_ao_tesouro.py index 58301775e..348c4fd4c 100644 --- a/sme_ptrf_apps/sme/tasks/exportar_devolucoes_ao_tesouro.py +++ b/sme_ptrf_apps/sme/tasks/exportar_devolucoes_ao_tesouro.py @@ -14,10 +14,16 @@ time_limet=600, soft_time_limit=30000 ) -def exportar_devolucoes_ao_tesouro_async(data_inicio, data_final, username): +def exportar_devolucoes_ao_tesouro_async(data_inicio, data_final, username, dre_uuid): logger.info("Exportando csv em processamento...") - queryset = SolicitacaoDevolucaoAoTesouro.objects.order_by('solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta__periodo_id', 'solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta_id', 'solicitacao_acerto_lancamento__analise_lancamento__despesa_id', '-criado_em').distinct('solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta__periodo_id', 'solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta_id', 'solicitacao_acerto_lancamento__analise_lancamento__despesa_id') + queryset = SolicitacaoDevolucaoAoTesouro.objects + + if dre_uuid: + queryset = queryset.filter( + solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta__associacao__unidade__dre__uuid=dre_uuid, + ) + queryset = queryset.order_by('solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta__periodo_id', 'solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta_id', 'solicitacao_acerto_lancamento__analise_lancamento__despesa_id', '-criado_em').distinct('solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta__periodo_id', 'solicitacao_acerto_lancamento__analise_lancamento__analise_prestacao_conta__prestacao_conta_id', 'solicitacao_acerto_lancamento__analise_lancamento__despesa_id') try: logger.info("Criando arquivo %s pcs_devolucoes_tesouro.csv") @@ -37,4 +43,4 @@ def exportar_devolucoes_ao_tesouro_async(data_inicio, data_final, username): logger.error(f"Erro ao exportar csv: {e}") raise e - logger.info("Exportação csv finalizada com sucesso.") \ No newline at end of file + logger.info("Exportação csv finalizada com sucesso.") diff --git a/sme_ptrf_apps/sme/tasks/exportar_receitas.py b/sme_ptrf_apps/sme/tasks/exportar_receitas.py index d93271630..2cbe42c90 100644 --- a/sme_ptrf_apps/sme/tasks/exportar_receitas.py +++ b/sme_ptrf_apps/sme/tasks/exportar_receitas.py @@ -12,9 +12,16 @@ time_limet=600, soft_time_limit=30000 ) -def exportar_receitas_async(data_inicio, data_final, username): +def exportar_receitas_async(data_inicio, data_final, username, dre_uuid=None): logger.info("Exportando csv em processamento...") - queryset = Receita.objects.all() + + if dre_uuid: + queryset = Receita.objects.filter( + associacao__unidade__dre__uuid=dre_uuid, + ).order_by('id') + else: + queryset = Receita.objects.all() + try: logger.info("Criando arquivo %s creditos_principal.csv") params = { @@ -37,4 +44,4 @@ def exportar_receitas_async(data_inicio, data_final, username): logger.error(f"Erro ao exportar csv: {e}") raise e - logger.info("Exportação csv finalizada com sucesso.") \ No newline at end of file + logger.info("Exportação csv finalizada com sucesso.") diff --git a/sme_ptrf_apps/sme/tasks/exportar_relacao_bens.py b/sme_ptrf_apps/sme/tasks/exportar_relacao_bens.py index a2503bd49..8e6023e2b 100644 --- a/sme_ptrf_apps/sme/tasks/exportar_relacao_bens.py +++ b/sme_ptrf_apps/sme/tasks/exportar_relacao_bens.py @@ -15,10 +15,17 @@ time_limet=600, soft_time_limit=30000 ) -def exportar_relacao_bens_async(data_inicio, data_final, username): +def exportar_relacao_bens_async(data_inicio, data_final, username, dre_uuid): logger.info("Exportando csv em processamento...") - queryset = RelacaoBens.objects.all().order_by('id') + queryset = RelacaoBens.objects + + if dre_uuid: + queryset = queryset.filter( + conta_associacao__associacao__unidade__dre__uuid=dre_uuid, + ) + + queryset = queryset.order_by('id') try: logger.info("Criando arquivo %s pcs_relacoes_bens.csv") @@ -37,4 +44,4 @@ def exportar_relacao_bens_async(data_inicio, data_final, username): logger.error(f"Erro ao exportar csv: {e}") raise e - logger.info("Exportação csv finalizada com sucesso.") \ No newline at end of file + logger.info("Exportação csv finalizada com sucesso.") diff --git a/sme_ptrf_apps/sme/tasks/exportar_repasses.py b/sme_ptrf_apps/sme/tasks/exportar_repasses.py index 890c7c4d6..5a16431f7 100644 --- a/sme_ptrf_apps/sme/tasks/exportar_repasses.py +++ b/sme_ptrf_apps/sme/tasks/exportar_repasses.py @@ -13,9 +13,16 @@ time_limet=600, soft_time_limit=30000 ) -def exportar_repasses_async(data_inicio, data_final, username): +def exportar_repasses_async(data_inicio, data_final, username, dre_uuid=None): logger.info("Exportando csv em processamento...") - queryset = Repasse.objects.all() + + if dre_uuid: + queryset = Repasse.objects.filter( + associacao__unidade__dre__uuid=dre_uuid, + ).order_by('id') + else: + queryset = Repasse.objects.all() + try: logger.info("Criando arquivo %s repasses.csv") params = { diff --git a/sme_ptrf_apps/sme/tests/tests_api_exportacoes_dados_creditos.py/test_exportacoes_dados_creditos.py b/sme_ptrf_apps/sme/tests/tests_api_exportacoes_dados_creditos.py/test_exportacoes_dados_creditos.py index 2249e800a..143cae82e 100644 --- a/sme_ptrf_apps/sme/tests/tests_api_exportacoes_dados_creditos.py/test_exportacoes_dados_creditos.py +++ b/sme_ptrf_apps/sme/tests/tests_api_exportacoes_dados_creditos.py/test_exportacoes_dados_creditos.py @@ -37,5 +37,6 @@ def test_exportacoes_dados_creditos(jwt_authenticated_client_sme, usuario_permis mock_exportar_receitas_async.assert_called_once_with( data_inicio='2020-03-26', data_final='2020-04-26', - username=usuario_permissao_sme.username + username=usuario_permissao_sme.username, + dre_uuid=None ) diff --git a/sme_ptrf_apps/sme/tests/tests_api_exportacoes_dados_repasses/test_exportacoes_dados_repasses.py b/sme_ptrf_apps/sme/tests/tests_api_exportacoes_dados_repasses/test_exportacoes_dados_repasses.py index f6391f27a..9a22b1a73 100644 --- a/sme_ptrf_apps/sme/tests/tests_api_exportacoes_dados_repasses/test_exportacoes_dados_repasses.py +++ b/sme_ptrf_apps/sme/tests/tests_api_exportacoes_dados_repasses/test_exportacoes_dados_repasses.py @@ -36,5 +36,6 @@ def test_exportacoes_dados_repasses(jwt_authenticated_client_sme, usuario_permis mock_exportar_repasse_async.assert_called_once_with( data_inicio='2020-03-26', data_final='2020-04-26', - username=usuario_permissao_sme.username + username=usuario_permissao_sme.username, + dre_uuid=None ) diff --git a/sme_ptrf_apps/sme/tests/tests_services/tests_exportacoes_dados_devolucao_tesouro/test_exportacoes_devolucao_tesouro_pc.py b/sme_ptrf_apps/sme/tests/tests_services/tests_exportacoes_dados_devolucao_tesouro/test_exportacoes_devolucao_tesouro_pc.py index 39a04b1b5..79db07fc1 100644 --- a/sme_ptrf_apps/sme/tests/tests_services/tests_exportacoes_dados_devolucao_tesouro/test_exportacoes_devolucao_tesouro_pc.py +++ b/sme_ptrf_apps/sme/tests/tests_services/tests_exportacoes_dados_devolucao_tesouro/test_exportacoes_devolucao_tesouro_pc.py @@ -28,6 +28,7 @@ def test_dados_esperados_csv(queryset_ordered): pc.associacao.unidade.codigo_eol, pc.associacao.unidade.nome, pc.associacao.nome, + pc.associacao.unidade.dre.nome, pc.periodo.referencia, pc.status, despesa.id, @@ -65,23 +66,24 @@ def test_cabecalho(): cabecalho = [cabecalho[0] for cabecalho in dados.cabecalho] resultado_esperado = [ - 'Código EOL', - 'Nome Unidade', - 'Nome Associação', - 'Referência do Período da PC', - 'Status da PC', + 'Código EOL', + 'Nome Unidade', + 'Nome Associação', + 'DRE', + 'Referência do Período da PC', + 'Status da PC', 'ID da despesa', 'Número do documento', - 'Tipo do documento', - 'Data do documento', - 'CPF_CNPJ do fornecedor', - 'Nome do fornecedor', - 'Tipo de transação', - 'Número do documento da transação', - 'Data da transação', - 'Valor (Despeza)', - 'Valor realizado (Despesa)', - 'Tipo de aplicação do recurso', + 'Tipo do documento', + 'Data do documento', + 'CPF_CNPJ do fornecedor', + 'Nome do fornecedor', + 'Tipo de transação', + 'Número do documento da transação', + 'Data da transação', + 'Valor (Despeza)', + 'Valor realizado (Despesa)', + 'Tipo de aplicação do recurso', 'Nome do Tipo de Custeio', 'Descrição da especificação de Material ou Serviço', 'Nome do tipo de Conta', @@ -103,7 +105,8 @@ def test_rodape(ambiente): dados = ExportacoesDevolucaoTesouroPrestacoesContaService().texto_rodape() data_atual = datetime.datetime.now().strftime("%d/%m/%Y às %H:%M:%S") - resultado_esperado = f"Arquivo gerado pelo {ambiente.prefixo} em {data_atual}" + + resultado_esperado = f"Arquivo gerado via {ambiente.prefixo} pelo usuário None em {data_atual}" assert dados == resultado_esperado @@ -155,7 +158,7 @@ def test_filtra_range_data_sem_data_inicio_e_sem_data_final(queryset_ordered): queryset_filtrado = ExportacoesDevolucaoTesouroPrestacoesContaService( queryset=queryset_ordered ).filtra_range_data('criado_em') - + assert queryset_filtrado.count() == len(queryset_ordered) def test_cria_registro_central_download(usuario_para_teste): @@ -191,4 +194,4 @@ def test_envia_arquivo_central_download(usuario_para_teste): assert objeto_arquivo_download.status == ArquivoDownload.STATUS_CONCLUIDO assert objeto_arquivo_download.identificador == 'pcs_devolucoes_tesouro.csv' - assert ArquivoDownload.objects.count() == 1 \ No newline at end of file + assert ArquivoDownload.objects.count() == 1