feat: Introduce structured table definitions in convex/tables for various entities and remove the todos example table.

This commit is contained in:
2025-12-02 09:55:07 -03:00
parent 1c0bd219b2
commit 05e7f1181d
30 changed files with 2700 additions and 2535 deletions

View File

@@ -1,200 +1,198 @@
import { mutation, query } from "./_generated/server";
import { v } from "convex/values";
import { situacaoContrato } from "./schema";
import { getCurrentUserFunction } from "./auth";
import { internal } from "./_generated/api";
import { mutation, query } from './_generated/server';
import { v } from 'convex/values';
import { situacaoContrato } from './tables/contratos';
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",
});
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");
let q = ctx.db.query('contratos');
if (args.responsavelId) {
q = q.withIndex("by_responsavel", (q) =>
q.eq("responsavelId", args.responsavelId!)
) as typeof q;
}
if (args.responsavelId) {
q = q.withIndex('by_responsavel', (q) =>
q.eq('responsavelId', args.responsavelId!)
) as typeof q;
}
const contratos = await q.collect();
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;
// 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.dataInicio) {
resultado = resultado.filter((c) => c.dataInicioVigencia >= args.dataInicio!);
}
if (args.dataFim) {
resultado = resultado.filter((c) => c.dataFimVigencia <= args.dataFim!);
}
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,
};
})
);
// 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;
},
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;
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);
const contratada = await ctx.db.get(contrato.contratadaId);
const responsavel = await ctx.db.get(contrato.responsavelId);
return {
...contrato,
contratada,
responsavel,
};
},
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",
});
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 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(),
});
const id = await ctx.db.insert('contratos', {
...args,
criadoPor: usuario._id,
criadoEm: Date.now()
});
return id;
},
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",
});
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;
const { id, ...campos } = args;
await ctx.db.patch(id, {
...campos,
atualizadoEm: Date.now(),
});
},
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);
},
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
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];
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();
// 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 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 dataFim = new Date(c.dataFimVigencia);
const dataAviso = new Date(dataFim);
dataAviso.setDate(dataAviso.getDate() - c.diasAvisoVencimento);
const dataAvisoStr = dataAviso.toISOString().split("T")[0];
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;
});
// Se hoje for maior ou igual a data de aviso e menor que a data fim
return hojeStr >= dataAvisoStr && hojeStr <= c.dataFimVigencia;
});
return proximosVencimento;
},
return proximosVencimento;
}
});