Files
reformix-hackaton/mvp/b2c/src/db/pricing-queries.ts
Carlos Narro b815b0532b Añade baremo de rentabilidad (valor de panel) e indicador en la ficha del lead
Parte C del plan: el baremo mínimo de rentabilidad es ahora un valor configurable
del reformista, solo informativo. Los agentes NO lo usan para decidir nada.

- schema: pricing_config.baremo_minimo (céntimos, nullable) + migración 0012.
- pricing-queries / budget types: exponen baremoMinimo.
- panel/precios: sección "Baremo de rentabilidad" + action actualizarBaremo
  (vacío = sin baremo).
- panel/[id]: el presupuesto estimado se muestra en rojo con aviso "Por debajo
  de tu baremo (X €)" cuando no alcanza el baremo del tenant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 16:58:55 +02:00

81 lines
2.3 KiB
TypeScript

import { eq } from 'drizzle-orm';
import { db } from './index';
import { pricingConfig, catalogItems, tenants } from './schema';
import type { PricingConfig, CatalogItem, ManoObraKey } from '@/budget/types';
import { getCurrentTenantId as getTenantId } from '@/lib/auth/current-user';
export type EnvioMode = (typeof tenants.envioPresupuesto.enumValues)[number];
export async function getEnvioModeFor(tenantId: string): Promise<EnvioMode> {
const [row] = await db
.select({ modo: tenants.envioPresupuesto })
.from(tenants)
.where(eq(tenants.id, tenantId))
.limit(1);
return row?.modo ?? 'automatico';
}
export async function getEnvioMode(): Promise<EnvioMode> {
return getEnvioModeFor(await getTenantId());
}
const MANO_OBRA_DEFAULT: Record<ManoObraKey, number> = {
demolicion: 0,
impermeabilizacion: 0,
fontaneria: 0,
electricidad: 0,
mano_de_obra: 0,
};
const EXTRAS_DEFAULT = { tuberias: 0, boletin: 0, distribucion: 0 };
export async function getPricingConfigFor(tenantId: string): Promise<PricingConfig> {
const [row] = await db
.select()
.from(pricingConfig)
.where(eq(pricingConfig.tenantId, tenantId))
.limit(1);
if (!row) {
return {
alturaTechoDefault: 2.5,
factorZona: {},
manoObra: { ...MANO_OBRA_DEFAULT },
extras: { ...EXTRAS_DEFAULT },
baremoMinimo: null,
};
}
return {
alturaTechoDefault: row.alturaTechoDefault,
factorZona: row.factorZona,
manoObra: { ...MANO_OBRA_DEFAULT, ...(row.manoObra as Record<ManoObraKey, number>) },
extras: { ...EXTRAS_DEFAULT, ...(row.extras ?? {}) },
baremoMinimo: row.baremoMinimo ?? null,
};
}
export async function getPricingConfig(): Promise<PricingConfig> {
return getPricingConfigFor(await getTenantId());
}
export async function getCatalogFor(tenantId: string): Promise<CatalogItem[]> {
const rows = await db.select().from(catalogItems).where(eq(catalogItems.tenantId, tenantId));
return rows.map((r) => ({
id: r.id,
categoria: r.categoria,
nombre: r.nombre,
calidad: r.calidad,
precioUnit: r.precioUnit,
unidad: r.unidad,
descriptorRender: r.descriptorRender,
esDefault: r.esDefault,
sku: r.sku,
}));
}
export async function getCatalog(): Promise<CatalogItem[]> {
return getCatalogFor(await getTenantId());
}
export { getTenantId };