- ET-SAAS-018-sales.md: Sales pipeline/CRM module - ET-SAAS-019-portfolio.md: Product catalog module - ET-SAAS-020-commissions.md: Commissions system - ET-SAAS-021-mlm.md: Multi-Level Marketing module - ET-SAAS-022-goals.md: Goals and objectives system - Update _MAP.md with all 9 specifications All specs document: - Data model (DDL, enums, tables) - Backend architecture (endpoints, services) - Security (RLS, RBAC permissions) - Frontend status and hooks - Module integrations Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
319 lines
9.6 KiB
Markdown
319 lines
9.6 KiB
Markdown
---
|
|
id: "ET-SAAS-019"
|
|
title: "Especificacion Tecnica Portfolio (Product Catalog)"
|
|
type: "TechnicalSpec"
|
|
status: "Implemented"
|
|
priority: "P1"
|
|
module: "portfolio"
|
|
version: "1.0.0"
|
|
created_date: "2026-02-03"
|
|
updated_date: "2026-02-03"
|
|
story_points: 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
|
|
|
|
```sql
|
|
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)
|
|
|
|
```sql
|
|
-- 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
ProductCreated, ProductUpdated, ProductStatusChanged
|
|
CategoryCreated, CategoryUpdated
|
|
VariantCreated, PriceUpdated
|
|
LowStockAlert // Cuando stock < threshold
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Filtros y Busqueda
|
|
|
|
### 7.1 Filtros Disponibles
|
|
|
|
```typescript
|
|
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/`
|