sgmp/solicitacoes/views.py

869 lines
37 KiB
Python
Raw Normal View History

2026-03-09 18:46:01 +00:00
#SGMP_PROD/solicitacoes/views.py
import logging
from datetime import date
from django.contrib.auth import login, logout, get_user_model
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.core.paginator import Paginator
from .models import (
HeadGestor,
PessoaRM,
Solicitacao,
UsuarioSistema,
TipoSolicitacao,
DecisaoAprovacao,
StatusSolicitacao,
EtapaAprovacao,
matriculas_gestores_do_head,
UsuarioPerfilExtra,
)
from . import services
from .decorators import pode_criar_solicitacao, requer_perfil
from solicitacoes.intf_winthor import autenticar_usuario, buscar_colaborador_oracle
logger = logging.getLogger(__name__)
def get_usuario_sistema(request) -> UsuarioSistema:
"""
Resolve o usuário autenticado do Django para o UsuarioSistema do SGMP.
"""
return get_object_or_404(
UsuarioSistema,
matricula=request.user.username,
ativo=True
)
@login_required
@pode_criar_solicitacao
def criar_desligamento(request, pessoa_id):
from .intf_sqlserver import verificar_estabilidades_colaborador
funcionario = get_object_or_404(PessoaRM, id=pessoa_id)
usuario = get_usuario_sistema(request)
if request.method == "POST":
tipo_desligamento = request.POST.get("tipo_desligamento", "").strip()
aviso_previo = request.POST.get("aviso_previo", "").strip()
motivo = request.POST.get("motivo", "").strip()
data_prevista_str = request.POST.get("data_prevista_desligamento", "").strip()
observacoes = request.POST.get("observacoes", "") or ""
arquivo_pedido = request.FILES.get("arquivo_pedido")
if not tipo_desligamento or not aviso_previo or not motivo or not data_prevista_str:
messages.error(request, "Preencha todos os campos obrigatórios.")
else:
try:
data_prevista = date.fromisoformat(data_prevista_str)
except ValueError:
messages.error(request, "Data prevista inválida. Use o formato AAAA-MM-DD.")
else:
try:
solicitacao = services.criar_solicitacao_desligamento(
solicitante=usuario,
funcionario=funcionario,
tipo_desligamento=tipo_desligamento,
aviso_previo=aviso_previo,
motivo=motivo,
data_prevista_desligamento=data_prevista,
observacoes=observacoes,
arquivo_pedido=arquivo_pedido,
)
messages.success(request, "Solicitação de desligamento criada com sucesso.")
return redirect("solicitacoes:solicitacao_detalhe", solicitacao_id=solicitacao.id)
except Exception:
logger.exception("Erro ao criar solicitação de desligamento")
messages.error(request, "Não foi possível processar a solicitação. Tente novamente ou contate o suporte.")
# Verifica estabilidades para exibir na UI
estabilidades = verificar_estabilidades_colaborador(funcionario.id_rm)
estabilidades_bloqueantes = [e for e in estabilidades if e.get('bloqueado', False)]
desligamento_bloqueado = len(estabilidades_bloqueantes) > 0
return render(
request,
"solicitacoes/desligamento_form.html",
{
"funcionario": funcionario,
"estabilidades": estabilidades,
"estabilidades_bloqueantes": estabilidades_bloqueantes,
"desligamento_bloqueado": desligamento_bloqueado,
},
)
@login_required
@pode_criar_solicitacao
def criar_admissao_substituicao(request, pessoa_id):
from .intf_sqlserver import listar_cargos_ativos_rm, listar_secoes_ativas_rm
funcionario = get_object_or_404(PessoaRM, id=pessoa_id)
usuario = get_usuario_sistema(request)
if request.method == "POST":
data_previsao_str = request.POST.get("data_previsao", "").strip()
cod_coligada = request.POST.get("cod_coligada", "").strip()
cod_filial = request.POST.get("cod_filial", "").strip()
cod_secao = request.POST.get("cod_secao", "").strip()
cod_funcao = request.POST.get("cod_funcao", "").strip()
justificativa = request.POST.get("justificativa", "").strip()
if not data_previsao_str or not cod_coligada or not cod_filial or not cod_secao or not cod_funcao or not justificativa:
messages.error(request, "Preencha todos os campos obrigatórios.")
else:
try:
data_previsao = date.fromisoformat(data_previsao_str)
except ValueError:
messages.error(request, "Data de previsão inválida. Use o formato AAAA-MM-DD.")
else:
dados = {
"data_previsao_contratacao": data_previsao,
"cod_coligada_destino": cod_coligada,
"cod_filial_destino": cod_filial,
"cod_secao_destino": cod_secao,
"cod_funcao_destino": cod_funcao,
"justificativa": justificativa,
}
try:
solicitacao = services.criar_solicitacao_substituicao(
solicitante=usuario,
funcionario_substituido=funcionario,
dados_admissao=dados,
)
messages.success(request, "Solicitação de admissão por substituição criada.")
return redirect("solicitacoes:solicitacao_detalhe", solicitacao_id=solicitacao.id)
except Exception:
logger.exception("Erro ao criar solicitação de admissão por substituição")
messages.error(request, "Não foi possível processar a solicitação. Tente novamente ou contate o suporte.")
# Busca cargos e seções ativos do RM
cargos = listar_cargos_ativos_rm()
secoes = listar_secoes_ativas_rm()
return render(
request,
"solicitacoes/admissao_substituicao_form.html",
{
"funcionario": funcionario,
"cargos": cargos,
"secoes": secoes,
},
)
@login_required
@pode_criar_solicitacao
def criar_admissao_aumento_quadro(request):
from .intf_sqlserver import (
listar_cargos_ativos_rm,
listar_secoes_ativas_rm,
listar_coligadas_rm,
)
usuario = get_usuario_sistema(request)
if request.method == "POST":
data_previsao_str = request.POST.get("data_previsao", "").strip()
cod_coligada = request.POST.get("cod_coligada", "").strip()
cod_filial = request.POST.get("cod_filial", "").strip()
cod_secao = request.POST.get("cod_secao", "").strip()
cod_funcao = request.POST.get("cod_funcao", "").strip()
justificativa = request.POST.get("justificativa", "").strip()
if not data_previsao_str or not cod_coligada or not cod_filial or not cod_secao or not cod_funcao or not justificativa:
messages.error(request, "Preencha todos os campos obrigatórios.")
else:
try:
data_previsao = date.fromisoformat(data_previsao_str)
except ValueError:
messages.error(request, "Data de previsão inválida. Use o formato AAAA-MM-DD.")
else:
dados = {
"data_previsao_contratacao": data_previsao,
"cod_coligada_destino": cod_coligada,
"cod_filial_destino": cod_filial,
"cod_secao_destino": cod_secao,
"cod_funcao_destino": cod_funcao,
"justificativa_estrategica": justificativa,
}
try:
solicitacao = services.criar_solicitacao_aumento_quadro(
solicitante=usuario,
dados_admissao=dados,
)
messages.success(request, "Solicitação de aumento de quadro criada.")
return redirect("solicitacoes:solicitacao_detalhe", solicitacao_id=solicitacao.id)
except Exception:
logger.exception("Erro ao criar solicitação de aumento de quadro")
messages.error(request, "Não foi possível processar a solicitação. Tente novamente ou contate o suporte.")
# Busca cargos, seções e coligadas ativos do RM (filial é informada manualmente, 113)
cargos = listar_cargos_ativos_rm()
secoes = listar_secoes_ativas_rm()
coligadas = listar_coligadas_rm()
return render(
request,
"solicitacoes/admissao_aumento_form.html",
{
"cargos": cargos,
"secoes": secoes,
"coligadas": coligadas,
},
)
@login_required
@pode_criar_solicitacao
def criar_movimentacao(request, pessoa_id):
from .intf_sqlserver import listar_cargos_ativos_rm, listar_secoes_ativas_rm
funcionario = get_object_or_404(PessoaRM, id=pessoa_id)
usuario = get_usuario_sistema(request)
if request.method == "POST":
data_efetivacao_str = request.POST.get("data_efetivacao", "").strip()
justificativa = request.POST.get("justificativa", "").strip()
if not data_efetivacao_str or not justificativa:
messages.error(request, "Preencha data de efetivação e justificativa.")
else:
try:
data_efetivacao = date.fromisoformat(data_efetivacao_str)
except ValueError:
messages.error(request, "Data de efetivação inválida. Use o formato AAAA-MM-DD.")
else:
dados = {
"altera_funcao": bool(request.POST.get("altera_funcao")),
"altera_centro_custo": bool(request.POST.get("altera_centro_custo")),
"novo_cod_funcao": request.POST.get("novo_cod_funcao"),
"novo_cod_secao": request.POST.get("novo_cod_secao"),
"novo_salario": request.POST.get("novo_salario") or None,
"data_efetivacao": data_efetivacao,
"justificativa": justificativa,
}
try:
solicitacao = services.criar_solicitacao_movimentacao(
solicitante=usuario,
funcionario=funcionario,
dados_movimentacao=dados,
)
messages.success(request, "Solicitação de movimentação criada.")
return redirect("solicitacoes:solicitacao_detalhe", solicitacao_id=solicitacao.id)
except Exception:
logger.exception("Erro ao criar solicitação de movimentação")
messages.error(request, "Não foi possível processar a solicitação. Tente novamente ou contate o suporte.")
# Busca cargos e seções ativos do RM
cargos = listar_cargos_ativos_rm()
secoes = listar_secoes_ativas_rm()
return render(
request,
"solicitacoes/movimentacao_form.html",
{
"funcionario": funcionario,
"cargos": cargos,
"secoes": secoes,
},
)
@login_required
def enviar_solicitacao(request, solicitacao_id):
solicitacao = get_object_or_404(Solicitacao, id=solicitacao_id)
usuario = get_usuario_sistema(request)
try:
services.enviar_solicitacao(solicitacao, usuario)
messages.success(request, "Solicitação enviada para aprovação.")
except Exception:
logger.exception("Erro ao enviar solicitação")
messages.error(request, "Não foi possível enviar a solicitação. Tente novamente ou contate o suporte.")
return redirect("solicitacoes:solicitacao_detalhe", solicitacao_id=solicitacao.id)
@login_required
@requer_perfil(UsuarioSistema.Perfil.HEAD, UsuarioSistema.Perfil.DIRETORIA)
def decidir_solicitacao(request, solicitacao_id):
"""
View para HEAD ou DIRETORIA aprovar/reprovar solicitações.
HEAD atua quando status é AGUARDANDO_HEAD; DIRETORIA quando é AGUARDANDO_DIRETORIA.
"""
solicitacao = get_object_or_404(Solicitacao, id=solicitacao_id)
usuario = get_usuario_sistema(request)
if request.method == "POST":
decisao = request.POST.get("decisao", "").strip()
justificativa = request.POST.get("justificativa", "").strip()
try:
if (
solicitacao.status == StatusSolicitacao.AGUARDANDO_HEAD
and usuario.tem_perfil(UsuarioSistema.Perfil.HEAD)
):
services.aprovar_reprovar_por_head(
solicitacao=solicitacao,
aprovador=usuario,
decisao=decisao,
justificativa=justificativa,
)
messages.success(request, "Decisão registrada com sucesso.")
elif (
solicitacao.status == StatusSolicitacao.AGUARDANDO_DIRETORIA
and usuario.tem_perfil(UsuarioSistema.Perfil.DIRETORIA)
):
services.aprovar_reprovar_solicitacao(
solicitacao=solicitacao,
aprovador=usuario,
decisao=decisao,
justificativa=justificativa,
)
messages.success(request, "Decisão registrada com sucesso.")
else:
messages.error(request, "Solicitação não está aguardando sua decisão.")
except Exception:
logger.exception("Erro ao registrar decisão da solicitação")
messages.error(request, "Não foi possível registrar a decisão. Tente novamente ou contate o suporte.")
return redirect("solicitacoes:solicitacao_detalhe", solicitacao_id=solicitacao.id)
@login_required
@requer_perfil(UsuarioSistema.Perfil.GG, UsuarioSistema.Perfil.CONTROLADORIA)
def registrar_parecer_view(request, solicitacao_id):
"""
View para GG e CONTROLADORIA registrarem pareceres.
Permite anexar arquivos junto com o parecer.
"""
solicitacao = get_object_or_404(Solicitacao, id=solicitacao_id)
usuario = get_usuario_sistema(request)
if request.method == "POST":
texto = request.POST.get("texto", "").strip()
anexo = request.FILES.get("anexo")
if not texto:
messages.error(request, "O parecer não pode estar vazio.")
else:
try:
services.registrar_parecer(
solicitacao=solicitacao,
usuario=usuario,
texto=texto,
anexo=anexo
)
messages.success(request, "Parecer registrado com sucesso.")
except Exception:
logger.exception("Erro ao registrar parecer")
messages.error(request, "Não foi possível registrar o parecer. Tente novamente ou contate o suporte.")
return redirect("solicitacoes:solicitacao_detalhe", solicitacao_id=solicitacao.id)
@login_required
def solicitacao_detalhe(request, solicitacao_id):
solicitacao = get_object_or_404(Solicitacao, id=solicitacao_id)
usuario = get_usuario_sistema(request)
# Verifica se o usuário é o solicitante
is_solicitante = solicitacao.solicitante.id == usuario.id
# Verifica se pode aprovar (apenas pela etapa/perfil, não bloqueia o solicitante)
pode_aprovar = solicitacao.pode_aprovar(usuario)
# Verifica se pode dar parecer
pode_dar_parecer = solicitacao.pode_dar_parecer(usuario)
# Busca pareceres existentes
pareceres_gg = solicitacao.pareceres.filter(etapa=EtapaAprovacao.GG)
pareceres_controladoria = solicitacao.pareceres.filter(etapa=EtapaAprovacao.CONTROLADORIA)
# Calcula horas do banco de horas se houver funcionário
horas_banco_horas = None
if solicitacao.funcionario and solicitacao.funcionario.saldo_banco_horas_minutos is not None:
try:
horas_banco_horas = float(solicitacao.funcionario.saldo_banco_horas_minutos) / 60.0
except (ValueError, TypeError):
horas_banco_horas = None
# Busca dados do Winthor se houver funcionário com CPF
dados_winthor = None
dados_winthor_organizados = None
if solicitacao.funcionario and solicitacao.funcionario.cpf:
try:
dados_winthor = buscar_colaborador_oracle(solicitacao.funcionario.cpf)
if dados_winthor:
dados_winthor_organizados = {
"basicos": {
"matricula": dados_winthor.get("matricula"),
"nome": dados_winthor.get("nome"),
"cpf": dados_winthor.get("cpf"),
},
"admissao": {
"admissao": dados_winthor.get("admissao"),
"situacao": dados_winthor.get("situacao"),
"dt_exclusao": dados_winthor.get("dt_exclusao"),
},
"endereco": {
"endereco": dados_winthor.get("endereco"),
"bairro": dados_winthor.get("bairro"),
"cidade": dados_winthor.get("cidade"),
"estado": dados_winthor.get("estado"),
}
}
except Exception:
logger.exception("Erro ao buscar dados do Winthor")
return render(
request,
"solicitacoes/solicitacao_detalhe.html",
{
"solicitacao": solicitacao,
"is_solicitante": is_solicitante,
"pode_aprovar": pode_aprovar,
"pode_dar_parecer": pode_dar_parecer,
"pareceres_gg": pareceres_gg,
"pareceres_controladoria": pareceres_controladoria,
"horas_banco_horas": horas_banco_horas,
"dados_winthor": dados_winthor,
"dados_winthor_organizados": dados_winthor_organizados,
},
)
# auth
User = get_user_model()
def login_view(request):
if request.user.is_authenticated:
# CORREÇÃO AQUI: adicionado solicitacoes:
return redirect("solicitacoes:dashboard")
if request.method == "POST":
login_input = request.POST.get("username", "").strip()
senha = request.POST.get("password", "").strip()
if not login_input or not senha:
messages.error(request, "Informe usuário e senha.")
return render(request, "auth/login.html")
# Chama a autenticação do Winthor
# Espera retorno dict: {'matricula': '123', 'nome': 'Fulano', 'usuariobd': 'FULANO.SILVA'}
dados = autenticar_usuario(login_input, senha)
if not dados:
messages.error(request, "Usuário ou senha inválidos no Winthor.")
return render(request, "auth/login.html")
# TRUQUE DE INTEGRAÇÃO:
# Salvamos o User do Django usando a MATRÍCULA como username.
# Isso garante que o get_usuario_sistema funcione corretamente.
user, _ = User.objects.get_or_create(
username=str(dados["matricula"]),
defaults={
"first_name": dados.get("nome", "Usuario").split(" ")[0],
},
)
# Loga no Django (sessão)
login(request, user)
# Atualiza/Cria o UsuarioSistema (Domínio)
# Se já existe, mantém o perfil atual. Se é novo, define como GESTOR por padrão
usuario_sistema, created = UsuarioSistema.objects.get_or_create(
matricula=str(dados["matricula"]),
defaults={
"nome": dados["nome"],
"ativo": True,
"perfil": UsuarioSistema.Perfil.GESTOR, # Default para novos usuários
},
)
# Se já existia, apenas atualiza o nome (mantém perfil e status)
if not created:
usuario_sistema.nome = dados["nome"]
usuario_sistema.ativo = True
usuario_sistema.save()
messages.success(request, f"Bem-vindo, {dados['nome']}!")
# O 'next' pega a url que o usuário tentou acessar antes de logar
next_url = request.GET.get("next", "solicitacoes:dashboard")
return redirect(next_url)
return render(request, "auth/login.html")
@login_required
def logout_view(request):
logout(request)
messages.info(request, "Você saiu do sistema.")
return redirect("solicitacoes:login")
@login_required
def dashboard_view(request):
usuario = get_usuario_sistema(request)
# Busca solicitações baseado no perfil
if usuario.perfil == UsuarioSistema.Perfil.GESTOR:
# Gestores veem suas próprias solicitações (todas, incluindo rascunhos)
qs_base = Solicitacao.objects.filter(solicitante=usuario)
solicitacoes = qs_base.order_by('-criado_em')
elif usuario.perfil == UsuarioSistema.Perfil.HEAD:
# Head vê solicitações AGUARDANDO_HEAD cujo solicitante está na sua lista de gestores
matriculas = matriculas_gestores_do_head(usuario)
qs_base = Solicitacao.objects.filter(status=StatusSolicitacao.AGUARDANDO_HEAD)
if matriculas:
qs_base = qs_base.filter(solicitante__matricula__in=matriculas)
solicitacoes = qs_base.order_by('-criado_em')
elif usuario.perfil in [UsuarioSistema.Perfil.GG, UsuarioSistema.Perfil.CONTROLADORIA]:
# GG e Controladoria veem solicitações ENVIADAS para dar parecer
# Verifica se já deram parecer para filtrar
qs_base = Solicitacao.objects.filter(status=StatusSolicitacao.ENVIADA)
# Filtra para mostrar apenas solicitações onde o usuário ainda não deu parecer
etapa_esperada = EtapaAprovacao.GG if usuario.perfil == UsuarioSistema.Perfil.GG else EtapaAprovacao.CONTROLADORIA
qs_base = qs_base.exclude(pareceres__etapa=etapa_esperada, pareceres__usuario=usuario)
solicitacoes = qs_base.order_by('-enviada_em', '-criado_em')
elif usuario.perfil == UsuarioSistema.Perfil.DIRETORIA:
# Diretoria vê solicitações AGUARDANDO_DIRETORIA para aprovar/reprovar
qs_base = Solicitacao.objects.filter(status=StatusSolicitacao.AGUARDANDO_DIRETORIA)
solicitacoes = qs_base.order_by('-enviada_em', '-criado_em')
else:
qs_base = Solicitacao.objects.none()
solicitacoes = Solicitacao.objects.none()
# Calcula contadores baseado no queryset
finalizados = [StatusSolicitacao.FINALIZADA, StatusSolicitacao.REPROVADA]
total = qs_base.count()
pendentes = qs_base.exclude(status__in=finalizados).count()
# Para gestores, ajusta os contadores considerando todos os status
if usuario.perfil == UsuarioSistema.Perfil.GESTOR:
# Total: todas as solicitações
total = qs_base.count()
# Pendentes: rascunho, enviada, aprovadas em etapas intermediárias
pendentes = qs_base.exclude(status__in=[StatusSolicitacao.FINALIZADA, StatusSolicitacao.REPROVADA]).count()
# Paginação
paginator = Paginator(solicitacoes, 10)
page = request.GET.get('page')
solicitacoes_page = paginator.get_page(page)
# Prepara informações sobre quais solicitações podem ser aprovadas ou receber parecer
solicitacoes_com_acao = []
for solicitacao in solicitacoes_page:
is_solicitante = solicitacao.solicitante.id == usuario.id
pode_aprovar = solicitacao.pode_aprovar(usuario)
pode_dar_parecer = solicitacao.pode_dar_parecer(usuario)
# Busca dados do Winthor se houver funcionário com CPF
dados_winthor_organizados = None
if solicitacao.funcionario and solicitacao.funcionario.cpf:
try:
dados_winthor = buscar_colaborador_oracle(solicitacao.funcionario.cpf)
if dados_winthor:
dados_winthor_organizados = {
"basicos": {
"matricula": dados_winthor.get("matricula"),
"nome": dados_winthor.get("nome"),
"cpf": dados_winthor.get("cpf"),
},
"admissao": {
"admissao": dados_winthor.get("admissao"),
"situacao": dados_winthor.get("situacao"),
"dt_exclusao": dados_winthor.get("dt_exclusao"),
},
"endereco": {
"endereco": dados_winthor.get("endereco"),
"bairro": dados_winthor.get("bairro"),
"cidade": dados_winthor.get("cidade"),
"estado": dados_winthor.get("estado"),
}
}
except Exception:
# Ignora erros silenciosamente no dashboard
pass
solicitacoes_com_acao.append({
'solicitacao': solicitacao,
'pode_aprovar': pode_aprovar,
'pode_dar_parecer': pode_dar_parecer,
'is_solicitante': is_solicitante,
'dados_winthor_organizados': dados_winthor_organizados,
})
return render(request, "dashboard.html", {
"solicitacoes": solicitacoes_page,
"solicitacoes_com_acao": solicitacoes_com_acao,
"total": total,
"pendentes": pendentes,
})
@login_required
def todas_solicitacoes_view(request):
"""Listagem de solicitações: Gestor não acessa; Head vê só dos gestores vinculados a ele; GG/Controladoria/Diretoria veem todas."""
usuario = get_usuario_sistema(request)
if usuario.perfil == UsuarioSistema.Perfil.GESTOR:
return redirect("solicitacoes:dashboard")
qs_base = Solicitacao.objects.all().order_by("-criado_em")
# Head: apenas solicitações dos gestores que ele aprova (subordinados imediatos)
if usuario.perfil == UsuarioSistema.Perfil.HEAD:
matriculas = matriculas_gestores_do_head(usuario)
if matriculas:
qs_base = qs_base.filter(solicitante__matricula__in=matriculas)
else:
qs_base = qs_base.none()
# Filtro por status (GET)
filtro_status = request.GET.get("status", "").strip()
if filtro_status:
qs_base = qs_base.filter(status=filtro_status)
total = qs_base.count()
# Paginação
paginator = Paginator(qs_base, 20)
page = request.GET.get("page")
solicitacoes_page = paginator.get_page(page)
# Contexto de ação por solicitação (pode_aprovar, pode_dar_parecer, etc.)
solicitacoes_com_acao = []
for solicitacao in solicitacoes_page:
is_solicitante = solicitacao.solicitante.id == usuario.id
pode_aprovar = solicitacao.pode_aprovar(usuario)
pode_dar_parecer = solicitacao.pode_dar_parecer(usuario)
dados_winthor_organizados = None
if solicitacao.funcionario and solicitacao.funcionario.cpf:
try:
dados_winthor = buscar_colaborador_oracle(solicitacao.funcionario.cpf)
if dados_winthor:
dados_winthor_organizados = {
"basicos": {
"matricula": dados_winthor.get("matricula"),
"nome": dados_winthor.get("nome"),
"cpf": dados_winthor.get("cpf"),
},
"admissao": {
"admissao": dados_winthor.get("admissao"),
"situacao": dados_winthor.get("situacao"),
"dt_exclusao": dados_winthor.get("dt_exclusao"),
},
"endereco": {
"endereco": dados_winthor.get("endereco"),
"bairro": dados_winthor.get("bairro"),
"cidade": dados_winthor.get("cidade"),
"estado": dados_winthor.get("estado"),
},
}
except Exception:
pass
solicitacoes_com_acao.append({
"solicitacao": solicitacao,
"pode_aprovar": pode_aprovar,
"pode_dar_parecer": pode_dar_parecer,
"is_solicitante": is_solicitante,
"dados_winthor_organizados": dados_winthor_organizados,
})
return render(request, "solicitacoes/todas_solicitacoes.html", {
"solicitacoes": solicitacoes_page,
"solicitacoes_com_acao": solicitacoes_com_acao,
"total": total,
"filtro_status": filtro_status,
"status_choices": StatusSolicitacao.choices,
})
@login_required
@pode_criar_solicitacao
def listar_colaboradores(request):
"""
Lista colaboradores para seleção ao criar solicitação.
Aceita parâmetro 'tipo' na URL:
- 'substituicao': busca apenas pessoas DESLIGADAS diretamente do RM (para admissão por substituição)
- outros ou ausente: busca pessoas que não estão desligadas do banco local (padrão)
"""
from .intf_sqlserver import buscar_colaboradores_rm_desligados
from .models import PessoaRM
# Verifica se é para admissão por substituição
tipo = request.GET.get('tipo', '')
apenas_desligados = (tipo == 'substituicao')
busca = request.GET.get('q', '')
# Para admissão por substituição: busca direto no RM (como no sgmp)
if apenas_desligados:
# Busca direto no SQL Server para garantir dados atualizados
resultados_rm = buscar_colaboradores_rm_desligados(nome=busca if busca else None)
# Converte resultados do RM para formato compatível com o template
colaboradores_list = []
for row in resultados_rm:
id_rm = f"{row['CODCOLIGADA']}-{row['CHAPA']}"
# Tenta encontrar no banco local ou sincroniza
try:
pessoa = PessoaRM.objects.get(id_rm=id_rm)
except PessoaRM.DoesNotExist:
# Sincroniza a pessoa no banco local com os dados do RM
# pymssql retorna chaves em maiúsculas por padrão
pessoa, _ = PessoaRM.objects.update_or_create(
id_rm=id_rm,
defaults={
"matricula": row['CHAPA'],
"nome": row['NOME'],
"cpf": row.get('CPF'),
"cargo": row['FUNCAO'],
"setor": row['SECAO'],
"centro_custo": row['CODSECAO'],
"situacao": row['CODSITUACAO'],
"cod_funcao": row.get('CODFUNCAO'),
"salario": row.get('SALARIO'),
"cod_sindicato": row.get('CODSINDICATO'),
}
)
colaboradores_list.append(pessoa)
# Paginação manual
paginator = Paginator(colaboradores_list, 20)
page = request.GET.get('page')
colaboradores_page = paginator.get_page(page)
else:
# Padrão: busca pessoas que não estão desligadas do banco local
colaboradores = PessoaRM.objects.exclude(situacao='D').order_by('nome')
# Busca por nome ou matrícula
if busca:
colaboradores = colaboradores.filter(
nome__icontains=busca
) | colaboradores.filter(
matricula__icontains=busca
)
# Paginação
paginator = Paginator(colaboradores, 20)
page = request.GET.get('page')
colaboradores_page = paginator.get_page(page)
return render(request, "solicitacoes/listar_colaboradores.html", {
"colaboradores": colaboradores_page,
"busca": busca,
"tipo": tipo,
"apenas_desligados": apenas_desligados,
})
@login_required
def gerenciar_permissoes(request):
"""View para gerenciar permissões de usuários. Para perfil HEAD, permite vincular gestores (em relação a quem o Head aprova)."""
usuario_atual = get_usuario_sistema(request)
# Pré-carrega perfis extras para evitar N+1
todos_usuarios = list(
UsuarioSistema.objects.all()
.prefetch_related("perfis_extras")
.order_by("nome")
)
# Gestores são usuários que possuem o perfil GESTOR (principal ou extra)
gestores_lista = [u for u in todos_usuarios if u.tem_perfil(UsuarioSistema.Perfil.GESTOR)]
usuarios = todos_usuarios
# Busca
busca = request.GET.get('q', '')
if busca:
usuarios = usuarios.filter(
nome__icontains=busca
) | usuarios.filter(
matricula__icontains=busca
)
if request.method == "POST":
# Salvar gestores vinculados ao Head (formulário "Este Head aprova os gestores:")
gestores_head_id = request.POST.get("gestores_head_id")
if gestores_head_id:
try:
head = UsuarioSistema.objects.get(id=gestores_head_id)
if not head.tem_perfil(UsuarioSistema.Perfil.HEAD):
messages.error(request, "Usuário selecionado não possui perfil de Head.")
else:
gestores_ids = request.POST.getlist("gestores_ids")
HeadGestor.objects.filter(head=head).delete()
# Mantém apenas vínculos com usuários que possuem perfil de Gestor (principal ou extra)
candidatos = UsuarioSistema.objects.filter(id__in=gestores_ids).prefetch_related("perfis_extras")
for gestor in candidatos:
if gestor.tem_perfil(UsuarioSistema.Perfil.GESTOR):
HeadGestor.objects.get_or_create(head=head, gestor=gestor)
messages.success(request, f"Gestores vinculados ao Head {head.nome} atualizados.")
except UsuarioSistema.DoesNotExist:
messages.error(request, "Head não encontrado.")
except Exception:
logger.exception("Erro ao salvar vínculos Head-Gestor")
messages.error(request, "Não foi possível salvar os vínculos. Tente novamente ou contate o suporte.")
return redirect("solicitacoes:gerenciar_permissoes")
# Atualização de perfil principal + perfis extras
usuario_id = request.POST.get("usuario_id")
novo_perfil = request.POST.get("perfil")
if usuario_id and novo_perfil:
try:
usuario_editado = UsuarioSistema.objects.get(id=usuario_id)
usuario_editado.perfil = novo_perfil
usuario_editado.save()
# Perfis extras selecionados no formulário
perfis_extras_selecionados = set(request.POST.getlist("perfis_extras"))
# Remove eventual duplicidade com o perfil principal
perfis_extras_selecionados.discard(novo_perfil)
# Sincroniza perfis extras no banco
UsuarioPerfilExtra.objects.filter(usuario=usuario_editado).exclude(
perfil__in=perfis_extras_selecionados
).delete()
for codigo_perfil in perfis_extras_selecionados:
UsuarioPerfilExtra.objects.get_or_create(
usuario=usuario_editado,
perfil=codigo_perfil,
)
if usuario_editado.id == usuario_atual.id:
messages.success(
request,
f"Seu perfil foi atualizado para: {usuario_editado.get_perfil_display()}",
)
else:
messages.success(
request,
f"Perfil de {usuario_editado.nome} atualizado para: {usuario_editado.get_perfil_display()}",
)
except UsuarioSistema.DoesNotExist:
messages.error(request, "Usuário não encontrado.")
except Exception:
logger.exception("Erro ao atualizar perfil")
messages.error(request, "Não foi possível atualizar o perfil. Tente novamente ou contate o suporte.")
return redirect("solicitacoes:gerenciar_permissoes")
# Lista de gestores (para o multi-select / listas dos Heads)
gestores = gestores_lista
# Paginação
paginator = Paginator(usuarios, 20)
page = request.GET.get('page')
usuarios_page = paginator.get_page(page)
# head_id -> lista de gestor_id já vinculados (apenas para usuários da página atual)
head_gestores = {}
for row in HeadGestor.objects.filter(head__in=list(usuarios_page)).values_list("head_id", "gestor_id"):
head_gestores.setdefault(str(row[0]), []).append(str(row[1]))
# Para o template: cada item tem usuario, seus perfis extras e lista (gestor, selected) para o multi-select HEAD
usuarios_com_gestores = [
{
"usuario": u,
"perfis_extras": list(u.perfis_extras.values_list("perfil", flat=True)),
"gestores_com_selecao": [
(g, str(g.id) in head_gestores.get(str(u.id), []))
for g in gestores
],
}
for u in usuarios_page
]
return render(request, "solicitacoes/gerenciar_permissoes.html", {
"usuarios_com_gestores": usuarios_com_gestores,
"busca": busca,
"perfis": UsuarioSistema.Perfil.choices,
"gestores": gestores,
})