first commit

This commit is contained in:
rodrigo committed 2025-04-10 23:07:21 -03:00
commit c9fb67e751
423 files changed
+53287

No files matched your search

+69
View File
@@ -0,0 +1,69 @@
import { Router } from 'express';
import multer from 'multer';
import { prisma } from '../prismaClient';
import { authMiddleware } from '../../middleware/auth';
const { NextCloudService } = require('../../services/nextcloud');
import { randomUUID } from 'crypto';
const upload = multer({ storage: multer.memoryStorage() });
const router = Router();
router.use(authMiddleware);
// Criar registro de anexo
router.post('/', async (req, res) => {
try {
const { nome, tamanho, url, tipo } = req.body;
const anexo = await prisma.anexo.create({
data: {
id: randomUUID(),
nome,
tipo,
tamanho,
url
}
});
res.json(anexo);
} catch (error) {
console.error('Erro ao criar anexo:', error);
res.status(500).json({ error: 'Erro ao criar anexo' });
}
});
// Deletar anexo
router.delete('/:id', async (req, res) => {
try {
await prisma.anexo.delete({
where: { id: req.params.id }
});
res.status(204).send();
} catch (error) {
console.error('Erro ao deletar anexo:', error);
res.status(500).json({ error: 'Erro ao deletar anexo' });
}
});
router.post('/upload', upload.single('file'), async (req, res) => {
try {
if (!req.file || !req.body.clienteNome) {
return res.status(400).json({ error: 'Arquivo e nome do cliente são obrigatórios' });
}
// Preparar caminho do arquivo
const pastaCliente = req.body.clienteNome.toUpperCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
const fileName = `${Date.now()}_${req.file.originalname.replace(/[^a-zA-Z0-9.-]/g, '_')}`;
const filePath = `${pastaCliente}/${fileName}`;
// Upload para o Nextcloud
await NextCloudService.uploadFile(req.file.buffer, filePath);
res.json({ url: filePath });
} catch (error) {
console.error('Erro no upload:', error);
res.status(500).json({ error: 'Erro no upload do arquivo' });
}
});
export default router;
+113
View File
@@ -0,0 +1,113 @@
import { Router } from 'express';
import { prisma } from '../prismaClient';
import { authMiddleware } from '../../middleware/auth';
import multer from 'multer';
import { NextCloudService } from '../../services/nextcloud';
const router = Router();
const upload = multer({ storage: multer.memoryStorage() });
router.use(authMiddleware);
// ...existing routes...
// Rota para atualizar atendimento com anexos
router.patch('/:id', async (req, res) => {
try {
const { id } = req.params;
const { anexos, ...data } = req.body;
console.log('Atualizando atendimento:', id, { anexos, data }); // Debug log
const atendimento = await prisma.atendimento.update({
where: { id },
data: {
...data,
...(anexos && {
anexos: {
createMany: {
data: anexos.map((anexo: any) => ({
nome: anexo.nome,
url: anexo.url,
tipo: anexo.tipo,
tamanho: anexo.tamanho
}))
}
}
})
},
include: {
anexos: true,
cliente: true
}
});
console.log('Atendimento atualizado:', atendimento); // Debug log
res.json(atendimento);
} catch (error) {
console.error('Erro ao atualizar atendimento:', error);
res.status(500).json({ error: 'Erro ao atualizar atendimento' });
}
});
// Nova rota para upload de arquivo via proxy
router.post('/upload', upload.single('file'), async (req, res) => {
try {
if (!req.file || !req.body.clienteNome) {
return res.status(400).json({ error: 'Arquivo e nome do cliente são obrigatórios' });
}
// Enviar para o NextCloud usando a estrutura de pastas correta
const buffer = req.file.buffer;
Object.defineProperty(buffer, 'name', {
value: req.file.originalname,
writable: true,
enumerable: true
});
const filePath = await NextCloudService.uploadFile(
buffer,
req.body.clienteNome,
'atendimento',
req.body.protocolo
);
res.json({ url: filePath });
} catch (error) {
console.error('Erro no upload:', error);
res.status(500).json({ error: 'Erro no upload do arquivo' });
}
});
router.get('/:id', async (req, res) => {
try {
const atendimento = await prisma.atendimento.findUnique({
where: {
id: req.params.id
},
include: {
cliente: true,
processo: true,
usuario: {
select: {
id: true,
nome: true,
oab: true,
oabUF: true
}
}
}
});
if (!atendimento) {
return res.status(404).json({ error: 'Atendimento não encontrado' });
}
res.json(atendimento);
} catch (error) {
console.error('Erro ao buscar atendimento:', error);
res.status(500).json({ error: 'Erro ao buscar atendimento' });
}
});
export default router;
+889
View File
@@ -0,0 +1,889 @@
import express from 'express';
import { CalculoJudicialService } from '../services/CalculoJudicialService';
import { authMiddleware } from '../../middleware/auth';
import { prisma } from '../prismaClient';
import { Decimal } from '@prisma/client/runtime/library';
import { getIndicesParaPeriodo, calcularFatorCorrecaoTotal, verificarIndicesDisponiveis } from '../services/IndiceMonetarioService';
import { IndiceMonetarioService } from '../services/IndiceMonetarioService';
import { ReportGenerator } from '../services/ReportGenerator';
// Importar o módulo xlsx com verificação de disponibilidade
let XLSX: any;
try {
XLSX = require('xlsx');
} catch (error) {
console.warn('Módulo XLSX não encontrado. Funcionalidade de exportação para Excel estará indisponível.');
XLSX = null;
}
const router = express.Router();
const calculoService = new CalculoJudicialService();
const indiceService = new IndiceMonetarioService();
const reportGenerator = new ReportGenerator();
// Aplicar middleware de autenticação em todas as rotas
router.use(authMiddleware);
// Função auxiliar para validação de campos obrigatórios
const validarDados = (camposObrigatorios: string[]) => {
return (req: express.Request, res: express.Response, next: express.NextFunction) => {
const camposFaltantes = camposObrigatorios.filter(campo => req.body[campo] === undefined);
if (camposFaltantes.length > 0) {
return res.status(400).json({
error: `Campos obrigatórios faltando: ${camposFaltantes.join(', ')}`,
camposFaltantes
});
}
next();
};
};
// Criar novo cálculo judicial
router.post('/', async (req, res) => {
try {
const {
titulo,
descricao,
dataBase,
valorBase,
processoId,
clienteId
} = req.body;
// Verificar se o usuário está autenticado
if (!req.user?.id) {
return res.status(401).json({ error: 'Usuário não autenticado' });
}
// Validar campos obrigatórios
if (!titulo || !dataBase || valorBase === undefined) {
return res.status(400).json({ error: 'Campos obrigatórios faltando' });
}
const calculo = await calculoService.criarCalculo({
titulo,
descricao,
dataBase: new Date(dataBase),
valorBase: parseFloat(valorBase),
processoId,
clienteId,
usuarioId: req.user.id
});
res.status(201).json(calculo);
} catch (error) {
console.error('Erro ao criar cálculo:', error);
res.status(500).json({ error: error.message });
}
});
// Buscar cálculo por ID
router.get('/:id', async (req, res) => {
try {
const { id } = req.params;
const calculo = await calculoService.buscarCalculo(id);
if (!calculo) {
return res.status(404).json({ error: 'Cálculo não encontrado' });
}
res.json(calculo);
} catch (error) {
console.error('Erro ao buscar cálculo:', error);
res.status(500).json({ error: error.message });
}
});
// Adicionar correção monetária
router.post('/:id/correcao', validarDados(['indice', 'dataInicial']), async (req, res) => {
try {
const { id } = req.params;
const {
indice,
dataInicial,
dataFinal,
valorOriginal
} = req.body;
// Verificar se o usuário está autenticado
if (!req.user?.id) {
return res.status(401).json({ error: 'Usuário não autenticado' });
}
const correcao = await calculoService.adicionarCorrecaoMonetaria({
calculoId: id,
indice,
dataInicial: new Date(dataInicial),
dataFinal: dataFinal ? new Date(dataFinal) : undefined,
valorOriginal: parseFloat(valorOriginal),
usuarioId: req.user.id
});
res.status(201).json(correcao);
} catch (error) {
console.error('Erro ao adicionar correção monetária:', error);
res.status(500).json({ error: error.message });
}
});
// Adicionar juros
router.post('/:id/juros', validarDados(['tipo', 'taxa', 'dataInicial']), async (req, res) => {
try {
const { id } = req.params;
const {
tipo,
taxa,
periodicidade,
natureza,
dataInicial,
dataFinal,
valorBase
} = req.body;
// Verificar se o usuário está autenticado
if (!req.user?.id) {
return res.status(401).json({ error: 'Usuário não autenticado' });
}
const juros = await calculoService.adicionarJuros({
calculoId: id,
tipo,
taxa: parseFloat(taxa),
periodicidade,
natureza,
dataInicial: new Date(dataInicial),
dataFinal: dataFinal ? new Date(dataFinal) : undefined,
valorBase: parseFloat(valorBase),
usuarioId: req.user.id
});
res.status(201).json(juros);
} catch (error) {
console.error('Erro ao adicionar juros:', error);
res.status(500).json({ error: error.message });
}
});
// Adicionar multa
router.post('/:id/multa', validarDados(['tipo', 'percentual', 'valorBase']), async (req, res) => {
try {
const { id } = req.params;
const {
tipo,
percentual,
valorBase,
fundamento
} = req.body;
// Verificar se o usuário está autenticado
if (!req.user?.id) {
return res.status(401).json({ error: 'Usuário não autenticado' });
}
const multa = await calculoService.adicionarMulta({
calculoId: id,
tipo,
percentual: parseFloat(percentual),
valorBase: parseFloat(valorBase),
fundamento,
usuarioId: req.user.id
});
res.status(201).json(multa);
} catch (error) {
console.error('Erro ao adicionar multa:', error);
res.status(500).json({ error: error.message });
}
});
// Listar cálculos com filtros
router.get('/', async (req, res) => {
try {
const { titulo, status, processoId, clienteId } = req.query;
const calculos = await calculoService.listarCalculos({
titulo: titulo as string,
status: status as string,
processoId: processoId as string,
clienteId: clienteId as string
});
res.json(calculos);
} catch (error) {
console.error('Erro ao listar cálculos:', error);
res.status(500).json({ error: error.message });
}
});
// Finalizar cálculo
router.post('/:id/finalizar', async (req, res) => {
try {
const { id } = req.params;
// Verificar se o usuário está autenticado
if (!req.user?.id) {
return res.status(401).json({ error: 'Usuário não autenticado' });
}
const calculo = await calculoService.finalizarCalculo(id, req.user.id);
res.json(calculo);
} catch (error) {
console.error('Erro ao finalizar cálculo:', error);
res.status(500).json({ error: error.message });
}
});
// Gerar relatório do cálculo
router.get('/:id/relatorio', authMiddleware, async (req, res) => {
try {
const { id } = req.params;
// Verificar se o usuário está autenticado - Já feito pelo middleware
if (!req.user) {
return res.status(401).json({ error: 'Usuário não autenticado' });
}
// Buscar dados do cálculo
const calculo = await prisma.calculoJudicial.findUnique({
where: { id },
include: {
usuario: true,
cliente: true,
processo: true,
correcoes: true,
juros: true,
multas: true,
honorarios: true,
parcelas: true,
descontos: true,
}
});
if (!calculo) {
return res.status(404).json({ error: 'Cálculo não encontrado' });
}
// Gerar relatório PDF
const pdfBuffer = await reportGenerator.gerarRelatorioPDF(calculo);
// Registrar exportação
await prisma.exportacaoCalculo.create({
data: {
calculoId: id,
usuarioId: req.user.id,
tipo: 'PDF',
arquivo: `relatorio_${id}_${new Date().toISOString()}.pdf`
}
});
// Enviar PDF como resposta
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', `attachment; filename="calculo_${id}.pdf"`);
res.send(pdfBuffer);
} catch (error) {
console.error('Erro ao gerar relatório:', error);
res.status(500).json({ error: 'Erro ao gerar relatório' });
}
});
// Exportar cálculo para Excel
router.get('/:id/excel', authMiddleware, async (req, res) => {
try {
const { id } = req.params;
// Verificar se o módulo XLSX está disponível
if (!XLSX) {
return res.status(500).json({
error: 'Funcionalidade de exportação para Excel não está disponível. Módulo XLSX não instalado.'
});
}
// Verificar se o usuário está autenticado - Já feito pelo middleware
if (!req.user) {
return res.status(401).json({ error: 'Usuário não autenticado' });
}
// Buscar dados do cálculo
const calculo = await prisma.calculoJudicial.findUnique({
where: { id },
include: {
usuario: true,
cliente: true,
processo: true,
correcoes: true,
juros: true,
multas: true,
honorarios: true,
parcelas: true,
descontos: true,
}
});
if (!calculo) {
return res.status(404).json({ error: 'Cálculo não encontrado' });
}
// Criar planilha Excel
const workbook = XLSX.utils.book_new();
// Dados resumo
const dadosResumo = [
['RESUMO DO CÁLCULO'],
['Título', calculo.titulo],
['Data Base', calculo.dataBase.toLocaleDateString('pt-BR')],
['Data do Cálculo', calculo.dataCalculo.toLocaleDateString('pt-BR')],
['Valor Base', calculo.valorBase.toString()],
['Valor Corrigido', calculo.valorCorrigido.toString()],
['Valor Final', calculo.valorFinal.toString()],
['Status', calculo.status],
];
// Adicionar planilha de resumo
const resumoSheet = XLSX.utils.aoa_to_sheet(dadosResumo);
XLSX.utils.book_append_sheet(workbook, resumoSheet, 'Resumo');
// Adicionar outras abas para cada componente do cálculo
if (calculo.correcoes.length > 0) {
const correcoesData = [
['Índice', 'Data Inicial', 'Data Final', 'Valor Original', 'Valor Corrigido', 'Fator']
];
calculo.correcoes.forEach(c => {
correcoesData.push([
c.indice,
c.dataInicial.toLocaleDateString('pt-BR'),
c.dataFinal?.toLocaleDateString('pt-BR') || '',
c.valorOriginal.toString(),
c.valorCorrigido.toString(),
c.fatorCorrecao.toString()
]);
});
const correcoesSheet = XLSX.utils.aoa_to_sheet(correcoesData);
XLSX.utils.book_append_sheet(workbook, correcoesSheet, 'Correções');
}
// Adicionar outras abas (juros, multas, etc.)
// ...
// Registrar exportação
await prisma.exportacaoCalculo.create({
data: {
calculoId: id,
usuarioId: req.user.id,
tipo: 'EXCEL',
arquivo: `calculo_${id}_${new Date().toISOString()}.xlsx`
}
});
// Enviar arquivo Excel
const excelBuffer = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' });
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.setHeader('Content-Disposition', `attachment; filename="calculo_${id}.xlsx"`);
res.send(excelBuffer);
} catch (error) {
console.error('Erro ao exportar Excel:', error);
res.status(500).json({ error: 'Erro ao exportar Excel' });
}
});
// Adicionar rota para correções monetárias
router.post('/:id/correcoes', async (req, res) => {
try {
const { id } = req.params;
const { indice, dataInicial, dataFinal, valorOriginal } = req.body;
console.log('Dados recebidos:', { dataInicial, dataFinal, valorOriginal });
// Buscar índices para o período
let indices = await prisma.indiceMonetario.findMany({
where: {
nome: indice,
OR: [
{
anoReferencia: new Date(dataInicial).getFullYear(),
mesReferencia: new Date(dataInicial).getMonth() + 1
},
{
anoReferencia: {
gt: new Date(dataInicial).getFullYear(),
lt: new Date(dataFinal).getFullYear()
}
},
{
anoReferencia: new Date(dataFinal).getFullYear(),
mesReferencia: {
lte: new Date(dataFinal).getMonth() + 1
}
}
]
},
orderBy: [
{ anoReferencia: 'asc' },
{ mesReferencia: 'asc' }
]
});
console.log(`Encontrados ${indices.length} índices`);
// Se não houver índices, buscar da API
if (indices.length === 0) {
console.log('Buscando índices da API do IBGE...');
await indiceService.importarIndicesINPC();
// Buscar novamente após importação
indices = await prisma.indiceMonetario.findMany({
where: {
nome: indice,
OR: [
{
anoReferencia: new Date(dataInicial).getFullYear(),
mesReferencia: new Date(dataInicial).getMonth() + 1
},
{
anoReferencia: {
gt: new Date(dataInicial).getFullYear(),
lt: new Date(dataFinal).getFullYear()
}
},
{
anoReferencia: new Date(dataFinal).getFullYear(),
mesReferencia: {
lte: new Date(dataFinal).getMonth() + 1
}
}
]
},
orderBy: [
{ anoReferencia: 'asc' },
{ mesReferencia: 'asc' }
]
});
}
// Criar a correção monetária
const correcao = await prisma.correcaoMonetaria.create({
data: {
calculoId: id,
indice,
dataInicial: new Date(dataInicial),
dataFinal: new Date(dataFinal),
valorOriginal: new Decimal(valorOriginal),
valorCorrigido: new Decimal(0), // Valor temporário
fatorCorrecao: new Decimal(1), // Valor temporário
}
});
// Atualizar valores da correção em background
atualizarValoresCorrecao(correcao.id).catch(console.error);
// Retornar resposta imediata
res.status(201).json(correcao);
} catch (error) {
console.error('Erro ao criar correção:', error);
res.status(500).json({
error: 'Erro ao criar correção monetária',
details: error instanceof Error ? error.message : 'Erro desconhecido'
});
}
});
// Função auxiliar para atualizar valores em background
async function atualizarValoresCorrecao(correcaoId: string) {
try {
const correcao = await prisma.correcaoMonetaria.findUnique({
where: { id: correcaoId }
});
if (!correcao) return;
// Buscar índices para o período
const indices = await prisma.indiceMonetario.findMany({
where: {
nome: correcao.indice,
OR: [
{
anoReferencia: correcao.dataInicial.getFullYear(),
mesReferencia: correcao.dataInicial.getMonth() + 1
},
{
anoReferencia: {
gt: correcao.dataInicial.getFullYear(),
lt: correcao.dataFinal.getFullYear()
}
},
{
anoReferencia: correcao.dataFinal.getFullYear(),
mesReferencia: {
lte: correcao.dataFinal.getMonth() + 1
}
}
]
},
orderBy: [
{ anoReferencia: 'asc' },
{ mesReferencia: 'asc' }
]
});
// Calcular fator acumulado
let fatorAcumulado = new Decimal(1);
for (const indice of indices) {
// Converter o índice para fator decimal (ex: 1.5% -> 1.015)
const fator = new Decimal(1).plus(new Decimal(indice.valor).div(100));
fatorAcumulado = fatorAcumulado.mul(fator);
}
// Calcular valor corrigido
const valorCorrigido = new Decimal(correcao.valorOriginal).mul(fatorAcumulado);
// Atualizar correção
await prisma.correcaoMonetaria.update({
where: { id: correcaoId },
data: {
valorCorrigido,
fatorCorrecao: fatorAcumulado
}
});
} catch (error) {
console.error('Erro ao atualizar valores da correção:', error);
throw error;
}
}
// Adicionar rota DELETE para correções monetárias
router.delete('/:calculoId/correcoes/:correcaoId', async (req, res) => {
try {
const { calculoId, correcaoId } = req.params;
// Verificar se a correção existe e pertence ao cálculo
const correcao = await prisma.correcaoMonetaria.findFirst({
where: {
id: correcaoId,
calculoId: calculoId
}
});
if (!correcao) {
return res.status(404).json({ error: 'Correção monetária não encontrada' });
}
// Excluir a correção
await prisma.correcaoMonetaria.delete({
where: { id: correcaoId }
});
// Atualizar o valor corrigido e final do cálculo
await prisma.calculoJudicial.update({
where: { id: calculoId },
data: {
valorCorrigido: {
decrement: correcao.valorCorrigido
},
valorFinal: {
decrement: correcao.valorCorrigido
}
}
});
res.status(204).send();
} catch (error) {
console.error('Erro ao excluir correção monetária:', error);
res.status(500).json({
error: 'Erro ao excluir correção monetária',
details: error instanceof Error ? error.message : 'Erro desconhecido'
});
}
});
// Adicionar rota para criar multa
router.post('/:id/multas', async (req, res) => {
try {
const { id } = req.params;
const { tipo, percentual, valorBase, fundamento } = req.body;
// Validar entrada
if (!tipo || !percentual || !valorBase) {
return res.status(400).json({ error: 'Dados incompletos' });
}
// Verificar se o cálculo existe
const calculo = await prisma.calculoJudicial.findUnique({
where: { id }
});
if (!calculo) {
return res.status(404).json({ error: 'Cálculo não encontrado' });
}
// Criar multa
const multa = await prisma.multaCalculo.create({
data: {
calculoId: id,
tipo,
percentual: parseFloat(percentual),
valorBase: parseFloat(valorBase),
valorMulta: parseFloat(valorBase) * parseFloat(percentual),
fundamento
}
});
res.status(201).json(multa);
} catch (error) {
console.error('Erro ao criar multa:', error);
res.status(500).json({ error: 'Erro ao criar multa' });
}
});
// Rota para adicionar parcelas
router.post('/:id/parcelas', async (req, res) => {
try {
const { id } = req.params;
const { numero, dataVencimento, valorOriginal } = req.body;
// Validar dados
if (!numero || !dataVencimento || !valorOriginal) {
return res.status(400).json({ error: 'Dados incompletos' });
}
const parcela = await prisma.parcelaCalculo.create({
data: {
calculoId: id,
numero: parseInt(numero),
dataVencimento: new Date(dataVencimento),
valorOriginal: new Decimal(valorOriginal),
valorCorrigido: new Decimal(valorOriginal),
valorJuros: new Decimal(0),
valorMulta: new Decimal(0),
valorTotal: new Decimal(valorOriginal),
}
});
res.status(201).json(parcela);
} catch (error) {
console.error('Erro ao criar parcela:', error);
res.status(500).json({ error: 'Erro ao criar parcela' });
}
});
// Rota para adicionar honorários
router.post('/:id/honorarios', async (req, res) => {
try {
const { id } = req.params;
const { tipo, percentual, valorBase, fundamento } = req.body;
const honorario = await prisma.honorarioCalculo.create({
data: {
calculoId: id,
tipo,
percentual: new Decimal(percentual),
valorBase: new Decimal(valorBase),
valorHonorario: new Decimal(valorBase).mul(percentual),
fundamento
}
});
res.status(201).json(honorario);
} catch (error) {
console.error('Erro ao criar honorário:', error);
res.status(500).json({ error: 'Erro ao criar honorário' });
}
});
// Rota para adicionar descontos
router.post('/:id/descontos', async (req, res) => {
try {
const calculoId = req.params.id;
const { tipo, descricao, percentual, valorFixo, valorBase } = req.body;
// Validar dados
if (!tipo || (!percentual && !valorFixo) || !valorBase) {
return res.status(400).json({
error: 'Dados inválidos. Forneça o tipo, valor base e percentual ou valor fixo.'
});
}
// Calcular valor do desconto
let valorDesconto;
if (percentual) {
// Certifique-se de que percentual é um número entre 0 e 1
const percentualDecimal = Number(percentual);
if (isNaN(percentualDecimal) || percentualDecimal <= 0 || percentualDecimal > 1) {
return res.status(400).json({
error: 'Percentual deve ser um número maior que 0 e no máximo 1 (100%)'
});
}
valorDesconto = new Decimal(valorBase).mul(percentualDecimal);
} else {
valorDesconto = new Decimal(valorFixo);
}
// Criar desconto
const desconto = await prisma.descontoCalculo.create({
data: {
calculoId,
tipo,
descricao,
percentual: percentual ? new Decimal(percentual) : null,
valorFixo: valorFixo ? new Decimal(valorFixo) : null,
valorBase: new Decimal(valorBase),
valorDesconto
}
});
// Atualizar valor do cálculo
await atualizarValorCalculoComDescontos(calculoId);
res.status(201).json(desconto);
} catch (error) {
console.error('Erro ao adicionar desconto:', error);
res.status(500).json({ error: 'Erro ao adicionar desconto' });
}
});
// Função para atualizar valor do cálculo com descontos
async function atualizarValorCalculoComDescontos(calculoId: string) {
try {
// Buscar cálculo
const calculo = await prisma.calculoJudicial.findUnique({
where: { id: calculoId },
include: {
descontos: true
}
});
if (!calculo) return;
// Calcular total de descontos
const totalDescontos = calculo.descontos.reduce(
(total, desconto) => total.add(desconto.valorDesconto),
new Decimal(0)
);
// Atualizar valor final do cálculo
// Valor final = Valor corrigido - Total de descontos
const valorFinal = calculo.valorCorrigido.minus(totalDescontos);
// Atualizar cálculo
await prisma.calculoJudicial.update({
where: { id: calculoId },
data: {
valorFinal
}
});
} catch (error) {
console.error('Erro ao atualizar valor do cálculo com descontos:', error);
}
}
// Adicionar rota para excluir desconto
router.delete('/:id/descontos/:descontoId', async (req, res) => {
try {
const { id, descontoId } = req.params;
// Verificar se o cálculo existe
const calculo = await prisma.calculoJudicial.findUnique({
where: { id }
});
if (!calculo) {
return res.status(404).json({ error: 'Cálculo não encontrado' });
}
// Verificar se o desconto existe e pertence ao cálculo
const desconto = await prisma.descontoCalculo.findFirst({
where: {
id: descontoId,
calculoId: id
}
});
if (!desconto) {
return res.status(404).json({ error: 'Desconto não encontrado ou não pertence a este cálculo' });
}
// Excluir o desconto
await prisma.descontoCalculo.delete({
where: { id: descontoId }
});
// Atualizar valor final do cálculo
await atualizarValorCalculo(id);
// Registrar no histórico
await prisma.historicoCalculo.create({
data: {
calculoId: id,
acao: 'EXCLUSAO',
descricao: `Desconto removido: ${desconto.tipo} - ${desconto.valorDesconto}`,
valorAnterior: desconto.valorDesconto,
valorNovo: null
}
});
res.status(200).json({ message: 'Desconto excluído com sucesso' });
} catch (error) {
console.error('Erro ao excluir desconto:', error);
res.status(500).json({ error: 'Erro ao excluir desconto' });
}
});
// Função auxiliar para atualizar o valor do cálculo
async function atualizarValorCalculo(calculoId: string) {
try {
// Buscar o cálculo com todos os componentes
const calculo = await prisma.calculoJudicial.findUnique({
where: { id: calculoId },
include: {
correcoes: true,
juros: true,
multas: true,
honorarios: true,
descontos: true
}
});
if (!calculo) return;
// Calcular o valor corrigido (base + correções)
let valorCorrigido = new Decimal(calculo.valorBase);
// Adicionar juros e multas
for (const juro of calculo.juros) {
valorCorrigido = valorCorrigido.add(juro.valorJuros);
}
for (const multa of calculo.multas) {
valorCorrigido = valorCorrigido.add(multa.valorMulta);
}
for (const honorario of calculo.honorarios) {
valorCorrigido = valorCorrigido.add(honorario.valorHonorario);
}
// Calcular valor final (valor corrigido - descontos)
let valorFinal = valorCorrigido;
for (const desconto of calculo.descontos) {
valorFinal = valorFinal.sub(desconto.valorDesconto);
}
// Atualizar o cálculo
await prisma.calculoJudicial.update({
where: { id: calculoId },
data: {
valorCorrigido,
valorFinal
}
});
} catch (error) {
console.error('Erro ao atualizar valor do cálculo:', error);
}
}
export default router;
+35
View File
@@ -0,0 +1,35 @@
import { Router } from 'express';
import { prisma } from '../prismaClient';
const router = Router();
router.get('/', async (req, res) => {
try {
const { search } = req.query;
const where: any = {};
if (search) {
const searchTerms = String(search).split(' ');
where.OR = searchTerms.map(term => ({
OR: [
{ nome: { contains: term, mode: 'insensitive' } },
{ cpfCnpj: { contains: term } },
{ email: { contains: term.toLowerCase() } }
]
}));
}
const clientes = await prisma.cliente.findMany({
where,
orderBy: {
nome: 'asc'
}
});
res.json(clientes);
} catch (error) {
res.status(500).json({ error: 'Erro ao buscar clientes' });
}
});
export default router;
+262
View File
@@ -0,0 +1,262 @@
import { Router } from 'express';
import { prisma } from '../prismaClient';
import { authMiddleware } from '../../middleware/auth'; // Corrigir caminho da importação
const router = Router();
// Adicionar middleware de auth em todas as rotas
router.use(authMiddleware);
// Estatísticas de processos
router.get('/processos/stats', async (req, res) => {
try {
const [total, porTribunal, porStatus] = await Promise.all([
prisma.processo.count(),
prisma.processo.groupBy({
by: ['tribunal'],
_count: true,
}),
prisma.processo.groupBy({
by: ['status'],
_count: true,
})
]);
res.json({
total,
porTribunal,
porStatus
});
} catch (error) {
res.status(500).json({ error: 'Erro ao buscar estatísticas de processos' });
}
});
// Taxa de sucesso
router.get('/processos/taxa-sucesso', async (req, res) => {
try {
const stats = await prisma.processo.groupBy({
by: ['resultado'],
_count: true,
});
res.json(stats);
} catch (error) {
res.status(500).json({ error: 'Erro ao buscar taxa de sucesso' });
}
});
// Estatísticas financeiras
router.get('/financeiro/stats', async (req, res) => {
try {
const stats = await prisma.$transaction(async (tx) => {
const [honorarios, custas] = await Promise.all([
tx.honorario.aggregate({
_sum: { valorTotalCausa: true }
}),
tx.custaProcessual.aggregate({
_sum: { valor: true }
})
]);
return {
honorarios: honorarios._sum.valorTotalCausa || 0,
custas: custas._sum.valor || 0
};
});
res.json(stats);
} catch (error) {
res.status(500).json({ error: 'Erro ao buscar estatísticas financeiras' });
}
});
// Prazos próximos
router.get('/prazos/proximos', async (req, res) => {
try {
const prazos = await prisma.diligencia.findMany({
where: {
dataLimite: {
gte: new Date(),
lte: new Date(Date.now() + 15 * 24 * 60 * 60 * 1000) // próximos 15 dias
}
},
include: {
processo: true
},
orderBy: {
dataLimite: 'asc'
}
});
res.json(prazos);
} catch (error) {
res.status(500).json({ error: 'Erro ao buscar prazos próximos' });
}
});
// Corrigir rota de atividades
router.get('/atividades', async (req, res) => {
try {
const atividades = await prisma.$transaction(async (tx) => {
const [processos, movimentacoes] = await Promise.all([
tx.processo.findMany({
take: 10,
orderBy: { dataAtualizacao: 'desc' },
select: {
id: true,
numero: true,
dataAtualizacao: true,
cliente: {
select: {
nome: true
}
}
}
}),
tx.movimentacao.findMany({
take: 10,
orderBy: { data: 'desc' },
select: {
id: true,
descricao: true,
data: true,
processo: {
select: {
numero: true
}
}
}
})
]);
const atividadesProcessadas = [
...processos.map(p => ({
id: p.id,
tipo: 'PROCESSO',
descricao: `Processo ${p.numero} atualizado`,
data: p.dataAtualizacao,
detalhes: p.cliente.nome
})),
...movimentacoes.map(m => ({
id: m.id,
tipo: 'MOVIMENTACAO',
descricao: m.descricao,
data: m.data,
detalhes: m.processo.numero
}))
].sort((a, b) => b.data.getTime() - a.data.getTime())
.slice(0, 10);
return atividadesProcessadas;
});
res.json(atividades);
} catch (error) {
console.error('Erro ao buscar atividades:', error);
res.status(500).json({ error: 'Erro ao buscar atividades recentes' });
}
});
// Corrigir rota de estatísticas de tarefas
router.get('/tarefas/stats', async (req, res) => {
try {
const today = new Date();
today.setHours(0, 0, 0, 0);
const [pendentes, atrasadas, concluidas] = await Promise.all([
prisma.tarefa.count({
where: {
status: 'PENDENTE',
dataVencimento: {
gte: today
}
}
}),
prisma.tarefa.count({
where: {
status: 'PENDENTE',
dataVencimento: {
lt: today
}
}
}),
prisma.tarefa.count({
where: {
status: 'CONCLUIDA',
atualizadoEm: { // Usar atualizadoEm enquanto não tiver dataConclusao
gte: new Date(today.getFullYear(), today.getMonth(), 1),
lt: new Date(today.getFullYear(), today.getMonth() + 1, 0)
}
}
})
]).catch(error => {
console.error('Erro ao consultar tarefas:', error);
return [0, 0, 0];
});
const total = pendentes + atrasadas + concluidas;
res.json({
pendentes,
atrasadas,
concluidas,
total,
taxaConclusao: total > 0 ? Math.round((concluidas / total) * 100) : 0,
porStatus: { PENDENTE: pendentes }
});
} catch (error) {
console.error('Erro ao buscar estatísticas de tarefas:', error);
res.status(500).json({
error: 'Erro ao buscar estatísticas de tarefas',
details: error instanceof Error ? error.message : 'Erro desconhecido'
});
}
});
// Estatísticas de atendimentos
router.get('/atendimentos/stats', async (req, res) => {
try {
const hoje = new Date();
const inicioMes = new Date(hoje.getFullYear(), hoje.getMonth(), 1);
const fimMes = new Date(hoje.getFullYear(), hoje.getMonth() + 1, 0);
const proximaSemana = new Date(hoje.setDate(hoje.getDate() + 7));
const [atendimentosHoje, proximosAtendimentos, totalMes] = await Promise.all([
prisma.atendimento.count({
where: {
dataAtendimento: {
gte: new Date(new Date().setHours(0,0,0,0)),
lt: new Date(new Date().setHours(23,59,59,999))
}
}
}),
prisma.atendimento.count({
where: {
dataAtendimento: {
gte: new Date(),
lt: proximaSemana
}
}
}),
prisma.atendimento.count({
where: {
dataAtendimento: {
gte: inicioMes,
lte: fimMes
}
}
})
]);
res.json({
hoje: atendimentosHoje,
proximaSemana: proximosAtendimentos,
totalMes: totalMes
});
} catch (error) {
res.status(500).json({ error: 'Erro ao buscar estatísticas de atendimentos' });
}
});
export default router;
+97
View File
@@ -0,0 +1,97 @@
import { Router } from 'express';
import { prisma } from '../prismaClient';
import { authMiddleware } from '../../middleware/auth';
import { TipoDocumento } from '@prisma/client';
const router = Router();
router.use(authMiddleware);
// Listar modelos de documentos
router.get('/', async (req, res) => {
try {
const { tipo } = req.query;
const where = tipo ? { tipo: tipo as TipoDocumento } : {};
const modelos = await prisma.documentoModelo.findMany({
where,
orderBy: {
criadoEm: 'desc'
}
});
res.json(modelos);
} catch (error) {
console.error('Erro ao listar modelos:', error);
res.status(500).json({ error: 'Erro ao listar modelos de documentos' });
}
});
// Criar novo modelo
router.post('/', async (req, res) => {
try {
const { nome, tipo, descricao, conteudo, variaveis } = req.body;
if (!nome || !tipo || !conteudo) {
return res.status(400).json({ error: 'Campos obrigatórios faltando' });
}
const modelo = await prisma.documentoModelo.create({
data: {
nome,
tipo: tipo as TipoDocumento,
descricao,
conteudo,
variaveis: Array.isArray(variaveis) ? variaveis : []
}
});
res.status(201).json(modelo);
} catch (error) {
console.error('Erro ao criar modelo:', error);
res.status(500).json({ error: 'Erro ao criar modelo de documento' });
}
});
// Atualizar modelo
router.put('/:id', async (req, res) => {
try {
const { id } = req.params;
const data = req.body;
// Remover o id do objeto de dados para evitar conflito
delete data.id;
const modelo = await prisma.documentoModelo.update({
where: { id },
data: {
nome: data.nome,
tipo: data.tipo,
descricao: data.descricao,
conteudo: data.conteudo,
variaveis: Array.isArray(data.variaveis) ? data.variaveis : []
}
});
res.json(modelo);
} catch (error) {
console.error('Erro ao atualizar modelo:', error);
res.status(500).json({ error: 'Erro ao atualizar modelo de documento' });
}
});
// Excluir modelo
router.delete('/:id', async (req, res) => {
try {
await prisma.documentoModelo.delete({
where: { id: req.params.id }
});
res.status(204).send();
} catch (error) {
console.error('Erro ao excluir modelo:', error);
res.status(500).json({ error: 'Erro ao excluir modelo de documento' });
}
});
export default router;
+411
View File
@@ -0,0 +1,411 @@
import { Router } from 'express';
import { prisma } from '../prismaClient';
import { authMiddleware } from '../../middleware/auth';
import { TipoDocumento } from '@prisma/client';
import { PdfService } from '../services/PdfService';
import multer from 'multer';
import path from 'path';
import fs from 'fs';
const router = Router();
router.use(authMiddleware);
// Listar documentos
router.get('/', async (req, res) => {
try {
const { processoId, atendimentoId, tipo } = req.query;
const where: any = {};
if (processoId) where.processoId = processoId;
if (atendimentoId) where.atendimentoId = atendimentoId;
if (tipo) where.tipo = tipo as TipoDocumento;
const documentos = await prisma.documento.findMany({
where,
include: {
criadoPor: {
select: {
id: true,
nome: true
}
}
},
orderBy: {
criadoEm: 'desc'
}
});
res.json(documentos);
} catch (error) {
console.error('Erro ao listar documentos:', error);
res.status(500).json({ error: 'Erro ao listar documentos' });
}
});
// Criar documento
router.post('/', async (req, res) => {
try {
const { nome, tipo, conteudo, processoId, atendimentoId } = req.body;
if (!req.user?.id) {
return res.status(401).json({ error: 'Usuário não autenticado' });
}
const documento = await prisma.documento.create({
data: {
nome,
tipo,
conteudo,
processoId,
atendimentoId,
criadoPorId: req.user.id
}
});
res.status(201).json(documento);
} catch (error) {
console.error('Erro ao criar documento:', error);
res.status(500).json({ error: 'Erro ao criar documento' });
}
});
// Criar documento a partir de modelo
router.post('/from-modelo/:modeloId', async (req, res) => {
try {
const { modeloId } = req.params;
const { processoId, atendimentoId, variaveis } = req.body;
if (!req.user?.id) {
return res.status(401).json({ error: 'Usuário não autenticado' });
}
// Buscar modelo
const modelo = await prisma.documentoModelo.findUnique({
where: { id: modeloId }
});
if (!modelo) {
return res.status(404).json({ error: 'Modelo não encontrado' });
}
// Buscar dados adicionais se necessário
let clienteData;
if (variaveis?.cliente?.id) {
const cliente = await prisma.cliente.findUnique({
where: { id: variaveis.cliente.id }
});
if (cliente) {
clienteData = cliente;
}
}
// Substituir variáveis no conteúdo
let conteudo = modelo.conteudo;
if (clienteData) {
const substituicoes = {
'cliente.nome': clienteData.nome,
'cliente.cpf': clienteData.cpfCnpj,
'cliente.rg': clienteData.rg,
'cliente.estadoCivil': clienteData.estadoCivil,
'cliente.profissao': clienteData.profissao,
'cliente.enderecoCompleto': clienteData.enderecoCompleto,
'data.atualExtenso': new Date().toLocaleDateString('pt-BR', {
day: 'numeric',
month: 'long',
year: 'numeric'
})
};
Object.entries(substituicoes).forEach(([key, value]) => {
const regex = new RegExp(`{{${key}}}`, 'g');
conteudo = conteudo.replace(regex, value || '');
});
}
// Criar o documento
const documento = await prisma.documento.create({
data: {
nome: modelo.nome, // Usando o nome do modelo
tipo: modelo.tipo,
conteudo,
processoId,
atendimentoId,
criadoPorId: req.user.id,
modeloId: modelo.id
}
});
res.status(201).json({
...documento,
clienteId: variaveis?.cliente?.id
});
} catch (error) {
console.error('Erro ao criar documento:', error);
res.status(500).json({ error: 'Erro ao criar documento' });
}
});
// Atualizar a rota de configurações
router.post('/configuracoes', async (req, res) => {
try {
const escritorioId = req.user.escritorioId; // Assumindo que você tem o ID do escritório no token
// Usar upsert para criar ou atualizar
const config = await prisma.configuracaoDocumento.upsert({
where: {
escritorioId
},
update: {
...req.body,
margensTemplate: req.body.margensTemplate || {
top: 35,
bottom: 29,
left: 25,
right: 25
}
},
create: {
escritorioId,
...req.body,
margensTemplate: req.body.margensTemplate || {
top: 35,
bottom: 29,
left: 25,
right: 25
}
}
});
res.json(config);
} catch (error) {
console.error('Erro ao salvar configuração:', error);
res.status(500).json({ error: 'Erro ao salvar configuração' });
}
});
// Rota para buscar última configuração
router.get('/configuracoes', async (req, res) => {
try {
console.log('Buscando configurações de documento');
const config = await prisma.configuracaoDocumento.findFirst({
orderBy: {
criadoEm: 'desc'
}
});
if (!config) {
// Criar configuração padrão se não existir
const defaultConfig = await prisma.configuracaoDocumento.create({
data: {
timbradoMarginTop: 100,
timbradoMarginBottom: 50,
timbradoMarginLeft: 50,
timbradoMarginRight: 50,
margensTemplate: {
top: 113,
bottom: 90,
left: 35,
right: 35
}
}
});
return res.json(defaultConfig);
}
res.json(config);
} catch (error) {
console.error('Erro ao buscar configurações:', error);
res.status(500).json({ error: 'Erro ao buscar configurações' });
}
});
// Atualizar configuração do documento
router.post('/configuracao', authMiddleware, async (req, res) => {
try {
// Primeiro, tente encontrar uma configuração existente
const configExistente = await prisma.configuracaoDocumento.findFirst({
where: {
escritorioId: req.user.escritorioId
}
});
let config;
if (configExistente) {
// Se existir, atualiza
config = await prisma.configuracaoDocumento.update({
where: {
id: configExistente.id
},
data: {
timbradoUrl: req.body.timbradoUrl,
timbradoMarginTop: req.body.timbradoMarginTop,
timbradoMarginBottom: req.body.timbradoMarginBottom,
timbradoMarginLeft: req.body.timbradoMarginLeft,
timbradoMarginRight: req.body.timbradoMarginRight,
templatePadrao: req.body.templatePadrao || "",
margensTemplate: req.body.margensTemplate
}
});
} else {
// Se não existir, cria
config = await prisma.configuracaoDocumento.create({
data: {
escritorioId: req.user.escritorioId,
timbradoUrl: req.body.timbradoUrl,
timbradoMarginTop: req.body.timbradoMarginTop,
timbradoMarginBottom: req.body.timbradoMarginBottom,
timbradoMarginLeft: req.body.timbradoMarginLeft,
timbradoMarginRight: req.body.timbradoMarginRight,
templatePadrao: req.body.templatePadrao || "",
margensTemplate: req.body.margensTemplate
}
});
}
res.json(config);
} catch (error) {
console.error('Erro ao salvar configuração:', error);
res.status(500).json({ error: 'Falha ao salvar configuração do documento' });
}
});
// Atualizar documento com manipulação de erro aprimorada
router.put('/:id', async (req, res) => {
try {
const { id } = req.params;
const { nome, tipo, conteudo } = req.body;
// Verificar se o documento existe
const documentoExistente = await prisma.documento.findUnique({
where: { id },
select: { id: true, criadoPorId: true }
});
if (!documentoExistente) {
return res.status(404).json({ error: 'Documento não encontrado' });
}
// Verificar permissão (apenas quem criou ou é admin pode editar)
if (documentoExistente.criadoPorId !== req.user.id && req.user.perfil !== 'ADVOGADO_ADMIN') {
return res.status(403).json({ error: 'Sem permissão para editar este documento' });
}
// Validar dados
if (!nome || !tipo || !conteudo) {
return res.status(400).json({ error: 'Dados incompletos para atualização do documento' });
}
// Atualizar o documento
const documento = await prisma.documento.update({
where: { id },
data: {
nome,
tipo: tipo as TipoDocumento,
conteudo,
atualizadoEm: new Date()
}
});
// Registrar evento de atualização (opcional)
// await prisma.auditoria.create({
// data: {
// entidade: 'DOCUMENTO',
// entidadeId: id,
// acao: 'ATUALIZAÇÃO',
// usuarioId: req.user.id
// }
// });
res.json(documento);
} catch (error) {
console.error('Erro ao atualizar documento:', error);
res.status(500).json({ error: 'Erro ao atualizar documento' });
}
});
// Excluir documento
router.delete('/:id', async (req, res) => {
try {
await prisma.documento.delete({
where: { id: req.params.id }
});
res.status(204).send();
} catch (error) {
console.error('Erro ao excluir documento:', error);
res.status(500).json({ error: 'Erro ao excluir documento' });
}
});
// Adicionar nova rota para exportar PDF
router.post('/exportar-pdf', async (req, res) => {
try {
const { conteudo, escritorio } = req.body;
const pdfService = new PdfService();
const pdfBuffer = await pdfService.gerarPDF(conteudo, escritorio);
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'attachment; filename=documento.pdf');
res.send(pdfBuffer);
} catch (error) {
console.error('Erro ao gerar PDF:', error);
res.status(500).json({ error: 'Erro ao gerar PDF' });
}
});
// Configuração do multer para upload de arquivos
const storage = multer.diskStorage({
destination: (req, file, cb) => {
const uploadDir = path.join(process.cwd(), 'public', 'uploads');
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
cb(null, uploadDir);
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
cb(null, `timbrado-${uniqueSuffix}-${file.originalname}`);
}
});
const fileFilter = (req: any, file: any, cb: any) => {
// Atualizar para aceitar PDF
const allowedTypes = ['image/png', 'image/svg+xml', 'application/pdf'];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('Formato de arquivo não suportado. Use PNG, SVG ou PDF.'));
}
};
const upload = multer({
storage,
fileFilter,
limits: {
fileSize: 5 * 1024 * 1024 // Aumentar para 5MB
}
});
// Rota de upload do timbrado
router.post('/upload-timbrado', upload.single('timbrado'), async (req, res) => {
try {
if (!req.file) {
throw new Error('Nenhum arquivo enviado');
}
// Criar URL relativa para o arquivo
const fileUrl = `/uploads/${req.file.filename}`;
res.json({ url: fileUrl });
} catch (error: any) {
console.error('Erro no upload do timbrado:', error);
res.status(400).json({ error: error.message });
}
});
export default router;
+161
View File
@@ -0,0 +1,161 @@
import express from 'express';
import { IndiceMonetarioService, importarIndicesINPC } from '../services/IndiceMonetarioService';
const router = express.Router();
const indiceService = new IndiceMonetarioService();
// Importar índices INPC (somente admin)
router.post('/importar/inpc', async (req, res) => {
try {
// Verificar permissão de admin
if (req.user?.perfil !== 'ADVOGADO_ADMIN') {
return res.status(403).json({ error: 'Permissão negada' });
}
const resultado = await indiceService.importarIndicesINPC();
res.json(resultado);
} catch (error) {
console.error('Erro ao importar índices INPC:', error);
res.status(500).json({ error: error.message });
}
});
// Importar índices SELIC (somente admin)
router.post('/importar/selic', async (req, res) => {
try {
// Verificar permissão de admin
if (req.user?.perfil !== 'ADVOGADO_ADMIN') {
return res.status(403).json({ error: 'Permissão negada' });
}
const resultado = await indiceService.importarIndicesSELIC();
res.json(resultado);
} catch (error) {
console.error('Erro ao importar índices SELIC:', error);
res.status(500).json({ error: error.message });
}
});
// Adicionar índice manualmente
router.post('/', async (req, res) => {
try {
const { nome, mesReferencia, anoReferencia, valor, dataPublicacao, fonte } = req.body;
// Verificar permissão de admin
if (req.user?.perfil !== 'ADVOGADO_ADMIN') {
return res.status(403).json({ error: 'Permissão negada' });
}
// Validação de dados
if (!nome || !mesReferencia || !anoReferencia || valor === undefined) {
return res.status(400).json({ error: 'Campos obrigatórios faltando' });
}
if (isNaN(parseInt(mesReferencia)) || parseInt(mesReferencia) < 1 || parseInt(mesReferencia) > 12) {
return res.status(400).json({ error: 'Mês de referência inválido (deve ser entre 1 e 12)' });
}
if (isNaN(parseInt(anoReferencia)) || parseInt(anoReferencia) < 1900 || parseInt(anoReferencia) > 2100) {
return res.status(400).json({ error: 'Ano de referência inválido' });
}
if (isNaN(parseFloat(valor))) {
return res.status(400).json({ error: 'Valor inválido' });
}
const resultado = await indiceService.adicionarIndice({
nome,
mesReferencia: parseInt(mesReferencia),
anoReferencia: parseInt(anoReferencia),
valor: parseFloat(valor),
dataPublicacao: dataPublicacao ? new Date(dataPublicacao) : undefined,
fonte,
});
res.status(201).json(resultado);
} catch (error) {
console.error('Erro ao adicionar índice:', error);
res.status(500).json({ error: error.message });
}
});
// Listar índices
router.get('/', async (req, res) => {
try {
const { nome, anoReferencia, fonte } = req.query;
const indices = await indiceService.listarIndices({
nome: nome as string,
anoReferencia: anoReferencia ? parseInt(anoReferencia as string) : undefined,
fonte: fonte as string,
});
res.json(indices);
} catch (error) {
console.error('Erro ao listar índices:', error);
res.status(500).json({ error: error.message });
}
});
// Buscar índice específico
router.get('/:nome/:mes/:ano', async (req, res) => {
try {
const { nome, mes, ano } = req.params;
// Validação de dados
if (isNaN(parseInt(mes)) || parseInt(mes) < 1 || parseInt(mes) > 12) {
return res.status(400).json({ error: 'Mês de referência inválido (deve ser entre 1 e 12)' });
}
if (isNaN(parseInt(ano)) || parseInt(ano) < 1900 || parseInt(ano) > 2100) {
return res.status(400).json({ error: 'Ano de referência inválido' });
}
const indice = await indiceService.buscarIndice({
nome,
mesReferencia: parseInt(mes),
anoReferencia: parseInt(ano),
});
if (!indice) {
return res.status(404).json({ error: 'Índice não encontrado' });
}
res.json(indice);
} catch (error) {
console.error('Erro ao buscar índice:', error);
res.status(500).json({ error: error.message });
}
});
// Remover índice
router.delete('/:id', async (req, res) => {
try {
const { id } = req.params;
// Verificar permissão de admin
if (req.user?.perfil !== 'ADVOGADO_ADMIN') {
return res.status(403).json({ error: 'Permissão negada' });
}
const resultado = await indiceService.removerIndice(id);
res.json({ success: true, data: resultado });
} catch (error) {
console.error('Erro ao remover índice:', error);
res.status(500).json({ error: error.message });
}
});
// Adicionar rota para forçar atualização dos índices
router.post('/atualizar', async (req, res) => {
try {
await importarIndicesINPC();
res.json({ message: 'Índices atualizados com sucesso' });
} catch (error) {
console.error('Erro ao atualizar índices:', error);
res.status(500).json({ error: 'Falha ao atualizar índices' });
}
});
export default router;
+111
View File
@@ -0,0 +1,111 @@
import { Router } from 'express';
import { PrismaClient } from '@prisma/client';
import { extractUser } from '../authMiddleware'; // Importar a função correta
const router = Router();
const prisma = new PrismaClient();
// Middleware de autenticação
router.use(extractUser);
// Criar novo cálculo de pensão alimentícia
router.post('/', async (req, res) => {
try {
const {
titulo,
descricao,
dataInicial,
dataFinal,
percentualSalarioMinimo,
multaPactuada,
tipoAtualizacao,
processoId,
clienteId,
observacoes
} = req.body;
const calculo = await prisma.calculoPensaoAlimenticia.create({
data: {
titulo,
descricao,
status: 'ATIVO',
dataInicial: new Date(dataInicial),
dataFinal: dataFinal ? new Date(dataFinal) : null,
percentualSalarioMinimo,
multaPactuada,
tipoAtualizacao,
valorCalculado: 0,
valorMulta: 0,
valorTotal: 0,
processoId,
clienteId,
observacoes,
usuarioId: req.user.id
}
});
res.json(calculo);
} catch (error) {
console.error('Erro ao criar cálculo de pensão:', error);
res.status(500).json({ error: 'Falha ao criar cálculo' });
}
});
// Buscar cálculo por ID
router.get('/:id', async (req, res) => {
try {
const { id } = req.params;
const calculo = await prisma.calculoPensaoAlimenticia.findUnique({
where: { id },
include: {
processo: true,
cliente: true,
usuario: {
select: {
id: true,
nome: true,
email: true
}
}
}
});
if (!calculo) {
return res.status(404).json({ error: 'Cálculo não encontrado' });
}
res.json(calculo);
} catch (error) {
console.error('Erro ao buscar cálculo:', error);
res.status(500).json({ error: 'Falha ao buscar cálculo' });
}
});
// Listar cálculos
router.get('/', async (req, res) => {
try {
const calculos = await prisma.calculoPensaoAlimenticia.findMany({
include: {
processo: true,
cliente: true,
usuario: {
select: {
id: true,
nome: true
}
}
},
orderBy: {
criadoEm: 'desc'
}
});
res.json(calculos);
} catch (error) {
console.error('Erro ao listar cálculos:', error);
res.status(500).json({ error: 'Falha ao listar cálculos' });
}
});
export default router;
+192
View File
@@ -0,0 +1,192 @@
import { Router } from 'express';
import { prisma } from '../../config/database';
import { authMiddleware } from '../../middleware/auth';
const router = Router();
// Listar processos
router.get('/', authMiddleware, async (req, res) => {
try {
const processos = await prisma.processo.findMany({
include: {
cliente: true,
partes: true,
diligencias: true,
audiencias: true,
documentos: true,
movimentacoes: true,
honorarios: {
include: {
parcelas: true,
},
},
custas: true,
},
orderBy: {
dataCriacao: 'desc',
},
});
res.json(processos);
} catch (error) {
console.error('Erro ao buscar processos:', error);
res.status(500).json({ error: 'Erro ao buscar processos' });
}
});
// Criar processo
router.post('/', authMiddleware, async (req, res) => {
try {
const processo = await prisma.processo.create({
data: {
...req.body,
cliente: {
connect: { id: req.body.clienteId },
},
},
include: {
cliente: true,
partes: true,
diligencias: true,
audiencias: true,
documentos: true,
movimentacoes: true,
honorarios: {
include: {
parcelas: true,
},
},
custas: true,
},
});
res.status(201).json(processo);
} catch (error) {
console.error('Erro ao criar processo:', error);
res.status(400).json({ error: 'Erro ao criar processo' });
}
});
// Busca de processo individual
router.get('/:id', async (req, res) => {
try {
const processo = await prisma.processo.findUnique({
where: { id: req.params.id },
include: {
cliente: true,
partes: true,
diligencias: true,
audiencias: true,
documentos: true,
movimentacoes: true,
honorarios: {
include: {
parcelas: true,
},
},
custas: true,
},
});
if (!processo) {
return res.status(404).json({ error: "Processo não encontrado" });
}
// Garantir que sempre tenha um resultado, mesmo que seja NAO_JULGADO
if (!processo.resultado) {
processo.resultado = 'NAO_JULGADO';
}
res.json(processo);
} catch (error) {
console.error("Erro ao buscar processo:", error);
res.status(500).json({ error: "Erro ao buscar processo" });
}
});
// Atualizar resultado do processo
router.patch('/:id/resultado', async (req, res) => {
try {
const { id } = req.params;
const { resultado } = req.body;
const processo = await prisma.processo.update({
where: { id },
data: {
resultado,
dataAtualizacao: new Date()
},
include: {
cliente: true,
partes: true,
diligencias: true,
audiencias: true,
documentos: true,
movimentacoes: true
}
});
res.json(processo);
} catch (error) {
console.error('Erro ao atualizar resultado:', error);
res.status(500).json({ error: 'Erro ao atualizar resultado do processo' });
}
});
// Atualizar processo
router.put('/:id', authMiddleware, async (req, res) => {
try {
const { id } = req.params;
const { clienteId, ...data } = req.body;
const processo = await prisma.processo.update({
where: { id },
data: {
...data,
cliente: clienteId ? {
connect: { id: clienteId }
} : undefined
},
include: {
cliente: true,
partes: true,
audiencias: true,
documentos: true,
movimentacoes: true,
honorarios: {
include: {
parcelas: true
}
},
custas: true
}
});
res.json(processo);
} catch (error) {
console.error('Erro ao atualizar processo:', error);
res.status(400).json({ error: 'Erro ao atualizar processo' });
}
});
// Deletar processo
router.delete('/:id', authMiddleware, async (req, res) => {
try {
const { id } = req.params;
// Deletar primeiro as relações
await prisma.$transaction([
prisma.parteProcesso.deleteMany({ where: { processoId: id } }),
prisma.audiencia.deleteMany({ where: { processoId: id } }),
prisma.movimentacao.deleteMany({ where: { processoId: id } }),
prisma.documento.deleteMany({ where: { processoId: id } }),
prisma.custaProcessual.deleteMany({ where: { processoId: id } }),
prisma.honorario.deleteMany({ where: { processoId: id } }),
prisma.processo.delete({ where: { id } })
]);
res.status(204).send();
} catch (error) {
console.error('Erro ao deletar processo:', error);
res.status(400).json({ error: 'Erro ao deletar processo' });
}
});
export default router;
@@ -0,0 +1,176 @@
import express from 'express';
import { ProcessoAdministrativoRepository } from '../../repositories/ProcessoAdministrativoRepository';
const router = express.Router();
const repository = new ProcessoAdministrativoRepository();
// Listar todos os processos administrativos
router.get('/', async (req, res) => {
try {
const processos = await repository.findAll();
res.json(processos);
} catch (error) {
console.error("Erro ao listar processos administrativos:", error);
res.status(500).json({ error: "Erro ao listar processos administrativos" });
}
});
// Buscar processo por ID
router.get('/:id', async (req, res) => {
try {
const { id } = req.params;
// Se o ID for "novo", retornar 404
if (id === 'novo') {
return res.status(404).json({ error: "Processo não encontrado" });
}
const processo = await repository.findById(id);
if (!processo) {
return res.status(404).json({ error: "Processo não encontrado" });
}
res.json(processo);
} catch (error) {
console.error("Erro ao buscar processo:", error);
res.status(500).json({ error: "Erro ao buscar processo" });
}
});
// Criar novo processo
router.post('/', async (req, res) => {
try {
const novoProcesso = await repository.create(req.body);
res.status(201).json(novoProcesso);
} catch (error) {
console.error("Erro ao criar processo:", error);
res.status(400).json({
error: "Erro ao criar processo",
details: error instanceof Error ? error.message : undefined
});
}
});
// Adicionar movimentação
router.post('/:id/movimentacoes', async (req, res) => {
try {
const movimentacao = await repository.addMovimentacao(
req.params.id,
req.body
);
res.status(201).json(movimentacao);
} catch (error) {
console.error("Erro ao adicionar movimentação:", error);
res.status(400).json({
error: "Erro ao adicionar movimentação",
details: error instanceof Error ? error.message : undefined
});
}
});
// Atualizar movimentação
router.put('/movimentacoes/:id', async (req, res) => {
try {
const { id } = req.params;
const movimentacao = await repository.updateMovimentacao(id, req.body);
res.json(movimentacao);
} catch (error) {
console.error("Erro ao atualizar movimentação:", error);
res.status(400).json({
error: "Erro ao atualizar movimentação",
details: error instanceof Error ? error.message : undefined
});
}
});
// Excluir movimentação
router.delete('/movimentacoes/:id', async (req, res) => {
try {
await repository.deleteMovimentacao(req.params.id);
res.status(204).send();
} catch (error) {
console.error("Erro ao excluir movimentação:", error);
res.status(400).json({
error: "Erro ao excluir movimentação",
details: error instanceof Error ? error.message : undefined
});
}
});
// Atualizar processo
router.put('/:id', async (req, res) => {
try {
const processoAtualizado = await repository.update(req.params.id, req.body);
res.json(processoAtualizado);
} catch (error) {
console.error("Erro ao atualizar processo:", error);
res.status(400).json({
error: "Erro ao atualizar processo",
details: error instanceof Error ? error.message : undefined
});
}
});
// Excluir processo
router.delete('/:id', async (req, res) => {
try {
await repository.delete(req.params.id);
res.status(204).send();
} catch (error) {
console.error("Erro ao excluir processo:", error);
res.status(400).json({
error: "Erro ao excluir processo",
details: error instanceof Error ? error.message : undefined
});
}
});
// Rotas para custas administrativas
router.get('/:id/custas', async (req, res) => {
try {
const custas = await repository.findCustasByProcesso(req.params.id);
res.json(custas);
} catch (error) {
res.status(500).json({
error: "Erro ao buscar custas",
details: error instanceof Error ? error.message : undefined
});
}
});
router.post('/:id/custas', async (req, res) => {
try {
const custa = await repository.addCusta(req.params.id, req.body);
res.status(201).json(custa);
} catch (error) {
res.status(400).json({
error: "Erro ao criar custa",
details: error instanceof Error ? error.message : undefined
});
}
});
router.put('/:processoId/custas/:id', async (req, res) => {
try {
const custa = await repository.updateCusta(req.params.id, req.body);
res.json(custa);
} catch (error) {
res.status(400).json({
error: "Erro ao atualizar custa",
details: error instanceof Error ? error.message : undefined
});
}
});
router.delete('/:processoId/custas/:id', async (req, res) => {
try {
await repository.deleteCusta(req.params.id);
res.status(204).send();
} catch (error) {
res.status(400).json({
error: "Erro ao excluir custa",
details: error instanceof Error ? error.message : undefined
});
}
});
export default router;
+241
View File
@@ -0,0 +1,241 @@
import express from 'express';
import multer from 'multer';
import { ProvaService } from '../services/ProvaService';
import { prisma } from '../prismaClient';
import * as fsPromises from 'fs/promises';
import * as fs from 'fs'; // Importando o módulo fs padrão
import * as path from 'path';
const router = express.Router();
const storage = multer.memoryStorage();
const upload = multer({ storage });
const provaService = new ProvaService();
// Upload de arquivo para validação
router.post('/upload', upload.array('files'), async (req, res) => {
try {
console.log('Iniciando upload de arquivos...');
if (!req.files || !Array.isArray(req.files) || req.files.length === 0) {
return res.status(400).json({ error: 'Nenhum arquivo recebido' });
}
// Extrair informações sobre o endereço IP e porta do cliente
const forwarded = req.headers['x-forwarded-for'] as string;
const socketInfo = req.socket.remoteAddress ? {
address: req.socket.remoteAddress,
port: req.socket.remotePort || 'desconhecida'
} : null;
// Metadata avançada seguindo RFC 3227
const metadata = {
userAgent: req.body.user_agent,
language: req.body.language,
viewportWidth: req.body.viewport_width,
viewportHeight: req.body.viewport_height,
ipAddress: req.ip || req.socket.remoteAddress,
clientPort: req.socket.remotePort || 'desconhecida',
connectionInfo: {
ip: req.ip || req.socket.remoteAddress,
forwardedIp: forwarded,
socketAddress: socketInfo?.address,
socketPort: socketInfo?.port,
remoteFamily: req.socket.remoteFamily
},
timestamp: new Date(),
modification_times: req.body.modification_times,
referrer: req.body.referrer,
timezone: req.body.timezone,
timezone_offset: req.body.timezone_offset,
platform: req.body.platform,
cookies_enabled: req.body.cookies_enabled,
browser_plugins: req.body.browser_plugins,
latitude: req.body.latitude,
longitude: req.body.longitude,
location_permission: req.body.location_permission,
client_timestamp: req.body.client_timestamp,
headers: {
'x-forwarded-for': req.headers['x-forwarded-for'],
'user-agent': req.headers['user-agent'],
'accept-language': req.headers['accept-language'],
'referer': req.headers['referer'],
'host': req.headers['host'],
'connection': req.headers['connection'],
},
server: {
timestamp: new Date().toISOString(),
hostname: require('os').hostname(),
process: process.pid,
nodeVersion: process.version,
}
};
console.log('Metadata recebida:', metadata);
console.log('Informações de conexão do cliente:', metadata.connectionInfo);
// Adicionar informações de segurança
const securityInfo = {
requestMethod: req.method,
protocol: req.protocol,
isSecure: req.secure,
originalUrl: req.originalUrl,
contentType: req.headers['content-type'],
};
console.log('Informações de segurança:', securityInfo);
// Processar cada arquivo
const results = [];
for (const file of req.files as Express.Multer.File[]) {
try {
// Adicionar metadados específicos do arquivo atual
const fileIndex = (req.files as Express.Multer.File[]).indexOf(file);
// Extrair e formatar a data de modificação do arquivo
let fileModifiedDate = null;
const modificationTime = req.body.modification_times ?
(Array.isArray(req.body.modification_times) ? req.body.modification_times[fileIndex] : req.body.modification_times) :
null;
if (modificationTime) {
try {
// Converter timestamp para objeto Date
const timestamp = parseInt(modificationTime);
if (!isNaN(timestamp)) {
fileModifiedDate = new Date(timestamp);
}
} catch (dateError) {
console.warn(`Erro ao processar data de modificação para ${file.originalname}:`, dateError);
}
}
const fileMetadata = {
...metadata,
file_specific: {
name: req.body.file_names ?
(Array.isArray(req.body.file_names) ? req.body.file_names[fileIndex] : req.body.file_names) :
file.originalname,
size: req.body.file_sizes ?
(Array.isArray(req.body.file_sizes) ? req.body.file_sizes[fileIndex] : req.body.file_sizes) :
file.size,
type: req.body.file_types ?
(Array.isArray(req.body.file_types) ? req.body.file_types[fileIndex] : req.body.file_types) :
file.mimetype,
modified: fileModifiedDate ? fileModifiedDate.toISOString() : null,
modifiedTimestamp: modificationTime
},
security: securityInfo
};
// Adicionar log para debug de data de modificação
console.log(`Informações de modificação do arquivo ${file.originalname}:`, {
rawTimestamp: modificationTime,
processedDate: fileModifiedDate,
isoString: fileModifiedDate ? fileModifiedDate.toISOString() : null
});
const result = await provaService.processFile(file, fileMetadata);
results.push(result);
} catch (error) {
console.error(`Erro ao processar arquivo ${file.originalname}:`, error);
return res.status(500).json({
error: `Erro ao processar arquivo ${file.originalname}`,
details: error.message
});
}
}
res.json({
success: true,
results,
id: results[0].id
});
} catch (error) {
console.error('Erro no upload:', error);
res.status(500).json({ error: 'Erro ao processar arquivos' });
}
});
// Status da prova
router.get('/:id/status', async (req, res) => {
try {
const { id } = req.params;
const prova = await prisma.prova.findUnique({
where: { id }
});
if (!prova) {
return res.status(404).json({ error: 'Prova não encontrada' });
}
res.json(prova);
} catch (error) {
console.error('Erro ao buscar status:', error);
res.status(500).json({ error: 'Erro ao buscar status da prova' });
}
});
// Verificar documento
router.get('/verificar/:hash', async (req, res) => {
try {
const { hash } = req.params;
const result = await provaService.verifyFile(hash);
res.json(result);
} catch (error) {
console.error('Erro ao verificar documento:', error);
res.status(500).json({ error: 'Erro ao verificar documento' });
}
});
// Modificar/adicionar esta rota para servir o relatório a partir do hash
router.get('/relatorio/:hash', async (req, res) => {
try {
const { hash } = req.params;
// Buscar a prova pelo hash
const prova = await prisma.prova.findFirst({
where: { checksumSha256: hash }
});
if (!prova || prova.status !== 'CONCLUIDO') {
return res.status(404).json({ error: 'Relatório não encontrado' });
}
// Construir o caminho para o arquivo do relatório
const reportPath = path.join(process.cwd(), 'storage', 'provas', `${hash}_report.pdf`);
// Verificar se o arquivo existe
try {
await fsPromises.access(reportPath); // Usando fsPromises para operação assíncrona
} catch (error) {
console.error('Erro ao acessar relatório:', error);
return res.status(404).json({ error: 'Arquivo do relatório não encontrado' });
}
// Enviar o arquivo como resposta
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', `inline; filename="${prova.fileName.replace(/\.[^/.]+$/, '')}_relatorio.pdf"`);
// Criar stream de leitura e enviar para o cliente
const fileStream = fs.createReadStream(reportPath); // Usando fs padrão para createReadStream
fileStream.pipe(res);
} catch (error) {
console.error('Erro ao servir relatório:', error);
res.status(500).json({ error: 'Erro ao gerar relatório' });
}
});
// Listar provas (admin)
router.get('/admin', async (req, res) => {
try {
const provas = await prisma.prova.findMany({
orderBy: { dataCriacao: 'desc' }
});
res.json(provas);
} catch (error) {
console.error('Erro ao listar provas:', error);
res.status(500).json({ error: 'Erro ao listar provas' });
}
});
export default router;
+191
View File
@@ -0,0 +1,191 @@
import { Router } from 'express';
import { prisma } from '../../config/database';
import { authMiddleware } from '../../middleware/auth';
const router = Router();
// Listar tarefas
router.get('/', authMiddleware, async (req, res) => {
try {
const tarefas = await prisma.tarefa.findMany({
include: {
responsavel: true,
subtarefas: true,
comentarios: {
include: {
criadoPor: true
}
}
},
orderBy: {
dataVencimento: 'asc'
}
});
res.json(tarefas);
} catch (error) {
res.status(500).json({ error: 'Erro ao buscar tarefas' });
}
});
// Criar tarefa
router.post('/', authMiddleware, async (req, res) => {
try {
const tarefa = await prisma.tarefa.create({
data: {
...req.body,
criadoPorId: req.user.id,
status: req.body.status || 'PENDENTE',
progresso: req.body.progresso || 0,
etiquetas: req.body.etiquetas || [],
subtarefas: {
create: req.body.subtarefas || []
},
comentarios: {
create: req.body.comentarios || []
}
},
include: {
subtarefas: true,
comentarios: true,
responsavel: true,
criadoPor: true
}
});
res.json(tarefa);
} catch (error) {
console.error('Erro ao criar tarefa:', error);
res.status(500).json({ error: 'Erro ao criar tarefa' });
}
});
// TODO: Implementar upload de anexos com integração Nextcloud
// router.post('/:id/anexos', authMiddleware, uploadHandler.array('files'), async (req, res) => { ... });
// Atualizar status da tarefa
router.patch('/:id/status', authMiddleware, async (req, res) => {
try {
const { id } = req.params;
const { status, progresso, justificativa } = req.body;
const tarefa = await prisma.tarefa.update({
where: { id },
data: {
status,
progresso: progresso || (status === 'CONCLUIDA' ? 100 : status === 'CANCELADA' ? 0 : undefined),
justificativa,
atualizadoEm: new Date()
},
include: {
responsavel: true,
subtarefas: true,
comentarios: {
include: {
criadoPor: true
}
}
}
});
res.json(tarefa);
} catch (error) {
console.error('Erro ao atualizar status:', error);
res.status(500).json({ error: 'Erro ao atualizar status' });
}
});
// Adicionar rota de edição
router.put('/:id', authMiddleware, async (req, res) => {
try {
const { id } = req.params;
const {
processoId,
clienteId,
responsavelId,
criadoPorId,
subtarefas,
comentarios,
id: _id,
atualizadoEm,
...data
} = req.body;
const updateData = {
...data,
atualizadoEm: new Date(),
responsavel: responsavelId ? {
connect: { id: responsavelId }
} : undefined,
processo: processoId ? {
connect: { id: processoId }
} : {
disconnect: true
},
cliente: clienteId ? {
connect: { id: clienteId }
} : {
disconnect: true
},
subtarefas: {
deleteMany: {},
create: subtarefas?.map((st: any) => ({
titulo: st.titulo,
concluida: st.concluida || false
})) || []
},
comentarios: comentarios?.length ? {
create: comentarios.map((c: any) => ({
texto: c.texto,
criadoPor: {
connect: { id: req.user.id }
}
}))
} : undefined
};
const tarefa = await prisma.tarefa.update({
where: { id },
data: updateData,
include: {
subtarefas: true,
comentarios: {
include: {
criadoPor: true
}
},
responsavel: true,
criadoPor: true,
processo: true,
cliente: true
}
});
res.json(tarefa);
} catch (error) {
console.error('Erro ao atualizar tarefa:', error);
res.status(500).json({ error: 'Erro ao atualizar tarefa' });
}
});
// Adicionar rota de exclusão
router.delete('/:id', authMiddleware, async (req, res) => {
try {
const { id } = req.params;
// Verificar se usuário tem permissão
if (!['ADVOGADO_ADMIN', 'ADVOGADO'].includes(req.user.perfil)) {
return res.status(403).json({ error: 'Sem permissão para excluir tarefas' });
}
await prisma.tarefa.delete({
where: { id }
});
res.status(204).send();
} catch (error) {
console.error('Erro ao excluir tarefa:', error);
res.status(500).json({ error: 'Erro ao excluir tarefa' });
}
});
export default router;