/** * Siembra el catálogo `Desarrollo` + `ModeloDesarrollo` + `ReglaCotizadorDesarrollo` * desde la realidad actual del sistema: * * - Desarrollos y modelos: lo que hoy hay en la tabla `Unidad` (espejo del * inventario), más SOLE y MUTUO, que existen en captura y altas pero todavía * no tienen inventario local. * - Reglas del cotizador: copiadas (replicadas, no importadas) de las * constantes de apps/cotizador/src/data/desarrollos.ts, que es la config con * la que hoy cotiza producción. * * Idempotente: upsert por `clave`. NUNCA pisa lo ya editado: si la ficha o las * reglas existen, el seed no las toca (el admin manda). * * Uso: npm run seed:desarrollos (tsx: la fuente de las reglas es TypeScript) */ import path from 'node:path' import { fileURLToPath } from 'node:url' import dotenv from 'dotenv' import { PrismaClient } from '@prisma/client' import { DESARROLLOS as PARAMETROS_COTIZADOR } from '../../apps/cotizador/src/data/desarrollos.ts' const raizApi = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') dotenv.config({ path: [path.resolve(raizApi, '.env'), path.resolve(raizApi, '..', '.env')] }) const prisma = new PrismaClient() /** * Desarrollos sin inventario local que sí existen en el ecosistema (captura y * altas de lead/afluencia/cliente). Los modelos de SOLE vienen de la lista * real del alta de clientes (apps/alta-clientes/src/data/constantes.ts); * MUTUO captura el modelo en texto libre, así que se siembra sin modelos. */ const SIN_INVENTARIO = [ { clave: 'SOLE', nombre: 'Sole', tipo: 'VERTICAL', modelos: ['ALBA', 'HORIZONTE', 'OCASO'] }, { clave: 'MUTUO', nombre: 'Mutuo', tipo: 'VERTICAL', modelos: [] }, ] const normalizar = (v) => String(v ?? '').trim().toUpperCase() /** Nombre comercial: el del cotizador si existe; si no, la clave capitalizada */ function nombreComercial(clave) { const delCotizador = PARAMETROS_COTIZADOR[clave]?.nombre if (typeof delCotizador === 'string' && delCotizador !== '') return delCotizador return clave.charAt(0) + clave.slice(1).toLowerCase() } async function main() { const [porDesarrollo, modelosInventario] = await Promise.all([ prisma.unidad.groupBy({ by: ['desarrollo'], _count: { codigo: true } }), prisma.unidad.groupBy({ by: ['desarrollo', 'modelo'], _count: { codigo: true } }), ]) if (porDesarrollo.length === 0) { throw new Error('La tabla Unidad está vacía: corre primero `npm run seed:unidades`') } const claves = new Set(porDesarrollo.map((d) => normalizar(d.desarrollo))) for (const extra of SIN_INVENTARIO) claves.add(extra.clave) const modelosPorClave = new Map() for (const fila of modelosInventario) { const clave = normalizar(fila.desarrollo) const modelo = String(fila.modelo ?? '').trim() if (modelo === '') continue if (!modelosPorClave.has(clave)) modelosPorClave.set(clave, new Set()) modelosPorClave.get(clave).add(modelo) } for (const extra of SIN_INVENTARIO) { if (!modelosPorClave.has(extra.clave)) modelosPorClave.set(extra.clave, new Set()) for (const modelo of extra.modelos) modelosPorClave.get(extra.clave).add(modelo) } let fichasNuevas = 0 let fichasExistentes = 0 let modelosNuevos = 0 let reglasNuevas = 0 for (const clave of [...claves].sort()) { const extra = SIN_INVENTARIO.find((e) => e.clave === clave) const parametros = PARAMETROS_COTIZADOR[clave] const previa = await prisma.desarrollo.findUnique({ where: { clave }, select: { id: true } }) const ficha = await prisma.desarrollo.upsert({ where: { clave }, // No pisa lo ya editado por el admin: si existe, se deja como está update: {}, create: { clave, nombre: extra?.nombre ?? nombreComercial(clave), // `tipo` no se deduce del inventario actual (los códigos y el prefijo // del PDF no lo distinguen): queda el default VERTICAL y el admin lo // ajusta desde el módulo. tipo: extra?.tipo ?? 'VERTICAL', prefijoUnidadPdf: typeof parametros?.prefijoUnidadPdf === 'string' ? parametros.prefijoUnidadPdf : null, }, }) if (previa) fichasExistentes++ else fichasNuevas++ for (const nombre of [...(modelosPorClave.get(clave) ?? [])].sort()) { const modeloPrevio = await prisma.modeloDesarrollo.findUnique({ where: { desarrolloId_nombre: { desarrolloId: ficha.id, nombre } }, select: { id: true }, }) await prisma.modeloDesarrollo.upsert({ where: { desarrolloId_nombre: { desarrolloId: ficha.id, nombre } }, update: {}, create: { desarrolloId: ficha.id, nombre }, }) if (!modeloPrevio) modelosNuevos++ } if (parametros) { const reglaPrevia = await prisma.reglaCotizadorDesarrollo.findUnique({ where: { desarrolloId: ficha.id }, select: { desarrolloId: true }, }) if (!reglaPrevia) { await prisma.reglaCotizadorDesarrollo.create({ data: { desarrolloId: ficha.id, parametrosJson: JSON.stringify(parametros) }, }) reglasNuevas++ } } } const total = await prisma.desarrollo.count() const conReglas = await prisma.reglaCotizadorDesarrollo.count() const totalModelos = await prisma.modeloDesarrollo.count() // eslint-disable-next-line no-console console.log( `[seed-desarrollos] fichas: ${fichasNuevas} nueva(s), ${fichasExistentes} ya existían; ` + `${modelosNuevos} modelo(s) nuevo(s); ${reglasNuevas} regla(s) de cotizador copiada(s)`, ) // eslint-disable-next-line no-console console.log(`[seed-desarrollos] total en BD: ${total} desarrollos, ${totalModelos} modelos, ${conReglas} con reglas`) for (const d of await prisma.desarrollo.findMany({ orderBy: { clave: 'asc' }, include: { modelos: { orderBy: { nombre: 'asc' } }, reglas: { select: { desarrolloId: true } } }, })) { // eslint-disable-next-line no-console console.log( ` ${d.clave} (${d.estado}/${d.tipo}) — ${d.modelos.length} modelo(s)` + `${d.reglas ? '' : ' — SIN reglas de cotizador'}: ${d.modelos.map((m) => m.nombre).join(', ') || '—'}`, ) } } main() .catch((err) => { console.error('[seed-desarrollos] error:', err) process.exitCode = 1 }) .finally(() => prisma.$disconnect())