feat(unidades): pipeline de 10 etapas por unidad + sync desde Reporte_EstatusVenta

- Columna Etapa en Monday Unidades (10 labels oficiales), 155 unidades pobladas
- sync-etapas.ts idempotente (--archivo/--dry-run/--semilla) con traspasos
  Vendido→Escrituración y Escriturado→SAC como actividades (sin retroactivas)
- API: etapa en unidades + porEtapa en /api/reportes/resumen
- Dashboard: gráfica pipeline por etapa + KPI en notaría; cotizador muestra etapa
- 28 cambios aplicados desde Reporte_EstatusVenta (8 avances, 20 nuevas);
  contradicciones retenidas para revisión humana (documentadas)
- Tests: api 168, dashboards 9, cotizador 71
This commit is contained in:
2026-08-28 04:43:02 +00:00
parent 04de7d703f
commit 9e04b76616
16 changed files with 697 additions and 4 deletions

View File

@@ -18,14 +18,30 @@ function Fila({ etiqueta, valor }: { etiqueta: string; valor: string }) {
const formatoM2 = (v: number | null): string =>
v === null ? '—' : `${v.toLocaleString('es-MX')} m²`
// Status grueso → etiqueta legible en femenino ("la unidad está …")
const ETIQUETA_STATUS: Record<string, string> = {
APARTADO: 'Apartada',
APARTADA: 'Apartada',
VENDIDO: 'Vendida',
VENDIDA: 'Vendida',
ESCRITURADO: 'Escriturada',
BLOQUEADO: 'Bloqueada',
}
/** Tarjeta con la ficha técnica de la unidad seleccionada */
export function UnitInfoCard({ unidad, numMeses }: Props) {
const extras = unidad.extras ?? {}
const etiquetaStatus = ETIQUETA_STATUS[unidad.status]
return (
<section className="border border-neutral-200 bg-white p-6">
<h2 className="mb-4 text-xs font-bold tracking-widest text-neutral-900 uppercase">
Unidad {unidad.codigo}
</h2>
{etiquetaStatus !== undefined && unidad.etapa != null && unidad.etapa !== '' && (
<p className="mb-4 text-xs text-neutral-500">
{etiquetaStatus} — en {unidad.etapa.toLowerCase()}
</p>
)}
<dl className="divide-y divide-neutral-200">
<Fila etiqueta="Modelo" valor={unidad.modelo} />
{extras['prototipo'] != null && <Fila etiqueta="Prototipo" valor={String(extras['prototipo'])} />}

View File

@@ -22,6 +22,8 @@ export interface Unidad {
precioLista?: number | null
/** Plazo de mensualidades cuando lo fija el inventario (SOLANA: meses para firma) */
mesesParaFirma?: number | null
/** Etapa del pipeline de venta (Apartado, Reserva, Vendido, … PostVenta); null si no aplica */
etapa?: string | null
/** Datos extra del inventario: prototipo, manzana, lote, etapa, cajones, etc. */
extras?: Record<string, string | number | null>
}