Skip to content

Commit

Permalink
Merge pull request #2913 from prefeiturasp/release/9.4.0
Browse files Browse the repository at this point in the history
Release/9.4.0
  • Loading branch information
Lucas-Santos-Rocha-dev authored May 6, 2024
2 parents 8e3c5e3 + 72053b8 commit 9c21c15
Show file tree
Hide file tree
Showing 40 changed files with 2,178 additions and 289 deletions.
14 changes: 7 additions & 7 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
# ------------------------------------------------------------------------------
django==4.2.11
django-admin-interface==0.26.1
django-admin-rangefilter==0.5.4
django-admin-rangefilter==0.11.2
django-allauth==0.56.1
django-auditlog==2.0.0
django-ckeditor==6.7.0
django-compressor==4.4
django-cors-headers
django-crispy-forms==1.9.0
django-cors-headers==4.3.1
django-crispy-forms==2.1
django-environ==0.4.5
django-filter==2.4.0
django-mathfilters==1.0.0
Expand All @@ -22,7 +22,7 @@ git+https://github.com/prefeiturasp/django-des-fork.git#egg=django-des
# Django Rest Framework
# ------------------------------------------------------------------------------
djangorestframework==3.14.0
djangorestframework-simplejwt==5.3.0 # JWT authentication for DRF
djangorestframework-simplejwt==5.3.1 # JWT authentication for DRF
drf-spectacular==0.26.2 # Auto-generate OpenAPI 3.0 schemas from Django Rest Framework code

# Celery e Redis
Expand All @@ -45,10 +45,10 @@ pika==1.3.2
# Outras libs e utilitários
# ------------------------------------------------------------------------------
Babel==2.12.1
Pillow==10.2.0
argon2-cffi==19.2.0
Pillow==10.3.0
argon2-cffi==23.1.0
brazilnum==0.8.8
openpyxl==3.0.3
openpyxl==3.0.10
python-slugify==8.0.1
pytz==2019.3
rcssmin==1.1.1
Expand Down
2 changes: 1 addition & 1 deletion requirements/local.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pytest==7.3.2
# ------------------------------------------------------------------------------
mypy==1.6.1
django-stubs==4.2.6
black==23.9.1
black==24.4.0
coverage==5.0.3
flake8==6.1.0
pre-commit==2.1.1
Expand Down
2 changes: 1 addition & 1 deletion sme_ptrf_apps/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "9.3.2"
__version__ = "9.4.0"

__version_info__ = tuple(
[
Expand Down
26 changes: 26 additions & 0 deletions sme_ptrf_apps/contrib/checa_processos_duplicados.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from django.db.models import Count
from sme_ptrf_apps.core.models import ProcessoAssociacao # Ajuste 'seu_app' para o nome real do seu app


def verificar_numeros_processo_duplicados():
# Agrega os números de processo, contando quantas vezes cada um aparece
duplicados = ProcessoAssociacao.objects.values('numero_processo')\
.annotate(num_count=Count('numero_processo'))\
.filter(num_count__gt=1)

total_duplicados = 0

if duplicados:
print("Encontrados números de processo duplicados:")
for item in duplicados:
print(f"Numero de Processo: {item['numero_processo']}, Contagem: {item['num_count']}")
total_duplicados += item['num_count'] - 1 # Contando apenas as entradas extras como duplicadas

# Listando as instâncias específicas que têm números duplicados
processos = ProcessoAssociacao.objects.filter(numero_processo=item['numero_processo'])
for processo in processos:
print(f" - ID: {processo.id}, Ano: {processo.ano}, UE: {processo.associacao.unidade.codigo_eol}-{processo.associacao.unidade.nome}")
else:
print("Não foram encontrados números de processo duplicados.")

print(f"Total de casos de duplicidade: {total_duplicados}")
5 changes: 3 additions & 2 deletions sme_ptrf_apps/core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,6 @@ def get_secretario(self, obj):

get_secretario.short_description = 'Secretário'


raw_id_fields = ('prestacao_conta', 'associacao', 'composicao', 'presidente_da_reuniao', 'secretario_da_reuniao')

list_display = (
Expand Down Expand Up @@ -537,6 +536,7 @@ def periodos_str(self, obj):

periodos_str.short_description = "Períodos"


@admin.register(ObservacaoConciliacao)
class ObservacaoConciliacaoAdmin(admin.ModelAdmin):
def get_unidade(self, obj):
Expand Down Expand Up @@ -1647,7 +1647,8 @@ class FalhaGeracaoPcAdmin(admin.ModelAdmin):
list_filter = ['ultimo_usuario', 'associacao', 'periodo', 'data_hora_ultima_ocorrencia',
'qtd_ocorrencias_sucessivas', 'resolvido', 'associacao__unidade__dre']
readonly_fields = ('uuid', 'id')
search_fields = ('ultimo_usuario__username', 'associacao__nome', 'associacao__unidade__nome')
search_fields = ('ultimo_usuario__username', 'associacao__nome',
'associacao__unidade__nome', 'associacao__unidade__codigo_eol')


@admin.register(TransferenciaEol)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,16 @@ 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
)


# Verifica unicidade do número de processo em toda a base de dados
if numero_processo:
processos_existentes = ProcessoAssociacao.objects.filter(numero_processo=numero_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 existe para o ano informado."
"numero_processo": "Este número de processo já está sendo usado."
})

# Verifica se os períodos estão sendo reutilizados na mesma associação
Expand Down
8 changes: 8 additions & 0 deletions sme_ptrf_apps/core/models/unidade.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ def formata_nome_dre(self):
else:
return ""

def tipo_unidade_pertence_a_grupo_ue(self):
TIPOS_UNIDADES_NAO_PERTENCENTES_A_GRUPO_UE = ['ADM', 'DRE', 'IFSP', 'CMCT']

if self.tipo_unidade not in TIPOS_UNIDADES_NAO_PERTENCENTES_A_GRUPO_UE:
return True

return False

class Meta:
verbose_name = 'Unidade'
verbose_name_plural = '06.0) Unidades'
Expand Down
10 changes: 9 additions & 1 deletion sme_ptrf_apps/core/services/processa_cargas.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
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
from sme_ptrf_apps.users.services.carga_usuario_v2_service import CargaUsuariosGestaoUsuarioService
from sme_ptrf_apps.despesas.services.carga_materiais_servicos_service import CargaMateriaisServicosService
from waffle import get_waffle_flag_model

def processa_cargas(queryset):
for arquivo in queryset.all():
Expand All @@ -31,7 +33,13 @@ def processa_carga(arquivo):
elif arquivo.tipo_carga == CARGA_ASSOCIACOES:
CargaAssociacoesService().carrega_associacoes(arquivo)
elif arquivo.tipo_carga == CARGA_USUARIOS:
CargaUsuariosService().carrega_usuarios(arquivo)
flags = get_waffle_flag_model()

if flags.objects.filter(name='gestao-usuarios', everyone=True).exists():
CargaUsuariosGestaoUsuarioService().carrega_usuarios(arquivo)
else:
CargaUsuariosService().carrega_usuarios(arquivo)

elif arquivo.tipo_carga == CARGA_CENSO:
carrega_censo(arquivo)
elif arquivo.tipo_carga == CARGA_REPASSE_PREVISTO_SME:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,22 +138,22 @@ def test_create_processo_associacao_com_mesmo_numero_processo_para_mesmo_ano_na_
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 existe para o ano informado.']}
assert result == {'numero_processo': ['Este número de processo já está sendo usado.']}

def test_create_processo_associacao_com_mesmo_numero_processo_para_outro_ano_na_mesma_associacao(
jwt_authenticated_client_a,
periodos_de_2019_ate_2023,
Expand All @@ -164,19 +164,20 @@ def test_create_processo_associacao_com_mesmo_numero_processo_para_outro_ano_na_
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
assert response.status_code == status.HTTP_400_BAD_REQUEST
result = json.loads(response.content)
assert result == {'numero_processo': ['Este número de processo já está sendo usado.']}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ 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,
Expand All @@ -76,14 +76,14 @@ def test_update_processo_associacao_com_mesmo_numero_processo_para_mesmo_ano_na_
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}/',
Expand All @@ -92,7 +92,7 @@ def test_update_processo_associacao_com_mesmo_numero_processo_para_mesmo_ano_na_

assert response.status_code == status.HTTP_400_BAD_REQUEST
result = json.loads(response.content)
assert result == {'numero_processo': ['Este número de processo SEI existe para o ano informado.']}
assert result == {'numero_processo': ['Este número de processo já está sendo usado.']}

def test_update_processo_associacao_com_mesmo_numero_processo_para_outro_ano_na_mesma_associacao(
jwt_authenticated_client_a,
Expand All @@ -105,22 +105,20 @@ def test_update_processo_associacao_com_mesmo_numero_processo_para_outro_ano_na_
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:', '')

assert response.status_code == status.HTTP_400_BAD_REQUEST
result = json.loads(response.content)
assert result == {'numero_processo': ['Este número de processo já está sendo usado.']}
29 changes: 26 additions & 3 deletions sme_ptrf_apps/sme/api/views/exportacoes_dados.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
exportar_dados_conta_async,
exportar_repasses_async,
exportar_dados_membros_apm_async,
exportar_processos_sei_regularidade_async
exportar_processos_sei_regularidade_async,
exportar_processos_sei_prestacao_contas_async
)

from sme_ptrf_apps.users.permissoes import (
Expand Down Expand Up @@ -59,11 +60,12 @@ def demonstrativos_financeiros(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(
{
"response": "Arquivo gerado com sucesso, enviado para a central de download"
"response": "O arquivo está sendo gerado e será enviado para a central de download após conclusão."
},
status=HTTP_201_CREATED,
)
Expand Down Expand Up @@ -215,6 +217,7 @@ def rateios(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(
Expand All @@ -235,11 +238,12 @@ def atas(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(
{
"response": "Arquivo gerado com sucesso, enviado para a central de download"
"response": "O arquivo está sendo gerado e será enviado para a central de download após conclusão."
},
status=HTTP_201_CREATED,
)
Expand Down Expand Up @@ -390,3 +394,22 @@ def processos_sei_regularidade(self, request):
},
status=HTTP_201_CREATED,
)

@action(
detail=False,
methods=["get"],
url_path="processos-sei-prestacao-contas",
permission_classes=permission_classes,
)
def processos_sei_prestacao_contas(self, request):
exportar_processos_sei_prestacao_contas_async.delay(
username=request.user.username,
dre_uuid=request.query_params.get("dre_uuid"),
)

return Response(
{
"response": "O arquivo está sendo gerado e será enviado para a central de download após conclusão."
},
status=HTTP_201_CREATED,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 4.2.11 on 2024-04-22 07:43

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("sme", "0004_remove_parametrossme_tipos_unidade_adm_da_sme"),
]

operations = [
migrations.AlterField(
model_name="parametrossme",
name="valida_unidades_login",
field=models.BooleanField(
default=False,
help_text="Remove definitivamente os acessos indevidos.",
verbose_name="Valida unidades ao logar",
),
),
]
6 changes: 5 additions & 1 deletion sme_ptrf_apps/sme/models/parametros_sme.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ def validate_tipos_unidade(value):
class ParametrosSme(SingletonModel, ModeloBase):
history = AuditlogHistoryField()

valida_unidades_login = models.BooleanField('Valida unidades ao logar', default=False)
valida_unidades_login = models.BooleanField(
'Valida unidades ao logar',
default=False,
help_text="Remove definitivamente os acessos indevidos."
)

def __str__(self):
return 'Parâmetros SME do PTRF'
Expand Down
Loading

0 comments on commit 9c21c15

Please sign in to comment.