245 lines
6.8 KiB
TypeScript
245 lines
6.8 KiB
TypeScript
import { v } from 'convex/values';
|
|
import { mutation, query } from './_generated/server';
|
|
import { getCurrentUserFunction } from './auth';
|
|
import type { Id } from './_generated/dataModel';
|
|
|
|
/**
|
|
* Lista todas as associações de endereços para um funcionário
|
|
*/
|
|
export const listarAssociacoesFuncionario = query({
|
|
args: {
|
|
funcionarioId: v.id('funcionarios'),
|
|
incluirInativos: v.optional(v.boolean())
|
|
},
|
|
handler: async (ctx, args) => {
|
|
const usuario = await getCurrentUserFunction(ctx);
|
|
if (!usuario) {
|
|
throw new Error('Usuário não autenticado');
|
|
}
|
|
|
|
let associacoes;
|
|
if (args.incluirInativos) {
|
|
associacoes = await ctx.db
|
|
.query('funcionarioEnderecosMarcacao')
|
|
.withIndex('by_funcionario', (q) => q.eq('funcionarioId', args.funcionarioId))
|
|
.collect();
|
|
} else {
|
|
associacoes = await ctx.db
|
|
.query('funcionarioEnderecosMarcacao')
|
|
.withIndex('by_funcionario_ativo', (q) =>
|
|
q.eq('funcionarioId', args.funcionarioId).eq('ativo', true)
|
|
)
|
|
.collect();
|
|
}
|
|
|
|
// Buscar dados completos dos endereços
|
|
const associacoesCompletas = await Promise.all(
|
|
associacoes.map(async (associacao) => {
|
|
const endereco = await ctx.db.get(associacao.enderecoMarcacaoId);
|
|
if (!endereco) return null;
|
|
|
|
// Usar raio personalizado se disponível, senão usar o raio padrão do endereço
|
|
const raioMetros = associacao.raioMetrosPersonalizado ?? endereco.raioMetros;
|
|
|
|
return {
|
|
...associacao,
|
|
raioMetros, // Raio usado (personalizado ou padrão)
|
|
endereco: {
|
|
_id: endereco._id,
|
|
nome: endereco.nome,
|
|
descricao: endereco.descricao,
|
|
latitude: endereco.latitude,
|
|
longitude: endereco.longitude,
|
|
endereco: endereco.endereco,
|
|
cep: endereco.cep,
|
|
cidade: endereco.cidade,
|
|
estado: endereco.estado,
|
|
pais: endereco.pais,
|
|
raioMetros: endereco.raioMetros, // Raio padrão do endereço
|
|
tipo: endereco.tipo,
|
|
ativo: endereco.ativo
|
|
}
|
|
};
|
|
})
|
|
);
|
|
|
|
return associacoesCompletas.filter((item): item is NonNullable<typeof item> => item !== null);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Associa um endereço a um funcionário
|
|
*/
|
|
export const associarEnderecoFuncionario = mutation({
|
|
args: {
|
|
funcionarioId: v.id('funcionarios'),
|
|
enderecoMarcacaoId: v.id('enderecosMarcacao'),
|
|
raioMetrosPersonalizado: v.optional(v.number()),
|
|
dataInicio: v.optional(v.string()), // YYYY-MM-DD
|
|
dataFim: v.optional(v.string()) // YYYY-MM-DD
|
|
},
|
|
handler: async (ctx, args) => {
|
|
const usuario = await getCurrentUserFunction(ctx);
|
|
if (!usuario) {
|
|
throw new Error('Usuário não autenticado');
|
|
}
|
|
|
|
// TODO: Verificar permissões (apenas TI ou admin)
|
|
|
|
// Validar que o funcionário existe
|
|
const funcionario = await ctx.db.get(args.funcionarioId);
|
|
if (!funcionario) {
|
|
throw new Error('Funcionário não encontrado');
|
|
}
|
|
|
|
// Validar que o endereço existe e está ativo
|
|
const endereco = await ctx.db.get(args.enderecoMarcacaoId);
|
|
if (!endereco) {
|
|
throw new Error('Endereço não encontrado');
|
|
}
|
|
if (!endereco.ativo) {
|
|
throw new Error('Endereço não está ativo');
|
|
}
|
|
|
|
// Validar raio personalizado se fornecido
|
|
if (args.raioMetrosPersonalizado !== undefined) {
|
|
if (args.raioMetrosPersonalizado < 0 || args.raioMetrosPersonalizado > 50000) {
|
|
throw new Error('Raio deve estar entre 0 e 50000 metros');
|
|
}
|
|
}
|
|
|
|
// Validar datas se fornecidas
|
|
if (args.dataInicio && args.dataFim) {
|
|
if (args.dataInicio > args.dataFim) {
|
|
throw new Error('Data de início deve ser anterior à data de fim');
|
|
}
|
|
}
|
|
|
|
// Verificar se já existe uma associação ativa
|
|
const associacaoExistente = await ctx.db
|
|
.query('funcionarioEnderecosMarcacao')
|
|
.withIndex('by_funcionario_ativo', (q) =>
|
|
q.eq('funcionarioId', args.funcionarioId).eq('ativo', true)
|
|
)
|
|
.filter((q) => q.eq(q.field('enderecoMarcacaoId'), args.enderecoMarcacaoId))
|
|
.first();
|
|
|
|
if (associacaoExistente) {
|
|
// Atualizar associação existente
|
|
await ctx.db.patch(associacaoExistente._id, {
|
|
raioMetrosPersonalizado: args.raioMetrosPersonalizado,
|
|
dataInicio: args.dataInicio,
|
|
dataFim: args.dataFim,
|
|
ativo: true
|
|
});
|
|
|
|
return { associacaoId: associacaoExistente._id, atualizado: true };
|
|
}
|
|
|
|
// Criar nova associação
|
|
const associacaoId = await ctx.db.insert('funcionarioEnderecosMarcacao', {
|
|
funcionarioId: args.funcionarioId,
|
|
enderecoMarcacaoId: args.enderecoMarcacaoId,
|
|
raioMetrosPersonalizado: args.raioMetrosPersonalizado,
|
|
dataInicio: args.dataInicio,
|
|
dataFim: args.dataFim,
|
|
ativo: true,
|
|
criadoPor: usuario._id as Id<'usuarios'>,
|
|
criadoEm: Date.now()
|
|
});
|
|
|
|
return { associacaoId, atualizado: false };
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Atualiza uma associação de endereço com funcionário
|
|
*/
|
|
export const atualizarAssociacao = mutation({
|
|
args: {
|
|
associacaoId: v.id('funcionarioEnderecosMarcacao'),
|
|
raioMetrosPersonalizado: v.optional(v.number()),
|
|
dataInicio: v.optional(v.string()),
|
|
dataFim: v.optional(v.string()),
|
|
ativo: v.optional(v.boolean())
|
|
},
|
|
handler: async (ctx, args) => {
|
|
const usuario = await getCurrentUserFunction(ctx);
|
|
if (!usuario) {
|
|
throw new Error('Usuário não autenticado');
|
|
}
|
|
|
|
// TODO: Verificar permissões (apenas TI ou admin)
|
|
|
|
const associacao = await ctx.db.get(args.associacaoId);
|
|
if (!associacao) {
|
|
throw new Error('Associação não encontrada');
|
|
}
|
|
|
|
const atualizacoes: {
|
|
raioMetrosPersonalizado?: number;
|
|
dataInicio?: string;
|
|
dataFim?: string;
|
|
ativo?: boolean;
|
|
} = {};
|
|
|
|
if (args.raioMetrosPersonalizado !== undefined) {
|
|
if (args.raioMetrosPersonalizado < 0 || args.raioMetrosPersonalizado > 50000) {
|
|
throw new Error('Raio deve estar entre 0 e 50000 metros');
|
|
}
|
|
atualizacoes.raioMetrosPersonalizado = args.raioMetrosPersonalizado;
|
|
}
|
|
|
|
if (args.dataInicio !== undefined) {
|
|
atualizacoes.dataInicio = args.dataInicio || undefined;
|
|
}
|
|
|
|
if (args.dataFim !== undefined) {
|
|
atualizacoes.dataFim = args.dataFim || undefined;
|
|
}
|
|
|
|
if (args.dataInicio && args.dataFim) {
|
|
if (args.dataInicio > args.dataFim) {
|
|
throw new Error('Data de início deve ser anterior à data de fim');
|
|
}
|
|
}
|
|
|
|
if (args.ativo !== undefined) {
|
|
atualizacoes.ativo = args.ativo;
|
|
}
|
|
|
|
await ctx.db.patch(args.associacaoId, atualizacoes);
|
|
|
|
return { sucesso: true };
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Remove uma associação de endereço com funcionário (desativa)
|
|
*/
|
|
export const removerAssociacao = mutation({
|
|
args: {
|
|
associacaoId: v.id('funcionarioEnderecosMarcacao')
|
|
},
|
|
handler: async (ctx, args) => {
|
|
const usuario = await getCurrentUserFunction(ctx);
|
|
if (!usuario) {
|
|
throw new Error('Usuário não autenticado');
|
|
}
|
|
|
|
// TODO: Verificar permissões (apenas TI ou admin)
|
|
|
|
const associacao = await ctx.db.get(args.associacaoId);
|
|
if (!associacao) {
|
|
throw new Error('Associação não encontrada');
|
|
}
|
|
|
|
// Desativar ao invés de deletar
|
|
await ctx.db.patch(args.associacaoId, {
|
|
ativo: false
|
|
});
|
|
|
|
return { sucesso: true };
|
|
}
|
|
});
|