# Research: Contratos MultiCliente MultiPunto (TipProCom = 3)

**Feature**: `007-contratos-multicliente-multipunto` | **Date**: 2026-05-28

## R1 — Dos herramientas vs. herramienta unificada

**Decisión**: Herramientas separadas `listar_contratos_multicliente` y `agregar_contratos_multicliente` (simétricas a 005/006), no extender las herramientas Unicliente con un flag.

**Justificación**: El mapa relacional difiere (titular = contacto, cliente empresa vía detalle). Enrutado LLM más claro ("multicliente multipunto" → herramientas dedicadas). Evita ramas complejas y errores de mezclar TipProCom en una sola query.

**Alternativas**: Un solo `listar_contratos` con `tipo=unicliente|multicliente` → más confuso para el modelo y más riesgo de mezclar joins.

---

## R2 — Titular y cliente empresa (mapa relacional)

**Decisión**:

- Puente `T_Propuesta_Comercial_Clientes`: para `TipProCom=3`, `CodCli` almacena `CodConCli` del contacto (ya documentado en `PropuestaComercialCliente.contacto()`).
- Cliente empresa: `LEFT JOIN T_ContactoDetalleCliente` ON `CodConCli`, luego `LEFT JOIN T_Cliente` ON `CodCli` — replica la SQL de referencia del usuario.

**Justificación**: Alineación exacta con la query proporcionada y con el modelo de negocio MultiCliente (representante + empresa(s) vinculada(s)).

**Implicación**: Si un contacto tiene **varios** registros en `ContactoDetalleCliente`, cada fila de CUPS puede **duplicarse** (una por cliente empresa). Es coherente con "multicliente". En vista `listado` agrupada por `CodProCom`, se anidan CUPS; si hay varios clientes empresa en la misma línea de negocio, pueden aparecer como filas de CUPS distintas con distinto `nombreCliente`.

**Alternativas**: Tomar solo el primer cliente detalle → perdería datos; rechazada.

---

## R3 — Columna `localidadCliente` en la SQL de referencia

**Decisión**: En la SQL del usuario, `localidadCliente` proviene de `tc.CodLocFis` (contacto/representante), **no** del cliente empresa. En dimensiones de agregación:

- `localidad_representante` → `Contacto` / `loc_rep.DesLoc` (`CodLocFis`)
- `localidad_cliente_empresa` → `Cliente` / `CodLocSoc` o `CodLocFis` del cliente (`tc3`), distinto del representante

**Justificación**: Evita confusión con la feature Unicliente donde `localidad_cliente` era domicilio social del titular empresa.

---

## R4 — Listado sin tope de 50 filas

**Decisión**: `agent.agent_database.contratos_multicliente.max_rows` (default **500**, env `AGENT_CONTRATOS_MULTICLIENTE_MAX_ROWS`), igual que 005. No usar `max_results=50`.

**Justificación**: FR-006, SC-007.

---

## R5 — Agregación (paridad 006)

**Decisión**: Implementar `agregarContratosMulticliente()` como espejo de `agregarContratosUnicliente()` con:

- `COUNT(DISTINCT pc.CodProCom)` para conteo de contratos
- Medidas en `cups.ConCup`, `cups.CauDiaGas`, `PotEleConP1..P6`
- Lista blanca de dimensiones (ver data-model.md)
- Alta cardinalidad: `representante`, `cliente_empresa`, `direccion_suministro` → Top-N default 20
- `truncated: false` siempre en respuesta de agregado

**Justificación**: FR-007, FR-008; mismo problema de "listar y contar" que resolvió la 006.

---

## R6 — Contacto sin cliente empresa vinculado

**Decisión**: `LEFT JOIN` a detalle y cliente; columnas de empresa vacías → `null` en serialización / `"Sin dato"` en dimensiones de agregación.

**Justificación**: Edge case del spec; no inventar empresa.

---

## R7 — Filtro `termino` en listado y agregación

**Decisión**: `termino` busca en **representante** (NIFConCli, NomConCli, EmaConCli, TelFijConCli, NomViaDomFis) **y** en **cliente empresa** (NomComCli, NumCifCli, EmaCli, TelFijCli, NomViaDomFis), con OR.

**Justificación**: FR-004; operador no sabe si el nombre es contacto o empresa.

---

## R8 — Enrutado LLM

**Decisión**: Pistas en `IntentClassifierService` y reglas `4d`/`4e` en handler:

- Listar/detalle multicliente → `listar_contratos_multicliente`
- Contar/agregar multicliente → `agregar_contratos_multicliente`
- Unicliente → herramientas 005/006 existentes

**Justificación**: FR-012, FR-014.
