import type { PlanPago } from '../types' import { generarSeed } from '../data/seed' import { generarCalendario, registrarAbono, statusPlan, agregarNotaAPlan, type AbonoInput } from '../lib/cobranza' import { uuid } from '../lib/uuid' export const CLAVE_STORAGE = 'frasa_cobranza_v1' // Inyectable para pruebas (stub en vitest, donde no hay localStorage) type StorageLike = Pick function storageDefault(): StorageLike { return globalThis.localStorage } /** * Carga los planes desde localStorage; si está vacío siembra los datos de ejemplo. * Migra planes guardados con versiones anteriores del modelo (sin `notas`). * TODO: reemplazar por GET /api/planes cuando el API Gateway esté disponible. */ export function cargarPlanes(storage: StorageLike = storageDefault()): PlanPago[] { const crudo = storage.getItem(CLAVE_STORAGE) if (crudo) { const planes = JSON.parse(crudo) as PlanPago[] return planes.map((p) => (p.notas ? p : { ...p, notas: [] })) } const planes = generarSeed() guardarPlanes(planes, storage) return planes } export function guardarPlanes(planes: PlanPago[], storage: StorageLike = storageDefault()): void { storage.setItem(CLAVE_STORAGE, JSON.stringify(planes)) } export function obtenerPlan(id: string, storage: StorageLike = storageDefault()): PlanPago | undefined { return cargarPlanes(storage).find((p) => p.id === id) } export interface NuevoPlanInput { clienteNombre: string clienteTelefono?: string unidadCodigo: string proyecto: string ventaRef: string fechaInicio: string engancheTotal: number numPagos: number tasaMoraMensual?: number } export function crearPlan(input: NuevoPlanInput, storage: StorageLike = storageDefault()): PlanPago { const { mensualidad, pagos } = generarCalendario(input.fechaInicio, input.engancheTotal, input.numPagos) const plan: PlanPago = { id: uuid(), ventaRef: input.ventaRef, clienteNombre: input.clienteNombre, clienteTelefono: input.clienteTelefono || undefined, unidadCodigo: input.unidadCodigo, proyecto: input.proyecto, fechaInicio: input.fechaInicio, engancheTotal: input.engancheTotal, numPagos: input.numPagos, mensualidad, tasaMoraMensual: input.tasaMoraMensual, status: 'ACTIVO', pagos, notas: [], createdAt: new Date().toISOString(), } guardarPlanes([...cargarPlanes(storage), plan], storage) return plan } export function actualizarPlan(plan: PlanPago, storage: StorageLike = storageDefault()): PlanPago { // El status operativo (ACTIVO/LIQUIDADO) se deriva del saldo; CANCELADO es manual const actualizado: PlanPago = { ...plan, status: statusPlan(plan) } guardarPlanes( cargarPlanes(storage).map((p) => (p.id === actualizado.id ? actualizado : p)), storage, ) return actualizado } export function cancelarPlan(id: string, storage: StorageLike = storageDefault()): PlanPago { const plan = obtenerPlan(id, storage) if (!plan) throw new Error('Plan no encontrado') const cancelado: PlanPago = { ...plan, status: 'CANCELADO' } guardarPlanes( cargarPlanes(storage).map((p) => (p.id === id ? cancelado : p)), storage, ) return cancelado } export function registrarAbonoEnPlan( planId: string, abono: Omit, pagoId: string, storage: StorageLike = storageDefault(), ): PlanPago { const plan = obtenerPlan(planId, storage) if (!plan) throw new Error('Plan no encontrado') return actualizarPlan(registrarAbono(plan, { ...abono, pagoId }), storage) } /** Agrega una nota a la bitácora del plan y persiste el cambio */ export function agregarNota( planId: string, texto: string, storage: StorageLike = storageDefault(), ): PlanPago { const plan = obtenerPlan(planId, storage) if (!plan) throw new Error('Plan no encontrado') return actualizarPlan(agregarNotaAPlan(plan, texto), storage) }