feat(desarrollos): modulo de alta de desarrollos con inventario, reglas del cotizador por desarrollo y micrositio publico

Modulo nuevo (solo admin) para dar de alta desarrollos sin tocar codigo:
- CRUD de la ficha, modelos e inventario (alta manual y generador por rango)
- Reglas del cotizador por desarrollo, editables y persistidas en la BD
- El cotizador consume las reglas desde /desarrollos/reglas-cotizador y cae a
  las constantes con aviso cuando un desarrollo aun no tiene reglas guardadas
- Micrositio publico en /d/:clave (sin sesion) con ficha, modelos y
  disponibilidad; jamas expone datos de clientes ni cotizaciones

Integracion:
- La landing monta el modulo en el grid (clave 'desarrollos', solo admin) y el
  micrositio fuera del login; el rol manager NO accede (matriz y API con 403)
- La tabla de roles de Configuracion ahora lista todos los modulos
- Se alinea la FK de ReglaCotizadorDesarrollo con la BD viva (Desarrollo.id)

Verificado: 708 pruebas del API, 115 del cotizador y 89 de la landing en verde;
12/12 cotizaciones identicas antes y despues del cambio de reglas; prueba en
vivo del API con alta, modelos, inventario, reglas, micrositio y baja (30/30).
This commit is contained in:
2026-09-21 09:52:47 +00:00
parent 2fc80e7f9e
commit d6548999bf
68 changed files with 10016 additions and 66 deletions

View File

@@ -0,0 +1,58 @@
-- Módulo Desarrollos: catálogo de desarrollos (ficha, modelos y reglas del
-- cotizador) más el piso (`nivel`) del inventario.
-- Patrón manual: sqlite3 + _prisma_migrations. NUNCA migrate dev.
-- CreateTable Desarrollo
CREATE TABLE IF NOT EXISTS "Desarrollo" (
"id" TEXT NOT NULL PRIMARY KEY,
"clave" TEXT NOT NULL,
"nombre" TEXT NOT NULL,
"estado" TEXT NOT NULL DEFAULT 'ACTIVO',
"tipo" TEXT NOT NULL DEFAULT 'VERTICAL',
"constructora" TEXT,
"direccion" TEXT,
"zona" TEXT,
"descripcion" TEXT,
"amenidades" TEXT,
"fechaInicioObra" DATETIME,
"fechaEntregaProgramada" DATETIME,
"fechaEntregaReprogramada" DATETIME,
"activoEnCotizador" BOOLEAN NOT NULL DEFAULT true,
"activoEnAlta" BOOLEAN NOT NULL DEFAULT true,
"prefijoUnidadPdf" TEXT,
"imagenUrl" TEXT,
"logoUrl" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS "Desarrollo_clave_key" ON "Desarrollo"("clave");
CREATE INDEX IF NOT EXISTS "Desarrollo_estado_idx" ON "Desarrollo"("estado");
-- CreateTable ModeloDesarrollo
CREATE TABLE IF NOT EXISTS "ModeloDesarrollo" (
"id" TEXT NOT NULL PRIMARY KEY,
"desarrolloId" TEXT NOT NULL,
"nombre" TEXT NOT NULL,
"recamaras" INTEGER,
"banos" REAL,
"estacionamientos" INTEGER,
"supConstruccion" REAL,
"activo" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "ModeloDesarrollo_desarrolloId_fkey" FOREIGN KEY ("desarrolloId") REFERENCES "Desarrollo" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE UNIQUE INDEX IF NOT EXISTS "ModeloDesarrollo_desarrolloId_nombre_key" ON "ModeloDesarrollo"("desarrolloId", "nombre");
CREATE INDEX IF NOT EXISTS "ModeloDesarrollo_desarrolloId_idx" ON "ModeloDesarrollo"("desarrolloId");
-- CreateTable ReglaCotizadorDesarrollo
-- `desarrolloId` guarda el uuid de `Desarrollo.id` (lo que escriben el seed y
-- PUT /desarrollos/:id/reglas). El endpoint del cotizador traduce uuid → clave.
CREATE TABLE IF NOT EXISTS "ReglaCotizadorDesarrollo" (
"desarrolloId" TEXT NOT NULL PRIMARY KEY,
"parametrosJson" TEXT NOT NULL,
"updatedAt" DATETIME NOT NULL,
"updatedPorId" TEXT,
CONSTRAINT "ReglaCotizadorDesarrollo_desarrolloId_fkey" FOREIGN KEY ("desarrolloId") REFERENCES "Desarrollo" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- AlterTable Unidad: piso del inventario ('' si no aplica)
ALTER TABLE "Unidad" ADD COLUMN "nivel" TEXT NOT NULL DEFAULT '';

View File

@@ -211,6 +211,7 @@ model Unidad {
modelo String @default("")
manzana String @default("")
lote String @default("")
nivel String @default("") // piso (desarrollos verticales); '' si no aplica
supConstruccion Float @default(0)
supExcedente Float @default(0)
supEstacionamiento Float @default(0)
@@ -530,3 +531,61 @@ model SolicitudSoporte {
@@index([usuarioId])
@@index([departamento])
}
// ── Módulo Desarrollos: catálogo de desarrollos nuevos ────────────────
// La BD es la fuente de verdad: la ficha del desarrollo, sus modelos y las
// reglas del cotizador (antes constantes fijas en el código de las apps).
model Desarrollo {
id String @id @default(uuid())
clave String @unique // SENDA, DIAGONAL… (forma los códigos de unidad)
nombre String // nombre comercial
estado String @default("ACTIVO") // ACTIVO | PREVENTA | CERRADO
tipo String @default("VERTICAL") // VERTICAL | HORIZONTAL
constructora String?
direccion String?
zona String?
descripcion String?
amenidades String? // texto libre o lista separada por comas
fechaInicioObra DateTime?
fechaEntregaProgramada DateTime?
fechaEntregaReprogramada DateTime?
activoEnCotizador Boolean @default(true) // aparece en el cotizador
activoEnAlta Boolean @default(true) // aparece en altas de lead/afluencia/cliente
prefijoUnidadPdf String? // p. ej. CASA (lo usa el PDF del cotizador)
imagenUrl String?
logoUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
modelos ModeloDesarrollo[]
reglas ReglaCotizadorDesarrollo?
@@index([estado])
}
model ModeloDesarrollo {
id String @id @default(uuid())
desarrolloId String
desarrollo Desarrollo @relation(fields: [desarrolloId], references: [id], onDelete: Cascade)
nombre String
recamaras Int?
banos Float?
estacionamientos Int?
supConstruccion Float?
activo Boolean @default(true)
@@unique([desarrolloId, nombre])
@@index([desarrolloId])
}
model ReglaCotizadorDesarrollo {
// `desarrolloId` guarda el uuid de `Desarrollo.id` (es lo que escriben el seed
// y PUT /desarrollos/:id/reglas). El endpoint que consume el cotizador
// (`/desarrollos/reglas-cotizador`) traduce uuid → clave antes de responder.
desarrolloId String @id
desarrollo Desarrollo @relation(fields: [desarrolloId], references: [id], onDelete: Cascade)
parametrosJson String // JSON con la forma ParametrosDesarrollo
updatedAt DateTime @updatedAt
updatedPorId String?
}