feat: add pricing_config, catalog_items and budget input fields to schema

This commit is contained in:
Carlos Narro
2026-05-30 12:30:03 +02:00
parent e6f8b47205
commit afef9f2cb0

View File

@@ -9,6 +9,8 @@ import {
timestamp,
jsonb,
index,
doublePrecision,
uniqueIndex,
} from 'drizzle-orm/pg-core';
// Estado comercial del lead — RF-D-03. Lo que el reformista gestiona a mano.
@@ -42,6 +44,17 @@ export const tipoReforma = pgEnum('tipo_reforma', [
'otro',
]);
export const calidad = pgEnum('calidad', ['basica', 'media', 'premium']);
export const categoriaMaterial = pgEnum('categoria_material', [
'suelo',
'pared',
'pintura',
'mobiliario',
]);
export const unidadMedida = pgEnum('unidad_medida', ['m2', 'ml', 'ud']);
// Reformista (cliente del SaaS). MVP = "Reformas Ejemplo" hardcoded.
// Multi-tenant real es F1.5; la tabla ya queda lista para ello.
export const tenants = pgTable('tenants', {
@@ -91,6 +104,17 @@ export const leads = pgTable(
audioUrl: text('audio_url'),
notas: text('notas'),
// Inputs del motor de presupuesto (capturados de menos a más en el funnel)
m2Suelo: doublePrecision('m2_suelo'),
alturaTecho: doublePrecision('altura_techo'),
calidadGlobal: calidad('calidad_global'),
estructural: boolean('estructural').notNull().default(false),
materialSelections: jsonb('material_selections')
.$type<Record<string, string>>()
.notNull()
.default({}),
desgloseSnapshot: jsonb('desglose_snapshot'),
},
(table) => [
index('leads_tenant_created_idx').on(table.tenantId, table.createdAt),
@@ -144,6 +168,42 @@ export const precisionHistory = pgTable('precision_history', {
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
});
// Configuración de precios del reformista (1 fila por tenant). RF-D-07.
export const pricingConfig = pgTable('pricing_config', {
id: uuid('id').primaryKey().defaultRandom(),
tenantId: uuid('tenant_id')
.notNull()
.references(() => tenants.id, { onDelete: 'cascade' })
.unique(),
alturaTechoDefault: doublePrecision('altura_techo_default').notNull().default(2.5),
factorZona: jsonb('factor_zona').$type<Record<string, number>>().notNull().default({}),
manoObra: jsonb('mano_obra').$type<Record<string, number>>().notNull().default({}),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
});
// Catálogo de materiales del reformista. Importable por CSV.
export const catalogItems = pgTable(
'catalog_items',
{
id: uuid('id').primaryKey().defaultRandom(),
tenantId: uuid('tenant_id')
.notNull()
.references(() => tenants.id, { onDelete: 'cascade' }),
categoria: categoriaMaterial('categoria').notNull(),
nombre: text('nombre').notNull(),
calidad: calidad('calidad').notNull(),
precioUnit: integer('precio_unit').notNull(), // céntimos por unidad
unidad: unidadMedida('unidad').notNull(),
descriptorRender: text('descriptor_render').notNull().default(''),
esDefault: boolean('es_default').notNull().default(false),
sku: text('sku').notNull(),
},
(table) => [
index('catalog_tenant_idx').on(table.tenantId),
uniqueIndex('catalog_tenant_sku_idx').on(table.tenantId, table.sku),
]
);
export type Tenant = typeof tenants.$inferSelect;
export type Lead = typeof leads.$inferSelect;
export type NewLead = typeof leads.$inferInsert;
@@ -151,3 +211,6 @@ export type LeadFoto = typeof leadFotos.$inferSelect;
export type LeadEstadoHistory = typeof leadEstadoHistory.$inferSelect;
export type LeadPipelineEvento = typeof leadPipelineEventos.$inferSelect;
export type PrecisionHistory = typeof precisionHistory.$inferSelect;
export type PricingConfigRow = typeof pricingConfig.$inferSelect;
export type CatalogItemRow = typeof catalogItems.$inferSelect;
export type NewCatalogItem = typeof catalogItems.$inferInsert;