623 lines
19 KiB
TypeScript
623 lines
19 KiB
TypeScript
import { Router, Request, Response } from "express";
|
|
import { db } from "../../db/index";
|
|
import {
|
|
peopleFuncionarios, peopleCargos, peopleDepartamentos, peopleDependentes,
|
|
peopleEventosFolha, peopleFolhaPagamento, peopleFolhaItens, peopleFolhaEventos,
|
|
peopleFerias, peoplePonto, peopleBeneficios, peopleFuncionarioBeneficios,
|
|
peopleTabelasCalculo,
|
|
insertPeopleFuncionarioSchema, insertPeopleCargoSchema, insertPeopleDepartamentoSchema,
|
|
insertPeopleDependenteSchema, insertPeopleEventoFolhaSchema, insertPeopleFolhaPagamentoSchema
|
|
} from "@shared/schema";
|
|
import { eq, and, desc, sql, like } from "drizzle-orm";
|
|
import { z } from "zod";
|
|
|
|
const router = Router();
|
|
|
|
const PEOPLE_SERVICE_URL = process.env.PEOPLE_PYTHON_URL || "http://localhost:8004";
|
|
|
|
// ========== Proxy para serviço Python ==========
|
|
|
|
async function proxyToPeopleService(path: string, method: string = "GET", body?: any) {
|
|
try {
|
|
const response = await fetch(`${PEOPLE_SERVICE_URL}${path}`, {
|
|
method,
|
|
headers: { "Content-Type": "application/json" },
|
|
body: body ? JSON.stringify(body) : undefined,
|
|
});
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error("Erro ao conectar com serviço People:", error);
|
|
throw new Error("Serviço People indisponível");
|
|
}
|
|
}
|
|
|
|
// ========== Health Check ==========
|
|
|
|
router.get("/health", async (req: Request, res: Response) => {
|
|
try {
|
|
const pythonHealth = await proxyToPeopleService("/health");
|
|
res.json({
|
|
status: "healthy",
|
|
database: "connected",
|
|
pythonService: pythonHealth
|
|
});
|
|
} catch (error) {
|
|
res.json({
|
|
status: "partial",
|
|
database: "connected",
|
|
pythonService: "unavailable"
|
|
});
|
|
}
|
|
});
|
|
|
|
// ========== Cargos ==========
|
|
|
|
router.get("/cargos", async (req: Request, res: Response) => {
|
|
try {
|
|
const { tenantId } = req.query;
|
|
|
|
let query = db.select().from(peopleCargos);
|
|
|
|
if (tenantId) {
|
|
query = query.where(eq(peopleCargos.tenantId, Number(tenantId))) as any;
|
|
}
|
|
|
|
const cargos = await query.orderBy(peopleCargos.nome);
|
|
res.json(cargos);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao listar cargos" });
|
|
}
|
|
});
|
|
|
|
router.post("/cargos", async (req: Request, res: Response) => {
|
|
try {
|
|
const validatedData = insertPeopleCargoSchema.parse(req.body);
|
|
|
|
const [cargo] = await db.insert(peopleCargos)
|
|
.values(validatedData)
|
|
.returning();
|
|
|
|
res.status(201).json(cargo);
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
return res.status(400).json({ error: "Dados inválidos", details: error.errors });
|
|
}
|
|
res.status(500).json({ error: "Erro ao criar cargo" });
|
|
}
|
|
});
|
|
|
|
router.put("/cargos/:id", async (req: Request, res: Response) => {
|
|
try {
|
|
const [cargo] = await db.update(peopleCargos)
|
|
.set(req.body)
|
|
.where(eq(peopleCargos.id, Number(req.params.id)))
|
|
.returning();
|
|
|
|
res.json(cargo);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao atualizar cargo" });
|
|
}
|
|
});
|
|
|
|
router.delete("/cargos/:id", async (req: Request, res: Response) => {
|
|
try {
|
|
await db.delete(peopleCargos)
|
|
.where(eq(peopleCargos.id, Number(req.params.id)));
|
|
|
|
res.json({ message: "Cargo excluído" });
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao excluir cargo" });
|
|
}
|
|
});
|
|
|
|
// ========== Departamentos ==========
|
|
|
|
router.get("/departamentos", async (req: Request, res: Response) => {
|
|
try {
|
|
const { tenantId } = req.query;
|
|
|
|
const departamentos = await db.select().from(peopleDepartamentos)
|
|
.orderBy(peopleDepartamentos.nome);
|
|
|
|
res.json(departamentos);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao listar departamentos" });
|
|
}
|
|
});
|
|
|
|
router.post("/departamentos", async (req: Request, res: Response) => {
|
|
try {
|
|
const validatedData = insertPeopleDepartamentoSchema.parse(req.body);
|
|
|
|
const [departamento] = await db.insert(peopleDepartamentos)
|
|
.values(validatedData)
|
|
.returning();
|
|
|
|
res.status(201).json(departamento);
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
return res.status(400).json({ error: "Dados inválidos", details: error.errors });
|
|
}
|
|
res.status(500).json({ error: "Erro ao criar departamento" });
|
|
}
|
|
});
|
|
|
|
router.put("/departamentos/:id", async (req: Request, res: Response) => {
|
|
try {
|
|
const [departamento] = await db.update(peopleDepartamentos)
|
|
.set(req.body)
|
|
.where(eq(peopleDepartamentos.id, Number(req.params.id)))
|
|
.returning();
|
|
|
|
res.json(departamento);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao atualizar departamento" });
|
|
}
|
|
});
|
|
|
|
router.delete("/departamentos/:id", async (req: Request, res: Response) => {
|
|
try {
|
|
await db.delete(peopleDepartamentos)
|
|
.where(eq(peopleDepartamentos.id, Number(req.params.id)));
|
|
|
|
res.json({ message: "Departamento excluído" });
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao excluir departamento" });
|
|
}
|
|
});
|
|
|
|
// ========== Funcionários ==========
|
|
|
|
router.get("/funcionarios", async (req: Request, res: Response) => {
|
|
try {
|
|
const { tenantId, status, departamentoId, search } = req.query;
|
|
|
|
let funcionarios = await db.select().from(peopleFuncionarios)
|
|
.orderBy(peopleFuncionarios.nome);
|
|
|
|
if (status) {
|
|
funcionarios = funcionarios.filter(f => f.status === status);
|
|
}
|
|
|
|
if (departamentoId) {
|
|
funcionarios = funcionarios.filter(f => f.departamentoId === Number(departamentoId));
|
|
}
|
|
|
|
if (search) {
|
|
const searchLower = String(search).toLowerCase();
|
|
funcionarios = funcionarios.filter(f =>
|
|
f.nome.toLowerCase().includes(searchLower) ||
|
|
f.cpf?.includes(String(search)) ||
|
|
f.matricula?.includes(String(search))
|
|
);
|
|
}
|
|
|
|
res.json(funcionarios);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao listar funcionários" });
|
|
}
|
|
});
|
|
|
|
router.get("/funcionarios/:id", async (req: Request, res: Response) => {
|
|
try {
|
|
const [funcionario] = await db.select().from(peopleFuncionarios)
|
|
.where(eq(peopleFuncionarios.id, Number(req.params.id)))
|
|
.limit(1);
|
|
|
|
if (!funcionario) {
|
|
return res.status(404).json({ error: "Funcionário não encontrado" });
|
|
}
|
|
|
|
// Buscar dependentes
|
|
const dependentes = await db.select().from(peopleDependentes)
|
|
.where(eq(peopleDependentes.funcionarioId, funcionario.id));
|
|
|
|
// Buscar benefícios
|
|
const beneficios = await db.select().from(peopleFuncionarioBeneficios)
|
|
.where(eq(peopleFuncionarioBeneficios.funcionarioId, funcionario.id));
|
|
|
|
res.json({ ...funcionario, dependentes, beneficios });
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao buscar funcionário" });
|
|
}
|
|
});
|
|
|
|
router.post("/funcionarios", async (req: Request, res: Response) => {
|
|
try {
|
|
const validatedData = insertPeopleFuncionarioSchema.parse(req.body);
|
|
|
|
const [funcionario] = await db.insert(peopleFuncionarios)
|
|
.values(validatedData)
|
|
.returning();
|
|
|
|
res.status(201).json(funcionario);
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
return res.status(400).json({ error: "Dados inválidos", details: error.errors });
|
|
}
|
|
console.error("Erro ao criar funcionário:", error);
|
|
res.status(500).json({ error: "Erro ao criar funcionário" });
|
|
}
|
|
});
|
|
|
|
router.put("/funcionarios/:id", async (req: Request, res: Response) => {
|
|
try {
|
|
const [funcionario] = await db.update(peopleFuncionarios)
|
|
.set({ ...req.body, updatedAt: new Date() })
|
|
.where(eq(peopleFuncionarios.id, Number(req.params.id)))
|
|
.returning();
|
|
|
|
res.json(funcionario);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao atualizar funcionário" });
|
|
}
|
|
});
|
|
|
|
router.delete("/funcionarios/:id", async (req: Request, res: Response) => {
|
|
try {
|
|
await db.delete(peopleFuncionarios)
|
|
.where(eq(peopleFuncionarios.id, Number(req.params.id)));
|
|
|
|
res.json({ message: "Funcionário excluído" });
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao excluir funcionário" });
|
|
}
|
|
});
|
|
|
|
// ========== Dependentes ==========
|
|
|
|
router.get("/funcionarios/:id/dependentes", async (req: Request, res: Response) => {
|
|
try {
|
|
const dependentes = await db.select().from(peopleDependentes)
|
|
.where(eq(peopleDependentes.funcionarioId, Number(req.params.id)));
|
|
|
|
res.json(dependentes);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao listar dependentes" });
|
|
}
|
|
});
|
|
|
|
router.post("/funcionarios/:id/dependentes", async (req: Request, res: Response) => {
|
|
try {
|
|
const [dependente] = await db.insert(peopleDependentes)
|
|
.values({
|
|
...req.body,
|
|
funcionarioId: Number(req.params.id)
|
|
})
|
|
.returning();
|
|
|
|
res.status(201).json(dependente);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao criar dependente" });
|
|
}
|
|
});
|
|
|
|
router.delete("/dependentes/:id", async (req: Request, res: Response) => {
|
|
try {
|
|
await db.delete(peopleDependentes)
|
|
.where(eq(peopleDependentes.id, Number(req.params.id)));
|
|
|
|
res.json({ message: "Dependente excluído" });
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao excluir dependente" });
|
|
}
|
|
});
|
|
|
|
// ========== Eventos de Folha ==========
|
|
|
|
router.get("/eventos-folha", async (req: Request, res: Response) => {
|
|
try {
|
|
const eventos = await db.select().from(peopleEventosFolha)
|
|
.orderBy(peopleEventosFolha.codigo);
|
|
|
|
res.json(eventos);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao listar eventos" });
|
|
}
|
|
});
|
|
|
|
router.post("/eventos-folha", async (req: Request, res: Response) => {
|
|
try {
|
|
const validatedData = insertPeopleEventoFolhaSchema.parse(req.body);
|
|
|
|
const [evento] = await db.insert(peopleEventosFolha)
|
|
.values(validatedData)
|
|
.returning();
|
|
|
|
res.status(201).json(evento);
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
return res.status(400).json({ error: "Dados inválidos", details: error.errors });
|
|
}
|
|
res.status(500).json({ error: "Erro ao criar evento" });
|
|
}
|
|
});
|
|
|
|
// ========== Folha de Pagamento ==========
|
|
|
|
router.get("/folhas", async (req: Request, res: Response) => {
|
|
try {
|
|
const { tenantId, competencia, status } = req.query;
|
|
|
|
let folhas = await db.select().from(peopleFolhaPagamento)
|
|
.orderBy(desc(peopleFolhaPagamento.competencia));
|
|
|
|
if (competencia) {
|
|
folhas = folhas.filter(f => f.competencia === competencia);
|
|
}
|
|
|
|
if (status) {
|
|
folhas = folhas.filter(f => f.status === status);
|
|
}
|
|
|
|
res.json(folhas);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao listar folhas" });
|
|
}
|
|
});
|
|
|
|
router.get("/folhas/:id", async (req: Request, res: Response) => {
|
|
try {
|
|
const [folha] = await db.select().from(peopleFolhaPagamento)
|
|
.where(eq(peopleFolhaPagamento.id, Number(req.params.id)))
|
|
.limit(1);
|
|
|
|
if (!folha) {
|
|
return res.status(404).json({ error: "Folha não encontrada" });
|
|
}
|
|
|
|
const itens = await db.select().from(peopleFolhaItens)
|
|
.where(eq(peopleFolhaItens.folhaId, folha.id));
|
|
|
|
res.json({ ...folha, itens });
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao buscar folha" });
|
|
}
|
|
});
|
|
|
|
router.post("/folhas", async (req: Request, res: Response) => {
|
|
try {
|
|
const validatedData = insertPeopleFolhaPagamentoSchema.parse(req.body);
|
|
|
|
const [folha] = await db.insert(peopleFolhaPagamento)
|
|
.values(validatedData)
|
|
.returning();
|
|
|
|
res.status(201).json(folha);
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
return res.status(400).json({ error: "Dados inválidos", details: error.errors });
|
|
}
|
|
res.status(500).json({ error: "Erro ao criar folha" });
|
|
}
|
|
});
|
|
|
|
// ========== Cálculos via Serviço Python ==========
|
|
|
|
router.get("/tabelas/inss", async (req: Request, res: Response) => {
|
|
try {
|
|
const resultado = await proxyToPeopleService("/tabelas/inss");
|
|
res.json(resultado);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao obter tabela INSS" });
|
|
}
|
|
});
|
|
|
|
router.get("/tabelas/irrf", async (req: Request, res: Response) => {
|
|
try {
|
|
const resultado = await proxyToPeopleService("/tabelas/irrf");
|
|
res.json(resultado);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao obter tabela IRRF" });
|
|
}
|
|
});
|
|
|
|
router.post("/calcular/inss", async (req: Request, res: Response) => {
|
|
try {
|
|
const { salario } = req.body;
|
|
const resultado = await proxyToPeopleService(`/calcular/inss?salario=${salario}`, "POST");
|
|
res.json(resultado);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao calcular INSS" });
|
|
}
|
|
});
|
|
|
|
router.post("/calcular/irrf", async (req: Request, res: Response) => {
|
|
try {
|
|
const { base, dependentes } = req.body;
|
|
const resultado = await proxyToPeopleService(`/calcular/irrf?base=${base}&dependentes=${dependentes || 0}`, "POST");
|
|
res.json(resultado);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao calcular IRRF" });
|
|
}
|
|
});
|
|
|
|
router.post("/calcular/folha", async (req: Request, res: Response) => {
|
|
try {
|
|
const resultado = await proxyToPeopleService("/calcular/folha", "POST", req.body);
|
|
res.json(resultado);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao calcular folha" });
|
|
}
|
|
});
|
|
|
|
router.post("/calcular/ferias", async (req: Request, res: Response) => {
|
|
try {
|
|
const resultado = await proxyToPeopleService("/calcular/ferias", "POST", req.body);
|
|
res.json(resultado);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao calcular férias" });
|
|
}
|
|
});
|
|
|
|
router.post("/calcular/rescisao", async (req: Request, res: Response) => {
|
|
try {
|
|
const resultado = await proxyToPeopleService("/calcular/rescisao", "POST", req.body);
|
|
res.json(resultado);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao calcular rescisão" });
|
|
}
|
|
});
|
|
|
|
router.post("/calcular/ponto", async (req: Request, res: Response) => {
|
|
try {
|
|
const resultado = await proxyToPeopleService("/calcular/ponto", "POST", req.body);
|
|
res.json(resultado);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao calcular ponto" });
|
|
}
|
|
});
|
|
|
|
// ========== eSocial ==========
|
|
|
|
router.get("/esocial/eventos", async (req: Request, res: Response) => {
|
|
try {
|
|
const resultado = await proxyToPeopleService("/esocial/eventos");
|
|
res.json(resultado);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao listar eventos eSocial" });
|
|
}
|
|
});
|
|
|
|
router.post("/esocial/gerar-xml/:evento", async (req: Request, res: Response) => {
|
|
try {
|
|
const resultado = await proxyToPeopleService(`/esocial/gerar-xml/${req.params.evento}`, "POST", req.body);
|
|
res.json(resultado);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao gerar XML eSocial" });
|
|
}
|
|
});
|
|
|
|
// ========== Férias ==========
|
|
|
|
router.get("/ferias", async (req: Request, res: Response) => {
|
|
try {
|
|
const { funcionarioId, status } = req.query;
|
|
|
|
let ferias = await db.select().from(peopleFerias)
|
|
.orderBy(desc(peopleFerias.periodoAquisitivoInicio));
|
|
|
|
if (funcionarioId) {
|
|
ferias = ferias.filter(f => f.funcionarioId === Number(funcionarioId));
|
|
}
|
|
|
|
if (status) {
|
|
ferias = ferias.filter(f => f.status === status);
|
|
}
|
|
|
|
res.json(ferias);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao listar férias" });
|
|
}
|
|
});
|
|
|
|
router.post("/ferias", async (req: Request, res: Response) => {
|
|
try {
|
|
const [ferias] = await db.insert(peopleFerias)
|
|
.values({
|
|
...req.body,
|
|
periodoAquisitivoInicio: new Date(req.body.periodoAquisitivoInicio),
|
|
periodoAquisitivoFim: new Date(req.body.periodoAquisitivoFim),
|
|
dataInicio: req.body.dataInicio ? new Date(req.body.dataInicio) : null,
|
|
dataFim: req.body.dataFim ? new Date(req.body.dataFim) : null,
|
|
})
|
|
.returning();
|
|
|
|
res.status(201).json(ferias);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao registrar férias" });
|
|
}
|
|
});
|
|
|
|
// ========== Ponto ==========
|
|
|
|
router.get("/ponto", async (req: Request, res: Response) => {
|
|
try {
|
|
const { funcionarioId, dataInicio, dataFim } = req.query;
|
|
|
|
let registros = await db.select().from(peoplePonto)
|
|
.orderBy(desc(peoplePonto.data));
|
|
|
|
if (funcionarioId) {
|
|
registros = registros.filter(r => r.funcionarioId === Number(funcionarioId));
|
|
}
|
|
|
|
res.json(registros);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao listar registros de ponto" });
|
|
}
|
|
});
|
|
|
|
router.post("/ponto", async (req: Request, res: Response) => {
|
|
try {
|
|
// Calcular horas via serviço Python
|
|
const calculo = await proxyToPeopleService("/calcular/ponto", "POST", req.body);
|
|
|
|
const [registro] = await db.insert(peoplePonto)
|
|
.values({
|
|
funcionarioId: req.body.funcionarioId,
|
|
data: new Date(req.body.data),
|
|
entrada1: req.body.entrada1,
|
|
saida1: req.body.saida1,
|
|
entrada2: req.body.entrada2 || null,
|
|
saida2: req.body.saida2 || null,
|
|
horasTrabalhadas: calculo.calculo?.horasTrabalhadas?.toString() || "0",
|
|
horasExtras: calculo.calculo?.horasExtras?.toString() || "0",
|
|
horasNoturnas: calculo.calculo?.horasNoturnas?.toString() || "0",
|
|
})
|
|
.returning();
|
|
|
|
res.status(201).json({ ...registro, calculo });
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao registrar ponto" });
|
|
}
|
|
});
|
|
|
|
// ========== Benefícios ==========
|
|
|
|
router.get("/beneficios", async (req: Request, res: Response) => {
|
|
try {
|
|
const beneficios = await db.select().from(peopleBeneficios)
|
|
.orderBy(peopleBeneficios.nome);
|
|
|
|
res.json(beneficios);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao listar benefícios" });
|
|
}
|
|
});
|
|
|
|
router.post("/beneficios", async (req: Request, res: Response) => {
|
|
try {
|
|
const [beneficio] = await db.insert(peopleBeneficios)
|
|
.values(req.body)
|
|
.returning();
|
|
|
|
res.status(201).json(beneficio);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao criar benefício" });
|
|
}
|
|
});
|
|
|
|
// ========== Estatísticas ==========
|
|
|
|
router.get("/stats/:tenantId?", async (req: Request, res: Response) => {
|
|
try {
|
|
const totalFuncionarios = await db.select({ count: sql`count(*)` }).from(peopleFuncionarios);
|
|
const funcionariosAtivos = await db.select({ count: sql`count(*)` }).from(peopleFuncionarios)
|
|
.where(eq(peopleFuncionarios.status, "ativo"));
|
|
const totalCargos = await db.select({ count: sql`count(*)` }).from(peopleCargos);
|
|
const totalDepartamentos = await db.select({ count: sql`count(*)` }).from(peopleDepartamentos);
|
|
|
|
res.json({
|
|
totalFuncionarios: Number(totalFuncionarios[0]?.count || 0),
|
|
funcionariosAtivos: Number(funcionariosAtivos[0]?.count || 0),
|
|
totalCargos: Number(totalCargos[0]?.count || 0),
|
|
totalDepartamentos: Number(totalDepartamentos[0]?.count || 0),
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Erro ao obter estatísticas" });
|
|
}
|
|
});
|
|
|
|
export default router;
|