sgmp/solicitacoes/views.py

869 lines
37 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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,
})