Merge remote-tracking branch 'origin' into feat-pedidos
This commit is contained in:
@@ -3,6 +3,7 @@ import type { Id } from './_generated/dataModel';
|
||||
import type { MutationCtx, QueryCtx } from './_generated/server';
|
||||
import { mutation, query } from './_generated/server';
|
||||
import { getCurrentUserFunction } from './auth';
|
||||
import { internal } from './_generated/api';
|
||||
import { registrarAtividade } from './logsAtividades';
|
||||
|
||||
// ========== HELPERS ==========
|
||||
@@ -26,6 +27,118 @@ function calcularDias(dataInicio: string, dataFim: string): number {
|
||||
return diffDays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper para recalcular banco de horas em um período
|
||||
*/
|
||||
async function recalcularBancoHorasPeriodo(
|
||||
ctx: MutationCtx,
|
||||
funcionarioId: Id<'funcionarios'>,
|
||||
dataInicio: string,
|
||||
dataFim: string
|
||||
): Promise<void> {
|
||||
// Gerar todas as datas do período
|
||||
const dataInicioObj = new Date(dataInicio);
|
||||
const dataFimObj = new Date(dataFim);
|
||||
const datas: string[] = [];
|
||||
const dataAtual = new Date(dataInicioObj);
|
||||
|
||||
while (dataAtual <= dataFimObj) {
|
||||
const ano = dataAtual.getFullYear();
|
||||
const mes = String(dataAtual.getMonth() + 1).padStart(2, '0');
|
||||
const dia = String(dataAtual.getDate()).padStart(2, '0');
|
||||
datas.push(`${ano}-${mes}-${dia}`);
|
||||
dataAtual.setDate(dataAtual.getDate() + 1);
|
||||
}
|
||||
|
||||
// Recalcular para cada data usando a mutation interna (agendar para execução assíncrona)
|
||||
for (let i = 0; i < datas.length; i++) {
|
||||
await ctx.scheduler.runAfter(i * 100, internal.pontos.recalcularBancoHorasData, {
|
||||
funcionarioId,
|
||||
data: datas[i]!
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper para verificar se um funcionário tem licença ou atestado ativo
|
||||
* Retorna true se há algum registro ativo (data atual entre dataInicio e dataFim)
|
||||
*/
|
||||
export async function verificarLicencaAtiva(
|
||||
ctx: QueryCtx | MutationCtx,
|
||||
funcionarioId: Id<'funcionarios'>,
|
||||
dataAtual?: Date
|
||||
): Promise<boolean> {
|
||||
// Normalizar data atual para comparar apenas a parte da data (sem hora)
|
||||
// Usar timezone local para evitar problemas de conversão
|
||||
const hoje = dataAtual || new Date();
|
||||
const hojeStr = `${hoje.getFullYear()}-${String(hoje.getMonth() + 1).padStart(2, '0')}-${String(hoje.getDate()).padStart(2, '0')}`; // Formato: "YYYY-MM-DD"
|
||||
|
||||
console.log(
|
||||
`[verificarLicencaAtiva] Verificando funcionário ${funcionarioId}, data atual: ${hojeStr}`
|
||||
);
|
||||
|
||||
// Buscar atestados e licenças do funcionário
|
||||
const [atestados, licencas] = await Promise.all([
|
||||
ctx.db
|
||||
.query('atestados')
|
||||
.withIndex('by_funcionario', (q) => q.eq('funcionarioId', funcionarioId))
|
||||
.collect(),
|
||||
ctx.db
|
||||
.query('licencas')
|
||||
.withIndex('by_funcionario', (q) => q.eq('funcionarioId', funcionarioId))
|
||||
.collect()
|
||||
]);
|
||||
|
||||
console.log(
|
||||
`[verificarLicencaAtiva] Encontrados ${atestados.length} atestados e ${licencas.length} licenças`
|
||||
);
|
||||
|
||||
// Verificar se há algum atestado ativo
|
||||
for (const atestado of atestados) {
|
||||
// Normalizar datas para formato "YYYY-MM-DD" (pode vir como "YYYY-MM-DD" ou "YYYY-MM-DDTHH:mm:ss")
|
||||
const inicioStr = atestado.dataInicio.includes('T')
|
||||
? atestado.dataInicio.split('T')[0]
|
||||
: atestado.dataInicio.substring(0, 10);
|
||||
const fimStr = atestado.dataFim.includes('T')
|
||||
? atestado.dataFim.split('T')[0]
|
||||
: atestado.dataFim.substring(0, 10);
|
||||
|
||||
console.log(
|
||||
`[verificarLicencaAtiva] Atestado: ${inicioStr} a ${fimStr}, hoje: ${hojeStr}, ativo: ${hojeStr >= inicioStr && hojeStr <= fimStr}`
|
||||
);
|
||||
|
||||
// Comparar strings de data diretamente (formato ISO permite comparação lexicográfica)
|
||||
if (hojeStr >= inicioStr && hojeStr <= fimStr) {
|
||||
console.log(`[verificarLicencaAtiva] ✅ Atestado ativo encontrado!`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Verificar se há alguma licença ativa
|
||||
for (const licenca of licencas) {
|
||||
// Normalizar datas para formato "YYYY-MM-DD" (pode vir como "YYYY-MM-DD" ou "YYYY-MM-DDTHH:mm:ss")
|
||||
const inicioStr = licenca.dataInicio.includes('T')
|
||||
? licenca.dataInicio.split('T')[0]
|
||||
: licenca.dataInicio.substring(0, 10);
|
||||
const fimStr = licenca.dataFim.includes('T')
|
||||
? licenca.dataFim.split('T')[0]
|
||||
: licenca.dataFim.substring(0, 10);
|
||||
|
||||
console.log(
|
||||
`[verificarLicencaAtiva] Licença: ${inicioStr} a ${fimStr}, hoje: ${hojeStr}, ativa: ${hojeStr >= inicioStr && hojeStr <= fimStr}`
|
||||
);
|
||||
|
||||
// Comparar strings de data diretamente (formato ISO permite comparação lexicográfica)
|
||||
if (hojeStr >= inicioStr && hojeStr <= fimStr) {
|
||||
console.log(`[verificarLicencaAtiva] ✅ Licença ativa encontrada!`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[verificarLicencaAtiva] ❌ Nenhuma licença/atestado ativo encontrado`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ========== QUERIES ==========
|
||||
|
||||
/**
|
||||
@@ -45,9 +158,23 @@ export const listarTodos = query({
|
||||
try {
|
||||
const funcionario = await ctx.db.get(a.funcionarioId);
|
||||
const criadoPor = await ctx.db.get(a.criadoPor);
|
||||
|
||||
// Buscar foto do perfil do funcionário através do usuário associado
|
||||
let fotoPerfilUrl: string | null = null;
|
||||
if (funcionario) {
|
||||
const usuario = await ctx.db
|
||||
.query('usuarios')
|
||||
.withIndex('by_funcionarioId', (q) => q.eq('funcionarioId', funcionario._id))
|
||||
.first();
|
||||
if (usuario?.fotoPerfil) {
|
||||
fotoPerfilUrl = await ctx.storage.getUrl(usuario.fotoPerfil);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...a,
|
||||
funcionario,
|
||||
fotoPerfilUrl,
|
||||
criadoPorNome: criadoPor?.nome || 'Sistema',
|
||||
dias: calcularDias(a.dataInicio, a.dataFim),
|
||||
status: new Date(a.dataFim) >= new Date() ? 'ativo' : 'finalizado'
|
||||
@@ -57,6 +184,7 @@ export const listarTodos = query({
|
||||
return {
|
||||
...a,
|
||||
funcionario: null,
|
||||
fotoPerfilUrl: null,
|
||||
criadoPorNome: 'Sistema',
|
||||
dias: calcularDias(a.dataInicio, a.dataFim),
|
||||
status: new Date(a.dataFim) >= new Date() ? 'ativo' : 'finalizado'
|
||||
@@ -73,9 +201,23 @@ export const listarTodos = query({
|
||||
const licencaOriginal = l.licencaOriginalId
|
||||
? await ctx.db.get(l.licencaOriginalId)
|
||||
: null;
|
||||
|
||||
// Buscar foto do perfil do funcionário através do usuário associado
|
||||
let fotoPerfilUrl: string | null = null;
|
||||
if (funcionario) {
|
||||
const usuario = await ctx.db
|
||||
.query('usuarios')
|
||||
.withIndex('by_funcionarioId', (q) => q.eq('funcionarioId', funcionario._id))
|
||||
.first();
|
||||
if (usuario?.fotoPerfil) {
|
||||
fotoPerfilUrl = await ctx.storage.getUrl(usuario.fotoPerfil);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...l,
|
||||
funcionario,
|
||||
fotoPerfilUrl,
|
||||
criadoPorNome: criadoPor?.nome || 'Sistema',
|
||||
licencaOriginal,
|
||||
dias: calcularDias(l.dataInicio, l.dataFim),
|
||||
@@ -86,6 +228,7 @@ export const listarTodos = query({
|
||||
return {
|
||||
...l,
|
||||
funcionario: null,
|
||||
fotoPerfilUrl: null,
|
||||
criadoPorNome: 'Sistema',
|
||||
licencaOriginal: null,
|
||||
dias: calcularDias(l.dataInicio, l.dataFim),
|
||||
@@ -175,6 +318,19 @@ export const listarPorPeriodo = query({
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Verificar se o funcionário atual tem licença/atestado ativo
|
||||
*/
|
||||
export const verificarStatusLicenca = query({
|
||||
args: {
|
||||
funcionarioId: v.id('funcionarios')
|
||||
},
|
||||
returns: v.boolean(),
|
||||
handler: async (ctx, args) => {
|
||||
return await verificarLicencaAtiva(ctx, args.funcionarioId);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Obter dados para gráficos
|
||||
*/
|
||||
@@ -747,6 +903,18 @@ export const criarAtestadoMedico = mutation({
|
||||
atestadoId
|
||||
);
|
||||
|
||||
// Recalcular banco de horas para todas as datas do período do atestado
|
||||
await recalcularBancoHorasPeriodo(ctx, args.funcionarioId, args.dataInicio, args.dataFim);
|
||||
|
||||
// Atualizar status do funcionário imediatamente
|
||||
console.log(
|
||||
`[criarAtestadoMedico] Atualizando status do funcionário ${args.funcionarioId} após criar atestado`
|
||||
);
|
||||
await ctx.runMutation(internal.ferias.atualizarStatusFuncionario, {
|
||||
funcionarioId: args.funcionarioId
|
||||
});
|
||||
console.log(`[criarAtestadoMedico] Status atualizado com sucesso`);
|
||||
|
||||
return atestadoId;
|
||||
}
|
||||
});
|
||||
@@ -792,6 +960,18 @@ export const criarDeclaracaoComparecimento = mutation({
|
||||
atestadoId
|
||||
);
|
||||
|
||||
// Recalcular banco de horas para todas as datas do período da declaração
|
||||
await recalcularBancoHorasPeriodo(ctx, args.funcionarioId, args.dataInicio, args.dataFim);
|
||||
|
||||
// Atualizar status do funcionário imediatamente
|
||||
console.log(
|
||||
`[criarDeclaracaoComparecimento] Atualizando status do funcionário ${args.funcionarioId} após criar declaração`
|
||||
);
|
||||
await ctx.runMutation(internal.ferias.atualizarStatusFuncionario, {
|
||||
funcionarioId: args.funcionarioId
|
||||
});
|
||||
console.log(`[criarDeclaracaoComparecimento] Status atualizado com sucesso`);
|
||||
|
||||
return atestadoId;
|
||||
}
|
||||
});
|
||||
@@ -845,6 +1025,18 @@ export const criarLicencaMaternidade = mutation({
|
||||
licencaId
|
||||
);
|
||||
|
||||
// Recalcular banco de horas para todas as datas do período da licença
|
||||
await recalcularBancoHorasPeriodo(ctx, args.funcionarioId, args.dataInicio, args.dataFim);
|
||||
|
||||
// Atualizar status do funcionário imediatamente
|
||||
console.log(
|
||||
`[criarLicencaMaternidade] Atualizando status do funcionário ${args.funcionarioId} após criar licença maternidade`
|
||||
);
|
||||
await ctx.runMutation(internal.ferias.atualizarStatusFuncionario, {
|
||||
funcionarioId: args.funcionarioId
|
||||
});
|
||||
console.log(`[criarLicencaMaternidade] Status atualizado com sucesso`);
|
||||
|
||||
return licencaId;
|
||||
}
|
||||
});
|
||||
@@ -891,6 +1083,18 @@ export const criarLicencaPaternidade = mutation({
|
||||
licencaId
|
||||
);
|
||||
|
||||
// Recalcular banco de horas para todas as datas do período da licença
|
||||
await recalcularBancoHorasPeriodo(ctx, args.funcionarioId, args.dataInicio, args.dataFim);
|
||||
|
||||
// Atualizar status do funcionário imediatamente
|
||||
console.log(
|
||||
`[criarLicencaPaternidade] Atualizando status do funcionário ${args.funcionarioId} após criar licença paternidade`
|
||||
);
|
||||
await ctx.runMutation(internal.ferias.atualizarStatusFuncionario, {
|
||||
funcionarioId: args.funcionarioId
|
||||
});
|
||||
console.log(`[criarLicencaPaternidade] Status atualizado com sucesso`);
|
||||
|
||||
return licencaId;
|
||||
}
|
||||
});
|
||||
@@ -947,6 +1151,11 @@ export const prorrogarLicencaMaternidade = mutation({
|
||||
prorrogacaoId
|
||||
);
|
||||
|
||||
// Atualizar status do funcionário imediatamente
|
||||
await ctx.runMutation(internal.ferias.atualizarStatusFuncionario, {
|
||||
funcionarioId: licencaOriginal.funcionarioId
|
||||
});
|
||||
|
||||
return prorrogacaoId;
|
||||
}
|
||||
});
|
||||
@@ -966,6 +1175,13 @@ export const excluirAtestado = mutation({
|
||||
const atestado = await ctx.db.get(args.id);
|
||||
if (!atestado) throw new Error('Atestado não encontrado');
|
||||
|
||||
// IMPORTANTE: Salvar o período exato do atestado ANTES de excluir
|
||||
// para recalcular o banco de horas apenas para esse período específico
|
||||
const funcionarioId = atestado.funcionarioId;
|
||||
const dataInicio = atestado.dataInicio; // Data início do atestado
|
||||
const dataFim = atestado.dataFim; // Data fim do atestado
|
||||
|
||||
// Excluir o registro do banco de dados
|
||||
await ctx.db.delete(args.id);
|
||||
|
||||
await registrarAtividade(
|
||||
@@ -977,6 +1193,15 @@ export const excluirAtestado = mutation({
|
||||
args.id
|
||||
);
|
||||
|
||||
// Recalcular banco de horas APENAS para o período específico do atestado excluído
|
||||
// Isso garante que os dias do atestado sejam removidos corretamente dos registros de ponto
|
||||
await recalcularBancoHorasPeriodo(ctx, funcionarioId, dataInicio, dataFim);
|
||||
|
||||
// Atualizar status do funcionário imediatamente
|
||||
await ctx.runMutation(internal.ferias.atualizarStatusFuncionario, {
|
||||
funcionarioId
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
@@ -996,6 +1221,13 @@ export const excluirLicenca = mutation({
|
||||
const licenca = await ctx.db.get(args.id);
|
||||
if (!licenca) throw new Error('Licença não encontrada');
|
||||
|
||||
// IMPORTANTE: Salvar o período exato da licença ANTES de excluir
|
||||
// para recalcular o banco de horas apenas para esse período específico
|
||||
const funcionarioId = licenca.funcionarioId;
|
||||
const dataInicio = licenca.dataInicio; // Data início da licença
|
||||
const dataFim = licenca.dataFim; // Data fim da licença
|
||||
|
||||
// Excluir o registro do banco de dados
|
||||
await ctx.db.delete(args.id);
|
||||
|
||||
await registrarAtividade(
|
||||
@@ -1007,6 +1239,15 @@ export const excluirLicenca = mutation({
|
||||
args.id
|
||||
);
|
||||
|
||||
// Recalcular banco de horas APENAS para o período específico da licença excluída
|
||||
// Isso garante que os dias da licença sejam removidos corretamente dos registros de ponto
|
||||
await recalcularBancoHorasPeriodo(ctx, funcionarioId, dataInicio, dataFim);
|
||||
|
||||
// Atualizar status do funcionário imediatamente
|
||||
await ctx.runMutation(internal.ferias.atualizarStatusFuncionario, {
|
||||
funcionarioId
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user