refactor: update vacation management structure and enhance status handling
- Renamed and refactored vacation-related types and components for clarity, transitioning from 'SolicitacaoFerias' to 'PeriodoFerias'. - Improved the handling of vacation statuses, including the addition of 'EmFérias' to the status options. - Streamlined the vacation request and approval components to better reflect individual vacation periods. - Enhanced data handling in backend queries and schema to support the new structure and ensure accurate status updates. - Improved user experience by refining UI elements related to vacation periods and their statuses.
This commit is contained in:
@@ -229,23 +229,20 @@ export const obterDadosGraficos = query({
|
||||
|
||||
// Buscar férias do período
|
||||
try {
|
||||
const solicitacoesFerias = await ctx.db
|
||||
.query('solicitacoesFerias')
|
||||
const ferias = await ctx.db
|
||||
.query('ferias')
|
||||
.filter((q) =>
|
||||
q.or(
|
||||
q.eq(q.field('status'), 'aprovado'),
|
||||
q.eq(q.field('status'), 'data_ajustada_aprovada')
|
||||
q.eq(q.field('status'), 'data_ajustada_aprovada'),
|
||||
q.eq(q.field('status'), 'EmFérias')
|
||||
)
|
||||
)
|
||||
.collect();
|
||||
|
||||
solicitacoesFerias.forEach((s) => {
|
||||
if (s.periodos && Array.isArray(s.periodos)) {
|
||||
s.periodos.forEach((p: { dataInicio: string; dataFim: string }) => {
|
||||
const dias = calcularDias(p.dataInicio, p.dataFim);
|
||||
totalDiasPorTipo.ferias += dias;
|
||||
});
|
||||
}
|
||||
ferias.forEach((f) => {
|
||||
const dias = calcularDias(f.dataInicio, f.dataFim);
|
||||
totalDiasPorTipo.ferias += dias;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar férias para gráfico:', error);
|
||||
@@ -619,48 +616,42 @@ export const obterEventosCalendario = query({
|
||||
// Integrar com férias (se não estiver filtrando por tipo específico)
|
||||
if (!args.tipoFiltro || args.tipoFiltro === 'todos' || args.tipoFiltro === 'ferias') {
|
||||
try {
|
||||
// Buscar solicitações de férias aprovadas
|
||||
const solicitacoesFerias = await ctx.db
|
||||
.query('solicitacoesFerias')
|
||||
// Buscar férias aprovadas
|
||||
const ferias = await ctx.db
|
||||
.query('ferias')
|
||||
.filter((q) =>
|
||||
q.or(
|
||||
q.eq(q.field('status'), 'aprovado'),
|
||||
q.eq(q.field('status'), 'data_ajustada_aprovada')
|
||||
q.eq(q.field('status'), 'data_ajustada_aprovada'),
|
||||
q.eq(q.field('status'), 'EmFérias')
|
||||
)
|
||||
)
|
||||
.collect();
|
||||
|
||||
for (const solicitacao of solicitacoesFerias) {
|
||||
for (const feriasRegistro of ferias) {
|
||||
try {
|
||||
const funcionario = await ctx.db.get(solicitacao.funcionarioId);
|
||||
const funcionario = await ctx.db.get(feriasRegistro.funcionarioId);
|
||||
if (!funcionario) continue;
|
||||
|
||||
// Verificar se periodos existe e é um array
|
||||
if (!solicitacao.periodos || !Array.isArray(solicitacao.periodos)) {
|
||||
continue;
|
||||
}
|
||||
if (!feriasRegistro.dataInicio || !feriasRegistro.dataFim) continue;
|
||||
|
||||
for (const periodo of solicitacao.periodos) {
|
||||
if (!periodo.dataInicio || !periodo.dataFim) continue;
|
||||
|
||||
eventos.push({
|
||||
id: `ferias-${solicitacao._id}-${periodo.dataInicio}`,
|
||||
title: `${funcionario.nome} - Férias`,
|
||||
start: periodo.dataInicio,
|
||||
end: periodo.dataFim,
|
||||
color: '#10b981', // verde
|
||||
tipo: 'ferias',
|
||||
funcionarioNome: funcionario.nome,
|
||||
funcionarioId: funcionario._id
|
||||
});
|
||||
}
|
||||
eventos.push({
|
||||
id: `ferias-${feriasRegistro._id}`,
|
||||
title: `${funcionario.nome} - Férias`,
|
||||
start: feriasRegistro.dataInicio,
|
||||
end: feriasRegistro.dataFim,
|
||||
color: '#10b981', // verde
|
||||
tipo: 'ferias',
|
||||
funcionarioNome: funcionario.nome,
|
||||
funcionarioId: funcionario._id
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Erro ao processar solicitação de férias ${solicitacao._id}:`, error);
|
||||
console.error(`Erro ao processar férias ${feriasRegistro._id}:`, error);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar solicitações de férias:', error);
|
||||
console.error('Erro ao buscar férias:', error);
|
||||
// Continua mesmo se houver erro ao buscar férias
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,13 +32,6 @@ crons.interval(
|
||||
{}
|
||||
);
|
||||
|
||||
// Criar períodos aquisitivos de férias automaticamente (diariamente)
|
||||
crons.interval(
|
||||
"criar-periodos-aquisitivos",
|
||||
{ hours: 24 },
|
||||
internal.saldoFerias.criarPeriodosAquisitivos,
|
||||
{}
|
||||
);
|
||||
|
||||
export default crons;
|
||||
|
||||
|
||||
@@ -10,22 +10,81 @@ const periodoValidator = v.object({
|
||||
diasCorridos: v.number(),
|
||||
});
|
||||
|
||||
// Query: Listar TODAS as solicitações (para RH)
|
||||
// Retorna tipo inferido automaticamente pelo Convex
|
||||
// Helper: Calcular dias entre duas datas
|
||||
function calcularDiasEntreDatas(dataInicio: string, dataFim: string): number {
|
||||
const inicio = new Date(dataInicio);
|
||||
const fim = new Date(dataFim);
|
||||
const diffTime = Math.abs(fim.getTime() - inicio.getTime());
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1;
|
||||
return diffDays;
|
||||
}
|
||||
|
||||
// Helper: Agrupar registros de ferias por funcionarioId + anoReferencia
|
||||
function agruparPorSolicitacao(
|
||||
registros: Array<Doc<"ferias">>
|
||||
): Array<{
|
||||
funcionarioId: Id<"funcionarios">;
|
||||
anoReferencia: number;
|
||||
periodos: Array<Doc<"ferias">>;
|
||||
status: string;
|
||||
observacao?: string;
|
||||
motivoReprovacao?: string;
|
||||
gestorId?: Id<"usuarios">;
|
||||
dataAprovacao?: number;
|
||||
dataReprovacao?: number;
|
||||
historicoAlteracoes?: Array<{
|
||||
data: number;
|
||||
usuarioId: Id<"usuarios">;
|
||||
acao: string;
|
||||
}>;
|
||||
}> {
|
||||
const grupos = new Map<string, Array<Doc<"ferias">>>();
|
||||
|
||||
for (const registro of registros) {
|
||||
const chave = `${registro.funcionarioId}_${registro.anoReferencia}`;
|
||||
if (!grupos.has(chave)) {
|
||||
grupos.set(chave, []);
|
||||
}
|
||||
grupos.get(chave)!.push(registro);
|
||||
}
|
||||
|
||||
return Array.from(grupos.entries()).map(([_, periodos]) => {
|
||||
// Ordenar por data de criação para manter ordem
|
||||
periodos.sort((a, b) => a._creationTime - b._creationTime);
|
||||
|
||||
// Pegar informações da primeira solicitação (todos têm os mesmos campos compartilhados)
|
||||
const primeiro = periodos[0];
|
||||
|
||||
return {
|
||||
funcionarioId: primeiro.funcionarioId,
|
||||
anoReferencia: primeiro.anoReferencia,
|
||||
periodos,
|
||||
status: primeiro.status,
|
||||
observacao: primeiro.observacao,
|
||||
motivoReprovacao: primeiro.motivoReprovacao,
|
||||
gestorId: primeiro.gestorId,
|
||||
dataAprovacao: primeiro.dataAprovacao,
|
||||
dataReprovacao: primeiro.dataReprovacao,
|
||||
historicoAlteracoes: primeiro.historicoAlteracoes,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Query: Listar TODAS as solicitações (para RH) - períodos individuais
|
||||
export const listarTodas = query({
|
||||
args: {},
|
||||
handler: async (ctx) => {
|
||||
const solicitacoes = await ctx.db.query("solicitacoesFerias").collect();
|
||||
const todasFerias = await ctx.db.query("ferias").collect();
|
||||
|
||||
const solicitacoesComDetalhes = await Promise.all(
|
||||
solicitacoes.map(async (s) => {
|
||||
const funcionario = await ctx.db.get(s.funcionarioId);
|
||||
const periodosComDetalhes = await Promise.all(
|
||||
todasFerias.map(async (ferias) => {
|
||||
const funcionario = await ctx.db.get(ferias.funcionarioId);
|
||||
|
||||
// Buscar time do funcionário
|
||||
const membroTime = await ctx.db
|
||||
.query("timesMembros")
|
||||
.withIndex("by_funcionario", (q) =>
|
||||
q.eq("funcionarioId", s.funcionarioId)
|
||||
q.eq("funcionarioId", ferias.funcionarioId)
|
||||
)
|
||||
.filter((q) => q.eq(q.field("ativo"), true))
|
||||
.first();
|
||||
@@ -36,65 +95,54 @@ export const listarTodas = query({
|
||||
}
|
||||
|
||||
return {
|
||||
...s,
|
||||
...ferias,
|
||||
funcionario,
|
||||
time,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return solicitacoesComDetalhes.sort(
|
||||
(a, b) => b._creationTime - a._creationTime
|
||||
);
|
||||
return periodosComDetalhes.sort((a, b) => b._creationTime - a._creationTime);
|
||||
},
|
||||
});
|
||||
|
||||
// Query: Listar solicitações do funcionário
|
||||
// Query: Listar solicitações do funcionário - períodos individuais
|
||||
export const listarMinhasSolicitacoes = query({
|
||||
args: { funcionarioId: v.id("funcionarios") },
|
||||
// returns não especificado - TypeScript inferirá automaticamente o tipo correto
|
||||
handler: async (ctx, args) => {
|
||||
const solicitacoes = await ctx.db
|
||||
.query("solicitacoesFerias")
|
||||
const todasFerias = await ctx.db
|
||||
.query("ferias")
|
||||
.withIndex("by_funcionario", (q) =>
|
||||
q.eq("funcionarioId", args.funcionarioId)
|
||||
)
|
||||
.order("desc")
|
||||
.collect();
|
||||
|
||||
// Enriquecer com dados do funcionário e time
|
||||
const solicitacoesComDetalhes = await Promise.all(
|
||||
solicitacoes.map(async (s) => {
|
||||
const funcionario = await ctx.db.get(s.funcionarioId);
|
||||
|
||||
// Buscar time do funcionário
|
||||
const membroTime = await ctx.db
|
||||
.query("timesMembros")
|
||||
.withIndex("by_funcionario", (q) =>
|
||||
q.eq("funcionarioId", s.funcionarioId)
|
||||
)
|
||||
.filter((q) => q.eq(q.field("ativo"), true))
|
||||
.first();
|
||||
const funcionario = await ctx.db.get(args.funcionarioId);
|
||||
|
||||
// Buscar time do funcionário
|
||||
const membroTime = await ctx.db
|
||||
.query("timesMembros")
|
||||
.withIndex("by_funcionario", (q) =>
|
||||
q.eq("funcionarioId", args.funcionarioId)
|
||||
)
|
||||
.filter((q) => q.eq(q.field("ativo"), true))
|
||||
.first();
|
||||
|
||||
let time = null;
|
||||
if (membroTime) {
|
||||
time = await ctx.db.get(membroTime.timeId);
|
||||
}
|
||||
let time = null;
|
||||
if (membroTime) {
|
||||
time = await ctx.db.get(membroTime.timeId);
|
||||
}
|
||||
|
||||
return {
|
||||
...s,
|
||||
funcionario,
|
||||
time,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return solicitacoesComDetalhes;
|
||||
// Retornar períodos individuais com detalhes
|
||||
return todasFerias.map((ferias) => ({
|
||||
...ferias,
|
||||
funcionario,
|
||||
time,
|
||||
})).sort((a, b) => b._creationTime - a._creationTime);
|
||||
},
|
||||
});
|
||||
|
||||
// Query: Listar solicitações dos subordinados (para gestores)
|
||||
// Retorna tipo inferido automaticamente pelo Convex
|
||||
// Query: Listar solicitações dos subordinados (para gestores) - períodos individuais
|
||||
export const listarSolicitacoesSubordinados = query({
|
||||
args: { gestorId: v.id("usuarios") },
|
||||
handler: async (ctx, args) => {
|
||||
@@ -105,10 +153,7 @@ export const listarSolicitacoesSubordinados = query({
|
||||
.filter((q) => q.eq(q.field("ativo"), true))
|
||||
.collect();
|
||||
|
||||
const solicitacoes: Array<Doc<"solicitacoesFerias"> & {
|
||||
funcionario: Doc<"funcionarios"> | null;
|
||||
time: Doc<"times"> | null;
|
||||
}> = [];
|
||||
const todasFerias: Array<Doc<"ferias">> = [];
|
||||
|
||||
for (const time of timesGestor) {
|
||||
// Buscar membros do time
|
||||
@@ -119,54 +164,90 @@ export const listarSolicitacoesSubordinados = query({
|
||||
)
|
||||
.collect();
|
||||
|
||||
// Buscar solicitações de cada membro
|
||||
// Buscar férias de cada membro
|
||||
for (const membro of membros) {
|
||||
const solic = await ctx.db
|
||||
.query("solicitacoesFerias")
|
||||
const ferias = await ctx.db
|
||||
.query("ferias")
|
||||
.withIndex("by_funcionario", (q) =>
|
||||
q.eq("funcionarioId", membro.funcionarioId)
|
||||
)
|
||||
.collect();
|
||||
|
||||
// Adicionar info do funcionário
|
||||
for (const s of solic) {
|
||||
const funcionario = await ctx.db.get(s.funcionarioId);
|
||||
solicitacoes.push({
|
||||
...s,
|
||||
funcionario,
|
||||
time,
|
||||
});
|
||||
}
|
||||
todasFerias.push(...ferias);
|
||||
}
|
||||
}
|
||||
|
||||
return solicitacoes.sort((a, b) => b._creationTime - a._creationTime);
|
||||
// Adicionar info do funcionário e time para cada período
|
||||
const periodosComDetalhes = await Promise.all(
|
||||
todasFerias.map(async (ferias) => {
|
||||
const funcionario = await ctx.db.get(ferias.funcionarioId);
|
||||
|
||||
// Buscar time do funcionário
|
||||
const membroTime = await ctx.db
|
||||
.query("timesMembros")
|
||||
.withIndex("by_funcionario", (q) =>
|
||||
q.eq("funcionarioId", ferias.funcionarioId)
|
||||
)
|
||||
.filter((q) => q.eq(q.field("ativo"), true))
|
||||
.first();
|
||||
|
||||
let time = null;
|
||||
if (membroTime) {
|
||||
time = await ctx.db.get(membroTime.timeId);
|
||||
}
|
||||
|
||||
return {
|
||||
...ferias,
|
||||
funcionario,
|
||||
time,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return periodosComDetalhes.sort((a, b) => b._creationTime - a._creationTime);
|
||||
},
|
||||
});
|
||||
|
||||
// Query: Obter detalhes completos de uma solicitação
|
||||
// Retorna tipo inferido automaticamente pelo Convex
|
||||
// Query: Obter detalhes de um período individual
|
||||
export const obterDetalhes = query({
|
||||
args: { solicitacaoId: v.id("solicitacoesFerias") },
|
||||
args: {
|
||||
feriasId: v.id("ferias")
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const solicitacao = await ctx.db.get(args.solicitacaoId);
|
||||
if (!solicitacao) return null;
|
||||
const ferias = await ctx.db.get(args.feriasId);
|
||||
|
||||
const funcionario = await ctx.db.get(solicitacao.funcionarioId);
|
||||
if (!ferias) return null;
|
||||
|
||||
const funcionario = await ctx.db.get(ferias.funcionarioId);
|
||||
let gestor = null;
|
||||
if (solicitacao.gestorId) {
|
||||
gestor = await ctx.db.get(solicitacao.gestorId);
|
||||
if (ferias.gestorId) {
|
||||
gestor = await ctx.db.get(ferias.gestorId);
|
||||
}
|
||||
|
||||
// Buscar time do funcionário
|
||||
const membroTime = await ctx.db
|
||||
.query("timesMembros")
|
||||
.withIndex("by_funcionario", (q) =>
|
||||
q.eq("funcionarioId", ferias.funcionarioId)
|
||||
)
|
||||
.filter((q) => q.eq(q.field("ativo"), true))
|
||||
.first();
|
||||
|
||||
let time = null;
|
||||
if (membroTime) {
|
||||
time = await ctx.db.get(membroTime.timeId);
|
||||
}
|
||||
|
||||
return {
|
||||
...solicitacao,
|
||||
...ferias,
|
||||
funcionario,
|
||||
gestor,
|
||||
time,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
// Mutation: Criar solicitação de férias (com validação de saldo)
|
||||
// Mutation: Criar solicitação de férias (cria um registro por período)
|
||||
export const criarSolicitacao = mutation({
|
||||
args: {
|
||||
funcionarioId: v.id("funcionarios"),
|
||||
@@ -174,7 +255,7 @@ export const criarSolicitacao = mutation({
|
||||
periodos: v.array(periodoValidator),
|
||||
observacao: v.optional(v.string()),
|
||||
},
|
||||
returns: v.id("solicitacoesFerias"),
|
||||
returns: v.array(v.id("ferias")),
|
||||
handler: async (ctx, args) => {
|
||||
if (args.periodos.length === 0) {
|
||||
throw new Error("É necessário adicionar pelo menos 1 período");
|
||||
@@ -183,19 +264,6 @@ export const criarSolicitacao = mutation({
|
||||
const funcionario = await ctx.db.get(args.funcionarioId);
|
||||
if (!funcionario) throw new Error("Funcionário não encontrado");
|
||||
|
||||
// Calcular total de dias
|
||||
let totalDias = 0;
|
||||
for (const p of args.periodos) {
|
||||
totalDias += p.diasCorridos;
|
||||
}
|
||||
|
||||
// Reservar dias no saldo (impede uso duplo)
|
||||
await ctx.runMutation(internal.saldoFerias.reservarDias, {
|
||||
funcionarioId: args.funcionarioId,
|
||||
anoReferencia: args.anoReferencia,
|
||||
totalDias,
|
||||
});
|
||||
|
||||
// Buscar usuário que está criando (pode não ser o próprio funcionário)
|
||||
const usuario = await ctx.db
|
||||
.query("usuarios")
|
||||
@@ -204,59 +272,75 @@ export const criarSolicitacao = mutation({
|
||||
)
|
||||
.first();
|
||||
|
||||
const solicitacaoId = await ctx.db.insert("solicitacoesFerias", {
|
||||
funcionarioId: args.funcionarioId,
|
||||
anoReferencia: args.anoReferencia,
|
||||
status: "aguardando_aprovacao",
|
||||
periodos: args.periodos,
|
||||
observacao: args.observacao,
|
||||
historicoAlteracoes: [
|
||||
{
|
||||
data: Date.now(),
|
||||
usuarioId: usuario?._id || funcionario.gestorId!,
|
||||
acao: "Solicitação criada",
|
||||
},
|
||||
],
|
||||
});
|
||||
const historicoInicial = [
|
||||
{
|
||||
data: Date.now(),
|
||||
usuarioId: usuario?._id || funcionario.gestorId!,
|
||||
acao: "Solicitação criada",
|
||||
},
|
||||
];
|
||||
|
||||
// Notificar gestor
|
||||
if (funcionario.gestorId) {
|
||||
// Criar um registro para cada período
|
||||
const idsCriados: Array<Id<"ferias">> = [];
|
||||
for (const periodo of args.periodos) {
|
||||
const feriasId = await ctx.db.insert("ferias", {
|
||||
funcionarioId: args.funcionarioId,
|
||||
anoReferencia: args.anoReferencia,
|
||||
dataInicio: periodo.dataInicio,
|
||||
dataFim: periodo.dataFim,
|
||||
diasFerias: periodo.diasCorridos,
|
||||
status: "aguardando_aprovacao",
|
||||
observacao: args.observacao,
|
||||
diasAbono: 0,
|
||||
historicoAlteracoes: historicoInicial,
|
||||
});
|
||||
idsCriados.push(feriasId);
|
||||
}
|
||||
|
||||
// Notificar gestor (usar o primeiro ID criado)
|
||||
if (funcionario.gestorId && idsCriados.length > 0) {
|
||||
await ctx.db.insert("notificacoesFerias", {
|
||||
destinatarioId: funcionario.gestorId,
|
||||
solicitacaoFeriasId: solicitacaoId,
|
||||
feriasId: idsCriados[0],
|
||||
tipo: "nova_solicitacao",
|
||||
lida: false,
|
||||
mensagem: `${funcionario.nome} solicitou férias`,
|
||||
});
|
||||
}
|
||||
|
||||
return solicitacaoId;
|
||||
return idsCriados;
|
||||
},
|
||||
});
|
||||
|
||||
// Mutation: Aprovar férias
|
||||
// Mutation: Aprovar período de férias individual
|
||||
export const aprovar = mutation({
|
||||
args: {
|
||||
solicitacaoId: v.id("solicitacoesFerias"),
|
||||
feriasId: v.id("ferias"),
|
||||
gestorId: v.id("usuarios"),
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const solicitacao = await ctx.db.get(args.solicitacaoId);
|
||||
if (!solicitacao) throw new Error("Solicitação não encontrada");
|
||||
// Buscar o registro específico
|
||||
const registro = await ctx.db.get(args.feriasId);
|
||||
|
||||
if (solicitacao.status !== "aguardando_aprovacao") {
|
||||
throw new Error("Esta solicitação já foi processada");
|
||||
if (!registro) {
|
||||
throw new Error("Período de férias não encontrado");
|
||||
}
|
||||
|
||||
const funcionario = await ctx.db.get(solicitacao.funcionarioId);
|
||||
// Verificar se está aguardando aprovação
|
||||
if (registro.status !== "aguardando_aprovacao") {
|
||||
throw new Error("Este período já foi processado");
|
||||
}
|
||||
|
||||
await ctx.db.patch(args.solicitacaoId, {
|
||||
const funcionario = await ctx.db.get(registro.funcionarioId);
|
||||
|
||||
// Atualizar o registro
|
||||
await ctx.db.patch(registro._id, {
|
||||
status: "aprovado",
|
||||
gestorId: args.gestorId,
|
||||
dataAprovacao: Date.now(),
|
||||
historicoAlteracoes: [
|
||||
...(solicitacao.historicoAlteracoes || []),
|
||||
...(registro.historicoAlteracoes || []),
|
||||
{
|
||||
data: Date.now(),
|
||||
usuarioId: args.gestorId,
|
||||
@@ -265,11 +349,6 @@ export const aprovar = mutation({
|
||||
],
|
||||
});
|
||||
|
||||
// Atualizar saldo (de pendente para usado)
|
||||
await ctx.runMutation(internal.saldoFerias.atualizarSaldoAposAprovacao, {
|
||||
solicitacaoId: args.solicitacaoId,
|
||||
});
|
||||
|
||||
// Notificar funcionário
|
||||
if (funcionario) {
|
||||
const usuario = await ctx.db
|
||||
@@ -282,10 +361,10 @@ export const aprovar = mutation({
|
||||
if (usuario) {
|
||||
await ctx.db.insert("notificacoesFerias", {
|
||||
destinatarioId: usuario._id,
|
||||
solicitacaoFeriasId: args.solicitacaoId,
|
||||
feriasId: registro._id,
|
||||
tipo: "aprovado",
|
||||
lida: false,
|
||||
mensagem: "Suas férias foram aprovadas!",
|
||||
mensagem: `Período de férias de ${registro.diasFerias} dias foi aprovado!`,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -294,31 +373,37 @@ export const aprovar = mutation({
|
||||
},
|
||||
});
|
||||
|
||||
// Mutation: Reprovar férias
|
||||
// Mutation: Reprovar período de férias individual
|
||||
export const reprovar = mutation({
|
||||
args: {
|
||||
solicitacaoId: v.id("solicitacoesFerias"),
|
||||
feriasId: v.id("ferias"),
|
||||
gestorId: v.id("usuarios"),
|
||||
motivoReprovacao: v.string(),
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const solicitacao = await ctx.db.get(args.solicitacaoId);
|
||||
if (!solicitacao) throw new Error("Solicitação não encontrada");
|
||||
// Buscar o registro específico
|
||||
const registro = await ctx.db.get(args.feriasId);
|
||||
|
||||
if (solicitacao.status !== "aguardando_aprovacao") {
|
||||
throw new Error("Esta solicitação já foi processada");
|
||||
if (!registro) {
|
||||
throw new Error("Período de férias não encontrado");
|
||||
}
|
||||
|
||||
const funcionario = await ctx.db.get(solicitacao.funcionarioId);
|
||||
// Verificar se está aguardando aprovação
|
||||
if (registro.status !== "aguardando_aprovacao") {
|
||||
throw new Error("Este período já foi processado");
|
||||
}
|
||||
|
||||
await ctx.db.patch(args.solicitacaoId, {
|
||||
const funcionario = await ctx.db.get(registro.funcionarioId);
|
||||
|
||||
// Atualizar o registro
|
||||
await ctx.db.patch(registro._id, {
|
||||
status: "reprovado",
|
||||
gestorId: args.gestorId,
|
||||
dataReprovacao: Date.now(),
|
||||
motivoReprovacao: args.motivoReprovacao,
|
||||
historicoAlteracoes: [
|
||||
...(solicitacao.historicoAlteracoes || []),
|
||||
...(registro.historicoAlteracoes || []),
|
||||
{
|
||||
data: Date.now(),
|
||||
usuarioId: args.gestorId,
|
||||
@@ -327,11 +412,6 @@ export const reprovar = mutation({
|
||||
],
|
||||
});
|
||||
|
||||
// Liberar dias reservados de volta ao saldo
|
||||
await ctx.runMutation(internal.saldoFerias.liberarDias, {
|
||||
solicitacaoId: args.solicitacaoId,
|
||||
});
|
||||
|
||||
// Notificar funcionário
|
||||
if (funcionario) {
|
||||
const usuario = await ctx.db
|
||||
@@ -344,10 +424,10 @@ export const reprovar = mutation({
|
||||
if (usuario) {
|
||||
await ctx.db.insert("notificacoesFerias", {
|
||||
destinatarioId: usuario._id,
|
||||
solicitacaoFeriasId: args.solicitacaoId,
|
||||
feriasId: registro._id,
|
||||
tipo: "reprovado",
|
||||
lida: false,
|
||||
mensagem: `Suas férias foram reprovadas: ${args.motivoReprovacao}`,
|
||||
mensagem: `Período de férias de ${registro.diasFerias} dias foi reprovado: ${args.motivoReprovacao}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -356,66 +436,51 @@ export const reprovar = mutation({
|
||||
},
|
||||
});
|
||||
|
||||
// Mutation: Ajustar data e aprovar
|
||||
// Mutation: Ajustar data e aprovar período individual
|
||||
export const ajustarEAprovar = mutation({
|
||||
args: {
|
||||
solicitacaoId: v.id("solicitacoesFerias"),
|
||||
feriasId: v.id("ferias"),
|
||||
gestorId: v.id("usuarios"),
|
||||
novosPeriodos: v.array(periodoValidator),
|
||||
novaDataInicio: v.string(),
|
||||
novaDataFim: v.string(),
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const solicitacao = await ctx.db.get(args.solicitacaoId);
|
||||
if (!solicitacao) throw new Error("Solicitação não encontrada");
|
||||
// Buscar o registro específico
|
||||
const registroAntigo = await ctx.db.get(args.feriasId);
|
||||
|
||||
if (solicitacao.status !== "aguardando_aprovacao") {
|
||||
throw new Error("Esta solicitação já foi processada");
|
||||
if (!registroAntigo) {
|
||||
throw new Error("Período de férias não encontrado");
|
||||
}
|
||||
|
||||
if (args.novosPeriodos.length === 0) {
|
||||
throw new Error("É necessário adicionar pelo menos 1 período");
|
||||
// Verificar se está aguardando aprovação
|
||||
if (registroAntigo.status !== "aguardando_aprovacao") {
|
||||
throw new Error("Este período já foi processado");
|
||||
}
|
||||
|
||||
const funcionario = await ctx.db.get(solicitacao.funcionarioId);
|
||||
const funcionario = await ctx.db.get(registroAntigo.funcionarioId);
|
||||
|
||||
// Liberar dias antigos
|
||||
await ctx.runMutation(internal.saldoFerias.liberarDias, {
|
||||
solicitacaoId: args.solicitacaoId,
|
||||
});
|
||||
// Calcular novos dias
|
||||
const novosDias = calcularDiasEntreDatas(args.novaDataInicio, args.novaDataFim);
|
||||
|
||||
// Calcular novos dias e reservar
|
||||
let totalNovosDias = 0;
|
||||
for (const p of args.novosPeriodos) {
|
||||
totalNovosDias += p.diasCorridos;
|
||||
}
|
||||
|
||||
await ctx.runMutation(internal.saldoFerias.reservarDias, {
|
||||
funcionarioId: solicitacao.funcionarioId,
|
||||
anoReferencia: solicitacao.anoReferencia,
|
||||
totalDias: totalNovosDias,
|
||||
});
|
||||
|
||||
await ctx.db.patch(args.solicitacaoId, {
|
||||
// Atualizar o registro com novas datas
|
||||
await ctx.db.patch(registroAntigo._id, {
|
||||
dataInicio: args.novaDataInicio,
|
||||
dataFim: args.novaDataFim,
|
||||
diasFerias: novosDias,
|
||||
status: "data_ajustada_aprovada",
|
||||
periodos: args.novosPeriodos,
|
||||
gestorId: args.gestorId,
|
||||
dataAprovacao: Date.now(),
|
||||
historicoAlteracoes: [
|
||||
...(solicitacao.historicoAlteracoes || []),
|
||||
...(registroAntigo.historicoAlteracoes || []),
|
||||
{
|
||||
data: Date.now(),
|
||||
usuarioId: args.gestorId,
|
||||
acao: "Data ajustada e aprovada",
|
||||
periodosAnteriores: solicitacao.periodos,
|
||||
acao: `Data ajustada e aprovada: ${registroAntigo.dataInicio} - ${registroAntigo.dataFim} → ${args.novaDataInicio} - ${args.novaDataFim}`,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Atualizar saldo (marcar como usado)
|
||||
await ctx.runMutation(internal.saldoFerias.atualizarSaldoAposAprovacao, {
|
||||
solicitacaoId: args.solicitacaoId,
|
||||
});
|
||||
|
||||
// Notificar funcionário
|
||||
if (funcionario) {
|
||||
const usuario = await ctx.db
|
||||
@@ -428,10 +493,10 @@ export const ajustarEAprovar = mutation({
|
||||
if (usuario) {
|
||||
await ctx.db.insert("notificacoesFerias", {
|
||||
destinatarioId: usuario._id,
|
||||
solicitacaoFeriasId: args.solicitacaoId,
|
||||
feriasId: registroAntigo._id,
|
||||
tipo: "data_ajustada",
|
||||
lida: false,
|
||||
mensagem: "Suas férias foram aprovadas com ajuste de datas",
|
||||
mensagem: `Período de férias foi aprovado com ajuste de datas: ${args.novaDataInicio} a ${args.novaDataFim}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -448,15 +513,15 @@ export const verificarStatusFerias = query({
|
||||
const hoje = new Date();
|
||||
hoje.setHours(0, 0, 0, 0);
|
||||
|
||||
const solicitacoesAprovadas = await ctx.db
|
||||
.query("solicitacoesFerias")
|
||||
const feriasAprovadas = await ctx.db
|
||||
.query("ferias")
|
||||
.withIndex("by_funcionario_and_status", (q) =>
|
||||
q.eq("funcionarioId", args.funcionarioId).eq("status", "aprovado")
|
||||
)
|
||||
.collect();
|
||||
|
||||
const solicitacoesAjustadas = await ctx.db
|
||||
.query("solicitacoesFerias")
|
||||
const feriasAjustadas = await ctx.db
|
||||
.query("ferias")
|
||||
.withIndex("by_funcionario_and_status", (q) =>
|
||||
q
|
||||
.eq("funcionarioId", args.funcionarioId)
|
||||
@@ -464,21 +529,27 @@ export const verificarStatusFerias = query({
|
||||
)
|
||||
.collect();
|
||||
|
||||
const todasSolicitacoes = [
|
||||
...solicitacoesAprovadas,
|
||||
...solicitacoesAjustadas,
|
||||
const feriasEmFerias = await ctx.db
|
||||
.query("ferias")
|
||||
.withIndex("by_funcionario_and_status", (q) =>
|
||||
q.eq("funcionarioId", args.funcionarioId).eq("status", "EmFérias")
|
||||
)
|
||||
.collect();
|
||||
|
||||
const todasFerias = [
|
||||
...feriasAprovadas,
|
||||
...feriasAjustadas,
|
||||
...feriasEmFerias,
|
||||
];
|
||||
|
||||
for (const solicitacao of todasSolicitacoes) {
|
||||
for (const periodo of solicitacao.periodos) {
|
||||
const inicio = new Date(periodo.dataInicio);
|
||||
const fim = new Date(periodo.dataFim);
|
||||
inicio.setHours(0, 0, 0, 0);
|
||||
fim.setHours(23, 59, 59, 999);
|
||||
for (const ferias of todasFerias) {
|
||||
const inicio = new Date(ferias.dataInicio);
|
||||
const fim = new Date(ferias.dataFim);
|
||||
inicio.setHours(0, 0, 0, 0);
|
||||
fim.setHours(23, 59, 59, 999);
|
||||
|
||||
if (hoje >= inicio && hoje <= fim) {
|
||||
return "em_ferias";
|
||||
}
|
||||
if (hoje >= inicio && hoje <= fim) {
|
||||
return "em_ferias";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,10 +580,10 @@ export const marcarComoLida = mutation({
|
||||
},
|
||||
});
|
||||
|
||||
// Mutation: Atualizar status da solicitação (para voltar para aguardando_aprovacao)
|
||||
// Mutation: Atualizar status de um período individual
|
||||
export const atualizarStatus = mutation({
|
||||
args: {
|
||||
solicitacaoId: v.id("solicitacoesFerias"),
|
||||
feriasId: v.id("ferias"),
|
||||
novoStatus: v.union(
|
||||
v.literal("aguardando_aprovacao"),
|
||||
v.literal("aprovado"),
|
||||
@@ -523,40 +594,16 @@ export const atualizarStatus = mutation({
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const solicitacao = await ctx.db.get(args.solicitacaoId);
|
||||
if (!solicitacao) throw new Error("Solicitação não encontrada");
|
||||
// Buscar o registro específico
|
||||
const registro = await ctx.db.get(args.feriasId);
|
||||
|
||||
// Se está mudando de aprovado para aguardando_aprovacao, precisa liberar os dias
|
||||
if (solicitacao.status === "aprovado" || solicitacao.status === "data_ajustada_aprovada") {
|
||||
if (args.novoStatus === "aguardando_aprovacao") {
|
||||
// Liberar dias de volta ao saldo
|
||||
await ctx.runMutation(internal.saldoFerias.liberarDias, {
|
||||
solicitacaoId: args.solicitacaoId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Se está mudando de reprovado para aguardando_aprovacao, os dias já foram liberados anteriormente
|
||||
// Mas precisamos reservar novamente
|
||||
if (solicitacao.status === "reprovado" && args.novoStatus === "aguardando_aprovacao") {
|
||||
// Calcular total de dias
|
||||
let totalDias = 0;
|
||||
for (const p of solicitacao.periodos) {
|
||||
totalDias += p.diasCorridos;
|
||||
}
|
||||
|
||||
// Reservar dias novamente
|
||||
await ctx.runMutation(internal.saldoFerias.reservarDias, {
|
||||
funcionarioId: solicitacao.funcionarioId,
|
||||
anoReferencia: solicitacao.anoReferencia,
|
||||
totalDias,
|
||||
});
|
||||
if (!registro) {
|
||||
throw new Error("Período de férias não encontrado");
|
||||
}
|
||||
|
||||
// Atualizar status e histórico
|
||||
const acao = `Status alterado de ${solicitacao.status} para ${args.novoStatus}`;
|
||||
const acao = `Status alterado para ${args.novoStatus}`;
|
||||
|
||||
// Preparar dados de atualização
|
||||
const updateData: {
|
||||
status: typeof args.novoStatus;
|
||||
historicoAlteracoes: Array<{
|
||||
@@ -564,10 +611,14 @@ export const atualizarStatus = mutation({
|
||||
usuarioId: Id<"usuarios">;
|
||||
acao: string;
|
||||
}>;
|
||||
gestorId?: undefined;
|
||||
dataAprovacao?: undefined;
|
||||
dataReprovacao?: undefined;
|
||||
motivoReprovacao?: undefined;
|
||||
} = {
|
||||
status: args.novoStatus,
|
||||
historicoAlteracoes: [
|
||||
...(solicitacao.historicoAlteracoes || []),
|
||||
...(registro.historicoAlteracoes || []),
|
||||
{
|
||||
data: Date.now(),
|
||||
usuarioId: args.usuarioId,
|
||||
@@ -576,16 +627,17 @@ export const atualizarStatus = mutation({
|
||||
],
|
||||
};
|
||||
|
||||
// Se voltar para aguardando_aprovacao, limpar campos relacionados usando replace
|
||||
// Se voltar para aguardando_aprovacao, limpar campos relacionados
|
||||
if (args.novoStatus === "aguardando_aprovacao") {
|
||||
// Usar replace para limpar campos opcionais - omitir os campos que queremos limpar
|
||||
const { gestorId, dataAprovacao, dataReprovacao, motivoReprovacao, ...solicitacaoLimpa } = solicitacao;
|
||||
await ctx.db.replace(args.solicitacaoId, {
|
||||
...solicitacaoLimpa,
|
||||
await ctx.db.patch(registro._id, {
|
||||
...updateData,
|
||||
gestorId: undefined,
|
||||
dataAprovacao: undefined,
|
||||
dataReprovacao: undefined,
|
||||
motivoReprovacao: undefined,
|
||||
});
|
||||
} else {
|
||||
await ctx.db.patch(args.solicitacaoId, updateData);
|
||||
await ctx.db.patch(registro._id, updateData);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -603,39 +655,106 @@ export const atualizarStatusTodosFuncionarios = internalMutation({
|
||||
const hoje = new Date();
|
||||
hoje.setHours(0, 0, 0, 0);
|
||||
|
||||
const solicitacoesAprovadas = await ctx.db
|
||||
.query("solicitacoesFerias")
|
||||
// Buscar todos os registros de férias que podem estar em férias
|
||||
// Buscar por status específico para criar mapas de referência
|
||||
const feriasAprovadas = await ctx.db
|
||||
.query("ferias")
|
||||
.withIndex("by_funcionario_and_status", (q) =>
|
||||
q.eq("funcionarioId", func._id).eq("status", "aprovado")
|
||||
)
|
||||
.collect();
|
||||
|
||||
const solicitacoesAjustadas = await ctx.db
|
||||
.query("solicitacoesFerias")
|
||||
const feriasAjustadas = await ctx.db
|
||||
.query("ferias")
|
||||
.withIndex("by_funcionario_and_status", (q) =>
|
||||
q.eq("funcionarioId", func._id).eq("status", "data_ajustada_aprovada")
|
||||
)
|
||||
.collect();
|
||||
|
||||
const todasSolicitacoes = [
|
||||
...solicitacoesAprovadas,
|
||||
...solicitacoesAjustadas,
|
||||
const feriasEmFerias = await ctx.db
|
||||
.query("ferias")
|
||||
.withIndex("by_funcionario_and_status", (q) =>
|
||||
q.eq("funcionarioId", func._id).eq("status", "EmFérias")
|
||||
)
|
||||
.collect();
|
||||
|
||||
// Criar mapas para verificar status original
|
||||
// Quando um registro está "EmFérias", precisamos saber qual era o status anterior
|
||||
// Vamos usar o histórico ou verificar se o ID estava nas listas antes
|
||||
const idsAprovados = new Set(feriasAprovadas.map(f => f._id));
|
||||
const idsAjustados = new Set(feriasAjustadas.map(f => f._id));
|
||||
|
||||
// Para registros que estão "EmFérias", verificar o histórico para determinar status anterior
|
||||
// Se não houver histórico claro, usar lógica: se foi aprovado recentemente, provavelmente era "aprovado"
|
||||
// Por enquanto, vamos usar uma heurística: se o registro está "EmFérias" e não está nas listas,
|
||||
// vamos verificar o histórico de alterações para encontrar o status anterior
|
||||
const statusAnteriorPorId = new Map<Id<"ferias">, "aprovado" | "data_ajustada_aprovada">();
|
||||
|
||||
for (const ferias of feriasEmFerias) {
|
||||
// Verificar histórico para encontrar status anterior
|
||||
if (ferias.historicoAlteracoes && ferias.historicoAlteracoes.length > 0) {
|
||||
// Procurar pela última alteração que mudou para "EmFérias" ou antes disso
|
||||
const historico = ferias.historicoAlteracoes;
|
||||
for (let i = historico.length - 1; i >= 0; i--) {
|
||||
const entrada = historico[i];
|
||||
if (entrada.acao.includes("Aprovado") || entrada.acao.includes("aprovado")) {
|
||||
statusAnteriorPorId.set(ferias._id, "aprovado");
|
||||
break;
|
||||
} else if (entrada.acao.includes("Data ajustada") || entrada.acao.includes("ajustada")) {
|
||||
statusAnteriorPorId.set(ferias._id, "data_ajustada_aprovada");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Se não encontrou no histórico, usar fallback: assumir "aprovado"
|
||||
if (!statusAnteriorPorId.has(ferias._id)) {
|
||||
statusAnteriorPorId.set(ferias._id, "aprovado");
|
||||
}
|
||||
}
|
||||
|
||||
// Combinar todos os registros
|
||||
const todasFerias = [
|
||||
...feriasAprovadas,
|
||||
...feriasAjustadas,
|
||||
...feriasEmFerias,
|
||||
];
|
||||
|
||||
let emFerias = false;
|
||||
for (const solicitacao of todasSolicitacoes) {
|
||||
for (const periodo of solicitacao.periodos) {
|
||||
const inicio = new Date(periodo.dataInicio);
|
||||
const fim = new Date(periodo.dataFim);
|
||||
inicio.setHours(0, 0, 0, 0);
|
||||
fim.setHours(23, 59, 59, 999);
|
||||
for (const ferias of todasFerias) {
|
||||
const inicio = new Date(ferias.dataInicio);
|
||||
const fim = new Date(ferias.dataFim);
|
||||
inicio.setHours(0, 0, 0, 0);
|
||||
fim.setHours(23, 59, 59, 999);
|
||||
|
||||
if (hoje >= inicio && hoje <= fim) {
|
||||
emFerias = true;
|
||||
break;
|
||||
if (hoje >= inicio && hoje <= fim) {
|
||||
emFerias = true;
|
||||
|
||||
// Atualizar status para "EmFérias" se ainda não estiver
|
||||
if (ferias.status !== "EmFérias") {
|
||||
await ctx.db.patch(ferias._id, {
|
||||
status: "EmFérias",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Se saiu do período e está "EmFérias", voltar para o status anterior
|
||||
if (ferias.status === "EmFérias") {
|
||||
// Determinar status anterior
|
||||
let statusAnterior: "aprovado" | "data_ajustada_aprovada";
|
||||
|
||||
if (idsAprovados.has(ferias._id)) {
|
||||
statusAnterior = "aprovado";
|
||||
} else if (idsAjustados.has(ferias._id)) {
|
||||
statusAnterior = "data_ajustada_aprovada";
|
||||
} else {
|
||||
// Usar histórico ou fallback
|
||||
statusAnterior = statusAnteriorPorId.get(ferias._id) || "aprovado";
|
||||
}
|
||||
|
||||
await ctx.db.patch(ferias._id, {
|
||||
status: statusAnterior,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (emFerias) break;
|
||||
}
|
||||
|
||||
const novoStatus = emFerias ? "em_ferias" : "ativo";
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { v } from "convex/values";
|
||||
import { query, mutation, internalMutation } from "./_generated/server";
|
||||
import { internal } from "./_generated/api";
|
||||
import { query } from "./_generated/server";
|
||||
import { Id } from "./_generated/dataModel";
|
||||
import type { QueryCtx } from "./_generated/server";
|
||||
|
||||
@@ -53,10 +52,11 @@ const REGIMES_CONFIG = {
|
||||
estatutario_pe: {
|
||||
nome: "Servidor Público Estadual de Pernambuco",
|
||||
maxPeriodos: 2,
|
||||
minDiasPeriodo: 10,
|
||||
minDiasPeriodo: 15, // Mínimo 15 dias por período
|
||||
minDiasPeriodoPrincipal: null, // Não há essa regra
|
||||
abonoPermitido: false,
|
||||
maxDiasAbono: 0,
|
||||
periodosPermitidos: [15, 30], // Apenas 15 ou 30 dias por período
|
||||
},
|
||||
estatutario_federal: {
|
||||
nome: "Servidor Público Federal",
|
||||
@@ -69,10 +69,11 @@ const REGIMES_CONFIG = {
|
||||
estatutario_municipal: {
|
||||
nome: "Servidor Público Municipal",
|
||||
maxPeriodos: 2,
|
||||
minDiasPeriodo: 10,
|
||||
minDiasPeriodo: 15, // Mínimo 15 dias por período
|
||||
minDiasPeriodoPrincipal: null,
|
||||
abonoPermitido: false,
|
||||
maxDiasAbono: 0,
|
||||
periodosPermitidos: [15, 30], // Apenas 15 ou 30 dias por período
|
||||
},
|
||||
};
|
||||
|
||||
@@ -98,6 +99,103 @@ async function obterRegimeTrabalho(ctx: QueryCtx, funcionarioId: Id<"funcionario
|
||||
return funcionario?.regimeTrabalho || "clt"; // Default CLT
|
||||
}
|
||||
|
||||
// Helper: Calcular saldo dinamicamente baseado na tabela ferias
|
||||
async function calcularSaldo(
|
||||
ctx: QueryCtx,
|
||||
funcionarioId: Id<"funcionarios">,
|
||||
anoReferencia: number,
|
||||
feriasIdExcluir?: Id<"ferias"> // ID do período a excluir do cálculo (para ajustes)
|
||||
): Promise<{
|
||||
diasDireito: number;
|
||||
diasUsados: number;
|
||||
diasPendentes: number;
|
||||
diasDisponiveis: number;
|
||||
diasAbono: number;
|
||||
dataInicio: string;
|
||||
dataFim: string;
|
||||
status: "ativo" | "vencido" | "concluido";
|
||||
} | null> {
|
||||
const funcionario = await ctx.db.get(funcionarioId);
|
||||
if (!funcionario || !funcionario.admissaoData) return null;
|
||||
|
||||
const regime = funcionario.regimeTrabalho || "clt";
|
||||
const config = REGIMES_CONFIG[regime];
|
||||
|
||||
// Calcular anos desde admissão
|
||||
const dataAdmissao = new Date(funcionario.admissaoData);
|
||||
const anosDesdeAdmissao = anoReferencia - dataAdmissao.getFullYear();
|
||||
|
||||
if (anosDesdeAdmissao < 1) return null; // Ainda não tem direito
|
||||
|
||||
const dataInicio = calcularDataFimPeriodo(
|
||||
funcionario.admissaoData,
|
||||
anosDesdeAdmissao - 1
|
||||
);
|
||||
const dataFim = calcularDataFimPeriodo(
|
||||
funcionario.admissaoData,
|
||||
anosDesdeAdmissao
|
||||
);
|
||||
|
||||
// Buscar todos os registros de férias para este funcionário e ano
|
||||
const todasFerias = await ctx.db
|
||||
.query("ferias")
|
||||
.withIndex("by_funcionario_and_ano", (q) =>
|
||||
q.eq("funcionarioId", funcionarioId).eq("anoReferencia", anoReferencia)
|
||||
)
|
||||
.collect();
|
||||
|
||||
// Filtrar períodos a excluir (para ajustes)
|
||||
const feriasFiltradas = feriasIdExcluir
|
||||
? todasFerias.filter((f) => f._id !== feriasIdExcluir)
|
||||
: todasFerias;
|
||||
|
||||
// Calcular dias usados (aprovado, data_ajustada_aprovada, EmFérias)
|
||||
const diasUsados = feriasFiltradas
|
||||
.filter(
|
||||
(f) =>
|
||||
f.status === "aprovado" ||
|
||||
f.status === "data_ajustada_aprovada" ||
|
||||
f.status === "EmFérias"
|
||||
)
|
||||
.reduce((acc, f) => acc + f.diasFerias, 0);
|
||||
|
||||
// Calcular dias pendentes (aguardando_aprovacao)
|
||||
const diasPendentes = feriasFiltradas
|
||||
.filter((f) => f.status === "aguardando_aprovacao")
|
||||
.reduce((acc, f) => acc + f.diasFerias, 0);
|
||||
|
||||
// Calcular dias de abono
|
||||
const diasAbono = feriasFiltradas.reduce((acc, f) => acc + f.diasAbono, 0);
|
||||
|
||||
// Calcular dias disponíveis
|
||||
const diasDireito = 30;
|
||||
const diasDisponiveis = diasDireito - diasUsados - diasPendentes - diasAbono;
|
||||
|
||||
// Determinar status do período
|
||||
const hoje = new Date();
|
||||
const dataFimPeriodo = new Date(dataFim);
|
||||
let status: "ativo" | "vencido" | "concluido";
|
||||
|
||||
if (diasDireito - diasUsados - diasAbono <= 0) {
|
||||
status = "concluido";
|
||||
} else if (hoje > dataFimPeriodo) {
|
||||
status = "vencido";
|
||||
} else {
|
||||
status = "ativo";
|
||||
}
|
||||
|
||||
return {
|
||||
diasDireito,
|
||||
diasUsados,
|
||||
diasPendentes,
|
||||
diasDisponiveis,
|
||||
diasAbono,
|
||||
dataInicio,
|
||||
dataFim,
|
||||
status,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Query: Obter saldo de férias de um funcionário para um ano específico
|
||||
*/
|
||||
@@ -123,67 +221,17 @@ export const obterSaldo = query({
|
||||
v.null()
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
// Buscar período aquisitivo
|
||||
const periodo = await ctx.db
|
||||
.query("periodosAquisitivos")
|
||||
.withIndex("by_funcionario_and_ano", (q) =>
|
||||
q.eq("funcionarioId", args.funcionarioId).eq("anoReferencia", args.anoReferencia)
|
||||
)
|
||||
.first();
|
||||
|
||||
if (!periodo) {
|
||||
// Se não existe, calcular e retornar dados previstos sem mutar o banco
|
||||
const funcionario = await ctx.db.get(args.funcionarioId);
|
||||
if (!funcionario || !funcionario.admissaoData) return null;
|
||||
|
||||
const regime = funcionario.regimeTrabalho || "clt";
|
||||
const config = REGIMES_CONFIG[regime];
|
||||
|
||||
// Calcular anos desde admissão
|
||||
const dataAdmissao = new Date(funcionario.admissaoData);
|
||||
const anosDesdeAdmissao = args.anoReferencia - dataAdmissao.getFullYear();
|
||||
|
||||
if (anosDesdeAdmissao < 1) return null; // Ainda não tem direito
|
||||
|
||||
const dataInicio = calcularDataFimPeriodo(
|
||||
funcionario.admissaoData,
|
||||
anosDesdeAdmissao - 1
|
||||
);
|
||||
const dataFim = calcularDataFimPeriodo(
|
||||
funcionario.admissaoData,
|
||||
anosDesdeAdmissao
|
||||
);
|
||||
|
||||
return {
|
||||
anoReferencia: args.anoReferencia,
|
||||
diasDireito: 30,
|
||||
diasUsados: 0,
|
||||
diasPendentes: 0,
|
||||
diasDisponiveis: 30,
|
||||
diasAbono: 0,
|
||||
abonoPermitido: config.abonoPermitido,
|
||||
status: "ativo" as const,
|
||||
dataInicio,
|
||||
dataFim,
|
||||
regimeTrabalho: config.nome,
|
||||
};
|
||||
}
|
||||
const saldo = await calcularSaldo(ctx, args.funcionarioId, args.anoReferencia);
|
||||
if (!saldo) return null;
|
||||
|
||||
const funcionario = await ctx.db.get(args.funcionarioId);
|
||||
const regime = funcionario?.regimeTrabalho || "clt";
|
||||
const config = REGIMES_CONFIG[regime];
|
||||
|
||||
return {
|
||||
anoReferencia: periodo.anoReferencia,
|
||||
diasDireito: periodo.diasDireito,
|
||||
diasUsados: periodo.diasUsados,
|
||||
diasPendentes: periodo.diasPendentes,
|
||||
diasDisponiveis: periodo.diasDisponiveis,
|
||||
diasAbono: periodo.diasAbono,
|
||||
anoReferencia: args.anoReferencia,
|
||||
...saldo,
|
||||
abonoPermitido: config.abonoPermitido,
|
||||
status: periodo.status,
|
||||
dataInicio: periodo.dataInicio,
|
||||
dataFim: periodo.dataFim,
|
||||
regimeTrabalho: config.nome,
|
||||
};
|
||||
},
|
||||
@@ -198,7 +246,6 @@ export const listarSaldos = query({
|
||||
},
|
||||
returns: v.array(
|
||||
v.object({
|
||||
_id: v.id("periodosAquisitivos"),
|
||||
anoReferencia: v.number(),
|
||||
diasDireito: v.number(),
|
||||
diasUsados: v.number(),
|
||||
@@ -212,24 +259,36 @@ export const listarSaldos = query({
|
||||
})
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
const periodos = await ctx.db
|
||||
.query("periodosAquisitivos")
|
||||
.withIndex("by_funcionario", (q) => q.eq("funcionarioId", args.funcionarioId))
|
||||
.collect();
|
||||
const funcionario = await ctx.db.get(args.funcionarioId);
|
||||
if (!funcionario || !funcionario.admissaoData) return [];
|
||||
|
||||
return periodos.map((p) => ({
|
||||
_id: p._id,
|
||||
anoReferencia: p.anoReferencia,
|
||||
diasDireito: p.diasDireito,
|
||||
diasUsados: p.diasUsados,
|
||||
diasPendentes: p.diasPendentes,
|
||||
diasDisponiveis: p.diasDisponiveis,
|
||||
diasAbono: p.diasAbono,
|
||||
abonoPermitido: p.abonoPermitido,
|
||||
status: p.status,
|
||||
dataInicio: p.dataInicio,
|
||||
dataFim: p.dataFim,
|
||||
}));
|
||||
const regime = funcionario.regimeTrabalho || "clt";
|
||||
const config = REGIMES_CONFIG[regime];
|
||||
|
||||
const dataAdmissao = new Date(funcionario.admissaoData);
|
||||
const anoAtual = new Date().getFullYear();
|
||||
const anosDesdeAdmissao = anoAtual - dataAdmissao.getFullYear();
|
||||
|
||||
const saldos = [];
|
||||
|
||||
// Calcular saldos para os últimos 3 anos (atual, anterior e anterior ao anterior)
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const ano = anoAtual - i;
|
||||
const anosPeriodo = ano - dataAdmissao.getFullYear();
|
||||
|
||||
if (anosPeriodo < 1) continue;
|
||||
|
||||
const saldo = await calcularSaldo(ctx, args.funcionarioId, ano);
|
||||
if (saldo) {
|
||||
saldos.push({
|
||||
anoReferencia: ano,
|
||||
...saldo,
|
||||
abonoPermitido: config.abonoPermitido,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return saldos;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -246,6 +305,7 @@ export const validarSolicitacao = query({
|
||||
dataFim: v.string(),
|
||||
})
|
||||
),
|
||||
feriasIdExcluir: v.optional(v.id("ferias")), // ID do período a excluir do cálculo de saldo (para ajustes)
|
||||
},
|
||||
returns: v.object({
|
||||
valido: v.boolean(),
|
||||
@@ -287,11 +347,48 @@ export const validarSolicitacao = query({
|
||||
`Período de ${dias} dias é inválido. Mínimo: ${config.minDiasPeriodo} dias corridos (${config.nome})`
|
||||
);
|
||||
}
|
||||
|
||||
// Validação específica para regime estatutário PE e Municipal
|
||||
if ((regime === "estatutario_pe" || regime === "estatutario_municipal") && 'periodosPermitidos' in config) {
|
||||
if (!config.periodosPermitidos.includes(dias)) {
|
||||
erros.push(
|
||||
`Para ${config.nome}, os períodos devem ter exatamente 15 ou 30 dias. Período de ${dias} dias não é permitido.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validação específica para regime estatutário PE e Municipal
|
||||
// Permite períodos fracionados: cada período deve ser 15 ou 30 dias
|
||||
// Total não pode exceder 30 dias, mas pode ser menos (períodos fracionados)
|
||||
if ((regime === "estatutario_pe" || regime === "estatutario_municipal")) {
|
||||
// Verificar se cada período individual é válido (15 ou 30 dias)
|
||||
for (const dias of diasPorPeriodo) {
|
||||
if (dias !== 15 && dias !== 30) {
|
||||
erros.push(
|
||||
`Para ${config.nome}, cada período deve ter exatamente 15 ou 30 dias. Período de ${dias} dias não é permitido.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Total não pode exceder 30 dias
|
||||
if (totalDias > 30) {
|
||||
erros.push(
|
||||
`Para ${config.nome}, o total de dias não pode exceder 30 dias. Total solicitado: ${totalDias} dias.`
|
||||
);
|
||||
}
|
||||
|
||||
// Máximo de 2 períodos
|
||||
if (args.periodos.length > 2) {
|
||||
erros.push(
|
||||
`Para ${config.nome}, o máximo de períodos permitidos é 2.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Validação 3: CLT requer um período com 14+ dias se dividir
|
||||
if (regime === "clt" && args.periodos.length > 1 && config.minDiasPeriodoPrincipal) {
|
||||
const temPeriodo14Dias = diasPorPeriodo.some((d) => d >= config.minDiasPeriodoPrincipal);
|
||||
const temPeriodo14Dias = diasPorPeriodo.some((d) => d >= config.minDiasPeriodoPrincipal!);
|
||||
if (!temPeriodo14Dias) {
|
||||
erros.push(
|
||||
`Ao dividir férias em CLT, um período deve ter no mínimo ${config.minDiasPeriodoPrincipal} dias corridos`
|
||||
@@ -299,43 +396,40 @@ export const validarSolicitacao = query({
|
||||
}
|
||||
}
|
||||
|
||||
// Validação 4: Verificar saldo disponível
|
||||
const periodo = await ctx.db
|
||||
.query("periodosAquisitivos")
|
||||
.withIndex("by_funcionario_and_ano", (q) =>
|
||||
q.eq("funcionarioId", args.funcionarioId).eq("anoReferencia", args.anoReferencia)
|
||||
)
|
||||
.first();
|
||||
// Validação 4: Verificar saldo disponível (calculado dinamicamente)
|
||||
// Se for um ajuste (feriasIdExcluir fornecido), excluir esse período do cálculo
|
||||
const saldo = await calcularSaldo(ctx, args.funcionarioId, args.anoReferencia, args.feriasIdExcluir);
|
||||
|
||||
if (!periodo) {
|
||||
if (!saldo) {
|
||||
erros.push(`Você ainda não tem direito a férias referentes ao ano ${args.anoReferencia}`);
|
||||
} else {
|
||||
if (totalDias > periodo.diasDisponiveis) {
|
||||
// Verificar saldo disponível (já excluindo o período original se for ajuste)
|
||||
if (totalDias > saldo.diasDisponiveis) {
|
||||
erros.push(
|
||||
`Total solicitado (${totalDias} dias) excede saldo disponível (${periodo.diasDisponiveis} dias)`
|
||||
`Total solicitado (${totalDias} dias) excede saldo disponível (${saldo.diasDisponiveis} dias)`
|
||||
);
|
||||
}
|
||||
|
||||
// Aviso: Saldo baixo
|
||||
if (periodo.diasDisponiveis < 15 && periodo.diasDisponiveis > totalDias) {
|
||||
if (saldo.diasDisponiveis < 15 && saldo.diasDisponiveis > totalDias) {
|
||||
avisos.push(
|
||||
`Após essa solicitação, restará ${periodo.diasDisponiveis - totalDias} dias de ${args.anoReferencia}`
|
||||
`Após essa solicitação, restará ${saldo.diasDisponiveis - totalDias} dias de ${args.anoReferencia}`
|
||||
);
|
||||
}
|
||||
|
||||
// Aviso: Férias vencendo
|
||||
const hoje = new Date();
|
||||
const dataFim = new Date(periodo.dataFim);
|
||||
const dataFim = new Date(saldo.dataFim);
|
||||
const diasAteVencer = Math.ceil((dataFim.getTime() - hoje.getTime()) / (1000 * 60 * 60 * 24));
|
||||
if (diasAteVencer < 90 && diasAteVencer > 0) {
|
||||
avisos.push(
|
||||
`⚠️ Atenção: Seu período aquisitivo ${periodo.anoReferencia} vence em ${diasAteVencer} dias!`
|
||||
`⚠️ Atenção: Seu período aquisitivo ${args.anoReferencia} vence em ${diasAteVencer} dias!`
|
||||
);
|
||||
}
|
||||
|
||||
if (diasAteVencer < 0) {
|
||||
avisos.push(
|
||||
`⚠️ URGENTE: Seu período aquisitivo ${periodo.anoReferencia} está VENCIDO há ${Math.abs(diasAteVencer)} dias!`
|
||||
`⚠️ URGENTE: Seu período aquisitivo ${args.anoReferencia} está VENCIDO há ${Math.abs(diasAteVencer)} dias!`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -388,166 +482,3 @@ export const validarSolicitacao = query({
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Internal Mutation: Atualizar saldo após aprovação de férias
|
||||
*/
|
||||
export const atualizarSaldoAposAprovacao = internalMutation({
|
||||
args: {
|
||||
solicitacaoId: v.id("solicitacoesFerias"),
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const solicitacao = await ctx.db.get(args.solicitacaoId);
|
||||
if (!solicitacao) return null;
|
||||
|
||||
// Buscar período aquisitivo
|
||||
const periodo = await ctx.db
|
||||
.query("periodosAquisitivos")
|
||||
.withIndex("by_funcionario_and_ano", (q) =>
|
||||
q.eq("funcionarioId", solicitacao.funcionarioId).eq("anoReferencia", solicitacao.anoReferencia)
|
||||
)
|
||||
.first();
|
||||
|
||||
if (!periodo) return null;
|
||||
|
||||
// Calcular total de dias
|
||||
let totalDias = 0;
|
||||
for (const p of solicitacao.periodos) {
|
||||
totalDias += p.diasCorridos;
|
||||
}
|
||||
|
||||
// Atualizar saldo
|
||||
await ctx.db.patch(periodo._id, {
|
||||
diasPendentes: periodo.diasPendentes - totalDias,
|
||||
diasUsados: periodo.diasUsados + totalDias,
|
||||
diasDisponiveis: periodo.diasDireito - (periodo.diasUsados + totalDias) - periodo.diasAbono,
|
||||
status: periodo.diasDireito - (periodo.diasUsados + totalDias) <= 0 ? "concluido" : periodo.status,
|
||||
});
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Internal Mutation: Reservar dias (ao criar solicitação)
|
||||
*/
|
||||
export const reservarDias = internalMutation({
|
||||
args: {
|
||||
funcionarioId: v.id("funcionarios"),
|
||||
anoReferencia: v.number(),
|
||||
totalDias: v.number(),
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const periodo = await ctx.db
|
||||
.query("periodosAquisitivos")
|
||||
.withIndex("by_funcionario_and_ano", (q) =>
|
||||
q.eq("funcionarioId", args.funcionarioId).eq("anoReferencia", args.anoReferencia)
|
||||
)
|
||||
.first();
|
||||
|
||||
if (!periodo) return null;
|
||||
|
||||
await ctx.db.patch(periodo._id, {
|
||||
diasPendentes: periodo.diasPendentes + args.totalDias,
|
||||
diasDisponiveis: periodo.diasDisponiveis - args.totalDias,
|
||||
});
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Internal Mutation: Liberar dias (ao reprovar solicitação)
|
||||
*/
|
||||
export const liberarDias = internalMutation({
|
||||
args: {
|
||||
solicitacaoId: v.id("solicitacoesFerias"),
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const solicitacao = await ctx.db.get(args.solicitacaoId);
|
||||
if (!solicitacao) return null;
|
||||
|
||||
const periodo = await ctx.db
|
||||
.query("periodosAquisitivos")
|
||||
.withIndex("by_funcionario_and_ano", (q) =>
|
||||
q.eq("funcionarioId", solicitacao.funcionarioId).eq("anoReferencia", solicitacao.anoReferencia)
|
||||
)
|
||||
.first();
|
||||
|
||||
if (!periodo) return null;
|
||||
|
||||
let totalDias = 0;
|
||||
for (const p of solicitacao.periodos) {
|
||||
totalDias += p.diasCorridos;
|
||||
}
|
||||
|
||||
await ctx.db.patch(periodo._id, {
|
||||
diasPendentes: periodo.diasPendentes - totalDias,
|
||||
diasDisponiveis: periodo.diasDisponiveis + totalDias,
|
||||
});
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Internal Mutation: Criar períodos aquisitivos para todos os funcionários
|
||||
*/
|
||||
export const criarPeriodosAquisitivos = internalMutation({
|
||||
args: {},
|
||||
returns: v.null(),
|
||||
handler: async (ctx) => {
|
||||
const funcionarios = await ctx.db.query("funcionarios").collect();
|
||||
const anoAtual = new Date().getFullYear();
|
||||
|
||||
for (const func of funcionarios) {
|
||||
if (!func.admissaoData) continue;
|
||||
|
||||
const regime = func.regimeTrabalho || "clt";
|
||||
const config = REGIMES_CONFIG[regime];
|
||||
|
||||
const dataAdmissao = new Date(func.admissaoData);
|
||||
const anosDesdeAdmissao = anoAtual - dataAdmissao.getFullYear();
|
||||
|
||||
// Criar períodos para os últimos 2 anos (atual e anterior)
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const ano = anoAtual - i;
|
||||
const anosPeriodo = ano - dataAdmissao.getFullYear();
|
||||
|
||||
if (anosPeriodo < 1) continue;
|
||||
|
||||
// Verificar se já existe
|
||||
const periodoExistente = await ctx.db
|
||||
.query("periodosAquisitivos")
|
||||
.withIndex("by_funcionario_and_ano", (q) =>
|
||||
q.eq("funcionarioId", func._id).eq("anoReferencia", ano)
|
||||
)
|
||||
.first();
|
||||
|
||||
if (periodoExistente) continue;
|
||||
|
||||
const dataInicio = calcularDataFimPeriodo(func.admissaoData, anosPeriodo - 1);
|
||||
const dataFim = calcularDataFimPeriodo(func.admissaoData, anosPeriodo);
|
||||
|
||||
await ctx.db.insert("periodosAquisitivos", {
|
||||
funcionarioId: func._id,
|
||||
anoReferencia: ano,
|
||||
dataInicio,
|
||||
dataFim,
|
||||
diasDireito: 30,
|
||||
diasUsados: 0,
|
||||
diasPendentes: 0,
|
||||
diasDisponiveis: 30,
|
||||
abonoPermitido: config.abonoPermitido,
|
||||
diasAbono: 0,
|
||||
status: "ativo",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -208,54 +208,44 @@ export default defineSchema({
|
||||
.index("by_licenca_original", ["licencaOriginalId"])
|
||||
.index("by_funcionario_and_tipo", ["funcionarioId", "tipo"]),
|
||||
|
||||
solicitacoesFerias: defineTable({
|
||||
ferias: defineTable({
|
||||
funcionarioId: v.id("funcionarios"),
|
||||
anoReferencia: v.number(),
|
||||
dataInicio: v.string(),
|
||||
dataFim: v.string(),
|
||||
diasFerias: v.number(),
|
||||
status: v.union(
|
||||
v.literal("aguardando_aprovacao"),
|
||||
v.literal("aprovado"),
|
||||
v.literal("reprovado"),
|
||||
v.literal("data_ajustada_aprovada")
|
||||
),
|
||||
periodos: v.array(
|
||||
v.object({
|
||||
dataInicio: v.string(),
|
||||
dataFim: v.string(),
|
||||
diasCorridos: v.number(),
|
||||
})
|
||||
v.literal("data_ajustada_aprovada"),
|
||||
v.literal("EmFérias")
|
||||
),
|
||||
gestorId: v.optional(v.id("usuarios")),
|
||||
observacao: v.optional(v.string()),
|
||||
motivoReprovacao: v.optional(v.string()),
|
||||
gestorId: v.optional(v.id("usuarios")),
|
||||
dataAprovacao: v.optional(v.number()),
|
||||
dataReprovacao: v.optional(v.number()),
|
||||
diasAbono: v.number(),
|
||||
historicoAlteracoes: v.optional(
|
||||
v.array(
|
||||
v.object({
|
||||
data: v.number(),
|
||||
usuarioId: v.id("usuarios"),
|
||||
acao: v.string(),
|
||||
periodosAnteriores: v.optional(
|
||||
v.array(
|
||||
v.object({
|
||||
dataInicio: v.string(),
|
||||
dataFim: v.string(),
|
||||
diasCorridos: v.number(),
|
||||
})
|
||||
)
|
||||
),
|
||||
})
|
||||
)
|
||||
),
|
||||
})
|
||||
.index("by_funcionario", ["funcionarioId"])
|
||||
.index("by_status", ["status"])
|
||||
.index("by_funcionario_and_ano", ["funcionarioId", "anoReferencia"])
|
||||
.index("by_funcionario_and_status", ["funcionarioId", "status"])
|
||||
.index("by_status", ["status"])
|
||||
.index("by_ano", ["anoReferencia"]),
|
||||
|
||||
notificacoesFerias: defineTable({
|
||||
destinatarioId: v.id("usuarios"),
|
||||
solicitacaoFeriasId: v.id("solicitacoesFerias"),
|
||||
feriasId: v.id("ferias"),
|
||||
tipo: v.union(
|
||||
v.literal("nova_solicitacao"),
|
||||
v.literal("aprovado"),
|
||||
@@ -304,27 +294,6 @@ export default defineSchema({
|
||||
.index("by_destinatario", ["destinatarioId"])
|
||||
.index("by_destinatario_and_lida", ["destinatarioId", "lida"]),
|
||||
|
||||
// Períodos aquisitivos e saldos de férias
|
||||
periodosAquisitivos: defineTable({
|
||||
funcionarioId: v.id("funcionarios"),
|
||||
anoReferencia: v.number(), // Ano do período aquisitivo (ex: 2024)
|
||||
dataInicio: v.string(), // Data de início do período aquisitivo
|
||||
dataFim: v.string(), // Data de fim do período aquisitivo
|
||||
diasDireito: v.number(), // Dias de férias que tem direito (30 ou proporcional)
|
||||
diasUsados: v.number(), // Dias já usados
|
||||
diasPendentes: v.number(), // Dias em solicitações aguardando aprovação
|
||||
diasDisponiveis: v.number(), // Dias disponíveis = direito - usados - pendentes
|
||||
abonoPermitido: v.boolean(), // Se pode vender 1/3 das férias
|
||||
diasAbono: v.number(), // Dias vendidos como abono pecuniário
|
||||
status: v.union(
|
||||
v.literal("ativo"), // Período vigente
|
||||
v.literal("vencido"), // Período vencido (não tirou férias)
|
||||
v.literal("concluido") // Período totalmente utilizado
|
||||
),
|
||||
})
|
||||
.index("by_funcionario", ["funcionarioId"])
|
||||
.index("by_funcionario_and_ano", ["funcionarioId", "anoReferencia"])
|
||||
.index("by_funcionario_and_status", ["funcionarioId", "status"]),
|
||||
|
||||
times: defineTable({
|
||||
nome: v.string(),
|
||||
|
||||
@@ -579,25 +579,15 @@ export const clearDatabase = internalMutation({
|
||||
` ✅ ${notificacoesFerias.length} notificações de férias removidas`
|
||||
);
|
||||
|
||||
// 4. Férias e períodos aquisitivos
|
||||
const solicitacoesFerias = await ctx.db
|
||||
.query("solicitacoesFerias")
|
||||
// 4. Férias
|
||||
const ferias = await ctx.db
|
||||
.query("ferias")
|
||||
.collect();
|
||||
for (const solicitacao of solicitacoesFerias) {
|
||||
await ctx.db.delete(solicitacao._id);
|
||||
for (const feriasRegistro of ferias) {
|
||||
await ctx.db.delete(feriasRegistro._id);
|
||||
}
|
||||
console.log(
|
||||
` ✅ ${solicitacoesFerias.length} solicitações de férias removidas`
|
||||
);
|
||||
|
||||
const periodosAquisitivos = await ctx.db
|
||||
.query("periodosAquisitivos")
|
||||
.collect();
|
||||
for (const periodo of periodosAquisitivos) {
|
||||
await ctx.db.delete(periodo._id);
|
||||
}
|
||||
console.log(
|
||||
` ✅ ${periodosAquisitivos.length} períodos aquisitivos removidos`
|
||||
` ✅ ${ferias.length} registros de férias removidos`
|
||||
);
|
||||
|
||||
// 5. Atestados
|
||||
@@ -849,25 +839,15 @@ export const limparBanco = mutation({
|
||||
` ✅ ${notificacoesFerias.length} notificações de férias removidas`
|
||||
);
|
||||
|
||||
// 4. Férias e períodos aquisitivos
|
||||
const solicitacoesFerias = await ctx.db
|
||||
.query("solicitacoesFerias")
|
||||
// 4. Férias
|
||||
const ferias = await ctx.db
|
||||
.query("ferias")
|
||||
.collect();
|
||||
for (const solicitacao of solicitacoesFerias) {
|
||||
await ctx.db.delete(solicitacao._id);
|
||||
for (const feriasRegistro of ferias) {
|
||||
await ctx.db.delete(feriasRegistro._id);
|
||||
}
|
||||
console.log(
|
||||
` ✅ ${solicitacoesFerias.length} solicitações de férias removidas`
|
||||
);
|
||||
|
||||
const periodosAquisitivos = await ctx.db
|
||||
.query("periodosAquisitivos")
|
||||
.collect();
|
||||
for (const periodo of periodosAquisitivos) {
|
||||
await ctx.db.delete(periodo._id);
|
||||
}
|
||||
console.log(
|
||||
` ✅ ${periodosAquisitivos.length} períodos aquisitivos removidos`
|
||||
` ✅ ${ferias.length} registros de férias removidos`
|
||||
);
|
||||
|
||||
// 5. Atestados
|
||||
|
||||
Reference in New Issue
Block a user