---
description: "Task list for feature 007-contratos-multicliente-multipunto"
---

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

**Input**: Design documents from `/specs/007-contratos-multicliente-multipunto/`

**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md; **features 005 y 006 implementadas**

**Tests**: Incluidos en fase Polish; no TDD estricto.

**Organization**: Tareas agrupadas por user story para implementación y prueba incremental independiente.

## Format: `[ID] [P?] [Story] Description`

- **[P]**: Paralelizable (archivos distintos, sin dependencias entre sí)
- **[Story]**: User story de spec.md (US1–US7)
- Rutas concretas en cada descripción

## Phase 1: Setup (Shared Infrastructure)

**Purpose**: Prerrequisitos y configuración compartida

- [x] T001 Verificar features 005 y 006 implementadas (`listar_contratos_unicliente`, `agregar_contratos_unicliente`, modelos suministro) antes de continuar
- [x] T002 Añadir clave `contratos_multicliente.max_rows` (default `env('AGENT_CONTRATOS_MULTICLIENTE_MAX_ROWS', 500)`) en `config/agent.php` dentro de `agent_database` (FR-006)
- [x] T003 [P] Verificar `AGENT_CAPABILITY_AGENT_DATABASE=true` y documentar prerequisitos en `specs/007-contratos-multicliente-multipunto/quickstart.md`

---

## Phase 2: Foundational (Blocking Prerequisites)

**Purpose**: Relación Contacto + esqueletos de ambas herramientas — **bloquea todas las user stories**

**⚠️ CRITICAL**: Ninguna user story puede completarse hasta terminar esta fase

- [x] T004 Añadir relación `localidadFisica()` (`CodLocFis` → `T_Localidad`) en `app/Models/Commercial/Contacto.php` (FR-005, data-model.md)
- [x] T005 Añadir early return `'listar_contratos_multicliente' => $this->listarContratosMulticliente($parameters)` y esqueleto del método en `app/Services/Agent/AgentCommercialQueryService.php` (FR-001)
- [x] T006 Añadir early return `'agregar_contratos_multicliente' => $this->agregarContratosMulticliente($parameters)` y esqueleto del método en `app/Services/Agent/AgentCommercialQueryService.php` (FR-007)
- [x] T007 Implementar consulta base de listado: `PropuestaComercialCups` + joins `pc` (`TipProCom=3`), `pcc`, `Contacto`, `LEFT JOIN ContactoDetalleCliente`, `LEFT JOIN Cliente`, suministro/tarifas en `app/Services/Agent/AgentCommercialQueryService.php` (FR-001, FR-005)
- [x] T008 Implementar consulta base de agregación con mismos joins (aliases para GROUP BY) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-007)
- [x] T009 Implementar mapa privado `dimensionesContratoMulticliente()` (17 dimensiones de data-model.md) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-008)
- [x] T010 [P] Reutilizar/adaptar `medidasNumericasContratoUnicliente()` como medidas multicliente (mismas columnas CUPS) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-007)

**Checkpoint**: Modelos, joins y whitelists listos

---

## Phase 3: User Story 1 — Listar contratos ordenados por fecha (Priority: P1) 🎯 MVP

**Goal**: Listado solo `TipProCom=3` ordenado por fecha de contrato desc con columnas de representante y suministro

**Independent Test**: "Lista los contratos multicliente multipunto" → solo TipProCom=3, orden fecha desc

### Implementation for User Story 1

- [x] T011 [US1] Completar `listarContratosMulticliente()`: `whereHas(propuestaComercial, TipProCom=3)` + `with([propuestaComercialCliente.contacto.localidadFisica, detalle→cliente, suministro...])` en `app/Services/Agent/AgentCommercialQueryService.php` (FR-001)
- [x] T012 [US1] Implementar orden `orden` (`fecha_desc` default | `fecha_asc`) por `FecProCom` en `app/Services/Agent/AgentCommercialQueryService.php` (FR-003)
- [x] T013 [US1] Implementar serializador inicial de fila CUPS con columnas mínimas (CodProCom, fechaContrato, representante) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-002)
- [x] T014 [US1] Registrar `listar_contratos_multicliente` en `allowed_queries` de `config/agent.php` según `specs/007-contratos-multicliente-multipunto/contracts/listar-contratos-multicliente-tool.md` (FR-001)
- [x] T015 [US1] Añadir pistas de enrutado ("contratos multicliente", "multicliente multipunto", "representante legal contratos") en `app/Services/Agent/IntentClassifierService.php` (FR-012)

**Checkpoint**: MVP listado multicliente vía chat

---

## Phase 4: User Story 2 — Filtrar por cualquier columna (Priority: P1)

**Goal**: Filtros por representante, cliente empresa, suministro, CUPS, tarifa, energía, fechas

**Independent Test**: "Contratos del representante [NIF]" y "contratos del cliente [empresa]" devuelven solo coincidencias

### Implementation for User Story 2

- [x] T016 [US2] Implementar `applyContratosMulticlienteFilters()`: filtro `termino` en contacto (NIFConCli, NomConCli, EmaConCli, TelFijConCli, NomViaDomFis) **y** cliente empresa (NomComCli, NumCifCli, etc.) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-004)
- [x] T017 [P] [US2] Implementar filtros `direccion_suministro` y `localidad` (suministro, representante o cliente) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-004)
- [x] T018 [P] [US2] Implementar filtros `cups`, `tarifa` y `tipo_energia` en `app/Services/Agent/AgentCommercialQueryService.php` (FR-004)
- [x] T019 [US2] Implementar `fec_desde`/`fec_hasta` sobre `FecProCom` en listado y agregación en `app/Services/Agent/AgentCommercialQueryService.php` (FR-004)
- [x] T020 [US2] Actualizar parámetros de filtro en descripción de `listar_contratos_multicliente` en `config/agent.php` (FR-004)

**Checkpoint**: US1 + US2 — listado con filtros completos

---

## Phase 5: User Story 3 — Detalle energético (Priority: P2)

**Goal**: CUPS, tarifas, potencias, consumos por punto de suministro

**Independent Test**: "Detalle de suministro del contrato [N]" devuelve datos energéticos por CUPS

### Implementation for User Story 3

- [x] T021 [US3] Completar `serializeContratoMulticlienteCup()` con todas las columnas FR-002 (representante, cliente empresa, CUPS, tarifas, potencias, consumos, localidades) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-002)
- [x] T022 [US3] Implementar `vista=listado` (agrupar por CodProCom, CUPS anidados) y `vista=detalle` (fila plana) en `listarContratosMulticliente()` en `app/Services/Agent/AgentCommercialQueryService.php` (FR-002)
- [x] T023 [US3] Resolver eléctrico vs gas (`TipCups`) en salida; campos no aplicables vacíos en `app/Services/Agent/AgentCommercialQueryService.php` (FR-002)

**Checkpoint**: Detalle energético consultable

---

## Phase 6: User Story 4 — Listados sin tope de 50 (Priority: P2)

**Goal**: Límite dedicado `contratos_multicliente.max_rows`; truncado solo al superar ese límite

**Independent Test**: >50 resultados no limitados a `max_results=50`

### Implementation for User Story 4

- [x] T024 [US4] Aplicar `contratos_multicliente.max_rows` (NO `max_results=50`) y marcar `truncated` solo al superar límite en `listarContratosMulticliente()` en `app/Services/Agent/AgentCommercialQueryService.php` (FR-006, SC-007)
- [x] T025 [US4] Manejar contacto sin cliente empresa: columnas empresa `null`/"Sin dato" sin inventar en serializador en `app/Services/Agent/AgentCommercialQueryService.php` (edge case spec)

**Checkpoint**: Listados completos sin tope genérico

---

## Phase 7: User Story 5 — Contar y agrupar por columna (Priority: P1)

**Goal**: COUNT agrupado exacto, sin "lista truncada"

**Independent Test**: "¿Cuántos contratos multicliente por localidad?" → totales + total_general, truncated false

### Implementation for User Story 5

- [x] T026 [US5] Implementar `agrupar_por` (0–2 dims) con `GROUP BY` y validación lista blanca en `agregarContratosMulticliente()` en `app/Services/Agent/AgentCommercialQueryService.php` (FR-007, FR-008)
- [x] T027 [US5] Implementar `operacion=count` con `COUNT(DISTINCT pc.CodProCom)` y `truncated: false` en `agregarContratosMulticliente()` en `app/Services/Agent/AgentCommercialQueryService.php` (FR-007, research R5)
- [x] T028 [US5] Rechazar dimensión no permitida con lista de válidas (`agent_query_invalid_params`) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-009, SC-006)
- [x] T029 [US5] Etiqueta "Sin dato" para nulos en dimensiones en `app/Services/Agent/AgentCommercialQueryService.php` (FR-008)
- [x] T030 [US5] Devolver `total_general` en respuesta de agregación en `app/Services/Agent/AgentCommercialQueryService.php` (FR-007)
- [x] T031 [US5] Registrar `agregar_contratos_multicliente` en `config/agent.php` según `specs/007-contratos-multicliente-multipunto/contracts/agregar-contratos-multicliente-tool.md` (FR-007)
- [x] T032 [US5] Añadir regla handler `4e` y pistas intent ("cuántos contratos multicliente por", "distribución multicliente") en `app/Services/Agent/Handlers/AgentDatabaseQueryCapabilityHandler.php` e `IntentClassifierService.php` (FR-012)

**Checkpoint**: Conteo agrupado multicliente operativo

---

## Phase 8: User Story 6 — Operaciones matemáticas (Priority: P1)

**Goal**: SUM/AVG/MIN/MAX sobre consumos y potencias

**Independent Test**: "Consumo eléctrico total por localidad en multicliente" devuelve suma por grupo

### Implementation for User Story 6

- [x] T033 [US6] Implementar `operacion` suma|promedio|minimo|maximo con validación de `medida` en `agregarContratosMulticliente()` en `app/Services/Agent/AgentCommercialQueryService.php` (FR-007)
- [x] T034 [P] [US6] Implementar buckets `bucket_consumo_electrico` y `bucket_consumo_gas` (reutilizar rangos 006) en whitelists multicliente en `app/Services/Agent/AgentCommercialQueryService.php` (FR-008)
- [x] T035 [US6] Excluir nulos de AVG/SUM; rechazar operación numérica sin medida válida en `app/Services/Agent/AgentCommercialQueryService.php` (FR-007, FR-009)
- [x] T036 [US6] Implementar parámetro `metricas[]` (varias métricas en una consulta) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-007)
- [x] T037 [US6] Pasar metadata de agregación multicliente al handler (`total_general`, `hay_mas`) en `app/Services/Agent/Handlers/AgentDatabaseQueryCapabilityHandler.php` (igual 006)

**Checkpoint**: Conteo + operaciones matemáticas multicliente

---

## Phase 9: User Story 7 — Alta cardinalidad y filtros en agregaciones (Priority: P2)

**Goal**: Top-N en representante/cliente/dirección; filtros previos en agregación

**Independent Test**: "¿Cuántos por representante?" → Top-N + hay_mas; filtro localidad restringe conteo

### Implementation for User Story 7

- [x] T038 [US7] Reutilizar `applyContratosMulticlienteFilters()` (y variante agregación) antes de GROUP BY en `agregarContratosMulticliente()` en `app/Services/Agent/AgentCommercialQueryService.php` (FR-004, FR-007)
- [x] T039 [US7] Aplicar `limite` Top-N solo presentación para dimensiones alta cardinalidad (`representante`, `cliente_empresa`, `direccion_suministro`) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-007, SC-005)
- [x] T040 [US7] Incluir `hay_mas` y `filtros_aplicados` en respuesta; handler sin lenguaje "lista truncada" para agregados en `app/Services/Agent/Handlers/AgentDatabaseQueryCapabilityHandler.php` (FR-007)
- [x] T041 [US7] Añadir regla handler `4d` para listar multicliente y distinguir de Unicliente (005/006) vs multicliente (007) en `app/Services/Agent/Handlers/AgentDatabaseQueryCapabilityHandler.php` (FR-014)

**Checkpoint**: Agregaciones con filtros y Top-N

---

## Phase 10: Polish & Cross-Cutting Concerns

**Purpose**: Tests, cache, validación

- [x] T042 [P] Crear `tests/Feature/Agent/ContratosMulticlienteQueryTest.php`: registro herramienta, max_rows, TipProCom=3, filtros, agrupación listado, sin tope 50
- [x] T043 [P] Crear `tests/Feature/Agent/ContratosMulticlienteAgregadoQueryTest.php`: whitelists, COUNT DISTINCT, dimensiones multicliente, rechazos, truncated false
- [x] T044 Invalidar cache: `php artisan cache:forget agent_database_queries_catalog`
- [ ] T045 Ejecutar checklist manual de `specs/007-contratos-multicliente-multipunto/quickstart.md` (requiere BD operativa)
- [x] T046 Ejecutar `php artisan test --filter=ContratosMulticliente` y `vendor/bin/pint --dirty`

---

## Dependencies & Execution Order

### Phase Dependencies

- **Setup (Phase 1)**: Sin dependencias
- **Foundational (Phase 2)**: Depende Phase 1 + 005/006 — **BLOQUEA** US1–US7
- **US1 (Phase 3)**: MVP listado
- **US2 (Phase 4)**: Depende US1 (consulta base T011)
- **US3 (Phase 5)**: Depende US1 (serializador)
- **US4 (Phase 6)**: Depende US1
- **US5 (Phase 7)**: Depende Phase 2 (T008–T009)
- **US6 (Phase 8)**: Depende US5
- **US7 (Phase 9)**: Depende US5/US6
- **Polish (Phase 10)**: Tras fases deseadas

### User Story Dependencies

| Story | Depende de | Independiente para probar |
|-------|------------|---------------------------|
| US1 | Phase 2 | ✅ Listado multicliente ordenado |
| US2 | US1 | ✅ Filtros representante/cliente/suministro |
| US3 | US1 | ✅ Detalle CUPS |
| US4 | US1 | ✅ Sin tope 50 |
| US5 | Phase 2 | ✅ Conteo por localidad |
| US6 | US5 | ✅ Suma consumo por tarifa |
| US7 | US5 | ✅ Top-N + filtros agregación |

### Parallel Opportunities

- **Phase 2**: T010 paralelo a T009
- **Phase 4**: T017, T018 en paralelo
- **Phase 8**: T034 paralelo tras T033
- **Phase 10**: T042, T043 en paralelo

---

## Parallel Example: Phase 2

```bash
T004: Contacto.php (localidadFisica)
T009: dimensionesContratoMulticliente()
T010: medidas (paralelo conceptual)
# Luego secuencial en AgentCommercialQueryService:
T005, T006, T007, T008
```

---

## Implementation Strategy

### MVP First (User Story 1 + registro)

1. Phase 1–2 (Setup + Foundational)
2. Phase 3 US1 (T011–T015)
3. **STOP**: "Lista contratos multicliente multipunto"

### Incremental Delivery

1. Setup + Foundational
2. US1 → listado MVP
3. US2 → filtros
4. US3 + US4 → detalle + sin tope 50
5. US5 → conteo agrupado
6. US6 → matemáticas
7. US7 → Top-N y filtros agregación
8. Polish

### Estrategia de reutilización

- Copiar estructura de `listarContratosUnicliente` / `agregarContratosUnicliente` (006) y adaptar joins a `Contacto` + `ContactoDetalleCliente`.
- Extraer helpers compartidos (buckets, wrap respuesta) solo si reduce duplicación sin sobre-abstraer (VIII).

---

## Notes

- `PropuestaComercialCliente.contacto()` ya existe para TipProCom=3.
- `localidadCliente` en SQL de referencia = localidad del **representante** (`CodLocFis`), no del cliente empresa.
- Dos herramientas separadas de Unicliente para enrutado LLM claro (FR-014).
- Referencia: `specs/007-contratos-multicliente-multipunto/contracts/`
