feat: enhance Banco de Horas management with new reporting features, including adjustments and inconsistencies tracking, advanced filters, and Excel export functionality
This commit is contained in:
@@ -204,11 +204,26 @@ export const pontoTables = {
|
||||
horasTrabalhadas: v.number(), // Horas realmente trabalhadas (em minutos)
|
||||
saldoMinutos: v.number(), // Saldo do dia (positivo = horas extras, negativo = déficit)
|
||||
registrosPontoIds: v.array(v.id('registrosPonto')), // IDs dos registros do dia
|
||||
// Novos campos para sistema avançado
|
||||
ajustesIds: v.optional(v.array(v.id('ajustesBancoHoras'))), // IDs dos ajustes aplicados no dia
|
||||
motivoAbono: v.optional(v.string()), // Motivo do abono (atestado, licença, ausência, etc.)
|
||||
tipoDia: v.optional(
|
||||
v.union(
|
||||
v.literal('normal'),
|
||||
v.literal('atestado'),
|
||||
v.literal('licenca'),
|
||||
v.literal('ausencia'),
|
||||
v.literal('abonado'),
|
||||
v.literal('descontado')
|
||||
)
|
||||
), // Tipo do dia
|
||||
inconsistenciasIds: v.optional(v.array(v.id('inconsistenciasBancoHoras'))), // IDs de inconsistências detectadas
|
||||
calculadoEm: v.number()
|
||||
})
|
||||
.index('by_funcionario_data', ['funcionarioId', 'data'])
|
||||
.index('by_funcionario', ['funcionarioId'])
|
||||
.index('by_data', ['data']),
|
||||
.index('by_data', ['data'])
|
||||
.index('by_tipo_dia', ['tipoDia']),
|
||||
|
||||
// Banco de Horas Mensal - Agregação mensal do banco de horas
|
||||
bancoHorasMensal: defineTable({
|
||||
@@ -220,6 +235,11 @@ export const pontoTables = {
|
||||
diasTrabalhados: v.number(), // Quantidade de dias com registros no mês
|
||||
horasExtras: v.number(), // Total de minutos positivos do mês
|
||||
horasDeficit: v.number(), // Total de minutos negativos do mês (valor absoluto)
|
||||
// Novos campos para sistema avançado
|
||||
totalAjustes: v.optional(v.number()), // Total de ajustes aplicados no mês (em minutos)
|
||||
totalAbonos: v.optional(v.number()), // Total de abonos no mês (em minutos)
|
||||
totalDescontos: v.optional(v.number()), // Total de descontos no mês (em minutos)
|
||||
inconsistenciasResolvidas: v.optional(v.number()), // Quantidade de inconsistências resolvidas
|
||||
calculadoEm: v.number(),
|
||||
atualizadoEm: v.number()
|
||||
})
|
||||
@@ -279,5 +299,119 @@ export const pontoTables = {
|
||||
.index('by_gestor', ['gestorId'])
|
||||
.index('by_ativo', ['ativo'])
|
||||
.index('by_data_inicio', ['dataInicio'])
|
||||
.index('by_data_fim', ['dataFim'])
|
||||
.index('by_data_fim', ['dataFim']),
|
||||
|
||||
// Configuração de Banco de Horas - Configurações gerais do sistema
|
||||
configuracaoBancoHoras: defineTable({
|
||||
// Limites de saldo
|
||||
limiteSaldoPositivoMinutos: v.optional(v.number()), // Limite máximo de saldo positivo (em minutos)
|
||||
limiteSaldoNegativoMinutos: v.optional(v.number()), // Limite máximo de saldo negativo (em minutos)
|
||||
// Regras de cálculo
|
||||
considerarAjustesAutomaticos: v.optional(v.boolean()), // Se deve considerar ajustes automáticos (atestados, licenças, ausências)
|
||||
// Periodicidade de verificação
|
||||
periodicidadeVerificacao: v.optional(
|
||||
v.union(v.literal('diario'), v.literal('semanal'), v.literal('mensal'))
|
||||
),
|
||||
// Metadados
|
||||
atualizadoPor: v.id('usuarios'),
|
||||
atualizadoEm: v.number()
|
||||
}).index('by_ativo', ['atualizadoEm']),
|
||||
|
||||
// Alertas de Banco de Horas - Configuração de alertas por tipo
|
||||
alertasBancoHoras: defineTable({
|
||||
tipoAlerta: v.union(
|
||||
v.literal('saldo_negativo'),
|
||||
v.literal('saldo_negativo_critico'),
|
||||
v.literal('inconsistencia_detectada'),
|
||||
v.literal('dias_sem_registro'),
|
||||
v.literal('limite_saldo_excedido')
|
||||
),
|
||||
// Periodicidade
|
||||
periodicidade: v.union(v.literal('diario'), v.literal('semanal'), v.literal('mensal')),
|
||||
// Canais de envio
|
||||
enviarEmail: v.boolean(),
|
||||
enviarChat: v.boolean(),
|
||||
// Destinatários específicos (opcional - se vazio, envia para gestor padrão)
|
||||
destinatariosEmail: v.optional(v.array(v.id('usuarios'))), // IDs de usuários que receberão email
|
||||
destinatariosChat: v.optional(v.array(v.id('usuarios'))), // IDs de usuários que receberão chat
|
||||
// Thresholds e limites
|
||||
threshold: v.optional(v.number()), // Valor limite para disparar alerta
|
||||
limiteMinutos: v.optional(v.number()), // Limite em minutos (para saldo negativo)
|
||||
// Status
|
||||
ativo: v.boolean(),
|
||||
// Metadados
|
||||
criadoPor: v.id('usuarios'),
|
||||
criadoEm: v.number(),
|
||||
atualizadoPor: v.optional(v.id('usuarios')),
|
||||
atualizadoEm: v.optional(v.number())
|
||||
})
|
||||
.index('by_tipo', ['tipoAlerta'])
|
||||
.index('by_ativo', ['ativo'])
|
||||
.index('by_tipo_ativo', ['tipoAlerta', 'ativo']),
|
||||
|
||||
// Ajustes de Banco de Horas - Registro de ajustes manuais e automáticos
|
||||
ajustesBancoHoras: defineTable({
|
||||
funcionarioId: v.id('funcionarios'),
|
||||
tipo: v.union(v.literal('abonar'), v.literal('descontar'), v.literal('compensar')),
|
||||
// Motivo vinculado
|
||||
motivoTipo: v.optional(
|
||||
v.union(
|
||||
v.literal('atestado'),
|
||||
v.literal('licenca'),
|
||||
v.literal('ausencia'),
|
||||
v.literal('manual')
|
||||
)
|
||||
),
|
||||
motivoId: v.optional(v.string()), // ID do atestado, licença, ausência ou null para manual
|
||||
motivoDescricao: v.optional(v.string()), // Descrição do motivo
|
||||
// Valor do ajuste
|
||||
valorMinutos: v.number(), // Valor em minutos (positivo para abonar, negativo para descontar)
|
||||
// Data de aplicação
|
||||
dataAplicacao: v.string(), // YYYY-MM-DD
|
||||
// Gestor responsável (null se automático)
|
||||
gestorId: v.optional(v.id('usuarios')),
|
||||
// Observações
|
||||
observacoes: v.optional(v.string()),
|
||||
// Status
|
||||
aplicado: v.boolean(), // Se já foi aplicado ao banco de horas
|
||||
// Metadados
|
||||
criadoEm: v.number(),
|
||||
aplicadoEm: v.optional(v.number())
|
||||
})
|
||||
.index('by_funcionario', ['funcionarioId'])
|
||||
.index('by_data_aplicacao', ['dataAplicacao'])
|
||||
.index('by_funcionario_data', ['funcionarioId', 'dataAplicacao'])
|
||||
.index('by_tipo', ['tipo'])
|
||||
.index('by_aplicado', ['aplicado'])
|
||||
.index('by_gestor', ['gestorId']),
|
||||
|
||||
// Inconsistências de Banco de Horas - Registro de inconsistências detectadas
|
||||
inconsistenciasBancoHoras: defineTable({
|
||||
funcionarioId: v.id('funcionarios'),
|
||||
tipo: v.union(
|
||||
v.literal('ponto_com_atestado'),
|
||||
v.literal('ponto_com_licenca'),
|
||||
v.literal('ponto_com_ausencia'),
|
||||
v.literal('registro_duplicado'),
|
||||
v.literal('sequencia_invalida'),
|
||||
v.literal('saldo_inconsistente')
|
||||
),
|
||||
descricao: v.string(), // Descrição detalhada da inconsistência
|
||||
dataDetectada: v.string(), // YYYY-MM-DD
|
||||
dataInconsistencia: v.string(), // YYYY-MM-DD (data do dia com inconsistência)
|
||||
// Status
|
||||
status: v.union(v.literal('pendente'), v.literal('resolvida'), v.literal('ignorada')),
|
||||
// Resolução
|
||||
resolucao: v.optional(v.string()), // Descrição da resolução
|
||||
resolvidoPor: v.optional(v.id('usuarios')), // Usuário que resolveu
|
||||
resolvidoEm: v.optional(v.number()), // Timestamp da resolução
|
||||
// Metadados
|
||||
criadoEm: v.number()
|
||||
})
|
||||
.index('by_funcionario', ['funcionarioId'])
|
||||
.index('by_status', ['status'])
|
||||
.index('by_funcionario_status', ['funcionarioId', 'status'])
|
||||
.index('by_data_detectada', ['dataDetectada'])
|
||||
.index('by_tipo', ['tipo'])
|
||||
.index('by_data_inconsistencia', ['dataInconsistencia'])
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user