import { NextResponse } from "next/server"; import { prisma } from "@/lib/prisma"; import { auth } from "@/lib/auth"; import { crearEstimacionSchema, calcularTotalesEstimacion } from "@/lib/validations/estimaciones"; // GET - Listar estimaciones (por presupuesto) export async function GET(request: Request) { try { const session = await auth(); if (!session?.user?.empresaId) { return NextResponse.json({ error: "No autorizado" }, { status: 401 }); } const { searchParams } = new URL(request.url); const presupuestoId = searchParams.get("presupuestoId"); const whereClause: Record = { presupuesto: { obra: { empresaId: session.user.empresaId, }, }, }; if (presupuestoId) { whereClause.presupuestoId = presupuestoId; } const estimaciones = await prisma.estimacion.findMany({ where: whereClause, include: { presupuesto: { select: { id: true, nombre: true, obra: { select: { id: true, nombre: true, }, }, }, }, partidas: { include: { partida: { select: { codigo: true, descripcion: true, unidad: true, }, }, }, }, _count: { select: { partidas: true, }, }, }, orderBy: [{ presupuestoId: "asc" }, { numero: "desc" }], }); return NextResponse.json(estimaciones); } catch (error) { console.error("Error al obtener estimaciones:", error); return NextResponse.json( { error: "Error al obtener estimaciones" }, { status: 500 } ); } } // POST - Crear nueva estimación export async function POST(request: Request) { try { const session = await auth(); if (!session?.user?.empresaId) { return NextResponse.json({ error: "No autorizado" }, { status: 401 }); } const body = await request.json(); const validation = crearEstimacionSchema.safeParse(body); if (!validation.success) { return NextResponse.json( { error: "Datos inválidos", details: validation.error.errors }, { status: 400 } ); } const data = validation.data; // Verificar que el presupuesto existe y pertenece a la empresa const presupuesto = await prisma.presupuesto.findFirst({ where: { id: data.presupuestoId, obra: { empresaId: session.user.empresaId, }, }, include: { partidas: true, }, }); if (!presupuesto) { return NextResponse.json( { error: "Presupuesto no encontrado" }, { status: 404 } ); } // Obtener el último número de estimación para este presupuesto const ultimaEstimacion = await prisma.estimacion.findFirst({ where: { presupuestoId: data.presupuestoId }, orderBy: { numero: "desc" }, select: { numero: true }, }); const nuevoNumero = (ultimaEstimacion?.numero || 0) + 1; // Obtener estimaciones anteriores para calcular acumulados const estimacionesAnteriores = await prisma.estimacionPartida.findMany({ where: { estimacion: { presupuestoId: data.presupuestoId, estado: { not: "RECHAZADA" }, }, }, select: { partidaId: true, cantidadEstimacion: true, importeEstimacion: true, }, }); // Calcular acumulados por partida const acumuladosPorPartida: Record = {}; for (const ep of estimacionesAnteriores) { if (!acumuladosPorPartida[ep.partidaId]) { acumuladosPorPartida[ep.partidaId] = { cantidad: 0, importe: 0 }; } acumuladosPorPartida[ep.partidaId].cantidad += ep.cantidadEstimacion; acumuladosPorPartida[ep.partidaId].importe += ep.importeEstimacion; } // Preparar partidas para cálculo const partidasParaCalculo = data.partidas.map((p) => { const partidaPresupuesto = presupuesto.partidas.find( (pp) => pp.id === p.partidaId ); if (!partidaPresupuesto) { throw new Error(`Partida ${p.partidaId} no encontrada en el presupuesto`); } const acumulado = acumuladosPorPartida[p.partidaId] || { cantidad: 0, importe: 0 }; return { cantidadEstimacion: p.cantidadEstimacion, precioUnitario: partidaPresupuesto.precioUnitario, cantidadAnterior: acumulado.cantidad, cantidadContrato: partidaPresupuesto.cantidad, }; }); // Calcular totales const totales = calcularTotalesEstimacion(partidasParaCalculo, { porcentajeRetencion: data.porcentajeRetencion || 5, porcentajeIVA: data.porcentajeIVA || 16, amortizacion: data.amortizacion || 0, deduccionesVarias: data.deduccionesVarias || 0, }); // Crear estimación con partidas const estimacion = await prisma.estimacion.create({ data: { numero: nuevoNumero, periodo: data.periodo, fechaInicio: new Date(data.fechaInicio), fechaFin: new Date(data.fechaFin), porcentajeRetencion: data.porcentajeRetencion || 5, porcentajeIVA: data.porcentajeIVA || 16, amortizacion: data.amortizacion || 0, deduccionesVarias: data.deduccionesVarias || 0, observaciones: data.observaciones, importeEjecutado: totales.importeEjecutado, importeAnterior: totales.importeAnterior, importeAcumulado: totales.importeAcumulado, retencion: totales.retencion, subtotal: totales.subtotal, iva: totales.iva, total: totales.total, importeNeto: totales.importeNeto, presupuestoId: data.presupuestoId, partidas: { create: data.partidas.map((p) => { const partidaPresupuesto = presupuesto.partidas.find( (pp) => pp.id === p.partidaId )!; const acumulado = acumuladosPorPartida[p.partidaId] || { cantidad: 0, importe: 0 }; const cantidadAcumulada = acumulado.cantidad + p.cantidadEstimacion; const importeEstimacion = p.cantidadEstimacion * partidaPresupuesto.precioUnitario; const importeAcumulado = acumulado.importe + importeEstimacion; const porcentajeAnterior = (acumulado.cantidad / partidaPresupuesto.cantidad) * 100; const porcentajeEstimacion = (p.cantidadEstimacion / partidaPresupuesto.cantidad) * 100; const porcentajeAcumulado = (cantidadAcumulada / partidaPresupuesto.cantidad) * 100; return { partidaId: p.partidaId, cantidadContrato: partidaPresupuesto.cantidad, cantidadAnterior: acumulado.cantidad, cantidadEstimacion: p.cantidadEstimacion, cantidadAcumulada, cantidadPendiente: partidaPresupuesto.cantidad - cantidadAcumulada, precioUnitario: partidaPresupuesto.precioUnitario, importeAnterior: acumulado.importe, importeEstimacion, importeAcumulado, porcentajeAnterior, porcentajeEstimacion, porcentajeAcumulado, notas: p.notas, }; }), }, }, include: { partidas: { include: { partida: { select: { codigo: true, descripcion: true, unidad: true, }, }, }, }, presupuesto: { select: { nombre: true, obra: { select: { nombre: true, }, }, }, }, }, }); return NextResponse.json(estimacion, { status: 201 }); } catch (error) { console.error("Error al crear estimación:", error); return NextResponse.json( { error: error instanceof Error ? error.message : "Error al crear estimación" }, { status: 500 } ); } }