feat(modo-local): fallback completo sin Monday + cola de sincronización
- Circuit breaker saludMonday (3 fallos → modo local; ping cada 60s)
- Endpoint GET /api/sistema/estado {mondayOk, pendientesSync}
- LeadCaptura extendida (board, etapa, origen, motivo, pendienteSync)
- Tablas Unidad (espejo inventario, 288 seed) + SyncPendiente
- crmLocal: crearLead, afluencia, cambio etapa, descarte, conversión, apartar
- Ruteo en controllers: fallback local ante 502 o Monday caído
- Mi día local: resumen completo desde SQLite + unión con Monday
- Fallbacks: unidades, contactos, reportes, prefill, buscador
- Sync worker cada 2min + handlers 15 tipos (con DependenciaPendienteError)
- Gate automatizaciones: skip si !mondayOk
- Banner 'Modo local' en 5 apps + badge pendiente en mi-dia
- 597 API tests + 263 front tests verdes
Env MODO_LOCAL_FORZADO=true para testing sin Monday.
This commit is contained in:
@@ -157,16 +157,86 @@ model LeadOportunidad {
|
||||
// Registro local de qué usuario del ecosistema capturó cada lead (alta de
|
||||
// afluencia). Monday no guarda al capturista (el creador del item siempre es
|
||||
// el token del API); esta tabla alimenta "Mis capturas" del panel Mi día.
|
||||
// Además es la entidad local del embudo en modo local (Monday caído): guarda
|
||||
// el board/etapa actuales y el perfil de la captura hasta que el sync worker
|
||||
// la replique en Monday.
|
||||
model LeadCaptura {
|
||||
id String @id @default(uuid())
|
||||
leadId String @unique // item del tablero Leads (Monday)
|
||||
leadId String @unique // item del tablero Leads (Monday); en modo local: id local loc_<uuid>
|
||||
oportunidadId String? // item del tablero Oportunidades ligado al capturar
|
||||
usuarioId String // Usuario.id del capturista
|
||||
nombreLead String
|
||||
telefono String?
|
||||
// Board del embudo donde vive el registro: LEADS | OPORTUNIDADES | APARTADO
|
||||
board String @default("LEADS")
|
||||
// Etapa/estado local actual (NUEVO al capturar)
|
||||
etapa String @default("NUEVO")
|
||||
origen String?
|
||||
desarrollo String?
|
||||
modelo String?
|
||||
email String?
|
||||
// Motivo / submotivo del descarte
|
||||
motivo String?
|
||||
submotivo String?
|
||||
// Perfilamiento completo de afluencia / payload extra (JSON serializado)
|
||||
datosJson String?
|
||||
// Item de Monday una vez sincronizado (rows viejas: = leadId)
|
||||
mondayItemId String?
|
||||
// true = falta replicar a Monday (lo drena el sync worker)
|
||||
pendienteSync Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt @default(now())
|
||||
|
||||
@@index([usuarioId])
|
||||
@@index([mondayItemId])
|
||||
}
|
||||
|
||||
// Espejo local del inventario (tablero Unidades de Monday). Lo siembra
|
||||
// scripts/seed-unidades-locales.js desde el INVENTARIO_SEED del cotizador y lo
|
||||
// mantiene fresco el write-through de monday.service.getUnidades(); es el
|
||||
// fallback de lectura del cotizador en modo local.
|
||||
model Unidad {
|
||||
id String @id @default(uuid())
|
||||
codigo String @unique // 'U-101', 'ATELIER-10', 'SENDA-411-1-7', ...
|
||||
desarrollo String // DIAGONAL | ATELIER | ESTANZA | SENDA | SOLANA
|
||||
modelo String @default("")
|
||||
manzana String @default("")
|
||||
lote String @default("")
|
||||
supConstruccion Float @default(0)
|
||||
supExcedente Float @default(0)
|
||||
supEstacionamiento Float @default(0)
|
||||
precioLista Float @default(0)
|
||||
precioAcabados Float @default(0)
|
||||
status String @default("DISPONIBLE") // DISPONIBLE | APARTADO | VENDIDO | ESCRITURADO | BLOQUEADO | ...
|
||||
etapa String @default("") // etapa del pipeline de venta ('' si no aplica)
|
||||
updatedAt DateTime @updatedAt @default(now())
|
||||
|
||||
@@index([desarrollo])
|
||||
@@index([status])
|
||||
}
|
||||
|
||||
// Cola de sincronización a Monday (modo local). Cada operación hecha contra la
|
||||
// base local mientras Monday está caído se encola aquí; sync-monday.service la
|
||||
// drena FIFO (createdAt asc) cuando Monday vuelve. Estados: PENDIENTE |
|
||||
// PROCESANDO | HECHO | ERROR. Idempotencia vía AutomatizacionState 'sync:<id>'.
|
||||
model SyncPendiente {
|
||||
id String @id @default(uuid())
|
||||
// CREAR_LEAD | CREAR_AFLUENCIA | ACTUALIZAR_AFLUENCIA | CAMBIO_ETAPA |
|
||||
// DESCARTE | CONVERSION | CREAR_CONTACTO | ACTUALIZAR_CONTACTO |
|
||||
// CREAR_APARTADO | COTIZACION_OPORTUNIDAD | CHECKBOX_DOC |
|
||||
// APARTO_OPORTUNIDAD | LIQUIDADO_ESCRITURAR | ASIGNAR_PERSONA |
|
||||
// DISPONIBILIDAD_UNIDAD
|
||||
tipo String
|
||||
payloadJson String
|
||||
// Referencia local (leadId / clienteId / cotizacionId) para actualizar ids
|
||||
referenciaLocal String?
|
||||
estado String @default("PENDIENTE")
|
||||
intentos Int @default(0)
|
||||
ultimoError String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([estado, createdAt])
|
||||
}
|
||||
|
||||
// Estado persistente de las automatizaciones de Monday (round-robin, recordatorios).
|
||||
|
||||
Reference in New Issue
Block a user