| id |
title |
type |
status |
priority |
module |
version |
created_date |
updated_date |
story_points |
| ET-SAAS-019 |
Especificacion Tecnica Portfolio (Product Catalog) |
TechnicalSpec |
Implemented |
P1 |
portfolio |
1.0.0 |
2026-02-03 |
2026-02-03 |
8 |
ET-SAAS-019: Especificacion Tecnica - Portfolio (Catalogo de Productos)
Metadata
- Codigo: ET-SAAS-019
- Modulo: Portfolio
- Version: 1.0.0
- Estado: Implementado
- Fecha: 2026-02-03
- Basado en: SAAS-019
1. Resumen Ejecutivo
1.1 Estado Actual
Sistema de catalogo de productos completamente implementado.
| Capacidad |
Estado |
Notas |
| Categories |
SI |
Jerarquicas con arbol |
| Products |
SI |
Multi-tipo, inventario |
| Variants |
SI |
Atributos configurables |
| Prices |
SI |
Multi-moneda, tiered |
| RLS |
SI |
Row Level Security |
1.2 Funcionalidades v1.0
Catalogo completo con:
- 4 Entidades: Categories, Products, Variants, Prices
- Categorias jerarquicas: Arbol ilimitado con parent_id
- 5 Tipos de producto: physical, digital, service, subscription, bundle
- Variantes: Atributos dinamicos (color, size, etc.)
- Precios flexibles: one_time, recurring, usage_based, tiered
2. Modelo de Datos
2.1 Schema: portfolio
Tabla: categories
| Campo |
Tipo |
Descripcion |
| id |
UUID |
PK |
| tenant_id |
UUID |
FK tenants |
| parent_id |
UUID |
FK categories (jerarquia) |
| name |
VARCHAR(100) |
Nombre categoria |
| slug |
VARCHAR(120) |
URL-friendly (unique per tenant) |
| description |
TEXT |
Descripcion |
| position |
INT |
Orden dentro del padre |
| image_url |
VARCHAR(500) |
Imagen |
| color |
VARCHAR(7) |
Color hex |
| icon |
VARCHAR(50) |
Icono |
| is_active |
BOOLEAN |
Estado |
| meta_title, meta_description |
VARCHAR/TEXT |
SEO |
| custom_fields |
JSONB |
Campos personalizados |
Tabla: products
| Campo |
Tipo |
Descripcion |
| id |
UUID |
PK |
| tenant_id |
UUID |
FK tenants |
| category_id |
UUID |
FK categories |
| name |
VARCHAR(255) |
Nombre producto |
| slug |
VARCHAR(280) |
URL-friendly |
| sku |
VARCHAR(100) |
Stock Keeping Unit |
| barcode |
VARCHAR(100) |
Codigo de barras |
| description |
TEXT |
Descripcion larga |
| short_description |
VARCHAR(500) |
Descripcion corta |
| product_type |
ENUM |
physical, digital, service, subscription, bundle |
| status |
ENUM |
draft, active, inactive, discontinued, out_of_stock |
| base_price |
DECIMAL(15,2) |
Precio base |
| cost_price |
DECIMAL(15,2) |
Costo |
| compare_at_price |
DECIMAL(15,2) |
Precio tachado |
| currency |
VARCHAR(3) |
Moneda (default USD) |
| track_inventory |
BOOLEAN |
Trackear inventario |
| stock_quantity |
INT |
Cantidad en stock |
| low_stock_threshold |
INT |
Umbral de stock bajo |
| allow_backorder |
BOOLEAN |
Permitir backorder |
| weight, length, width, height |
DECIMAL |
Dimensiones |
| images |
JSONB |
Array de URLs |
| featured_image_url |
VARCHAR(500) |
Imagen principal |
| tags |
JSONB |
Array de tags |
| is_visible |
BOOLEAN |
Visible en frontend |
| is_featured |
BOOLEAN |
Producto destacado |
| has_variants |
BOOLEAN |
Tiene variantes |
| variant_attributes |
JSONB |
Atributos para variantes |
Tabla: variants
| Campo |
Tipo |
Descripcion |
| id |
UUID |
PK |
| tenant_id |
UUID |
FK tenants |
| product_id |
UUID |
FK products |
| sku |
VARCHAR(100) |
SKU de variante |
| barcode |
VARCHAR(100) |
Codigo de barras |
| name |
VARCHAR(255) |
Nombre (e.g., "Rojo - XL") |
| attributes |
JSONB |
{"color": "red", "size": "XL"} |
| price |
DECIMAL(15,2) |
Override de precio |
| cost_price |
DECIMAL(15,2) |
Costo variante |
| stock_quantity |
INT |
Stock de variante |
| weight |
DECIMAL(10,3) |
Peso (override) |
| image_url |
VARCHAR(500) |
Imagen de variante |
| is_active |
BOOLEAN |
Estado |
| position |
INT |
Orden |
Tabla: prices
| Campo |
Tipo |
Descripcion |
| id |
UUID |
PK |
| tenant_id |
UUID |
FK tenants |
| product_id |
UUID |
FK products (o variant_id) |
| variant_id |
UUID |
FK variants (o product_id) |
| price_type |
ENUM |
one_time, recurring, usage_based, tiered |
| currency |
VARCHAR(3) |
Moneda |
| amount |
DECIMAL(15,2) |
Precio |
| compare_at_amount |
DECIMAL(15,2) |
Precio comparativo |
| billing_period |
VARCHAR(20) |
day, week, month, year |
| billing_interval |
INT |
Intervalo (e.g., 1 = mensual) |
| min_quantity, max_quantity |
INT |
Rango para tiered |
| valid_from, valid_until |
TIMESTAMPTZ |
Validez |
| priority |
INT |
Prioridad (0 = default) |
| is_active |
BOOLEAN |
Estado |
2.2 Enums
portfolio.product_type: physical, digital, service, subscription, bundle
portfolio.product_status: draft, active, inactive, discontinued, out_of_stock
portfolio.price_type: one_time, recurring, usage_based, tiered
portfolio.attribute_type: color, size, material, style, capacity, custom
3. Arquitectura Backend
3.1 Estructura de Archivos
backend/src/modules/portfolio/
├── portfolio.module.ts
├── controllers/
│ ├── categories.controller.ts
│ └── products.controller.ts
├── services/
│ ├── categories.service.ts
│ └── products.service.ts
├── entities/
│ ├── category.entity.ts
│ ├── product.entity.ts
│ ├── variant.entity.ts
│ └── price.entity.ts
├── dto/
│ ├── create-category.dto.ts
│ ├── create-product.dto.ts
│ └── ...
└── __tests__/
3.2 Endpoints API
Categories
| Metodo |
Endpoint |
Descripcion |
| GET |
/portfolio/categories |
Listar (paginado) |
| GET |
/portfolio/categories/tree |
Arbol jerarquico |
| GET |
/portfolio/categories/:id |
Obtener |
| POST |
/portfolio/categories |
Crear |
| PUT |
/portfolio/categories/:id |
Actualizar |
| DELETE |
/portfolio/categories/:id |
Eliminar |
Products
| Metodo |
Endpoint |
Descripcion |
| GET |
/portfolio/products |
Listar (paginado, filtros) |
| GET |
/portfolio/products/:id |
Obtener con variantes |
| POST |
/portfolio/products |
Crear |
| PUT |
/portfolio/products/:id |
Actualizar |
| PATCH |
/portfolio/products/:id/status |
Cambiar status |
| POST |
/portfolio/products/:id/duplicate |
Duplicar |
| DELETE |
/portfolio/products/:id |
Eliminar |
Variants
| Metodo |
Endpoint |
Descripcion |
| GET |
/portfolio/products/:id/variants |
Listar variantes |
| POST |
/portfolio/products/:id/variants |
Crear variante |
| PATCH |
/portfolio/products/:id/variants/:vid |
Actualizar |
| DELETE |
/portfolio/products/:id/variants/:vid |
Eliminar |
Prices
| Metodo |
Endpoint |
Descripcion |
| GET |
/portfolio/products/:id/prices |
Listar precios |
| POST |
/portfolio/products/:id/prices |
Crear precio |
| PATCH |
/portfolio/products/:id/prices/:pid |
Actualizar |
| DELETE |
/portfolio/products/:id/prices/:pid |
Eliminar |
4. Seguridad
4.1 Row Level Security (RLS)
-- Aislamiento por tenant
CREATE POLICY categories_tenant_isolation ON portfolio.categories
USING (tenant_id = current_setting('app.tenant_id')::uuid);
CREATE POLICY products_tenant_isolation ON portfolio.products
USING (tenant_id = current_setting('app.tenant_id')::uuid);
4.2 Permisos RBAC
| Permiso |
Descripcion |
| portfolio:read |
Ver catalogo |
| portfolio:write |
Crear/editar productos |
| portfolio:delete |
Eliminar productos |
| portfolio:publish |
Publicar/despublicar |
| portfolio:manage |
Configuracion avanzada |
5. Frontend
5.1 Paginas Implementadas
| Ruta |
Componente |
Estado |
| /dashboard/portfolio |
PortfolioPage |
Implementado |
| /dashboard/portfolio/products |
ProductsPage |
Implementado |
| /dashboard/portfolio/categories |
CategoriesPage |
Implementado |
| /dashboard/portfolio/products/:id |
ProductDetailPage |
Pendiente |
| /dashboard/portfolio/products/new |
ProductFormPage |
Pendiente |
5.2 Hooks
// frontend/src/hooks/usePortfolio.ts
useCategories, useCategory, useCategoryTree
useCreateCategory, useUpdateCategory, useDeleteCategory
useProducts, useProduct
useCreateProduct, useUpdateProduct, useDeleteProduct, useDuplicateProduct
useProductVariants, useCreateVariant, useUpdateVariant, useDeleteVariant
useProductPrices, useCreatePrice, useUpdatePrice, useDeletePrice
6. Integraciones
6.1 Modulos Relacionados
| Modulo |
Integracion |
| sales |
Productos en oportunidades |
| commissions |
Comisiones por producto/categoria |
| storage |
Imagenes de productos |
| audit |
Log de cambios |
6.2 Eventos Emitidos
ProductCreated, ProductUpdated, ProductStatusChanged
CategoryCreated, CategoryUpdated
VariantCreated, PriceUpdated
LowStockAlert // Cuando stock < threshold
7. Filtros y Busqueda
7.1 Filtros Disponibles
interface ProductFilters {
category_id?: string;
product_type?: ProductType;
status?: ProductStatus;
is_visible?: boolean;
is_featured?: boolean;
min_price?: number;
max_price?: number;
search?: string; // Busca en name, sku, description
tags?: string[];
sort_by?: 'name' | 'price' | 'created_at' | 'stock';
sort_order?: 'ASC' | 'DESC';
}
8. Referencias
- DDL:
database/ddl/schemas/portfolio/
- Backend:
backend/src/modules/portfolio/
- Frontend Services:
frontend/src/services/portfolio/
- Frontend Hooks:
frontend/src/hooks/usePortfolio.ts
- Frontend Pages:
frontend/src/pages/dashboard/portfolio/