feat: add pricing_config, catalog_items and budget input fields to schema
This commit is contained in:
@@ -9,6 +9,8 @@ import {
|
|||||||
timestamp,
|
timestamp,
|
||||||
jsonb,
|
jsonb,
|
||||||
index,
|
index,
|
||||||
|
doublePrecision,
|
||||||
|
uniqueIndex,
|
||||||
} from 'drizzle-orm/pg-core';
|
} from 'drizzle-orm/pg-core';
|
||||||
|
|
||||||
// Estado comercial del lead — RF-D-03. Lo que el reformista gestiona a mano.
|
// 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',
|
'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.
|
// Reformista (cliente del SaaS). MVP = "Reformas Ejemplo" hardcoded.
|
||||||
// Multi-tenant real es F1.5; la tabla ya queda lista para ello.
|
// Multi-tenant real es F1.5; la tabla ya queda lista para ello.
|
||||||
export const tenants = pgTable('tenants', {
|
export const tenants = pgTable('tenants', {
|
||||||
@@ -91,6 +104,17 @@ export const leads = pgTable(
|
|||||||
audioUrl: text('audio_url'),
|
audioUrl: text('audio_url'),
|
||||||
|
|
||||||
notas: text('notas'),
|
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) => [
|
(table) => [
|
||||||
index('leads_tenant_created_idx').on(table.tenantId, table.createdAt),
|
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(),
|
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 Tenant = typeof tenants.$inferSelect;
|
||||||
export type Lead = typeof leads.$inferSelect;
|
export type Lead = typeof leads.$inferSelect;
|
||||||
export type NewLead = typeof leads.$inferInsert;
|
export type NewLead = typeof leads.$inferInsert;
|
||||||
@@ -151,3 +211,6 @@ export type LeadFoto = typeof leadFotos.$inferSelect;
|
|||||||
export type LeadEstadoHistory = typeof leadEstadoHistory.$inferSelect;
|
export type LeadEstadoHistory = typeof leadEstadoHistory.$inferSelect;
|
||||||
export type LeadPipelineEvento = typeof leadPipelineEventos.$inferSelect;
|
export type LeadPipelineEvento = typeof leadPipelineEventos.$inferSelect;
|
||||||
export type PrecisionHistory = typeof precisionHistory.$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;
|
||||||
|
|||||||
Reference in New Issue
Block a user