# Implementation Plan: Contratos MultiCliente MultiPunto — listado, filtros y agregación

**Branch**: `007-contratos-multicliente-multipunto` | **Date**: 2026-05-28 | **Spec**: [spec.md](./spec.md)

**Input**: Feature specification from `/specs/007-contratos-multicliente-multipunto/spec.md`

## Summary

Extender el catálogo `agent_database` con **dos herramientas** para contratos **MultiCliente MultiPunto** (`TipProCom = 3`), con paridad respecto a las features `005` (listado/detalle/filtros) y `006` (agregación COUNT + SUM/AVG/MIN/MAX):

1. **`listar_contratos_multicliente`** — lista o detalla contratos con representante legal/contacto, cliente empresa vinculado (`ContactoDetalleCliente` → `Cliente`) y datos de suministro; sin tope genérico de 50 filas.
2. **`agregar_contratos_multicliente`** — conteo y operaciones matemáticas agrupadas por lista blanca de dimensiones; cálculo en BD sobre todos los registros; `truncated` siempre `false` en agregados.

La consulta de referencia une `T_PropuestaComercial` (TipProCom=3) → puente con **ContactoCliente** (titular) → **ContactoDetalleCliente** (opcional) → **Cliente** (empresa) → **CUPs** y suministro. Reutiliza modelos de suministro de la `005`; añade relaciones en `Contacto` y reutiliza `PropuestaComercialCliente.contacto()` ya existente.

## Technical Context

**Language/Version**: PHP 8.2+, Laravel 12

**Primary Dependencies**: Eloquent (`agent_db_secondary`), `AgentCommercialQueryService`, `AgentDatabaseQueryCapabilityHandler`, `IntentClassifierService`, `DatabaseSchemaService`, `ConversationHistoryService` (004). Modelos: `Contacto`, `ContactoDetalleCliente`, `Cliente`, `PropuestaComercialCups`, suministro (005).

**Storage**: BD secundaria — `T_PropuestaComercial`, `T_Propuesta_Comercial_Clientes`, `T_Propuesta_Comercial_CUPs`, `T_ContactoCliente`, `T_ContactoDetalleCliente`, `T_Cliente`, `T_PuntoSuministro`, `T_CUPsElectrico`, `T_CUPsGas`, `T_TarifaElectrica`, `T_TarifaGas`, `T_Localidad`, `T_Provincia`, `T_Producto`. Sin migraciones.

**Testing**: PHPUnit (`ContratosMulticlienteQueryTest`, `ContratosMulticlienteAgregadoQueryTest`)

**Target Platform**: API Laravel; sin cambios en `ChatFront/`

**Project Type**: Extensión backend brownfield (002 + 005 + 006)

**Performance Goals**: Respuesta < 30 s en local; agregación en una consulta `GROUP BY`

**Constraints**: Solo lectura; `TipProCom = 3` exclusivo; listado sin `max_results=50`; agregación sin truncar cálculo; lista blanca dimensiones/medidas

**Scale/Scope**: 2 herramientas nuevas; ~17 dimensiones agrupables; 8 medidas numéricas; 1 relación nueva en `Contacto` (`localidadFisica`)

## Constitution Check

*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*

| Principle | Status | Notes |
|-----------|--------|-------|
| **I. Spec-First** | ✅ | FR-001–FR-014, US1–US7 |
| **II. Skinny Controllers** | ✅ | Lógica en `AgentCommercialQueryService` |
| **III. Contratos IA** | ✅ | Handler + catálogo; sin SQL al LLM |
| **IV. API v1** | ✅ | Reutiliza chat existente |
| **V. Contrato API** | ✅ N/A | `contracts/*.md` |
| **VI. Frontend desacoplado** | ✅ N/A |
| **VII. Fidelidad al dato** | ✅ | FR-010 |
| **VIII. Simplicidad** | ✅ | Dos herramientas simétricas a 005/006; reutiliza patrones |

**Post-design re-check**: ✅ Sin violaciones. Duplicación controlada con 005/006 (métodos separados por TipProCom) evita ramas gigantes en una sola herramienta y mantiene enrutado LLM claro (VIII).

## Project Structure

### Documentation (this feature)

```text
specs/007-contratos-multicliente-multipunto/
├── spec.md
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│   ├── listar-contratos-multicliente-tool.md
│   └── agregar-contratos-multicliente-tool.md
└── tasks.md                             # (/speckit-tasks)
```

### Source Code (cambios previstos)

```text
app/
├── Models/Commercial/
│   └── Contacto.php                          # EDITAR — localidadFisica() (CodLocFis)
├── Services/Agent/
│   ├── AgentCommercialQueryService.php       # EDITAR — listarContratosMulticliente + agregarContratosMulticliente
│   ├── IntentClassifierService.php           # EDITAR — pistas multicliente
│   └── Handlers/AgentDatabaseQueryCapabilityHandler.php  # EDITAR — reglas 4d/4e
config/
└── agent.php                                 # EDITAR — 2 herramientas + max_rows multicliente
tests/Feature/Agent/
├── ContratosMulticlienteQueryTest.php        # NUEVO
└── ContratosMulticlienteAgregadoQueryTest.php # NUEVO
```

**Structure Decision**: Backend-only. Patrones copiados de `listarContratosUnicliente` / `agregarContratosUnicliente` con joins y dimensiones adaptados a contacto + cliente empresa.

## Architecture

```mermaid
sequenceDiagram
    participant U as Operador
    participant H as AgentDatabaseQueryCapabilityHandler
    participant S as AgentCommercialQueryService
    participant DB as agent_db_secondary

    U->>H: "¿Cuántos contratos multicliente por localidad?"
    H->>S: agregar_contratos_multicliente
    S->>DB: GROUP BY + COUNT(DISTINCT CodProCom) WHERE TipProCom=3
    DB-->>S: grupos + total_general
    S-->>H: truncated false
    H-->>U: tabla sin "lista truncada"
```

## Key design decisions (research.md)

- **Titular**: `PropuestaComercialCliente.CodCli` = `Contacto.CodConCli` cuando `TipProCom=3` (ya modelado en `contacto()`).
- **Cliente empresa**: `LEFT JOIN ContactoDetalleCliente → Cliente`; si hay varios detalles, la SQL de referencia puede multiplicar filas por CUPS (documentado).
- **COUNT**: `COUNT(DISTINCT pc.CodProCom)` en agregados; medidas numéricas a nivel CUPS.
- **Límite listado**: `agent.agent_database.contratos_multicliente.max_rows` (default 500), no `max_results=50`.

## Implementation Phases (for /speckit-tasks)

### Phase A — Modelo Contacto

1. Añadir `localidadFisica()` en `Contacto` (`CodLocFis` → `T_Localidad`) para localidad del representante.

### Phase B — Listado (`listar_contratos_multicliente`)

1. `listarContratosMulticliente()` en `AgentCommercialQueryService` + early return en `execute()`.
2. Base: `PropuestaComercialCups` + joins (pc TipProCom=3, pcc, contacto, left detalle→cliente, suministro).
3. Filtros: representante, cliente empresa, suministro, CUPS, tarifa, energía, fechas.
4. Vistas `listado`/`detalle`, orden, `max_rows` dedicado.
5. Serializador con columnas FR-002.
6. Registro en `config/agent.php` + enrutado handler/intent.

### Phase C — Agregación (`agregar_contratos_multicliente`)

1. `agregarContratosMulticliente()` — whitelists dimensiones/medidas adaptadas (representante, cliente_empresa, localidad_representante, etc.).
2. Misma semántica que `agregarContratosUnicliente`: Top-N, total_general, `truncated: false`.
3. Registro config + reglas handler (no usar listar para contar).

### Phase D — Tests

1. Tests unitarios whitelists/filtros/COUNT DISTINCT sin BD real.
2. Manual `quickstart.md` con BD operativa.

## Tool Catalog

| Tool | Priority | User Story |
|------|----------|------------|
| `listar_contratos_multicliente` | P1 | US1, US2, US3, US4 |
| `agregar_contratos_multicliente` | P1 | US5, US6, US7 |

Detalle: [contracts/listar-contratos-multicliente-tool.md](./contracts/listar-contratos-multicliente-tool.md), [contracts/agregar-contratos-multicliente-tool.md](./contracts/agregar-contratos-multicliente-tool.md).

## Phase 0 / Research

[research.md](./research.md)

## Phase 1 / Design

[data-model.md](./data-model.md), [contracts/](./contracts/), [quickstart.md](./quickstart.md)

## Complexity Tracking

> Sin violaciones. Dos herramientas nuevas (no unificar con Unicliente) mantienen enrutado LLM explícito y evitan parámetros `tipo` ambiguos entre TipProCom 1/2 y 3.

## Next Steps

1. `/speckit-tasks`
2. `/speckit-implement`
