Ajustes final etapa1 #71
@@ -1254,6 +1254,7 @@ export const excluirAtestado = mutation({
|
||||
const funcionarioId = atestado.funcionarioId;
|
||||
const dataInicio = atestado.dataInicio; // Data início do atestado
|
||||
const dataFim = atestado.dataFim; // Data fim do atestado
|
||||
const atestadoId = args.id.toString(); // ID do atestado para remover ajustes
|
||||
|
||||
// Excluir o registro do banco de dados
|
||||
await ctx.db.delete(args.id);
|
||||
@@ -1267,6 +1268,19 @@ export const excluirAtestado = mutation({
|
||||
args.id
|
||||
);
|
||||
|
||||
// Remover ajustes automáticos relacionados ao atestado excluído
|
||||
try {
|
||||
await ctx.runMutation(internal.pontos.removerAjustesAutomaticosInternal, {
|
||||
funcionarioId,
|
||||
motivoTipo: 'atestado',
|
||||
motivoId: atestadoId,
|
||||
dataInicio,
|
||||
dataFim
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[excluirAtestado] Erro ao remover ajustes automáticos:', error);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -941,10 +941,13 @@ export const excluirSolicitacao = mutation({
|
||||
throw new Error('Solicitação não encontrada');
|
||||
}
|
||||
|
||||
// Apenas solicitações ainda não processadas podem ser excluídas
|
||||
if (solicitacao.status !== 'aguardando_aprovacao') {
|
||||
throw new Error('Apenas solicitações pendentes podem ser excluídas');
|
||||
}
|
||||
// IMPORTANTE: Salvar o período exato da ausência ANTES de excluir
|
||||
// para recalcular o banco de horas apenas para esse período específico
|
||||
const funcionarioId = solicitacao.funcionarioId;
|
||||
const dataInicio = solicitacao.dataInicio;
|
||||
const dataFim = solicitacao.dataFim;
|
||||
const statusOriginal = solicitacao.status;
|
||||
const ausenciaId = args.solicitacaoId.toString(); // ID da ausência para remover ajustes
|
||||
|
||||
// Verificar se o usuário é o criador original da solicitação
|
||||
const usuario = await ctx.db.get(args.usuarioId);
|
||||
@@ -963,7 +966,34 @@ export const excluirSolicitacao = mutation({
|
||||
throw new Error('Você não tem permissão para excluir esta solicitação');
|
||||
}
|
||||
|
||||
// Permitir exclusão de ausências aprovadas (não apenas pendentes)
|
||||
// Se estiver aprovada, o gestor pode excluir para corrigir erros
|
||||
if (statusOriginal === 'aprovado' && !usuarioEhGestor) {
|
||||
throw new Error('Apenas o gestor pode excluir ausências aprovadas');
|
||||
}
|
||||
|
||||
// Excluir o registro do banco de dados
|
||||
await ctx.db.delete(args.solicitacaoId);
|
||||
|
||||
// Remover ajustes automáticos relacionados à ausência excluída (apenas se estava aprovada)
|
||||
if (statusOriginal === 'aprovado') {
|
||||
try {
|
||||
await ctx.runMutation(internal.pontos.removerAjustesAutomaticosInternal, {
|
||||
funcionarioId,
|
||||
motivoTipo: 'ausencia',
|
||||
motivoId: ausenciaId,
|
||||
dataInicio,
|
||||
dataFim
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[excluirSolicitacao] Erro ao remover ajustes automáticos:', error);
|
||||
}
|
||||
|
||||
// Recalcular banco de horas APENAS para o período específico da ausência excluída
|
||||
// Isso garante que os dias da ausência sejam removidos corretamente dos registros de ponto
|
||||
await recalcularBancoHorasPeriodo(ctx, funcionarioId, dataInicio, dataFim);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,12 +1,43 @@
|
||||
import { v } from 'convex/values';
|
||||
import { mutation, query, internalMutation } from './_generated/server';
|
||||
import { internal } from './_generated/api';
|
||||
import type { MutationCtx } from './_generated/server';
|
||||
import { Id, Doc } from './_generated/dataModel';
|
||||
import { verificarLicencaAtiva } from './atestadosLicencas';
|
||||
import { getCurrentUserFunction } from './auth';
|
||||
import { formatarDataBR } from './utils/datas';
|
||||
import { api } from './_generated/api';
|
||||
|
||||
// Helper: 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]!
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Validador para períodos
|
||||
const periodoValidator = v.object({
|
||||
dataInicio: v.string(),
|
||||
@@ -878,6 +909,26 @@ export const atualizarStatus = mutation({
|
||||
);
|
||||
}
|
||||
|
||||
// Se o status foi alterado para Cancelado_RH, recalcular banco de horas
|
||||
// para garantir que os dias de férias sejam removidos do registro de ponto
|
||||
if (args.novoStatus === 'Cancelado_RH') {
|
||||
// IMPORTANTE: Recalcular banco de horas para o período das férias canceladas
|
||||
// Isso garante que os dias de férias sejam removidos corretamente dos registros de ponto
|
||||
try {
|
||||
await recalcularBancoHorasPeriodo(
|
||||
ctx,
|
||||
registro.funcionarioId,
|
||||
registro.dataInicio,
|
||||
registro.dataFim
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'[ferias.atualizarStatus] Erro ao recalcular banco de horas após cancelamento:',
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Se o status foi alterado para Cancelado_RH, notificar o funcionário
|
||||
if (args.novoStatus === 'Cancelado_RH') {
|
||||
const funcionario = await ctx.db.get(registro.funcionarioId);
|
||||
|
||||
@@ -1535,6 +1535,54 @@ async function verificarAusenciaAprovada(
|
||||
return { temAusencia: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove ajustes automáticos relacionados a um registro excluído
|
||||
* Busca e remove ajustes que referenciam o motivoId fornecido
|
||||
*/
|
||||
async function removerAjustesAutomaticos(
|
||||
ctx: MutationCtx,
|
||||
funcionarioId: Id<'funcionarios'>,
|
||||
motivoTipo: 'atestado' | 'licenca' | 'ausencia',
|
||||
motivoId: string,
|
||||
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);
|
||||
}
|
||||
|
||||
// Buscar todos os ajustes automáticos relacionados ao motivoId no período
|
||||
for (const data of datas) {
|
||||
const ajustes = await ctx.db
|
||||
.query('ajustesBancoHoras')
|
||||
.filter((q) =>
|
||||
q.and(
|
||||
q.eq(q.field('funcionarioId'), funcionarioId),
|
||||
q.eq(q.field('dataAplicacao'), data),
|
||||
q.eq(q.field('motivoTipo'), motivoTipo),
|
||||
q.eq(q.field('motivoId'), motivoId),
|
||||
q.eq(q.field('aplicado'), true)
|
||||
)
|
||||
)
|
||||
.collect();
|
||||
|
||||
// Remover cada ajuste encontrado
|
||||
for (const ajuste of ajustes) {
|
||||
await ctx.db.delete(ajuste._id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica ajustes manuais aplicados no dia
|
||||
*/
|
||||
@@ -1693,10 +1741,13 @@ async function atualizarBancoHoras(
|
||||
const ajustesIds: Array<Id<'ajustesBancoHoras'>> = [];
|
||||
|
||||
// Aplicar ajustes automáticos se houver atestado, licença ou ausência
|
||||
if (atestadoInfo.temAtestado) {
|
||||
tipoDia = 'atestado';
|
||||
motivoAbono = atestadoInfo.motivo;
|
||||
if (atestadoInfo.atestadoId) {
|
||||
// IMPORTANTE: Verificar se o registro ainda existe antes de criar ajuste
|
||||
if (atestadoInfo.temAtestado && atestadoInfo.atestadoId) {
|
||||
// Verificar se o atestado ainda existe no banco
|
||||
const atestado = await ctx.db.get(atestadoInfo.atestadoId as Id<'atestados'>);
|
||||
if (atestado) {
|
||||
tipoDia = 'atestado';
|
||||
motivoAbono = atestadoInfo.motivo;
|
||||
const ajusteId = await aplicarAjusteAutomatico(
|
||||
ctx,
|
||||
funcionarioId,
|
||||
@@ -1708,10 +1759,12 @@ async function atualizarBancoHoras(
|
||||
);
|
||||
ajustesIds.push(ajusteId);
|
||||
}
|
||||
} else if (licencaInfo.temLicenca) {
|
||||
tipoDia = 'licenca';
|
||||
motivoAbono = licencaInfo.motivo;
|
||||
if (licencaInfo.licencaId) {
|
||||
} else if (licencaInfo.temLicenca && licencaInfo.licencaId) {
|
||||
// Verificar se a licença ainda existe no banco
|
||||
const licenca = await ctx.db.get(licencaInfo.licencaId as Id<'licencas'>);
|
||||
if (licenca) {
|
||||
tipoDia = 'licenca';
|
||||
motivoAbono = licencaInfo.motivo;
|
||||
const ajusteId = await aplicarAjusteAutomatico(
|
||||
ctx,
|
||||
funcionarioId,
|
||||
@@ -1723,10 +1776,12 @@ async function atualizarBancoHoras(
|
||||
);
|
||||
ajustesIds.push(ajusteId);
|
||||
}
|
||||
} else if (ausenciaInfo.temAusencia) {
|
||||
tipoDia = 'ausencia';
|
||||
motivoAbono = ausenciaInfo.motivo;
|
||||
if (ausenciaInfo.ausenciaId) {
|
||||
} else if (ausenciaInfo.temAusencia && ausenciaInfo.ausenciaId) {
|
||||
// Verificar se a ausência ainda existe no banco e está aprovada
|
||||
const ausencia = await ctx.db.get(ausenciaInfo.ausenciaId as Id<'solicitacoesAusencias'>);
|
||||
if (ausencia && ausencia.status === 'aprovado') {
|
||||
tipoDia = 'ausencia';
|
||||
motivoAbono = ausenciaInfo.motivo;
|
||||
const ajusteId = await aplicarAjusteAutomatico(
|
||||
ctx,
|
||||
funcionarioId,
|
||||
@@ -4238,6 +4293,31 @@ export const recalcularBancoHoras = mutation({
|
||||
/**
|
||||
* Mutation interna para recalcular banco de horas de uma data específica
|
||||
*/
|
||||
/**
|
||||
* Internal mutation para remover ajustes automáticos relacionados a um registro excluído
|
||||
*/
|
||||
export const removerAjustesAutomaticosInternal = internalMutation({
|
||||
args: {
|
||||
funcionarioId: v.id('funcionarios'),
|
||||
motivoTipo: v.union(v.literal('atestado'), v.literal('licenca'), v.literal('ausencia')),
|
||||
motivoId: v.string(),
|
||||
dataInicio: v.string(),
|
||||
dataFim: v.string()
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
await removerAjustesAutomaticos(
|
||||
ctx,
|
||||
args.funcionarioId,
|
||||
args.motivoTipo,
|
||||
args.motivoId,
|
||||
args.dataInicio,
|
||||
args.dataFim
|
||||
);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
export const recalcularBancoHorasData = internalMutation({
|
||||
args: {
|
||||
funcionarioId: v.id('funcionarios'),
|
||||
|
||||
Reference in New Issue
Block a user