import { v } from "convex/values"; import { query, mutation } from "./_generated/server"; import { internal } from "./_generated/api"; import { simboloTipo } from "./schema"; // Validadores para campos opcionais const sexoValidator = v.optional( v.union(v.literal("masculino"), v.literal("feminino"), v.literal("outro")) ); const estadoCivilValidator = v.optional( v.union( v.literal("solteiro"), v.literal("casado"), v.literal("divorciado"), v.literal("viuvo"), v.literal("uniao_estavel") ) ); const grauInstrucaoValidator = v.optional( v.union( v.literal("fundamental"), v.literal("medio"), v.literal("superior"), v.literal("pos_graduacao"), v.literal("mestrado"), v.literal("doutorado") ) ); const grupoSanguineoValidator = v.optional( v.union(v.literal("A"), v.literal("B"), v.literal("AB"), v.literal("O")) ); const fatorRHValidator = v.optional( v.union(v.literal("positivo"), v.literal("negativo")) ); const aposentadoValidator = v.optional( v.union(v.literal("nao"), v.literal("funape_ipsep"), v.literal("inss")) ); export const getAll = query({ args: {}, handler: async (ctx) => { // Autorização: listar funcionários await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: "funcionarios", acao: "listar", }); const funcionarios = await ctx.db.query("funcionarios").collect(); // Retornar apenas os campos necessários para listagem return funcionarios.map((f) => ({ _id: f._id, _creationTime: f._creationTime, nome: f.nome, matricula: f.matricula, cpf: f.cpf, rg: f.rg, nascimento: f.nascimento, email: f.email, telefone: f.telefone, endereco: f.endereco, cep: f.cep, cidade: f.cidade, uf: f.uf, simboloId: f.simboloId, simboloTipo: f.simboloTipo, admissaoData: f.admissaoData, desligamentoData: f.desligamentoData, descricaoCargo: f.descricaoCargo, })); }, }); export const getById = query({ args: { id: v.id("funcionarios") }, returns: v.union(v.any(), v.null()), handler: async (ctx, args) => { // Autorização: ver funcionário await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: "funcionarios", acao: "ver", }); return await ctx.db.get(args.id); }, }); export const create = mutation({ args: { // Campos obrigatórios nome: v.string(), matricula: v.optional(v.string()), simboloId: v.id("simbolos"), nascimento: v.string(), rg: v.string(), cpf: v.string(), endereco: v.string(), cep: v.string(), cidade: v.string(), uf: v.string(), telefone: v.string(), email: v.string(), admissaoData: v.optional(v.string()), desligamentoData: v.optional(v.string()), simboloTipo: simboloTipo, // Dados Pessoais Adicionais nomePai: v.optional(v.string()), nomeMae: v.optional(v.string()), naturalidade: v.optional(v.string()), naturalidadeUF: v.optional(v.string()), sexo: sexoValidator, estadoCivil: estadoCivilValidator, nacionalidade: v.optional(v.string()), // Documentos Pessoais rgOrgaoExpedidor: v.optional(v.string()), rgDataEmissao: v.optional(v.string()), carteiraProfissionalNumero: v.optional(v.string()), carteiraProfissionalSerie: v.optional(v.string()), carteiraProfissionalDataEmissao: v.optional(v.string()), reservistaNumero: v.optional(v.string()), reservistaSerie: v.optional(v.string()), tituloEleitorNumero: v.optional(v.string()), tituloEleitorZona: v.optional(v.string()), tituloEleitorSecao: v.optional(v.string()), pisNumero: v.optional(v.string()), // Formação e Saúde grauInstrucao: grauInstrucaoValidator, formacao: v.optional(v.string()), formacaoRegistro: v.optional(v.string()), grupoSanguineo: grupoSanguineoValidator, fatorRH: fatorRHValidator, // Cargo e Vínculo descricaoCargo: v.optional(v.string()), nomeacaoPortaria: v.optional(v.string()), nomeacaoData: v.optional(v.string()), nomeacaoDOE: v.optional(v.string()), pertenceOrgaoPublico: v.optional(v.boolean()), orgaoOrigem: v.optional(v.string()), aposentado: aposentadoValidator, // Dados Bancários contaBradescoNumero: v.optional(v.string()), contaBradescoDV: v.optional(v.string()), contaBradescoAgencia: v.optional(v.string()), // Documentos Anexos (Storage IDs) certidaoAntecedentesPF: v.optional(v.id("_storage")), certidaoAntecedentesJFPE: v.optional(v.id("_storage")), certidaoAntecedentesSDS: v.optional(v.id("_storage")), certidaoAntecedentesTJPE: v.optional(v.id("_storage")), certidaoImprobidade: v.optional(v.id("_storage")), rgFrente: v.optional(v.id("_storage")), rgVerso: v.optional(v.id("_storage")), cpfFrente: v.optional(v.id("_storage")), cpfVerso: v.optional(v.id("_storage")), situacaoCadastralCPF: v.optional(v.id("_storage")), tituloEleitorFrente: v.optional(v.id("_storage")), tituloEleitorVerso: v.optional(v.id("_storage")), comprovanteVotacao: v.optional(v.id("_storage")), carteiraProfissionalFrente: v.optional(v.id("_storage")), carteiraProfissionalVerso: v.optional(v.id("_storage")), comprovantePIS: v.optional(v.id("_storage")), certidaoRegistroCivil: v.optional(v.id("_storage")), certidaoNascimentoDependentes: v.optional(v.id("_storage")), cpfDependentes: v.optional(v.id("_storage")), reservistaDoc: v.optional(v.id("_storage")), comprovanteEscolaridade: v.optional(v.id("_storage")), comprovanteResidencia: v.optional(v.id("_storage")), comprovanteContaBradesco: v.optional(v.id("_storage")), // Declarações (Storage IDs) declaracaoAcumulacaoCargo: v.optional(v.id("_storage")), declaracaoDependentesIR: v.optional(v.id("_storage")), declaracaoIdoneidade: v.optional(v.id("_storage")), termoNepotismo: v.optional(v.id("_storage")), termoOpcaoRemuneracao: v.optional(v.id("_storage")), }, returns: v.id("funcionarios"), handler: async (ctx, args) => { // Autorização: criar await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: "funcionarios", acao: "criar", }); // Unicidade: CPF const cpfExists = await ctx.db .query("funcionarios") .withIndex("by_cpf", (q) => q.eq("cpf", args.cpf)) .unique(); if (cpfExists) { throw new Error("CPF já cadastrado"); } // Unicidade: Matrícula (apenas se fornecida) if (args.matricula) { const matriculaExists = await ctx.db .query("funcionarios") .withIndex("by_matricula", (q) => q.eq("matricula", args.matricula)) .unique(); if (matriculaExists) { throw new Error( "Já existe um funcionário com esta matrícula. Por favor, use outra ou deixe em branco." ); } } const novoFuncionarioId = await ctx.db.insert("funcionarios", args as any); return novoFuncionarioId; }, }); export const update = mutation({ args: { id: v.id("funcionarios"), // Campos obrigatórios nome: v.string(), matricula: v.optional(v.string()), simboloId: v.id("simbolos"), nascimento: v.string(), rg: v.string(), cpf: v.string(), endereco: v.string(), cep: v.string(), cidade: v.string(), uf: v.string(), telefone: v.string(), email: v.string(), admissaoData: v.optional(v.string()), desligamentoData: v.optional(v.string()), simboloTipo: simboloTipo, // Dados Pessoais Adicionais nomePai: v.optional(v.string()), nomeMae: v.optional(v.string()), naturalidade: v.optional(v.string()), naturalidadeUF: v.optional(v.string()), sexo: sexoValidator, estadoCivil: estadoCivilValidator, nacionalidade: v.optional(v.string()), // Documentos Pessoais rgOrgaoExpedidor: v.optional(v.string()), rgDataEmissao: v.optional(v.string()), carteiraProfissionalNumero: v.optional(v.string()), carteiraProfissionalSerie: v.optional(v.string()), carteiraProfissionalDataEmissao: v.optional(v.string()), reservistaNumero: v.optional(v.string()), reservistaSerie: v.optional(v.string()), tituloEleitorNumero: v.optional(v.string()), tituloEleitorZona: v.optional(v.string()), tituloEleitorSecao: v.optional(v.string()), pisNumero: v.optional(v.string()), // Formação e Saúde grauInstrucao: grauInstrucaoValidator, formacao: v.optional(v.string()), formacaoRegistro: v.optional(v.string()), grupoSanguineo: grupoSanguineoValidator, fatorRH: fatorRHValidator, // Cargo e Vínculo descricaoCargo: v.optional(v.string()), nomeacaoPortaria: v.optional(v.string()), nomeacaoData: v.optional(v.string()), nomeacaoDOE: v.optional(v.string()), pertenceOrgaoPublico: v.optional(v.boolean()), orgaoOrigem: v.optional(v.string()), aposentado: aposentadoValidator, // Dados Bancários contaBradescoNumero: v.optional(v.string()), contaBradescoDV: v.optional(v.string()), contaBradescoAgencia: v.optional(v.string()), // Documentos Anexos (Storage IDs) certidaoAntecedentesPF: v.optional(v.id("_storage")), certidaoAntecedentesJFPE: v.optional(v.id("_storage")), certidaoAntecedentesSDS: v.optional(v.id("_storage")), certidaoAntecedentesTJPE: v.optional(v.id("_storage")), certidaoImprobidade: v.optional(v.id("_storage")), rgFrente: v.optional(v.id("_storage")), rgVerso: v.optional(v.id("_storage")), cpfFrente: v.optional(v.id("_storage")), cpfVerso: v.optional(v.id("_storage")), situacaoCadastralCPF: v.optional(v.id("_storage")), tituloEleitorFrente: v.optional(v.id("_storage")), tituloEleitorVerso: v.optional(v.id("_storage")), comprovanteVotacao: v.optional(v.id("_storage")), carteiraProfissionalFrente: v.optional(v.id("_storage")), carteiraProfissionalVerso: v.optional(v.id("_storage")), comprovantePIS: v.optional(v.id("_storage")), certidaoRegistroCivil: v.optional(v.id("_storage")), certidaoNascimentoDependentes: v.optional(v.id("_storage")), cpfDependentes: v.optional(v.id("_storage")), reservistaDoc: v.optional(v.id("_storage")), comprovanteEscolaridade: v.optional(v.id("_storage")), comprovanteResidencia: v.optional(v.id("_storage")), comprovanteContaBradesco: v.optional(v.id("_storage")), // Declarações (Storage IDs) declaracaoAcumulacaoCargo: v.optional(v.id("_storage")), declaracaoDependentesIR: v.optional(v.id("_storage")), declaracaoIdoneidade: v.optional(v.id("_storage")), termoNepotismo: v.optional(v.id("_storage")), termoOpcaoRemuneracao: v.optional(v.id("_storage")), }, returns: v.null(), handler: async (ctx, args) => { // Autorização: editar await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: "funcionarios", acao: "editar", }); // Unicidade: CPF (excluindo o próprio registro) const cpfExists = await ctx.db .query("funcionarios") .withIndex("by_cpf", (q) => q.eq("cpf", args.cpf)) .unique(); if (cpfExists && cpfExists._id !== args.id) { throw new Error("CPF já cadastrado"); } // Unicidade: Matrícula (apenas se fornecida, excluindo o próprio registro) if (args.matricula) { const matriculaExists = await ctx.db .query("funcionarios") .withIndex("by_matricula", (q) => q.eq("matricula", args.matricula)) .unique(); if (matriculaExists && matriculaExists._id !== args.id) { throw new Error( "Já existe um funcionário com esta matrícula. Por favor, use outra ou deixe em branco." ); } } const { id, ...updateData } = args; await ctx.db.patch(id, updateData as any); return null; }, }); export const remove = mutation({ args: { id: v.id("funcionarios") }, returns: v.null(), handler: async (ctx, args) => { // Autorização: excluir await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: "funcionarios", acao: "excluir", }); // TODO: Talvez queiramos também remover os arquivos do storage await ctx.db.delete(args.id); return null; }, }); // Query para obter ficha completa para impressão export const getFichaCompleta = query({ args: { id: v.id("funcionarios") }, returns: v.union(v.any(), v.null()), handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: "funcionarios", acao: "ver", }); const funcionario = await ctx.db.get(args.id); if (!funcionario) { return null; } // Buscar informações do símbolo const simbolo = await ctx.db.get(funcionario.simboloId); // Buscar cursos do funcionário const cursos = await ctx.db .query("cursos") .withIndex("by_funcionario", (q) => q.eq("funcionarioId", args.id)) .collect(); // Buscar URLs dos certificados const cursosComUrls = await Promise.all( cursos.map(async (curso) => { let certificadoUrl = null; if (curso.certificadoId) { certificadoUrl = await ctx.storage.getUrl(curso.certificadoId); } return { ...curso, certificadoUrl, }; }) ); return { ...funcionario, simbolo: simbolo ? { nome: simbolo.nome, descricao: simbolo.descricao, // campos adicionais, se existirem no símbolo tipo: (simbolo as any).tipo, vencValor: (simbolo as any).vencValor, repValor: (simbolo as any).repValor, valor: (simbolo as any).valor, } : null, cursos: cursosComUrls, }; }, }); // Mutation: Configurar gestor (apenas para TI_MASTER) export const configurarGestor = mutation({ args: { funcionarioId: v.id("funcionarios"), gestorId: v.optional(v.id("usuarios")), }, returns: v.null(), handler: async (ctx, args) => { await ctx.db.patch(args.funcionarioId, { gestorId: args.gestorId, }); return null; }, });