# Feature Specification: Contratos MultiCliente MultiPunto — listado, filtros y agregación

**Feature Branch**: `007-contratos-multicliente-multipunto`

**Created**: 2026-05-28

**Status**: Draft

**Input**: User description: "Agregar a las herramientas de consulta de contratos la capacidad de consultar contratos MultiCliente MultiPunto (TipProCom = 3). Misma capacidad de filtros, agrupamiento y conteos que la especificación 006-contratos-conteo-agrupado, con la consulta de referencia que une propuesta comercial, representante legal/contacto, cliente empresa vinculado, puntos de suministro y datos energéticos."

> **Constitution (I. Spec-First)**: Este spec MUST ser agnóstico de stack.
> No mencionar Laravel, Angular ni rutas de código. El CÓMO va en `plan.md`.

> **Término de dominio**: «contrato» = propuesta comercial. Esta feature cubre exclusivamente contratos **MultiCliente MultiPunto** (`TipProCom = 3`). Los contratos Unicliente (`TipProCom` 1 y 2) siguen en las features `005` y `006`.

> **Modelo de titularidad (diferencia clave respecto a Unicliente)**: En `TipProCom = 3` el titular del contrato en el puente comercial es el **representante legal / contacto** (`ContactoCliente`), no el cliente empresa directamente. El **cliente empresa** asociado se obtiene mediante la relación contacto → detalle de contacto → cliente (`ContactoDetalleCliente` → `Cliente`). La consulta de referencia devuelve columnas tanto del contacto/representante como del cliente empresa y del punto de suministro.

## User Scenarios & Testing *(mandatory)*

### User Story 1 - Listar contratos MultiCliente MultiPunto ordenados por fecha (Priority: P1)

Como operador autenticado, quiero listar contratos **MultiCliente MultiPunto** (`TipProCom = 3`) ordenados por **fecha de propuesta comercial (= fecha de contrato)**, con datos del representante legal/contacto, del cliente empresa vinculado y de cada punto de suministro.

**Why this priority**: Es el flujo base para consultar este tipo contractual, hoy fuera de alcance de las herramientas Unicliente.

**Independent Test**: "Lista los contratos multicliente multipunto" devuelve solo `TipProCom = 3`, ordenados por fecha descendente, con columnas de representante, cliente empresa y suministro.

**Acceptance Scenarios**:

1. **Given** existen contratos con `TipProCom = 3`, **When** el operador pide listarlos, **Then** el asistente devuelve solo ese tipo, **excluyendo** `TipProCom` 1 y 2, ordenados por fecha de contrato (más reciente primero).
2. **Given** no existen contratos de ese tipo, **When** el operador pregunta, **Then** el asistente indica que no hay resultados, sin inventar datos.
3. **Given** un contrato con varios puntos de suministro, **When** el operador pide un listado agrupado, **Then** puede ver los CUPS anidados por contrato o el detalle por punto según el modo de vista.

---

### User Story 2 - Filtrar por cualquier columna del resultado (Priority: P1)

Como operador, quiero filtrar por **representante legal** (documento, nombre, correo, teléfono, dirección), por **cliente empresa** (nombre, dirección, correo, teléfono), por **punto de suministro** (dirección, localidad), por **CUPS**, **tarifa**, **tipo de energía** o **rango de fechas**, usando lenguaje natural.

**Why this priority**: Sin filtros, el listado de contratos multicliente no es operativamente útil.

**Independent Test**: "Contratos del representante [NIF]", "contratos del cliente [nombre empresa]", "contratos en [localidad]" o "contrato con CUPS [código]" devuelven solo coincidencias.

**Acceptance Scenarios**:

1. **Given** un representante con contratos, **When** el operador filtra por su documento, nombre, correo o teléfono, **Then** devuelve solo contratos de ese titular-contacto.
2. **Given** un cliente empresa vinculado, **When** el operador filtra por nombre, dirección, correo o teléfono del cliente, **Then** devuelve contratos donde ese cliente empresa aparece en la relación.
3. **Given** criterios de suministro (dirección, localidad, CUPS, tarifa, tipo de energía), **When** el operador filtra, **Then** devuelve las líneas/contratos que cumplen el criterio.
4. **Given** un filtro sin coincidencias, **When** el operador pregunta, **Then** informa que no hay resultados.

---

### User Story 3 - Detalle energético de contrato o punto de suministro (Priority: P2)

Como operador, quiero consultar CUPS, tarifas, potencias P1–P6, consumos y fechas de activación/vencimiento por punto de suministro, igual que en contratos Unicliente pero en el contexto MultiCliente.

**Why this priority**: Las consultas técnicas/comerciales requieren datos de suministro, no solo cabecera.

**Independent Test**: "Detalle de suministro del contrato [referencia]" devuelve por cada CUPS sus tarifas, potencias y consumos según tipo eléctrico/gas.

**Acceptance Scenarios**:

1. **Given** un contrato con varios CUPS, **When** el operador pide detalle, **Then** presenta cada punto con sus datos energéticos.
2. **Given** una línea eléctrica o de gas, **When** pregunta por potencias/consumo, **Then** devuelve solo los campos aplicables; los no aplicables quedan vacíos sin inventarse.

---

### User Story 4 - Listados completos sin tope artificial de filas (Priority: P2)

Como operador, quiero listados de contratos multicliente **sin recorte arbitrario a 50 filas**, con límite dedicado y agrupación por contrato cuando el volumen sea alto.

**Why this priority**: Coherente con la feature `005` para Unicliente; el tope genérico de 50 filas oculta contratos.

**Independent Test**: Con más de 50 líneas que cumplen el criterio, el listado no se trunca al tope genérico; si se supera el límite dedicado, se indica explícitamente.

**Acceptance Scenarios**:

1. **Given** más resultados que el tope genérico de listados, **When** el operador consulta, **Then** **no** aplica el tope de 50 filas del catálogo general.
2. **Given** error de base de datos, **When** consulta, **Then** mensaje claro sin datos ficticios.

---

### User Story 5 - Contar y agrupar contratos por cualquier columna (Priority: P1)

Como operador, quiero **contar** contratos multicliente agrupados por cualquier columna permitida (localidad de suministro, localidad del representante, localidad del cliente empresa, provincia, mes de contrato, tarifa, representante, cliente empresa, etc.) con totales exactos sobre **todos** los registros, **sin** respuesta "lista truncada".

**Why this priority**: Misma necesidad que originó la feature `006`; el conteo debe hacerse en agregación, no listando y contando en memoria.

**Independent Test**: "¿Cuántos contratos multicliente por localidad?" devuelve totales por localidad + total general; `truncated` conceptualmente ausente en el conteo.

**Acceptance Scenarios**:

1. **Given** muchos contratos multicliente, **When** pide conteo agrupado, **Then** los totales reflejan todos los registros del criterio.
2. **Given** dos dimensiones (p. ej. localidad y mes), **When** las solicita, **Then** devuelve totales por combinación.
3. **Given** columna no permitida, **When** la solicita, **Then** rechazo seguro con columnas válidas sugeridas.

---

### User Story 6 - Operaciones matemáticas sobre columnas numéricas (Priority: P1)

Como operador, quiero **suma, promedio, mínimo y máximo** sobre consumo eléctrico, consumo de gas y potencias P1–P6, opcionalmente agrupados por una dimensión, calculados sobre todos los registros.

**Why this priority**: Paridad funcional con la feature `006` para métricas de negocio ("consumo total por localidad", "potencia media por tarifa").

**Independent Test**: "Consumo eléctrico total por localidad de suministro en contratos multicliente" devuelve suma por localidad + total general.

**Acceptance Scenarios**:

1. **Given** una medida numérica y operación válida, **When** la solicita, **Then** el valor agregado se calcula sobre todos los registros del criterio.
2. **Given** operación numérica sobre columna no numérica, **When** la solicita, **Then** rechazo con alternativa (conteo o medidas válidas).
3. **Given** varias medidas a la vez, **When** las pide, **Then** las devuelve en la misma respuesta.

---

### User Story 7 - Alta cardinalidad y filtros en agregaciones (Priority: P2)

Como operador, quiero aplicar **filtros** antes de agregar (representante, cliente empresa, tipo de energía, fechas, localidad) y **Top-N** al agrupar por columnas de alta cardinalidad (representante, cliente empresa, dirección de suministro), sin dejar de calcular sobre todos los registros.

**Why this priority**: Paridad con `006` (US4 y US5).

**Independent Test**: "¿Cuántos contratos por representante?" muestra Top-N e indica que hay más; con filtro de localidad el conteo se restringe correctamente.

**Acceptance Scenarios**:

1. **Given** dimensión de alta cardinalidad, **When** agrupa sin límite explícito, **Then** presenta Top-N ordenado por total e indica más grupos.
2. **Given** filtros combinados con agrupación, **When** consulta, **Then** el agregado refleja solo el subconjunto filtrado.

---

### Edge Cases

- **Contacto sin cliente empresa vinculado** (`ContactoDetalleCliente` vacío): el contrato aparece con datos del representante; columnas de cliente empresa vacías o "Sin dato", sin inventar empresa.
- **Varios clientes empresa** vinculados al mismo contacto en distintos contratos: cada contrato muestra el cliente empresa de su relación en ese contrato.
- **MultiPuntos**: un contrato puede tener varias filas de CUPS; el conteo de contratos usa conteo de contratos distintos, no de filas de CUPS (coherente con `006`).
- **Solicitud de Unicliente** en herramienta multicliente: el asistente indica el alcance o enruta a la herramienta adecuada.
- **Nulos en dimensiones**: etiqueta "Sin dato"; nulos en medidas numéricas excluidos del promedio (no como cero).
- **Volumen alto en listado**: agrupación por contrato; límite dedicado documentado, sin tope genérico de 50.
- Operador sin sesión válida: sin acceso (auth existente del producto).

## Requirements *(mandatory)*

### Functional Requirements

- **FR-001**: El sistema MUST ofrecer capacidad de **consulta de contratos** con `TipProCom = 3` (MultiCliente MultiPunto) exclusivamente.
- **FR-002**: El listado MUST incluir, como mínimo, las columnas de salida de la consulta de referencia: identificador y fecha de contrato; documento y nombre del representante legal/contacto; correo y teléfono del contacto; nombre, dirección, correo y teléfono del cliente empresa; fechas de CUPS; códigos CUPS eléctrico/gas; tarifas; consumos y potencias P1–P6; dirección y localidad del punto de suministro; dirección y localidad del representante/contacto.
- **FR-003**: El listado MUST ordenar por **fecha de contrato** descendente por defecto y MUST permitir orden ascendente.
- **FR-004**: El sistema MUST permitir **filtrar** por representante (documento, nombre, correo, teléfono, dirección), por cliente empresa (nombre, dirección, correo, teléfono), por punto de suministro (dirección, localidad), por CUPS, tarifa, tipo de energía y rango de fechas de contrato.
- **FR-005**: El sistema MUST resolver el titular del contrato contra **ContactoCliente** y el cliente empresa contra la cadena **ContactoDetalleCliente → Cliente**, coherente con la consulta de referencia.
- **FR-006**: El listado MUST NOT aplicar el tope genérico de filas del catálogo (p. ej. 50); MUST usar límite dedicado y/o agrupación por contrato, indicando truncado solo si se supera el límite dedicado.
- **FR-007**: El sistema MUST ofrecer capacidad de **agregación** (conteo y operaciones matemáticas) sobre contratos `TipProCom = 3` con paridad funcional a la feature `006`: lista blanca de dimensiones, hasta dos dimensiones combinadas, COUNT sobre todos los registros sin "lista truncada", SUM/AVG/MIN/MAX sobre medidas numéricas, total general, Top-N en alta cardinalidad, filtros previos.
- **FR-008**: Las dimensiones agrupables MUST incluir al menos: localidad del punto de suministro, localidad del representante/contacto, localidad del cliente empresa, provincia (suministro, representante, cliente), dirección de suministro, mes y año de contrato, tarifa eléctrica, tarifa de gas, tipo de energía, representante legal/contacto, cliente empresa, estado de propuesta, y buckets de consumo eléctrico/gas.
- **FR-009**: El sistema MUST **rechazar** dimensiones o medidas no permitidas; MUST NOT ejecutar agrupaciones arbitrarias definidas por el modelo.
- **FR-010**: Las respuestas MUST basarse exclusivamente en datos consultados; MUST NOT inventar valores.
- **FR-011**: El sistema MUST operar en **solo lectura**.
- **FR-012**: El clasificador de intención MUST enrutar preguntas sobre contratos multicliente, multicliente multipunto, representante legal en contratos, o conteos/agregados de ese tipo a la capacidad de base de datos adecuada.
- **FR-013**: El asistente MUST NOT exponer detalles técnicos internos al operador.
- **FR-014**: Cuando el operador pida contratos Unicliente en contexto multicliente (o viceversa), el asistente MUST aclarar el alcance o usar la herramienta correcta.

### Key Entities

- **Contrato** (= propuesta comercial, `TipProCom = 3`).
- **Representante legal / contacto** (`ContactoCliente`): titular del puente comercial en este tipo de contrato.
- **Cliente empresa** (`Cliente`): vinculado al contacto vía `ContactoDetalleCliente`.
- **Punto de suministro / CUPS**: línea de detalle energético (igual dominio que Unicliente).
- **Dimensión de agrupación** y **medida numérica**: mismos conceptos que en `006`, adaptados a columnas multicliente.

## Success Criteria *(mandatory)*

### Measurable Outcomes

- **SC-001**: El **100%** de listados solicitados de contratos multicliente devuelven solo `TipProCom = 3`, ordenados por fecha de contrato por defecto.
- **SC-002**: Al menos el **90%** de filtros por representante, cliente empresa, suministro, CUPS y tarifa devuelven resultados coherentes en pruebas de aceptación.
- **SC-003**: En conteos agrupados con más registros que el tope de listado genérico, el **100%** de respuestas no incluyen aviso de "lista truncada" por conteo incorrecto; totales calculados en agregación.
- **SC-004**: Al menos **8** dimensiones de agrupación distintas (localidad suministro, representante, cliente empresa, mes, tarifa, tipo energía, provincia, estado) funcionan correctamente en pruebas.
- **SC-005**: Operaciones numéricas (suma/promedio) sobre consumos y potencias devuelven valores verificables en al menos **4** escenarios.
- **SC-006**: El **100%** de solicitudes con dimensión no permitida se rechazan con sugerencia de alternativas válidas.
- **SC-007**: Listados con más de 50 filas relevantes **no** se limitan al tope genérico de 50 del catálogo.

## Assumptions

- La consulta de referencia SQL define el **conjunto de columnas y relaciones** del dominio; la implementación materializará el mismo mapa relacional sin SQL ad hoc expuesto al modelo.
- `TipProCom = 3` implica titular **contacto**; la columna `localidadCliente` en la consulta de referencia corresponde a la **localidad física del contacto/representante** (`CodLocFis`), distinta de la localidad del punto de suministro y de la dirección física del cliente empresa.
- Paridad de agregación con `006`: COUNT de contratos distintos (no filas CUPS), medidas numéricas a nivel CUPS, buckets por defecto para distribución de consumos, Top-N en representante/cliente empresa/dirección de suministro.
- Reutiliza infraestructura de conversación (`004`), catálogo de herramientas de BD (`002`) y modelos de suministro ya introducidos en `005`.
- Escritura en base de datos fuera de alcance; solo lectura; respuestas en español.

## Dependencies

- Feature `005-contratos-unicliente-detalle`: modelos de suministro (punto, CUPS, tarifas) y patrones de listado/filtro (adaptados a `TipProCom = 3`).
- Feature `006-contratos-conteo-agrupado`: patrones de agregación, lista blanca, operaciones matemáticas y ausencia de truncado en conteos (adaptados a `TipProCom = 3`).
- Feature `002-agent-db-clients-contracts`: catálogo `agent_database` y modelos de contacto/cliente existentes o a completar (`ContactoDetalleCliente`).
- Feature `004-chat-conversations`: contexto conversacional para seguimientos.
- Conexión secundaria con datos poblados para pruebas de aceptación.
