import { mutation, query } from "./_generated/server"; import { v } from "convex/values"; import { situacaoContrato } from "./schema"; import { getCurrentUserFunction } from "./auth"; import { internal } from "./_generated/api"; export const listar = query({ args: { responsavelId: v.optional(v.id("funcionarios")), dataInicio: v.optional(v.string()), dataFim: v.optional(v.string()), }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: "contratos", acao: "listar", }); let q = ctx.db.query("contratos"); if (args.responsavelId) { q = q.withIndex("by_responsavel", (q) => q.eq("responsavelId", args.responsavelId!) ) as typeof q; } const contratos = await q.collect(); // Filtros em memória para datas (já que Convex não tem filtro de range nativo eficiente combinado com outros índices sem setup complexo) // Se o volume for muito grande, ideal seria criar índices específicos ou usar search. let resultado = contratos; if (args.dataInicio) { resultado = resultado.filter( (c) => c.dataInicioVigencia >= args.dataInicio! ); } if (args.dataFim) { resultado = resultado.filter((c) => c.dataFimVigencia <= args.dataFim!); } // Enriquecer com dados relacionados const contratosEnriquecidos = await Promise.all( resultado.map(async (c) => { const contratada = await ctx.db.get(c.contratadaId); const responsavel = await ctx.db.get(c.responsavelId); return { ...c, contratada, responsavel, }; }) ); return contratosEnriquecidos; }, }); export const obter = query({ args: { id: v.id("contratos") }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: "contratos", acao: "ver", }); const contrato = await ctx.db.get(args.id); if (!contrato) return null; const contratada = await ctx.db.get(contrato.contratadaId); const responsavel = await ctx.db.get(contrato.responsavelId); return { ...contrato, contratada, responsavel, }; }, }); export const criar = mutation({ args: { contratadaId: v.id("empresas"), objeto: v.string(), numeroNotaEmpenho: v.string(), responsavelId: v.id("funcionarios"), departamento: v.string(), situacao: situacaoContrato, numeroProcessoLicitatorio: v.string(), modalidade: v.string(), numeroContrato: v.string(), anoContrato: v.number(), dataInicioVigencia: v.string(), dataFimVigencia: v.string(), nomeFiscal: v.string(), valorTotal: v.string(), dataAditivoPrazo: v.optional(v.string()), diasAvisoVencimento: v.number(), }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: "contratos", acao: "criar", }); const usuario = await getCurrentUserFunction(ctx); if (!usuario) throw new Error("Não autenticado"); const id = await ctx.db.insert("contratos", { ...args, criadoPor: usuario._id, criadoEm: Date.now(), }); return id; }, }); export const editar = mutation({ args: { id: v.id("contratos"), contratadaId: v.optional(v.id("empresas")), objeto: v.optional(v.string()), numeroNotaEmpenho: v.optional(v.string()), responsavelId: v.optional(v.id("funcionarios")), departamento: v.optional(v.string()), situacao: v.optional(situacaoContrato), numeroProcessoLicitatorio: v.optional(v.string()), modalidade: v.optional(v.string()), numeroContrato: v.optional(v.string()), anoContrato: v.optional(v.number()), dataInicioVigencia: v.optional(v.string()), dataFimVigencia: v.optional(v.string()), nomeFiscal: v.optional(v.string()), valorTotal: v.optional(v.string()), dataAditivoPrazo: v.optional(v.string()), diasAvisoVencimento: v.optional(v.number()), }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: "contratos", acao: "editar", }); const { id, ...campos } = args; await ctx.db.patch(id, { ...campos, atualizadoEm: Date.now(), }); }, }); export const excluir = mutation({ args: { id: v.id("contratos") }, handler: async (ctx, args) => { await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { recurso: "contratos", acao: "excluir", }); await ctx.db.delete(args.id); }, }); export const verificarVencimentos = query({ args: {}, handler: async (ctx) => { // Esta query pode ser usada por um componente de notificação ou cron job // Retorna contratos que estão próximos do vencimento baseados no diasAvisoVencimento const hoje = new Date(); const hojeStr = hoje.toISOString().split("T")[0]; // Buscar contratos ativos (em execução ou aguardando assinatura) const contratos = await ctx.db .query("contratos") .filter((q) => q.or( q.eq(q.field("situacao"), "em_execucao"), q.eq(q.field("situacao"), "aguardando_assinatura") ) ) .collect(); const proximosVencimento = contratos.filter((c) => { if (!c.dataFimVigencia) return false; const dataFim = new Date(c.dataFimVigencia); const dataAviso = new Date(dataFim); dataAviso.setDate(dataAviso.getDate() - c.diasAvisoVencimento); const dataAvisoStr = dataAviso.toISOString().split("T")[0]; // Se hoje for maior ou igual a data de aviso e menor que a data fim return hojeStr >= dataAvisoStr && hojeStr <= c.dataFimVigencia; }); return proximosVencimento; }, });