'use server'; import { and, eq } from 'drizzle-orm'; import { revalidatePath } from 'next/cache'; import { db } from '@/db'; import { leads, leadEstadoHistory, leadPipelineEventos, precisionHistory } from '@/db/schema'; import { getCurrentTenantId as getTenantId } from '@/lib/auth/current-user'; import { getPricingConfig, getCatalog } from '@/db/pricing-queries'; import { computeBudget } from '@/budget'; import type { BudgetInputs } from '@/budget/types'; type Estado = (typeof leads.estado.enumValues)[number]; export async function cambiarEstado(leadId: string, estado: Estado) { const tenantId = await getTenantId(); const [updated] = await db .update(leads) .set({ estado, updatedAt: new Date() }) .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId))) .returning(); if (!updated) throw new Error('Lead no encontrado.'); await db.insert(leadEstadoHistory).values({ leadId, estado }); revalidatePath('/panel'); revalidatePath(`/panel/${leadId}`); } export async function marcarGanado(leadId: string, precioFinalEuros: number) { const tenantId = await getTenantId(); const [lead] = await db .select() .from(leads) .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId))) .limit(1); if (!lead) throw new Error('Lead no encontrado.'); if (lead.presupuestoEstimado == null) { throw new Error('El lead no tiene presupuesto estimado, no se puede calcular la precisión.'); } const finalCents = Math.round(precioFinalEuros * 100); const deltaPct = ((finalCents - lead.presupuestoEstimado) / lead.presupuestoEstimado) * 100; await db .update(leads) .set({ estado: 'ganado', updatedAt: new Date() }) .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId))); await db.insert(leadEstadoHistory).values({ leadId, estado: 'ganado' }); await db.insert(precisionHistory).values({ leadId, estimated: lead.presupuestoEstimado, final: finalCents, deltaPct: deltaPct.toFixed(2), }); revalidatePath('/panel'); revalidatePath(`/panel/${leadId}`); } export async function recalcularPresupuesto(leadId: string) { const tenantId = await getTenantId(); const [lead] = await db .select() .from(leads) .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId))) .limit(1); if (!lead) throw new Error('Lead no encontrado.'); const [config, catalog] = await Promise.all([getPricingConfig(), getCatalog()]); const inputs: BudgetInputs = { tipoReforma: lead.tipoReforma ?? 'otro', m2Suelo: lead.m2Suelo ?? null, alturaTecho: lead.alturaTecho ?? null, calidadGlobal: lead.calidadGlobal ?? 'media', estructural: lead.estructural, provincia: lead.provincia ?? null, materialSelections: (lead.materialSelections as Record) ?? {}, }; const result = computeBudget(inputs, config, catalog); await db .update(leads) .set({ presupuestoEstimado: result.total, desgloseSnapshot: { stage: lead.pipelineStage, result }, updatedAt: new Date(), }) .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId))); await db.insert(leadPipelineEventos).values({ leadId, stage: 'presupuesto_generado', metadata: { total: result.total, confianza: result.confianza }, }); revalidatePath('/panel'); revalidatePath(`/panel/${leadId}`); }