# Feature Specification: Consultas de clientes y contratos vía asistente

**Feature Branch**: `002-agent-db-clients-contracts`  
**Created**: 2026-05-24  
**Status**: Draft  
**Input**: El asistente debe acceder a la base de datos operativa usando herramientas preconfiguradas, consultar clientes y **propuestas comerciales (contratos)** relacionados (según `TipProCom`), cruzar tablas y responder con información verificable existente en la base de datos.

> **Término de dominio**: En lenguaje de negocio, «contrato» = **`PropuestaComercial`** (`T_PropuestaComercial`). Las consultas MUST centrarse en esta entidad y sus relaciones.

## Clarifications

### Session 2026-05-24

- Q: ¿Cómo deben construirse las consultas del catálogo de herramientas (agent_database)? → A: A partir de las **relaciones definidas en los modelos Eloquent de dominio comercial** del proyecto; esos modelos son la fuente de verdad para joins entre cliente, contacto y contrato (no SQL ad hoc inventado fuera del mapa relacional).
- Q: ¿Dónde están los modelos Eloquent de referencia para este dominio? → A: En el proyecto **`desarrolloeneon`** (`app/Models/`): `Cliente`, `Contacto`, `ContactoDetalleCliente`, `PropuestaComercial`, `PropuestaComercialCliente`, `PropuestaComercialCups`, `Contrato` (y relaciones asociadas). El plan MUST portar/adaptar esos modelos y relaciones a `ia_agent` antes de registrar consultas.
- Q: ¿Qué entidad representa un «contrato» en el dominio de negocio? → A: **`PropuestaComercial` (`T_PropuestaComercial`)**. Cada propuesta comercial **es** un contrato; es la entidad central con la información contractual (`TipProCom`, `EstProCom`, `RefProCom`, `FecProCom`, etc.). `T_Contrato` es registro complementario de formalización cuando existe, vinculado por `CodProCom`.

## User Scenarios & Testing

### User Story 1 - Buscar un cliente y ver sus contratos (Priority: P1)

Como operador autenticado, quiero preguntar al asistente por un cliente (nombre, NIF/CIF, email o teléfono) y obtener sus **propuestas comerciales (contratos)** asociadas con datos clave (`TipProCom`, `EstProCom`, referencia, fechas), para atender consultas comerciales sin abrir la base de datos manualmente.

**Why this priority**: Es el flujo principal de valor: localizar cliente → ver contratos vinculados.

**Independent Test**: Con un cliente conocido en la BD, el operador pregunta "¿Qué contratos tiene [nombre/NIF]?" y recibe una respuesta basada en datos reales, diferenciando contratos según el tipo de propuesta comercial.

**Acceptance Scenarios**:

1. **Given** un cliente existente con contratos tipo propuesta comercial 2, **When** pregunto por ese cliente usando nombre o NIF, **Then** el asistente identifica al cliente en el catálogo de clientes empresa y lista sus contratos con información coherente.
2. **Given** un cliente existente vinculado vía contacto/representante (tipo propuesta comercial 3), **When** pregunto por ese contacto, **Then** el asistente lo localiza en el catálogo de contactos y muestra los contratos relacionados correctamente.
3. **Given** un término de búsqueda ambiguo que coincide con varios registros, **When** pregunto al asistente, **Then** indica que hay múltiples coincidencias y pide concretar (NIF, nombre completo u otro dato) antes de afirmar un resultado único.

---

### User Story 2 - Consultar detalle de un contrato con contexto del cliente (Priority: P1)

Como operador, quiero preguntar por un contrato concreto (número, referencia o criterio identificable) y recibir el detalle del contrato junto con los datos del cliente asociado, para resolver dudas operativas en una sola interacción.

**Why this priority**: Complementa la búsqueda por cliente; muchas consultas parten del contrato, no del cliente.

**Independent Test**: Preguntar "Dame información del contrato [referencia o CodProCom]" devuelve datos de la **PropuestaComercial** y del cliente/contacto titular según `TipProCom`.

**Acceptance Scenarios**:

1. **Given** un contrato con tipo propuesta comercial 2, **When** solicito su detalle, **Then** la respuesta incluye datos del contrato y del cliente empresa asociado.
2. **Given** un contrato con tipo propuesta comercial 3, **When** solicito su detalle, **Then** la respuesta incluye datos del contrato y del contacto/representante asociado (y la relación con la empresa cliente cuando exista en los datos).
3. **Given** un identificador de contrato inexistente, **When** pregunto por él, **Then** el asistente indica que no encontró resultados y sugiere verificar el identificador.

---

### User Story 3 - Relacionar información entre tablas en lenguaje natural (Priority: P2)

Como operador, quiero formular preguntas que requieran cruzar información (p. ej. "clientes con contratos activos en modalidad X", "contactos de la empresa Y con contratos vigentes"), para obtener respuestas integradas sin conocer la estructura interna de la base de datos.

**Why this priority**: Amplía el valor del asistente más allá de búsquedas simples por un solo criterio.

**Independent Test**: Preguntas que exigen unir datos de cliente/contacto y contrato devuelven respuestas consistentes y basadas únicamente en los resultados obtenidos.

**Acceptance Scenarios**:

1. **Given** datos existentes que cumplen el criterio, **When** pregunto por una relación cliente–contrato con filtros (estado, tipo, periodo), **Then** el asistente responde con un listado o resumen coherente derivado de los datos consultados.
2. **Given** una pregunta que requiere datos no disponibles en las herramientas permitidas, **When** la formulo, **Then** el asistente indica que no puede responder con la información accesible y no inventa datos.

---

### User Story 4 - Respuestas fiables y trazables (Priority: P2)

Como operador, quiero que el asistente base sus respuestas exclusivamente en datos consultados y comunique claramente cuando no hay resultados o la consulta falla, para confiar en la información en contexto comercial.

**Why this priority**: Alineado con fidelidad al dato (constitución): no inventar información de negocio.

**Independent Test**: Ante BD vacía para un criterio o error de conexión, el mensaje al usuario es claro y no contiene datos ficticios.

**Acceptance Scenarios**:

1. **Given** una consulta sin resultados, **When** el asistente responde, **Then** indica explícitamente que no encontró registros y sugiere reformular o ampliar criterios.
2. **Given** indisponibilidad temporal de la base de datos, **When** pregunto algo que requiere consulta, **Then** recibo un mensaje de error comprensible sin datos inventados.

---

### Edge Cases

- Cliente con contratos de ambos tipos de propuesta comercial (2 y 3): la respuesta debe distinguir el origen del cliente según cada contrato.
- Mismo NIF o nombre en cliente empresa y contacto: pedir desambiguación o mostrar ambos contextos claramente separados.
- Consultas con caracteres especiales o términos muy cortos (< 3 caracteres): manejo seguro sin errores opacos.
- Preguntas fuera del dominio (actualizar precios, borrar registros): rechazar o redirigir; solo lectura de información.
- Volumen alto de resultados: resumir o paginar conceptualmente (límite razonable de filas mostradas al operador).
- Operador sin sesión válida: no debe acceder a consultas de negocio (reutiliza auth existente del producto).

## Requirements

### Functional Requirements

- **FR-001**: El sistema MUST permitir al operador autenticado formular preguntas en lenguaje natural en el chat del asistente sobre clientes y contratos.
- **FR-002**: El asistente MUST usar únicamente herramientas de consulta preaprobadas y configuradas (catálogo de consultas permitidas); MUST NOT ejecutar consultas arbitrarias definidas por el modelo.
- **FR-013**: El catálogo de consultas MUST derivarse de las **relaciones Eloquent** entre modelos de dominio comercial (cliente empresa, contacto, contrato); los joins entre entidades MUST reflejar esas relaciones y MUST NOT definirse fuera de ese mapa.
- **FR-003**: El sistema MUST soportar búsqueda de clientes empresa (tabla de clientes comerciales) y de contactos/representantes (tabla de contactos de cliente), coherentes con las herramientas ya orientadas a ese dominio.
- **FR-004**: El sistema MUST relacionar cada **PropuestaComercial (contrato)** con su titular según `TipProCom`: valor 2 → `Cliente` (`T_Cliente`); valor 3 → `Contacto` (`T_ContactoCliente`), vía `PropuestaComercialCliente`.
- **FR-005**: El sistema MUST exponer herramientas para consultar **propuestas comerciales/contratos** (`T_PropuestaComercial`): listado por cliente/contacto, detalle por `CodProCom`/`RefProCom`/`IdOferta` y filtros (`EstProCom`, `TipProCom`, fechas).
- **FR-014**: Las herramientas de contrato MUST incluir las relaciones de `PropuestaComercial`: `propuestaComercialCliente` (titulares), `documentosPropuestas` (documentación) y, cuando aplique, CUPs/puntos de suministro vía `PropuestaComercialCliente` → `PropuestaComercialCups`.
- **FR-006**: El asistente MUST poder encadenar o combinar herramientas cuando la pregunta requiera cruzar información entre clientes/contactos y contratos.
- **FR-007**: Las respuestas MUST basarse exclusivamente en los resultados de las consultas ejecutadas; MUST NOT inventar clientes, contratos ni atributos.
- **FR-008**: Cuando no haya resultados, el asistente MUST informarlo claramente y sugerir criterios alternativos de búsqueda.
- **FR-009**: El sistema MUST operar en modo solo lectura sobre la base de datos operativa; MUST NOT modificar, insertar ni eliminar registros de negocio vía el asistente.
- **FR-010**: El clasificador de intenciones MUST enrutar preguntas sobre clientes, contratos y datos comerciales hacia la capacidad de consulta a base de datos cuando esté habilitada.
- **FR-011**: El catálogo de herramientas MUST documentar para cada consulta su propósito, parámetros esperados y relaciones de negocio relevantes (p. ej. vínculo contrato–cliente según tipo de propuesta).
- **FR-012**: El sistema MUST limitar el volumen de datos devueltos al operador en una sola respuesta para mantener legibilidad (umbral configurable en implementación).

### Key Entities

- **Contrato (dominio)** = **Propuesta comercial** (`PropuestaComercial` → `T_PropuestaComercial`, PK `CodProCom`): Entidad central; cada registro es un contrato. Campos clave: `TipProCom`, `EstProCom`, `RefProCom`, `FecProCom`, `IdOferta`, `ObsProCom`.
- **Cliente empresa** (`Cliente` → `T_Cliente`, PK `CodCli`): Titular cuando `TipProCom = 2`.
- **Contacto de cliente** (`Contacto` → `T_ContactoCliente`, PK `CodConCli`): Titular cuando `TipProCom = 3`; vinculado a empresa vía `ContactoDetalleCliente`.
- **Propuesta–cliente** (`PropuestaComercialCliente` → `T_Propuesta_Comercial_Clientes`): Vincula una propuesta/contrato con su titular (`CodProCom`, `CodCli`).
- **CUPs de propuesta** (`PropuestaComercialCups` → `T_Propuesta_Comercial_CUPs`): Líneas de suministro/CUPs asociadas a cada titular de la propuesta (detalle contractual).
- **Contrato formalizado** (`Contrato` → `T_Contrato`, PK `CodConCom`): Registro complementario de formalización (`FecIniCon`, `FecVenCon`, `RefCon`); `belongsTo(PropuestaComercial)` — consulta secundaria cuando el operador pide detalle de formalización.
- **Herramienta de consulta**: Consulta predefinida alineada a traversar las relaciones Eloquent anteriores.

### Modelo relacional de referencia (fuente: `desarrolloeneon/app/Models`)

**Entidad central: `PropuestaComercial` (= contrato)**

| Modelo | Tabla | Relaciones clave |
|--------|-------|------------------|
| **`PropuestaComercial`** | **`T_PropuestaComercial`** | `hasMany(PropuestaComercialCliente)`; `hasMany(DocumentoPropuesta)`; `hasOne(Comercializadora)` |
| `PropuestaComercialCliente` | `T_Propuesta_Comercial_Clientes` | `belongsTo(PropuestaComercial)`; `belongsTo(Cliente)` si TipProCom=2; `belongsTo(Contacto)` si TipProCom=3; `hasMany(PropuestaComercialCups)` |
| `PropuestaComercialCups` | `T_Propuesta_Comercial_CUPs` | `belongsTo(PropuestaComercialCliente)` — CUPs, tarifas, fechas de activación/vencimiento |
| `Cliente` | `T_Cliente` | `hasMany(ContactoDetalleCliente)`; scope `buscar($valor)` |
| `Contacto` | `T_ContactoCliente` | `hasMany(ContactoDetalleCliente)` via `detallesCliente()` |
| `ContactoDetalleCliente` | `T_ContactoDetalleCliente` | Puente `CodCli` ↔ `CodConCli` |
| `Contrato` | `T_Contrato` | `belongsTo(PropuestaComercial)` — formalización opcional |

**Cadena titular → contrato (PropuestaComercial):**

```text
[TipProCom=2] Cliente → PropuestaComercialCliente → PropuestaComercial (contrato)
                                              └→ PropuestaComercialCups (detalle CUPs)

[TipProCom=3] Contacto → PropuestaComercialCliente → PropuestaComercial (contrato)
                                               └→ PropuestaComercialCups

Contacto ↔ Cliente empresa: ContactoDetalleCliente (CodCli, CodConCli)

Formalización (opcional): Contrato (T_Contrato) → PropuestaComercial (CodProCom)
```

Las consultas del catálogo MUST usar **`PropuestaComercial` como nodo central** al listar o detallar contratos. `T_Contrato` solo cuando el operador pregunte por formalización/fechas de contrato formal.

## Success Criteria

### Measurable Outcomes

- **SC-001**: En pruebas con al menos 10 preguntas representativas (cliente, contrato, cruce), al menos 90% devuelven información correcta verificable contra la base de datos.
- **SC-002**: El 100% de las respuestas sobre clientes/contratos incluyen solo datos presentes en resultados de consulta (cero invención en escenarios de prueba documentados).
- **SC-003**: Un operador obtiene respuesta a "buscar cliente + listar contratos" en menos de 30 segundos en entorno local con BD accesible.
- **SC-004**: Cuando la BD no está disponible, el 100% de los intentos muestran mensaje claro de error sin datos ficticios.
- **SC-005**: Preguntas clasificadas como consulta comercial en el dominio clientes/contratos se enrutan a la capacidad de base de datos en al menos 95% de los casos de prueba definidos.

## Assumptions

- La base de datos operativa ya está conectada y accesible en entorno de integración (conexión secundaria del producto).
- **Los modelos Eloquent de referencia** existen en **`desarrolloeneon`** (`app/Models/`). El plan MUST portar: `PropuestaComercial`, `PropuestaComercialCliente`, `PropuestaComercialCups`, `Cliente`, `Contacto`, `ContactoDetalleCliente`, `Contrato` (formalización), conservando relaciones y regla **`PropuestaComercial` = contrato**.
- Tablas confirmadas: `T_PropuestaComercial` (contrato), `T_Propuesta_Comercial_Clientes`, `T_Propuesta_Comercial_CUPs`, `T_Cliente`, `T_ContactoCliente`, `T_ContactoDetalleCliente`, `T_Contrato` (formalización).
- Solo operadores autenticados del panel IA pueden usar esta capacidad (misma política de acceso que el chat actual).
- Las consultas existentes de búsqueda de clientes y contactos se mantienen y se **refactorizan** para alinearse con relaciones de modelos; se extienden con herramientas de contratos.
- `tipProCom` con valor 2 y 3 son los únicos discriminadores de cliente requeridos en esta feature; otros valores se tratarán como fuera de alcance hasta nueva especificación.
- La respuesta al operador es en español, en lenguaje natural, sin exponer detalles técnicos de herramientas o consultas internas.
- Escritura en BD (altas, bajas, actualizaciones) queda fuera de alcance; solo consultas de lectura.

## Dependencies

- Feature baseline `001-chatbot-pdf-extractor`: chat conversacional, orquestador de agente, autenticación JWT y capacidad `agent_database` parcialmente implementada.
- **Modelos Eloquent** portados desde `desarrolloeneon/app/Models/` con relaciones cliente ↔ contacto ↔ propuesta ↔ contrato; prerequisito para el catálogo de consultas.
- Configuración de herramientas permitidas en catálogo de consultas (SQL derivado de relaciones de modelos, no escrito a mano sin ese mapa).
- Esquema de BD secundaria con datos de clientes, contactos y contratos poblados para pruebas de aceptación.
