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