# RF-CATALOG-005: Categorias y Tags

## Identificacion

| Campo | Valor |
|-------|-------|
| **ID** | RF-CATALOG-005 |
| **Modulo** | MGN-005 Catalogs |
| **Titulo** | Categorias y Tags |
| **Prioridad** | Media |
| **Estado** | Draft |
| **Fecha** | 2025-12-05 |

---

## Descripcion

El sistema debe proporcionar un sistema flexible de categorias y tags para organizar contactos, productos y otros registros. Las categorias son jerarquicas (arbol) mientras que los tags son planos con colores.

**Referencia Odoo:**
- `res.partner.category` (odoo/addons/base/models/res_partner.py)

---

## Requisitos Funcionales

### RF-CATALOG-005.1: Contact Tags

Tags para clasificar contactos (clientes VIP, proveedores preferidos, etc.):

| Campo | Tipo | Descripcion |
|-------|------|-------------|
| id | UUID | Identificador unico |
| tenant_id | FK | Tenant propietario |
| name | string | Nombre del tag |
| color | int | Color (1-11) |
| parent_id | FK | Tag padre (jerarquia) |
| is_active | boolean | Disponible para uso |

**Jerarquia:**
- Tags pueden tener padre para organizacion
- Display name incluye path: "Clientes / VIP / Gold"

### RF-CATALOG-005.2: Colores de Tags

Sistema de colores predefinidos (similar a Odoo):

| Color ID | Nombre | Hex |
|----------|--------|-----|
| 1 | Red | #F06050 |
| 2 | Orange | #F4A460 |
| 3 | Yellow | #F7CD1F |
| 4 | Light Blue | #6CC1ED |
| 5 | Dark Purple | #814968 |
| 6 | Salmon Pink | #EB7E7F |
| 7 | Medium Blue | #2C8397 |
| 8 | Dark Blue | #475577 |
| 9 | Fuchsia | #D6145F |
| 10 | Green | #30C381 |
| 11 | Purple | #9365B8 |

### RF-CATALOG-005.3: Asignacion de Tags a Contactos

Relacion muchos a muchos:

```sql
core_catalogs.contact_tag_rel (
  contact_id UUID NOT NULL,
  tag_id UUID NOT NULL,
  PRIMARY KEY (contact_id, tag_id)
)
```

**Operaciones:**
- Agregar tag a contacto
- Remover tag de contacto
- Obtener contactos por tag
- Obtener tags de contacto

### RF-CATALOG-005.4: Categorias Genericas

Sistema extensible de categorias para diferentes entidades:

| Campo | Tipo | Descripcion |
|-------|------|-------------|
| id | UUID | Identificador unico |
| tenant_id | FK | Tenant propietario |
| entity_type | string | Tipo de entidad (contact, product, etc.) |
| name | string | Nombre de categoria |
| code | string | Codigo unico por tipo |
| parent_id | FK | Categoria padre |
| sort_order | int | Orden de visualizacion |
| is_active | boolean | Disponible para uso |

**Tipos de entidad soportados:**
- `contact` - Categorias de contactos
- `product` - Categorias de productos
- `expense` - Categorias de gastos
- `document` - Categorias de documentos

### RF-CATALOG-005.5: Jerarquia de Categorias

Implementacion con path materializado para consultas eficientes:

```sql
parent_path VARCHAR(255)  -- Ej: "1/5/12/"
```

**Funciones:**
- Obtener hijos directos
- Obtener todos los descendientes
- Obtener ancestros
- Mover categoria (cambiar parent)

### RF-CATALOG-005.6: Display Name Computed

El nombre mostrado incluye la jerarquia completa:

```
display_name = "Categoria Padre / Categoria Hijo / Esta Categoria"
```

---

## Operaciones CRUD

### Crear Tag

```typescript
POST /api/v1/catalogs/contact-tags
{
  "name": "Cliente VIP",
  "color": 10,
  "parentId": null
}
```

### Listar Tags

```typescript
GET /api/v1/catalogs/contact-tags

Response:
{
  "data": [
    {
      "id": "uuid",
      "name": "Cliente VIP",
      "displayName": "Clientes / Cliente VIP",
      "color": 10,
      "contactsCount": 25
    }
  ]
}
```

### Asignar Tags a Contacto

```typescript
POST /api/v1/contacts/:id/tags
{
  "tagIds": ["uuid-tag-1", "uuid-tag-2"]
}
```

### Buscar Contactos por Tag

```typescript
GET /api/v1/contacts?tags=uuid-tag-1,uuid-tag-2

// Devuelve contactos que tienen TODOS los tags especificados
```

### Crear Categoria

```typescript
POST /api/v1/catalogs/categories
{
  "entityType": "product",
  "name": "Electronicos",
  "code": "ELEC",
  "parentId": null
}
```

### Listar Categorias (Arbol)

```typescript
GET /api/v1/catalogs/categories?entityType=product&format=tree

Response:
{
  "data": [
    {
      "id": "uuid",
      "name": "Electronicos",
      "code": "ELEC",
      "children": [
        {
          "id": "uuid",
          "name": "Computadoras",
          "code": "COMP",
          "children": []
        },
        {
          "id": "uuid",
          "name": "Celulares",
          "code": "CEL",
          "children": []
        }
      ]
    }
  ]
}
```

---

## Data Seed

### Contact Tags por Defecto

| Nombre | Color | Descripcion |
|--------|-------|-------------|
| Cliente VIP | 10 (green) | Clientes prioritarios |
| Proveedor Preferido | 7 (blue) | Proveedores de confianza |
| Prospecto | 3 (yellow) | Clientes potenciales |
| Inactivo | 8 (dark blue) | Contactos sin actividad |

### Categorias de Producto por Defecto

```
Todos
├── Productos Vendibles
│   ├── Bienes
│   │   ├── Materias Primas
│   │   └── Productos Terminados
│   └── Servicios
├── Consumibles
└── Activos Fijos
```

### Categorias de Gasto por Defecto

```
Gastos
├── Operativos
│   ├── Nomina
│   ├── Servicios
│   └── Mantenimiento
├── Administrativos
│   ├── Papeleria
│   └── Comunicacion
└── Financieros
    └── Intereses
```

---

## Reglas de Negocio

| ID | Regla | Severidad |
|----|-------|-----------|
| BR-001 | Nombre unico por parent_id | Error |
| BR-002 | Code unico por entity_type | Error |
| BR-003 | No ciclos en jerarquia | Error |
| BR-004 | Color entre 1-11 | Error |
| BR-005 | No eliminar con registros asociados | Warning |

---

## Casos de Prueba

| ID | Escenario | Resultado Esperado |
|----|-----------|-------------------|
| TC-001 | Crear tag con color | Tag creado |
| TC-002 | Crear tag hijo | display_name incluye padre |
| TC-003 | Asignar tag a contacto | Relacion creada |
| TC-004 | Buscar por tag | Contactos filtrados |
| TC-005 | Crear categoria jerarquica | parent_path calculado |
| TC-006 | Mover categoria | parent_path actualizado |
| TC-007 | Crear ciclo en jerarquia | Error |

---

## Criterios de Aceptacion

- [ ] CRUD de contact tags
- [ ] Sistema de colores funcional
- [ ] Asignacion M:N tags-contactos
- [ ] CRUD de categorias genericas
- [ ] Jerarquia con parent_path
- [ ] Display name computed
- [ ] Seed de datos iniciales

---

## Notas Tecnicas

### Indice para Jerarquia

```sql
-- Para consultas de descendientes
CREATE INDEX idx_categories_parent_path
ON core_catalogs.categories USING gin (parent_path gin_trgm_ops);

-- Para validar ciclos
CREATE OR REPLACE FUNCTION core_catalogs.check_category_cycle()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.parent_id IS NOT NULL AND
       EXISTS (
           SELECT 1 FROM core_catalogs.categories
           WHERE id = NEW.parent_id
           AND parent_path LIKE '%/' || NEW.id || '/%'
       ) THEN
        RAISE EXCEPTION 'Cycle detected in category hierarchy';
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;
```

### Computed Display Name

```sql
CREATE OR REPLACE FUNCTION core_catalogs.get_category_display_name(p_id UUID)
RETURNS TEXT AS $$
WITH RECURSIVE ancestors AS (
    SELECT id, name, parent_id, 1 as level
    FROM core_catalogs.categories WHERE id = p_id
    UNION ALL
    SELECT c.id, c.name, c.parent_id, a.level + 1
    FROM core_catalogs.categories c
    JOIN ancestors a ON c.id = a.parent_id
)
SELECT string_agg(name, ' / ' ORDER BY level DESC)
FROM ancestors;
$$ LANGUAGE SQL STABLE;
```

---

## Historial

| Version | Fecha | Autor | Cambios |
|---------|-------|-------|---------|
| 1.0 | 2025-12-05 | System | Creacion inicial |
