---
description: "Task list for feature 005-contratos-unicliente-detalle"
---

# Tasks: Contratos Unicliente (UniPunto y MultiPuntos) con detalle de suministro

**Input**: Design documents from `/specs/005-contratos-unicliente-detalle/`

**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/contratos-unicliente-tool.md, quickstart.md

**Tests**: Incluidos en fase Polish (plan Phase D); no TDD estricto — la spec no lo exige explícitamente.

**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–US4)
- Rutas concretas en cada descripción

## Phase 1: Setup (Shared Infrastructure)

**Purpose**: Configuración compartida y prerrequisitos de entorno

- [x] T001 Añadir clave de límite dedicado `contratos_unicliente.max_rows` (default `(int) env('AGENT_CONTRATOS_UNICLIENTE_MAX_ROWS', 500)`) dentro de la sección `agent_database` de `config/agent.php` (FR-008)
- [x] T002 [P] Documentar variable `AGENT_CONTRATOS_UNICLIENTE_MAX_ROWS` y verificación de conexión `agent_db_secondary` en `specs/005-contratos-unicliente-detalle/quickstart.md`
- [x] T003 [P] Verificar que la capacidad `agent_database` está habilitada (`AGENT_CAPABILITY_AGENT_DATABASE=true`) en `config/agent.php` / `.env`

---

## Phase 2: Foundational (Blocking Prerequisites)

**Purpose**: Modelos Eloquent nuevos, relaciones y esqueleto del método de consulta — **bloquea todas las user stories**

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

- [x] T004 [P] Crear modelo `PuntoSuministro` en `app/Models/Commercial/PuntoSuministro.php` (`T_PuntoSuministro`, PK `CodPunSum`, `$connection='agent_db_secondary'`, `$timestamps=false`, `belongsTo(Localidad,'CodLoc','CodLoc')`) (FR-006)
- [x] T005 [P] Crear modelo `CupsElectrico` en `app/Models/Commercial/CupsElectrico.php` (`T_CUPsElectrico`, PK `CodCupsEle`, campo `CUPsEle`) (FR-006)
- [x] T006 [P] Crear modelo `CupsGas` en `app/Models/Commercial/CupsGas.php` (`T_CUPsGas`, PK `CodCupGas`, campo `CupsGas`) (FR-006)
- [x] T007 [P] Crear modelo `TarifaElectrica` en `app/Models/Commercial/TarifaElectrica.php` (`T_TarifaElectrica`, PK `CodTarEle`, campo `NomTarEle`) (FR-006)
- [x] T008 [P] Crear modelo `TarifaGas` en `app/Models/Commercial/TarifaGas.php` (`T_TarifaGas`, PK `CodTarGas`, campo `NomTarGas`) (FR-006)
- [x] T009 [P] Crear modelo `Producto` en `app/Models/Commercial/Producto.php` (`T_Producto`, PK `CodPro`) (FR-014)
- [x] T010 [P] Crear modelo `AnexoProducto` en `app/Models/Commercial/AnexoProducto.php` (`T_AnexoProducto`, PK `CodAnePro`) (FR-014)
- [x] T011 Añadir relaciones en `app/Models/Commercial/PropuestaComercialCups.php`: `puntoSuministro()`, `cupsElectrico()` (`CodCup`→`CodCupsEle`), `cupsGas()` (`CodCup`→`CodCupGas`), `tarifaElectrica()` (`CodTar`→`CodTarEle`), `tarifaGas()` (`CodTar`→`CodTarGas`), `producto()`, `anexoProducto()` (data-model.md) (FR-006, FR-014)
- [x] T012 Añadir relación `localidadSocial()` (`CodLocSoc`→`CodLoc`) en `app/Models/Commercial/Cliente.php`, sin tocar la `localidad()` existente (`CodLocFis`) (FR-006)
- [x] T013 Añadir el caso `'listar_contratos_unicliente' => $this->listarContratosUnicliente($parameters)` al `match()` de `execute()` y crear el esqueleto del método en `app/Services/Agent/AgentCommercialQueryService.php` (FR-002)
- [x] T014 Implementar el serializador de fila con las columnas de salida de FR-006 (alias de negocio, eléctrico vs gas según `TipCups`) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-006)

**Checkpoint**: Modelos, relaciones y esqueleto de servicio listos — pueden empezar las user stories

---

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

**Goal**: El operador lista contratos `TipProCom` 1 y 2 ordenados por fecha de contrato desc, excluyendo `TipProCom=3`

**Independent Test**: "Lista los contratos Unicliente" devuelve contratos tipo 1 y 2 ordenados por `FecProCom` desc; sin tipo 3; vacío → mensaje claro

### Implementation for User Story 1

- [x] T015 [US1] Implementar la consulta base en `listarContratosUnicliente()`: `PropuestaComercialCups` con `whereHas('propuestaComercialCliente.propuestaComercial', fn($q)=>$q->whereIn('TipProCom',[1,2]))`, `whereHas('producto')` y `with([cliente.localidadSocial, puntoSuministro.localidad, cupsElectrico, cupsGas, tarifaElectrica, tarifaGas])` en `app/Services/Agent/AgentCommercialQueryService.php` (FR-001)
- [x] T016 [US1] Implementar parámetro `tipo` (`unicliente_unipunto`=1 | `unicliente_multipuntos`=2 | `ambos`) y derivar `tipoContrato` en la salida en `app/Services/Agent/AgentCommercialQueryService.php` (FR-005)
- [x] T017 [US1] Implementar orden por `FecProCom` con parámetro `orden` (`fecha_desc` default | `fecha_asc`) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-003)
- [x] T018 [US1] Implementar agrupación por `CodProCom` para `vista=listado` (CUPS anidados en `puntos_suministro`) y filas planas para `vista=detalle` en `app/Services/Agent/AgentCommercialQueryService.php` (FR-007)
- [x] T019 [US1] Registrar la herramienta `listar_contratos_unicliente` (description + parameters, sin SQL) en `allowed_queries` de `config/agent.php` según `specs/005-contratos-unicliente-detalle/contracts/contratos-unicliente-tool.md` (FR-002)
- [x] T020 [US1] Añadir pistas de enrutado ("contratos unicliente", "unipunto", "multipuntos", "CUPS", "punto de suministro", "tarifa", "potencia") en `app/Services/Agent/IntentClassifierService.php` (FR-002)

**Checkpoint**: MVP funcional — listado de contratos Unicliente ordenado por fecha vía chat

---

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

**Goal**: El operador filtra por cliente, NIF/CIF, dirección, teléfono, correo, punto de suministro, localidad, CUPS o tarifa

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

### Implementation for User Story 2

- [x] T021 [US2] Implementar filtro `termino` (cliente: `NomComCli`, `NumCifCli`, `EmaCli`, `TelFijCli`, `NomViaDomSoc`) con comodines `%` en `app/Services/Agent/AgentCommercialQueryService.php` (FR-004)
- [x] T022 [P] [US2] Implementar filtro `direccion_suministro` (`PuntoSuministro.NomViaPunSum`) y `localidad` (`DesLoc` de suministro o cliente) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-004)
- [x] T023 [P] [US2] Implementar filtro `cups` (`CUPsEle` o `CupsGas`) y `tarifa` (`NomTarEle` o `NomTarGas`) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-004)
- [x] T024 [P] [US2] Implementar filtro `tipo_energia` (`electrico`=TipCups 1 | `gas`=TipCups 2) y rango `fec_desde`/`fec_hasta` sobre `FecProCom` en `app/Services/Agent/AgentCommercialQueryService.php` (FR-004)
- [x] T025 [US2] Actualizar la descripción de parámetros de filtro de `listar_contratos_unicliente` en `config/agent.php` (FR-002)

**Checkpoint**: US1 + US2 — listado + filtrado por cualquier columna

---

## Phase 5: User Story 3 — Detalle energético de un contrato o punto de suministro (Priority: P2)

**Goal**: El operador obtiene CUPS, tarifas, potencias P1–P6 y consumos por punto de suministro

**Independent Test**: "Detalle de suministro del contrato [N]" devuelve por cada CUPS su tarifa, potencias y consumos; dato no aplicable → vacío sin inventar

### Implementation for User Story 3

- [x] T026 [US3] Garantizar resolución eléctrico vs gas en la salida (`codigoCupsElectrico`/`codigoCupsGas`, `NombreTarifaElectrica`/`NombreTarifaGas`, `consumoElectrico`=`ConCup`, `consumoGas`=`CauDiaGas`, `PotEleConP1..P6`) según `TipCups` en `app/Services/Agent/AgentCommercialQueryService.php` (FR-006)
- [x] T027 [US3] Verificar que `vista=detalle` expone una fila por CUPS con todas las columnas y fechas (`FecActCUPs`/`FecVenCUPs`) en `app/Services/Agent/AgentCommercialQueryService.php` (FR-006, FR-007)
- [x] T028 [US3] Reforzar el prompt de interpretación (turno 2) para tablas/listas legibles con datos de suministro y campos vacíos cuando no apliquen en `app/Services/Agent/Handlers/AgentDatabaseQueryCapabilityHandler.php` (FR-010, FR-013)

**Checkpoint**: Detalle de suministro consultable y coherente en seguimientos (contexto 004)

---

## Phase 6: User Story 4 — Respuestas completas y fiables sin tope de 50 (Priority: P2)

**Goal**: No truncar a 50 filas; usar límite dedicado + agrupación; comunicar errores y vacíos sin inventar

**Independent Test**: Criterio con > 50 resultados → no truncado a 50; BD caída → error claro; criterio sin datos → "sin resultados"

### Implementation for User Story 4

- [x] T029 [US4] Aplicar `agent.agent_database.contratos_unicliente.max_rows` (NO `max_results=50`) y marcar `truncated` solo al superar ese límite en `app/Services/Agent/AgentCommercialQueryService.php` (FR-008, FR-009)
- [x] T030 [US4] Asegurar manejo de resultado vacío (`agent_query_no_results`) y de errores de conexión (`agent_db_unreachable`) para esta herramienta en `app/Services/Agent/Handlers/AgentDatabaseQueryCapabilityHandler.php` (FR-011, SC-006)
- [x] T031 [US4] Reforzar que el turno 2 no expone nombres de herramienta/consulta ni inventa datos para esta herramienta en `app/Services/Agent/Handlers/AgentDatabaseQueryCapabilityHandler.php` (FR-010, FR-015)

**Checkpoint**: Listados completos sin tope artificial y fidelidad al dato

---

## Phase 7: Polish & Cross-Cutting Concerns

**Purpose**: Tests, cache, formato y validación final

- [x] T032 [P] Crear `tests/Feature/Agent/ContratosUniclienteQueryTest.php`: filtros por cada columna, eléctrico vs gas, `TipProCom` 1 y 2 incluidos / 3 excluido, orden por fecha, sin resultados, y verificación de que NO se trunca a 50 (SC-001–SC-005)
- [x] T033 [P] Verificar enrutado a `AgentCapability::AgentDatabase` para preguntas de contratos Unicliente en `app/Services/Agent/IntentClassifierService.php` (SC-005)
- [x] T034 Invalidar cache del catálogo tras editar config: `php artisan cache:forget agent_database_queries_catalog`
- [ ] T035 Ejecutar checklist manual de `specs/005-contratos-unicliente-detalle/quickstart.md` (requiere BD operativa con datos reales — pendiente de validación manual)
- [x] T036 Ejecutar `php artisan test --filter=ContratosUnicliente` y `vendor/bin/pint --dirty`

---

## Dependencies & Execution Order

### Phase Dependencies

- **Setup (Phase 1)**: Sin dependencias — inicio inmediato
- **Foundational (Phase 2)**: Depende de Phase 1 — **BLOQUEA** US1–US4
- **US1 (Phase 3)**: Depende de Phase 2 — MVP
- **US2 (Phase 4)**: Depende de US1 (consulta base T015)
- **US3 (Phase 5)**: Depende de US1 (serializador y vistas)
- **US4 (Phase 6)**: Depende de US1 (consulta base y límite)
- **Polish (Phase 7)**: Depende de las user stories deseadas completadas

### User Story Dependencies

| Story | Depende de | Independiente para probar |
|-------|------------|---------------------------|
| US1 | Phase 2 | ✅ Chat: listar contratos Unicliente ordenados |
| US2 | Phase 2 + T015 | ✅ Chat: filtrar por cualquier columna |
| US3 | Phase 2 + T014/T018 | ✅ Chat: detalle de suministro |
| US4 | Phase 2 + T015 | ✅ Volumen > 50, errores y vacíos |

### Within Each User Story

- Modelos y relaciones (Phase 2) antes que la consulta base (T015)
- Consulta base (T015) antes que filtros (US2), detalle (US3) y límite (US4)
- Registro en `config/agent.php` (T019/T025) antes de pruebas end-to-end

### Parallel Opportunities

- **Phase 2**: T004–T010 (7 modelos nuevos) en paralelo; luego T011/T012 (edición de modelos existentes)
- **Phase 4**: T022, T023, T024 en paralelo (filtros independientes)
- **Phase 7**: T032 y T033 en paralelo

---

## Parallel Example: Phase 2 (modelos nuevos)

```bash
# Crear modelos en paralelo (archivos distintos):
T004: app/Models/Commercial/PuntoSuministro.php
T005: app/Models/Commercial/CupsElectrico.php
T006: app/Models/Commercial/CupsGas.php
T007: app/Models/Commercial/TarifaElectrica.php
T008: app/Models/Commercial/TarifaGas.php
T009: app/Models/Commercial/Producto.php
T010: app/Models/Commercial/AnexoProducto.php
# Luego (mismo archivo cada uno, secuencial entre sí):
T011 (PropuestaComercialCups.php) ; T012 (Cliente.php)
```

## Parallel Example: User Story 2 (filtros)

```bash
# Tras T015/T021 (consulta base + filtro cliente):
T022: filtro dirección suministro / localidad
T023: filtro CUPS / tarifa
T024: filtro tipo_energia / rango fechas
```

---

## Implementation Strategy

### MVP First (User Story 1)

1. Phase 1: Setup (T001–T003)
2. Phase 2: Foundational (T004–T014) — **crítico**
3. Phase 3: User Story 1 (T015–T020)
4. **STOP y VALIDAR** con quickstart: "Lista los contratos Unicliente"
5. Demo si listo

### Incremental Delivery

1. Setup + Foundational → base lista
2. US1 → MVP en chat (listado ordenado)
3. US2 → filtrado por cualquier columna
4. US3 → detalle de suministro
5. US4 → sin tope de 50 + fiabilidad
6. Polish → tests + SC-001..SC-006

### Parallel Team Strategy

1. Equipo completa Phase 1–2 junto (modelos en paralelo)
2. Tras Phase 2:
   - Dev A: US1 (T015–T020)
   - Dev B: US2 (T021–T025) tras T015
   - Dev C: US3 (T026–T028) tras T014/T018
3. US4 + Polish al final

---

## Notes

- Reutiliza infraestructura de la feature 002 (`AgentDatabaseQueryCapabilityHandler`, `DatabaseSchemaService`) y el contexto conversacional de la 004 (`ConversationHistoryService`).
- Sin cambios en `ChatFront/` (feature backend-only).
- Solo lectura (FR-012): el método MUST NOT usar `insert`/`update`/`delete`.
- `TipProCom=3` (MultiCliente MultiPunto) queda fuera de alcance (feature futura).
- Esta herramienta NO usa el tope de 50 filas; usa `contratos_unicliente.max_rows` (R4 de research.md).
- Referencia contrato: `specs/005-contratos-unicliente-detalle/contracts/contratos-unicliente-tool.md`
