import { prisma } from "@/lib/db"; import { getConfigBranding } from "@/lib/config-helpers"; import { formatDate, formatCurrency, calcularTotalesOpcion, esCotizacionPorTiempo, tarifaHoraSugerida, IVA_RATE, type MetaOpcion, } from "@/lib/calculators"; import Link from "next/link"; import { ArrowLeft, Pencil } from "lucide-react"; import { notFound } from "next/navigation"; import { ExportExcelButtonSaved, ExportPDFButtonSaved, PreviewPDFButtonSaved } from "@/components/ExportButtons"; import { DeleteCotizacionButton } from "./DeleteButton"; import { CambiarEstadoButtons } from "./CambiarEstadoButtons"; import { PreciosEditables } from "./PreciosEditables"; import { RegistroHorasPanel } from "./RegistroHorasPanel"; export const dynamic = "force-dynamic"; export default async function CotizacionDetailPage({ params, }: { params: Promise<{ id: string }>; }) { const { id } = await params; const cot = await prisma.cotizacion.findUnique({ where: { id }, include: { cliente: true, asesor: true, servicios: { include: { servicioCatalogo: true } }, planBucefalo: true, registrosHoras: { orderBy: [{ fecha: "asc" }, { horaInicio: "asc" }] }, }, }); if (!cot) notFound(); // El registro de horas (notas de pago) solo aplica a cotizaciones aprobadas que se // cobran por tiempo (horas/retainer/demanda). const mostrarHoras = cot.estado === "aprobada" && esCotizacionPorTiempo(cot.servicios); const branding = mostrarHoras ? await getConfigBranding() : null; const serviciosUnicos = cot.servicios .filter((s) => s.tipoPago === "unico" && s.seleccionado) .map((s) => ({ id: s.id, nombre: s.servicioCatalogo?.nombre || s.nombre || "Servicio", precio: s.precio, modeloCobro: s.modeloCobro, tarifaHora: s.tarifaHora, })); const serviciosMensuales = cot.servicios .filter((s) => s.tipoPago === "mensual" && s.seleccionado) .map((s) => ({ id: s.id, nombre: s.servicioCatalogo?.nombre || s.nombre || "Servicio", precio: s.precio, modeloCobro: s.modeloCobro, tarifaHora: s.tarifaHora, })); const opcionesMeta = (cot.opcionesMetadata as { "1"?: MetaOpcion; "2"?: MetaOpcion } | null) ?? {}; const totalesOpcion = cot.esDoble ? { "1": calcularTotalesOpcion(cot.servicios, "1"), "2": calcularTotalesOpcion(cot.servicios, "2"), } : null; return (

{cot.numero}

{cot.cliente.empresa || cot.cliente.nombre} | {cot.proyecto}

Editar

Información de la Cotización

Cliente

{cot.cliente.nombre}

Empresa

{cot.cliente.empresa || "-"}

Asesor

{cot.asesor.name}

Fecha

{formatDate(cot.fecha)}

Vigencia

{formatDate(cot.vigencia)}

Moneda

{cot.moneda}

Esquema

{cot.esquemaPago}

Estado
{cot.esDoble && totalesOpcion && (

Doble propuesta — comparativa

{(["1", "2"] as const).map((op) => { const t = totalesOpcion[op]; const meta = opcionesMeta[op]; return (

Opcion {op} {meta?.titulo ? `: ${meta.titulo}` : ""}

{meta?.descripcion && (

{meta.descripcion}

)} {meta?.noIncluye && (

No incluye:{" "} {meta.noIncluye}

)}
Total unico (c/IVA) {formatCurrency(t.totalUnico * (1 + IVA_RATE))}
Total mensual (c/IVA) {formatCurrency(t.totalMensual * (1 + IVA_RATE))}
{t.horas > 0 && (
Horas estimadas {t.horas} h
)}
); })}

Las partidas marcadas Ambas suman en las dos opciones. Edita la asignacion de cada partida desde Editar.

)} {mostrarHoras && ( ({ id: r.id, fecha: r.fecha.toISOString(), horaInicio: r.horaInicio, horaFin: r.horaFin, horas: r.horas, tarifaHora: r.tarifaHora, descripcion: r.descripcion, }))} /> )} {cot.observaciones && (

Observaciones

{cot.observaciones}

)}
); }