import { mondayService, type Unidad } from './monday.service.js' import { prisma } from './prisma.js' import { saludMonday } from './salud-monday.service.js' import { AppError } from '../utils/errors.js' /** * Fallback de LECTURA del inventario (modo local): la tabla `Unidad` es el * espejo local del tablero Unidades de Monday (la siembra * scripts/seed-unidades-locales.js y la mantiene fresco el write-through de * monday.service.getUnidades). Cuando Monday está caído (saludMonday) o lanza * AppError 502, el cotizador lee de aquí con la MISMA forma de DTO. * * La tabla local no guarda fechaEntrega ni mesesParaFirma (no están en el * seed curado): salen en null, que el cotizador ya tolera (plazo desconocido). */ /** Fila de la tabla Unidad tal como la devuelve Prisma (estructural) */ export interface FilaUnidad { codigo: string desarrollo: string modelo: string supConstruccion: number supExcedente: number supEstacionamiento: number precioLista: number precioAcabados: number status: string etapa: string } /** Fila local → DTO `Unidad` (UnidadCotizable) que consumen controllers y fronts */ export function unidadLocalADto(u: FilaUnidad): Unidad { return { codigo: u.codigo, modelo: u.modelo, fechaEntrega: null, // no está en el espejo local: plazo desconocido supEstacionamiento: u.supEstacionamiento, supConstruccion: u.supConstruccion, supExcedente: u.supExcedente, precioSinAcabados: u.precioLista, precioConAcabados: u.precioAcabados, status: u.status, desarrollo: u.desarrollo, mesesParaFirma: null, etapa: u.etapa === '' ? null : u.etapa, } } /** true si el error es un fallo de Monday (AppError 502) que amerita fallback local */ function esFalloMonday502(err: unknown): boolean { return err instanceof AppError && err.status === 502 } /** Unidades del espejo local; filtros equivalentes a los del controller */ export async function getUnidadesLocal( filtro: { desarrollo?: string | undefined; soloDisponibles?: boolean | undefined } = {}, ): Promise { const rows = await prisma.unidad.findMany({ orderBy: { codigo: 'asc' } }) let lista = rows.map(unidadLocalADto) if (filtro.soloDisponibles === true) { lista = lista.filter((u) => u.status.trim().toUpperCase() === 'DISPONIBLE') } if (filtro.desarrollo !== undefined && filtro.desarrollo !== '') { const desarrollo = filtro.desarrollo.toUpperCase() lista = lista.filter((u) => (u.desarrollo || 'DIAGONAL').toUpperCase() === desarrollo) } return lista } /** Una unidad local por código; null si no existe en el espejo */ export async function getUnidadLocal(codigo: string): Promise { const row = await prisma.unidad.findUnique({ where: { codigo } }) return row === null ? null : unidadLocalADto(row) } /** * Lectura con fallback: Monday si está disponible; espejo local si el circuit * breaker lo da por caído o si la llamada lanza AppError 502. Cualquier otro * error se propaga intacto (no se traga nada del camino feliz). */ export async function listarUnidadesConFallback(): Promise { if (!saludMonday.estaDisponible()) return getUnidadesLocal() try { return await mondayService.getUnidades() } catch (err) { if (esFalloMonday502(err)) return getUnidadesLocal() throw err } } /** Igual que listarUnidadesConFallback pero para una unidad por código */ export async function obtenerUnidadConFallback(codigo: string): Promise { if (!saludMonday.estaDisponible()) return getUnidadLocal(codigo) try { return await mondayService.getUnidad(codigo) } catch (err) { if (esFalloMonday502(err)) return getUnidadLocal(codigo) throw err } }