feat: update ESLint and TypeScript configurations across frontend and backend; enhance component structure and improve data handling in various modules
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import { v } from "convex/values";
|
||||
import { query } from "./_generated/server";
|
||||
import { Id } from "./_generated/dataModel";
|
||||
import type { QueryCtx } from "./_generated/server";
|
||||
import { v } from 'convex/values';
|
||||
import { query } from './_generated/server';
|
||||
import { Id } from './_generated/dataModel';
|
||||
import type { QueryCtx } from './_generated/server';
|
||||
|
||||
/**
|
||||
* SISTEMA DE CÁLCULO DE SALDO DE FÉRIAS
|
||||
* Suporte a múltiplos regimes de trabalho
|
||||
*
|
||||
*
|
||||
* ============================================
|
||||
* REGRAS CLT (Consolidação das Leis do Trabalho):
|
||||
* ============================================
|
||||
@@ -17,7 +17,7 @@ import type { QueryCtx } from "./_generated/server";
|
||||
* - Um período deve ter no mínimo 14 dias
|
||||
* - Demais períodos: mínimo 5 dias cada
|
||||
* - Abono pecuniário: vender 1/3 das férias (10 dias) - OPCIONAL
|
||||
*
|
||||
*
|
||||
* ============================================
|
||||
* REGRAS SERVIDOR PÚBLICO ESTADUAL DE PERNAMBUCO
|
||||
* Lei nº 6.123/1968 - Estatuto dos Funcionários Públicos Civis do Estado de PE
|
||||
@@ -37,448 +37,444 @@ import type { QueryCtx } from "./_generated/server";
|
||||
* - Seguem as mesmas diretrizes do regime estadual acima
|
||||
*/
|
||||
|
||||
type RegimeTrabalho = "clt" | "estatutario_pe" | "estatutario_federal" | "estatutario_municipal";
|
||||
type RegimeTrabalho = 'clt' | 'estatutario_pe' | 'estatutario_federal' | 'estatutario_municipal';
|
||||
|
||||
// Configurações por regime
|
||||
const REGIMES_CONFIG = {
|
||||
clt: {
|
||||
nome: "CLT - Consolidação das Leis do Trabalho",
|
||||
maxPeriodos: 3,
|
||||
minDiasPeriodo: 5,
|
||||
minDiasPeriodoPrincipal: 14,
|
||||
abonoPermitido: true,
|
||||
maxDiasAbono: 10,
|
||||
},
|
||||
estatutario_pe: {
|
||||
nome: "Servidor Público Estadual de Pernambuco",
|
||||
maxPeriodos: 2,
|
||||
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",
|
||||
maxPeriodos: 3,
|
||||
minDiasPeriodo: 5,
|
||||
minDiasPeriodoPrincipal: 14,
|
||||
abonoPermitido: true,
|
||||
maxDiasAbono: 10,
|
||||
},
|
||||
estatutario_municipal: {
|
||||
nome: "Servidor Público Municipal",
|
||||
maxPeriodos: 2,
|
||||
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
|
||||
},
|
||||
clt: {
|
||||
nome: 'CLT - Consolidação das Leis do Trabalho',
|
||||
maxPeriodos: 3,
|
||||
minDiasPeriodo: 5,
|
||||
minDiasPeriodoPrincipal: 14,
|
||||
abonoPermitido: true,
|
||||
maxDiasAbono: 10
|
||||
},
|
||||
estatutario_pe: {
|
||||
nome: 'Servidor Público Estadual de Pernambuco',
|
||||
maxPeriodos: 2,
|
||||
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',
|
||||
maxPeriodos: 3,
|
||||
minDiasPeriodo: 5,
|
||||
minDiasPeriodoPrincipal: 14,
|
||||
abonoPermitido: true,
|
||||
maxDiasAbono: 10
|
||||
},
|
||||
estatutario_municipal: {
|
||||
nome: 'Servidor Público Municipal',
|
||||
maxPeriodos: 2,
|
||||
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
|
||||
}
|
||||
};
|
||||
|
||||
// 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; // +1 para incluir ambos os dias
|
||||
return diffDays;
|
||||
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; // +1 para incluir ambos os dias
|
||||
return diffDays;
|
||||
}
|
||||
|
||||
// Helper: Calcular data de fim do período aquisitivo
|
||||
function calcularDataFimPeriodo(dataAdmissao: string, anosPassados: number): string {
|
||||
const dataInicio = new Date(dataAdmissao);
|
||||
dataInicio.setFullYear(dataInicio.getFullYear() + anosPassados);
|
||||
return dataInicio.toISOString().split('T')[0];
|
||||
const dataInicio = new Date(dataAdmissao);
|
||||
dataInicio.setFullYear(dataInicio.getFullYear() + anosPassados);
|
||||
return dataInicio.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
// Helper: Obter regime de trabalho do funcionário
|
||||
async function obterRegimeTrabalho(ctx: QueryCtx, funcionarioId: Id<"funcionarios">): Promise<RegimeTrabalho> {
|
||||
const funcionario = await ctx.db.get(funcionarioId);
|
||||
return funcionario?.regimeTrabalho || "clt"; // Default CLT
|
||||
async function obterRegimeTrabalho(
|
||||
ctx: QueryCtx,
|
||||
funcionarioId: Id<'funcionarios'>
|
||||
): Promise<RegimeTrabalho> {
|
||||
const funcionario = await ctx.db.get(funcionarioId);
|
||||
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)
|
||||
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";
|
||||
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 funcionario = await ctx.db.get(funcionarioId);
|
||||
if (!funcionario || !funcionario.admissaoData) return null;
|
||||
|
||||
const regime = funcionario.regimeTrabalho || "clt";
|
||||
const config = REGIMES_CONFIG[regime];
|
||||
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();
|
||||
// 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
|
||||
if (anosDesdeAdmissao < 1) return null; // Ainda não tem direito
|
||||
|
||||
const dataInicio = calcularDataFimPeriodo(
|
||||
funcionario.admissaoData,
|
||||
anosDesdeAdmissao - 1
|
||||
);
|
||||
const dataFim = calcularDataFimPeriodo(
|
||||
funcionario.admissaoData,
|
||||
anosDesdeAdmissao
|
||||
);
|
||||
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();
|
||||
// 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;
|
||||
// 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 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 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 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;
|
||||
// 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";
|
||||
}
|
||||
// Determinar status do período
|
||||
const hoje = new Date();
|
||||
const dataFimPeriodo = new Date(dataFim);
|
||||
let status: 'ativo' | 'vencido' | 'concluido';
|
||||
|
||||
return {
|
||||
diasDireito,
|
||||
diasUsados,
|
||||
diasPendentes,
|
||||
diasDisponiveis,
|
||||
diasAbono,
|
||||
dataInicio,
|
||||
dataFim,
|
||||
status,
|
||||
};
|
||||
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
|
||||
*/
|
||||
export const obterSaldo = query({
|
||||
args: {
|
||||
funcionarioId: v.id("funcionarios"),
|
||||
anoReferencia: v.number(),
|
||||
},
|
||||
returns: v.union(
|
||||
v.object({
|
||||
anoReferencia: v.number(),
|
||||
diasDireito: v.number(),
|
||||
diasUsados: v.number(),
|
||||
diasPendentes: v.number(),
|
||||
diasDisponiveis: v.number(),
|
||||
diasAbono: v.number(),
|
||||
abonoPermitido: v.boolean(),
|
||||
status: v.union(v.literal("ativo"), v.literal("vencido"), v.literal("concluido")),
|
||||
dataInicio: v.string(),
|
||||
dataFim: v.string(),
|
||||
regimeTrabalho: v.string(),
|
||||
}),
|
||||
v.null()
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
const saldo = await calcularSaldo(ctx, args.funcionarioId, args.anoReferencia);
|
||||
if (!saldo) return null;
|
||||
args: {
|
||||
funcionarioId: v.id('funcionarios'),
|
||||
anoReferencia: v.number()
|
||||
},
|
||||
returns: v.union(
|
||||
v.object({
|
||||
anoReferencia: v.number(),
|
||||
diasDireito: v.number(),
|
||||
diasUsados: v.number(),
|
||||
diasPendentes: v.number(),
|
||||
diasDisponiveis: v.number(),
|
||||
diasAbono: v.number(),
|
||||
abonoPermitido: v.boolean(),
|
||||
status: v.union(v.literal('ativo'), v.literal('vencido'), v.literal('concluido')),
|
||||
dataInicio: v.string(),
|
||||
dataFim: v.string(),
|
||||
regimeTrabalho: v.string()
|
||||
}),
|
||||
v.null()
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
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];
|
||||
const funcionario = await ctx.db.get(args.funcionarioId);
|
||||
const regime = funcionario?.regimeTrabalho || 'clt';
|
||||
const config = REGIMES_CONFIG[regime];
|
||||
|
||||
return {
|
||||
anoReferencia: args.anoReferencia,
|
||||
...saldo,
|
||||
abonoPermitido: config.abonoPermitido,
|
||||
regimeTrabalho: config.nome,
|
||||
};
|
||||
},
|
||||
return {
|
||||
anoReferencia: args.anoReferencia,
|
||||
...saldo,
|
||||
abonoPermitido: config.abonoPermitido,
|
||||
regimeTrabalho: config.nome
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Query: Listar todos os saldos de um funcionário
|
||||
*/
|
||||
export const listarSaldos = query({
|
||||
args: {
|
||||
funcionarioId: v.id("funcionarios"),
|
||||
},
|
||||
returns: v.array(
|
||||
v.object({
|
||||
anoReferencia: v.number(),
|
||||
diasDireito: v.number(),
|
||||
diasUsados: v.number(),
|
||||
diasPendentes: v.number(),
|
||||
diasDisponiveis: v.number(),
|
||||
diasAbono: v.number(),
|
||||
abonoPermitido: v.boolean(),
|
||||
status: v.union(v.literal("ativo"), v.literal("vencido"), v.literal("concluido")),
|
||||
dataInicio: v.string(),
|
||||
dataFim: v.string(),
|
||||
})
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
const funcionario = await ctx.db.get(args.funcionarioId);
|
||||
if (!funcionario || !funcionario.admissaoData) return [];
|
||||
args: {
|
||||
funcionarioId: v.id('funcionarios')
|
||||
},
|
||||
returns: v.array(
|
||||
v.object({
|
||||
anoReferencia: v.number(),
|
||||
diasDireito: v.number(),
|
||||
diasUsados: v.number(),
|
||||
diasPendentes: v.number(),
|
||||
diasDisponiveis: v.number(),
|
||||
diasAbono: v.number(),
|
||||
abonoPermitido: v.boolean(),
|
||||
status: v.union(v.literal('ativo'), v.literal('vencido'), v.literal('concluido')),
|
||||
dataInicio: v.string(),
|
||||
dataFim: v.string()
|
||||
})
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
const funcionario = await ctx.db.get(args.funcionarioId);
|
||||
if (!funcionario || !funcionario.admissaoData) return [];
|
||||
|
||||
const regime = funcionario.regimeTrabalho || "clt";
|
||||
const config = REGIMES_CONFIG[regime];
|
||||
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 dataAdmissao = new Date(funcionario.admissaoData);
|
||||
const anoAtual = new Date().getFullYear();
|
||||
const anosDesdeAdmissao = anoAtual - dataAdmissao.getFullYear();
|
||||
|
||||
const saldos = [];
|
||||
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();
|
||||
// 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;
|
||||
if (anosPeriodo < 1) continue;
|
||||
|
||||
const saldo = await calcularSaldo(ctx, args.funcionarioId, ano);
|
||||
if (saldo) {
|
||||
saldos.push({
|
||||
anoReferencia: ano,
|
||||
...saldo,
|
||||
abonoPermitido: config.abonoPermitido,
|
||||
});
|
||||
}
|
||||
}
|
||||
const saldo = await calcularSaldo(ctx, args.funcionarioId, ano);
|
||||
if (saldo) {
|
||||
saldos.push({
|
||||
anoReferencia: ano,
|
||||
...saldo,
|
||||
abonoPermitido: config.abonoPermitido
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return saldos;
|
||||
},
|
||||
return saldos;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Query: Validar solicitação de férias (regras CLT ou Servidor Público PE)
|
||||
*/
|
||||
export const validarSolicitacao = query({
|
||||
args: {
|
||||
funcionarioId: v.id("funcionarios"),
|
||||
anoReferencia: v.number(),
|
||||
periodos: v.array(
|
||||
v.object({
|
||||
dataInicio: v.string(),
|
||||
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(),
|
||||
erros: v.array(v.string()),
|
||||
avisos: v.array(v.string()),
|
||||
totalDias: v.number(),
|
||||
regimeTrabalho: v.string(),
|
||||
}),
|
||||
handler: async (ctx, args) => {
|
||||
const erros: string[] = [];
|
||||
const avisos: string[] = [];
|
||||
let totalDias = 0;
|
||||
args: {
|
||||
funcionarioId: v.id('funcionarios'),
|
||||
anoReferencia: v.number(),
|
||||
periodos: v.array(
|
||||
v.object({
|
||||
dataInicio: v.string(),
|
||||
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(),
|
||||
erros: v.array(v.string()),
|
||||
avisos: v.array(v.string()),
|
||||
totalDias: v.number(),
|
||||
regimeTrabalho: v.string()
|
||||
}),
|
||||
handler: async (ctx, args) => {
|
||||
const erros: string[] = [];
|
||||
const avisos: string[] = [];
|
||||
let totalDias = 0;
|
||||
|
||||
// Obter regime de trabalho
|
||||
const regime = await obterRegimeTrabalho(ctx, args.funcionarioId);
|
||||
const config = REGIMES_CONFIG[regime];
|
||||
// Obter regime de trabalho
|
||||
const regime = await obterRegimeTrabalho(ctx, args.funcionarioId);
|
||||
const config = REGIMES_CONFIG[regime];
|
||||
|
||||
// Validação 1: Número de períodos
|
||||
if (args.periodos.length === 0) {
|
||||
erros.push("É necessário adicionar pelo menos 1 período de férias");
|
||||
}
|
||||
// Validação 1: Número de períodos
|
||||
if (args.periodos.length === 0) {
|
||||
erros.push('É necessário adicionar pelo menos 1 período de férias');
|
||||
}
|
||||
|
||||
if (args.periodos.length > config.maxPeriodos) {
|
||||
erros.push(
|
||||
`Máximo de ${config.maxPeriodos} períodos permitidos para ${config.nome}`
|
||||
);
|
||||
}
|
||||
if (args.periodos.length > config.maxPeriodos) {
|
||||
erros.push(`Máximo de ${config.maxPeriodos} períodos permitidos para ${config.nome}`);
|
||||
}
|
||||
|
||||
// Calcular dias de cada período e validar
|
||||
const diasPorPeriodo: number[] = [];
|
||||
for (const periodo of args.periodos) {
|
||||
const dias = calcularDiasEntreDatas(periodo.dataInicio, periodo.dataFim);
|
||||
diasPorPeriodo.push(dias);
|
||||
totalDias += dias;
|
||||
// Calcular dias de cada período e validar
|
||||
const diasPorPeriodo: number[] = [];
|
||||
for (const periodo of args.periodos) {
|
||||
const dias = calcularDiasEntreDatas(periodo.dataInicio, periodo.dataFim);
|
||||
diasPorPeriodo.push(dias);
|
||||
totalDias += dias;
|
||||
|
||||
// Validação 2: Mínimo de dias por período
|
||||
if (dias < config.minDiasPeriodo) {
|
||||
erros.push(
|
||||
`Período de ${dias} dias é inválido. Mínimo: ${config.minDiasPeriodo} dias corridos (${config.nome})`
|
||||
);
|
||||
}
|
||||
// Validação 2: Mínimo de dias por período
|
||||
if (dias < config.minDiasPeriodo) {
|
||||
erros.push(
|
||||
`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
|
||||
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.`
|
||||
);
|
||||
}
|
||||
}
|
||||
// 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.`
|
||||
);
|
||||
}
|
||||
// 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.`
|
||||
);
|
||||
}
|
||||
}
|
||||
// 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!);
|
||||
if (!temPeriodo14Dias) {
|
||||
erros.push(
|
||||
`Ao dividir férias em CLT, um período deve ter no mínimo ${config.minDiasPeriodoPrincipal} dias corridos`
|
||||
);
|
||||
}
|
||||
}
|
||||
// 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!);
|
||||
if (!temPeriodo14Dias) {
|
||||
erros.push(
|
||||
`Ao dividir férias em CLT, um período deve ter no mínimo ${config.minDiasPeriodoPrincipal} dias corridos`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
// 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 (!saldo) {
|
||||
erros.push(`Você ainda não tem direito a férias referentes ao ano ${args.anoReferencia}`);
|
||||
} else {
|
||||
// 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 (${saldo.diasDisponiveis} dias)`
|
||||
);
|
||||
}
|
||||
if (!saldo) {
|
||||
erros.push(`Você ainda não tem direito a férias referentes ao ano ${args.anoReferencia}`);
|
||||
} else {
|
||||
// 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 (${saldo.diasDisponiveis} dias)`
|
||||
);
|
||||
}
|
||||
|
||||
// Aviso: Saldo baixo
|
||||
if (saldo.diasDisponiveis < 15 && saldo.diasDisponiveis > totalDias) {
|
||||
avisos.push(
|
||||
`Após essa solicitação, restará ${saldo.diasDisponiveis - totalDias} dias de ${args.anoReferencia}`
|
||||
);
|
||||
}
|
||||
// Aviso: Saldo baixo
|
||||
if (saldo.diasDisponiveis < 15 && saldo.diasDisponiveis > totalDias) {
|
||||
avisos.push(
|
||||
`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(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 ${args.anoReferencia} vence em ${diasAteVencer} dias!`
|
||||
);
|
||||
}
|
||||
// Aviso: Férias vencendo
|
||||
const hoje = new Date();
|
||||
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 ${args.anoReferencia} vence em ${diasAteVencer} dias!`
|
||||
);
|
||||
}
|
||||
|
||||
if (diasAteVencer < 0) {
|
||||
avisos.push(
|
||||
`⚠️ URGENTE: Seu período aquisitivo ${args.anoReferencia} está VENCIDO há ${Math.abs(diasAteVencer)} dias!`
|
||||
);
|
||||
}
|
||||
}
|
||||
if (diasAteVencer < 0) {
|
||||
avisos.push(
|
||||
`⚠️ URGENTE: Seu período aquisitivo ${args.anoReferencia} está VENCIDO há ${Math.abs(diasAteVencer)} dias!`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Validação 5: Verificar conflitos de datas (sobreposição)
|
||||
for (let i = 0; i < args.periodos.length; i++) {
|
||||
for (let j = i + 1; j < args.periodos.length; j++) {
|
||||
const inicio1 = new Date(args.periodos[i].dataInicio);
|
||||
const fim1 = new Date(args.periodos[i].dataFim);
|
||||
const inicio2 = new Date(args.periodos[j].dataInicio);
|
||||
const fim2 = new Date(args.periodos[j].dataFim);
|
||||
// Validação 5: Verificar conflitos de datas (sobreposição)
|
||||
for (let i = 0; i < args.periodos.length; i++) {
|
||||
for (let j = i + 1; j < args.periodos.length; j++) {
|
||||
const inicio1 = new Date(args.periodos[i].dataInicio);
|
||||
const fim1 = new Date(args.periodos[i].dataFim);
|
||||
const inicio2 = new Date(args.periodos[j].dataInicio);
|
||||
const fim2 = new Date(args.periodos[j].dataFim);
|
||||
|
||||
if (
|
||||
(inicio1 <= fim2 && fim1 >= inicio2) ||
|
||||
(inicio2 <= fim1 && fim2 >= inicio1)
|
||||
) {
|
||||
erros.push("Os períodos não podem se sobrepor");
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((inicio1 <= fim2 && fim1 >= inicio2) || (inicio2 <= fim1 && fim2 >= inicio1)) {
|
||||
erros.push('Os períodos não podem se sobrepor');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validação 6: Datas no futuro (aviso)
|
||||
const hoje = new Date();
|
||||
hoje.setHours(0, 0, 0, 0);
|
||||
for (const periodo of args.periodos) {
|
||||
const inicio = new Date(periodo.dataInicio);
|
||||
if (inicio < hoje) {
|
||||
avisos.push("⚠️ Período(s) com data de início no passado ou hoje");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Validação 6: Datas no futuro (aviso)
|
||||
const hoje = new Date();
|
||||
hoje.setHours(0, 0, 0, 0);
|
||||
for (const periodo of args.periodos) {
|
||||
const inicio = new Date(periodo.dataInicio);
|
||||
if (inicio < hoje) {
|
||||
avisos.push('⚠️ Período(s) com data de início no passado ou hoje');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Validação 7: Servidor PE - aviso sobre período preferencial para docentes
|
||||
if (regime === "estatutario_pe" || regime === "estatutario_municipal") {
|
||||
for (const periodo of args.periodos) {
|
||||
const mes = new Date(periodo.dataInicio).getMonth() + 1;
|
||||
if (mes === 12 || mes === 1) {
|
||||
avisos.push("📅 Período preferencial para docentes (20/12 a 10/01)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Validação 7: Servidor PE - aviso sobre período preferencial para docentes
|
||||
if (regime === 'estatutario_pe' || regime === 'estatutario_municipal') {
|
||||
for (const periodo of args.periodos) {
|
||||
const mes = new Date(periodo.dataInicio).getMonth() + 1;
|
||||
if (mes === 12 || mes === 1) {
|
||||
avisos.push('📅 Período preferencial para docentes (20/12 a 10/01)');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
valido: erros.length === 0,
|
||||
erros,
|
||||
avisos,
|
||||
totalDias,
|
||||
regimeTrabalho: config.nome,
|
||||
};
|
||||
},
|
||||
return {
|
||||
valido: erros.length === 0,
|
||||
erros,
|
||||
avisos,
|
||||
totalDias,
|
||||
regimeTrabalho: config.nome
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user