# Data Model: Agent DB — Clientes y contratos

**Feature**: `002-agent-db-clients-contracts` | **Connection**: `agent_db_secondary`

## Dominio

En lenguaje de negocio: **contrato = PropuestaComercial**.

## Entity Relationship (logical)

```mermaid
erDiagram
    Cliente ||--o{ ContactoDetalleCliente : has
    Contacto ||--o{ ContactoDetalleCliente : has
    PropuestaComercial ||--o{ PropuestaComercialCliente : has
    PropuestaComercialCliente ||--o{ PropuestaComercialCups : has
    PropuestaComercialCliente }o--|| Cliente : "TipProCom=2"
    PropuestaComercialCliente }o--|| Contacto : "TipProCom=3"
    PropuestaComercial ||--o| Contrato : formalizes
```

## Entities

### Cliente (`T_Cliente`)

| Field | Role |
|-------|------|
| `CodCli` | PK |
| `NomComCli`, `RazSocCli` | Nombre comercial / razón social |
| `NumCifCli` | NIF/CIF |
| `TelFijCli`, `TelMovCli`, `EmaCli` | Contacto |
| `EstCli` | Estado |

**Relations**: `hasMany(ContactoDetalleCliente)`, scope `buscar($term)`.

### Contacto (`T_ContactoCliente`)

| Field | Role |
|-------|------|
| `CodConCli` | PK |
| `NomConCli`, `NIFConCli` | Identificación |
| `TelFijConCli`, `TelCelConCli`, `EmaConCli` | Contacto |
| `CarConCli` | Cargo |

**Relations**: `hasMany(ContactoDetalleCliente)` via `detallesCliente()`.

### ContactoDetalleCliente (`T_ContactoDetalleCliente`)

| Field | Role |
|-------|------|
| `CodDetCliCont` | PK |
| `CodCli` | FK → Cliente |
| `CodConCli` | FK → Contacto |
| `EsRepLeg` | Representante legal |

**Relations**: `belongsTo(Cliente)`, `belongsTo(Contacto)`.

### PropuestaComercial (`T_PropuestaComercial`) — **CONTRATO**

| Field | Role |
|-------|------|
| `CodProCom` | PK |
| `TipProCom` | 2 = cliente empresa, 3 = contacto |
| `EstProCom` | Estado propuesta/contrato |
| `RefProCom`, `IdOferta` | Referencias de búsqueda |
| `FecProCom` | Fecha propuesta |
| `ObsProCom` | Observaciones |

**Relations**: `hasMany(PropuestaComercialCliente)`, `hasMany(DocumentoPropuesta)` (fase 2).

### PropuestaComercialCliente (`T_Propuesta_Comercial_Clientes`)

| Field | Role |
|-------|------|
| `CodProComCli` | PK |
| `CodProCom` | FK → PropuestaComercial |
| `CodCli` | FK titular: `CodCli` (tipo 2) o `CodConCli` (tipo 3) |

**Relations**:
- `belongsTo(PropuestaComercial)`
- `belongsTo(Cliente)` when TipProCom=2
- `belongsTo(Contacto)` when TipProCom=3 (maps `CodCli` → `CodConCli`)
- `hasMany(PropuestaComercialCups)`

### PropuestaComercialCups (`T_Propuesta_Comercial_CUPs`)

| Field | Role |
|-------|------|
| `CodProComCup` | PK |
| `CodProComCli` | FK → PropuestaComercialCliente |
| `CodCup`, `TipCups` | CUP (1=eléctrico, 2=gas) |
| `FecActCUPs`, `FecVenCUPs` | Vigencia |
| `EstConCups` | Estado línea |

**Relations**: `belongsTo(PropuestaComercialCliente)` (via `CodProComCli`).

### Contrato formalizado (`T_Contrato`)

| Field | Role |
|-------|------|
| `CodConCom` | PK |
| `CodProCom` | FK → PropuestaComercial |
| `CodCli` | FK → Cliente |
| `RefCon`, `FecIniCon`, `FecVenCon`, `FecFinCon` | Formalización |
| `EstBajCon` | Baja |

**Relations**: `belongsTo(PropuestaComercial)`, `belongsTo(Cliente)`.

## Titular resolution rules

```
IF PropuestaComercial.TipProCom = 2
  THEN titular = PropuestaComercialCliente → Cliente (CodCli)

IF PropuestaComercial.TipProCom = 3
  THEN titular = PropuestaComercialCliente → Contacto (CodCli column holds CodConCli)

Contacto ↔ Cliente empresa: ContactoDetalleCliente (CodCli, CodConCli)
```

## Laravel model location (planned)

```text
app/Models/Commercial/
├── Cliente.php
├── Contacto.php
├── ContactoDetalleCliente.php
├── PropuestaComercial.php
├── PropuestaComercialCliente.php
├── PropuestaComercialCups.php
└── Contrato.php
```

All models: `protected $connection = 'agent_db_secondary';`

## Validation / read-only rules

- All agent queries: **SELECT only** (FR-009).
- No mass assignment from agent path.
- Search terms: trim, max 120 chars, `%` wildcards allowed for LIKE.
