7.5 KiB
7.5 KiB
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:
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 contactosproduct- Categorias de productosexpense- Categorias de gastosdocument- Categorias de documentos
RF-CATALOG-005.5: Jerarquia de Categorias
Implementacion con path materializado para consultas eficientes:
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
POST /api/v1/catalogs/contact-tags
{
"name": "Cliente VIP",
"color": 10,
"parentId": null
}
Listar Tags
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
POST /api/v1/contacts/:id/tags
{
"tagIds": ["uuid-tag-1", "uuid-tag-2"]
}
Buscar Contactos por Tag
GET /api/v1/contacts?tags=uuid-tag-1,uuid-tag-2
// Devuelve contactos que tienen TODOS los tags especificados
Crear Categoria
POST /api/v1/catalogs/categories
{
"entityType": "product",
"name": "Electronicos",
"code": "ELEC",
"parentId": null
}
Listar Categorias (Arbol)
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
-- 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
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 |