Módulo Products - Documentación
Módulo Products - Documentación
📋 Descripción General
El módulo Products es el componente central de Master Data en Asterisk Suite. Gestiona:
- Productos base con propiedades generales (SKU, tipo, estado)
- Variantes de productos (diferentes presentaciones/configuraciones)
- Composición de productos (relaciones padre-hijo entre productos)
- Costos (múltiples estrategias de cálculo)
- Ingeniería/Estructura (árbol de componentes técnicos)
- Categorías, Tags y Atributos asociados
- Precios por moneda/variante
- Impuestos aplicables
🏗️ Estructura del Módulo
src/modules/master-data/products/
├── products.module.ts # Módulo principal
├── products.service.ts # Lógica de negocio base
├── products.controller.ts # Endpoints REST
├── dto/
│ ├── create-product.dto.ts
│ └── update-product.dto.ts
├── product-components/ # Relaciones entre productos
│ ├── product-components.module.ts
│ ├── product-components.service.ts
│ └── product-components.controller.ts
├── engineering/ # Árbol técnico de componentes
│ ├── engineering.module.ts
│ ├── engineering.service.ts
│ ├── engineering.controller.ts
│ ├── engineering-tree.service.ts
│ ├── engineering-calculation.service.ts
│ ├── engineering-validation.service.ts
│ ├── product-structure-version.service.ts
│ └── ...
├── costing/ # Cálculo de costos
│ ├── costing.module.ts
│ ├── costing.service.ts
│ ├── costing.controller.ts
│ ├── costing-calculator.service.ts
│ ├── costing-tree.service.ts
│ ├── costing-history.service.ts
│ ├── strategies/
│ │ ├── bom-cost.strategy.ts
│ │ ├── engineering-cost.strategy.ts
│ │ ├── manual-cost.strategy.ts
│ │ ├── purchase-cost.strategy.ts
│ │ └── rate-cost.strategy.ts
│ └── ...
├── variant-costs/ # Costos por variante
│ ├── variant-costs.module.ts
│ ├── variant-costs.service.ts
│ └── ...
├── product-variants/ # Variantes del producto
│ ├── product-variants.module.ts
│ ├── product-variants.service.ts
│ └── ...
├── product-categories/ # Categorización
│ ├── product-categories.module.ts
│ ├── product-categories.service.ts
│ └── ...
├── product-tags/ # Tags/Etiquetas
│ ├── product-tags.module.ts
│ ├── product-tags.service.ts
│ └── ...
└── product-attribute-values/ # Atributos personalizados
├── product-attribute-values.module.ts
├── product-attribute-values.service.ts
└── ...
📊 Entidades Principales
Producto (Products)
Propiedades Clave:
id- UUID úniconame- Nombre del productosku- Stock Keeping Unit (único, validado en BD)product_type- ENUM: MATERIAL, FINISHED_GOOD, SERVICE, KIT, etc.is_composed- Indica si es un producto compuesto (tiene componentes)has_engineering- Si tiene estructura técnica definidamanages_stock- Si gestiona inventarioauto_calculate_cost- Calcula costos automáticamenteprice_enabled- Si tiene precios definidosrequires_refrigeration- Control especial de temperaturais_rate_type- Si es una tarifaactive- Estado activo/inactivodeleted_at- Soft delete timestamp
Relaciones:
product_price- Precios por monedaproduct_costs- Costos calculadosproduct_variants- Variantesproduct_categories- Categorías asignadasproduct_tags- Tags asignadosproduct_attribute_values- Valores de atributosproduct_taxes- Impuestos aplicablesparent_components- Productos que tienen este como hijochild_components- Productos que tiene como hijos (BOM)income_account,expense_account,inventory_account- Cuentas contables
🔗 Relaciones Clave
Estructura BOM (Bill of Materials)
Un producto puede estar compuesto por otros productos usando product_components:
Parent Product
├── Child Product 1 (qty: 2, unit: pcs)
├── Child Product 2 (qty: 0.5, unit: m)
└── Child Product 3 (qty: 1, unit: pcs)
Campos de Relación:
parent_product_id- Producto padrechild_product_id- Producto hijochild_variant_id- Variante específica del hijo (opcional)quantity- Cantidad requeridaunit_id- Unidad de medidalength_mm,width_mm,height_mm- Dimensiones para cálculowaste_percentage- % de desperdicioorder- Orden de visualización
Validación: Se previene creación de referencias circulares.
📡 Endpoints
Productos Base
POST /master-data/products # Crear producto
GET /master-data/products # Listar todos (con relaciones incluidas)
GET /master-data/products/:id # Obtener uno (detalles completos)
GET /master-data/products/:id/root-products # Obtener productos raíz
PATCH /master-data/products/:id # Actualizar
DELETE /master-data/products/:id # Eliminar (soft delete)
Componentes de Producto (BOM)
POST /master-data/products/:id/components # Agregar componente
GET /master-data/products/:id/components # Listar componentes
PATCH /master-data/products/components/:id # Actualizar
DELETE /master-data/products/components/:id # Eliminar
Ingeniería (Engineering Tree)
GET /master-data/products/:id/engineering # Obtener árbol técnico
GET /master-data/products/:id/engineering/tree # Árbol con cálculos
POST /master-data/products/:id/engineering # Crear componente técnico
PATCH /master-data/products/:id/engineering/:compId # Actualizar
POST /master-data/products/:id/engineering/versions # Crear versión
Costos (Costing)
GET /master-data/products/:id/costing # Obtener costos
GET /master-data/products/:id/costing/tree # Árbol de costos
GET /master-data/products/:id/costing/breakdown # Desglose de costos
GET /master-data/products/:id/costing/history # Historial
POST /master-data/products/:id/costing/calculate # Recalcular
POST /master-data/products/:id/costing/templates # Crear plantilla
Variantes
POST /master-data/products/:id/variants # Crear variante
GET /master-data/products/:id/variants # Listar variantes
PATCH /master-data/products/variants/:variantId # Actualizar
DELETE /master-data/products/variants/:variantId # Eliminar
Categorías
POST /master-data/products/:id/categories # Asignar categoría
DELETE /master-data/products/:id/categories/:catId # Remover categoría
Tags
POST /master-data/products/:id/tags # Asignar tag
DELETE /master-data/products/:id/tags/:tagId # Remover tag
🧮 Estrategias de Cálculo de Costos
El módulo costing soporta múltiples estrategias:
1. Manual Cost Strategy (ManualCostStrategy)
- Costos ingresados manualmente por el usuario
- Usado para productos sin componentes o cuando no aplica otro cálculo
2. Purchase Cost Strategy (PurchaseCostStrategy)
- Basado en costos de compra históricos
- Típico para materias primas, productos comprados
3. BOM Cost Strategy (BomCostStrategy)
- Suma de costos de componentes más overhead
- Para productos compuestos (kits, ensambles)
- Fórmula:
Σ(child_cost × qty) × (1 + overhead%)
4. Engineering Cost Strategy (EngineeringCostStrategy)
- Basado en especificaciones técnicas (dimensiones, materiales)
- Usado para productos con estructura técnica definida
- Calcula consumo de material según propiedades de componentes
5. Rate Cost Strategy (RateCostStrategy)
- Basado en tarifas (servicios, mano de obra)
- Multiplica cantidad × tarifa
Selection Logic:
if (product.cost_source === ProductCostSource.MANUAL) {
return manualStrategy.calculate()
} else if (product.is_composed) {
return bomStrategy.calculate()
} else if (product.has_engineering) {
return engineeringStrategy.calculate()
} else if (product.is_rate_type) {
return rateStrategy.calculate()
} else {
return purchaseStrategy.calculate()
}
🌳 Árbol de Ingeniería (Engineering)
Estructura técnica de componentes para productos complejos.
Características:
- Árbol jerárquico de componentes
- Cálculo de dimensiones totales
- Validación de especificaciones
- Versionado de estructuras
- Cálculo automático de cantidad/desperdicio
Servicios:
EngineeringService- Operaciones CRUDEngineeringTreeService- Construcción y recorrido del árbolEngineeringCalculationService- Cálculos dimensionalEngineeringValidationService- ValidacionesProductStructureVersionService- Control de versiones
💾 Flujos Principales
1. Crear un Producto Simple
const product = await productService.create({
name: "Tornillo M8",
sku: "SCREW-M8-100",
product_type: ProductType.MATERIAL,
manages_stock: true,
active: true
})
Validaciones:
- SKU único en BD (excluye soft-deleted)
- Campos requeridos validados por DTO
2. Crear Producto Compuesto (BOM)
// 1. Crear producto padre
const parent = await productService.create({
name: "Kit Ensamble",
sku: "KIT-ASSEMBLY-001",
is_composed: true,
auto_calculate_cost: true,
product_type: ProductType.KIT
})
// 2. Agregar componentes
await componentService.create({
parent_product_id: parent.id,
child_product_id: "child-id-1",
quantity: 2,
unit_id: "unit-pcs",
waste_percentage: 5,
order: 1
})
Validaciones Automáticas:
- ✅ No se permite referencia circular (recursivo)
- ✅ Ambos productos deben existir y no estar deleted
- ✅ Máximo nivel de profundidad (si aplica)
3. Obtener Producto con Detalles Completos
const fullProduct = await productService.findOne(productId)
// Retorna:
{
id, name, sku, active, ...
product_price: [...], // Precios
product_costs: [...], // Costos
product_variants: [...], // Variantes con atributos
product_categories: [...], // Categorías
product_tags: [...], // Tags
product_attribute_values: [...], // Atributos
parent_components: [...], // Como padre
child_components: [...], // Como hijo
root_products: [...] // Productos raíz (si es componente)
}
4. Obtener Productos Raíz
Si un producto es componente de otros, obtiene el/los "raíces" (top-level parents).
const roots = await productService.getRootProducts(productId)
// Algoritmo:
// - Si el producto no tiene padres → es raíz, retorna [self]
// - Si tiene padres → recursivamente busca raíces de cada padre
// - Evita loops con Set de visitados
5. Calcular Costo de Producto
const calculatedCost = await costingService.calculateCost(
productId,
{
includeHistory: true,
includeBreakdown: true,
currency: "USD"
}
)
// Retorna:
{
productId,
totalCost: 150.50,
currency: "USD",
breakdown: [
{ componentId, componentName, quantity, unitCost, totalCost },
...
],
history: [...], // Cálculos anteriores
strategyUsed: "BOM_COST"
}
🔍 Particularidades Importantes
Soft Delete
- Los productos no se eliminan sino que se marcan con
deleted_at - Las queries excluyen registros con
deleted_at != null - Permite auditoría y recuperación
Validación de SKU Única
// SKU solo es único entre productos no eliminados
const existing = await prisma.products.findFirst({
where: { sku, deleted_at: null }
})
Inclusión de Relaciones
El findOne() incluye muchas relaciones por defecto:
- Variantes con sus atributos y precios
- Costos con moneda
- Componentes (padre-hijo) con detalles
- Categorías, tags, impuestos, etc.
⚠️ Nota: Esto puede ser lento en BDs grandes. Considerar lazy-loading o queries selectivas.
Root Products
Método recursivo que encuentra el/los producto(s) "raíz" (sin padres):
- Previene loops infinitos con
Set<string> - Útil para reportes, trazabilidad completa
🗂️ DTOs Principales
CreateProductDto
{
name: string (required)
sku?: string
requires_refrigeration?: boolean
price_enabled?: boolean
is_rate_type?: boolean
rate_id?: UUID
taxId?: UUID
active?: boolean
product_type?: ProductType enum
is_composed?: boolean
auto_calculate_cost?: boolean
has_engineering?: boolean
manages_stock?: boolean
income_account_id?: UUID
expense_account_id?: UUID
inventory_account_id?: UUID
calculation_type?: CalculationType enum
cost_source?: ProductCostSource enum
}
UpdateProductDto
Similar a Create, pero todos los campos son opcionales.
🔐 Seguridad
- Autenticación: JWT Guard en todos los endpoints
- Autorización: A definir según roles/permisos (pendiente)
📈 Consideraciones de Performance
- N+1 Queries: El
findOne()hace una query compleja. Para listados, considerar paginación. - Soft Deletes: Las queries siempre filtran
deleted_at: null. Asegurar índices. - Relaciones Profundas: Engineering y Costing pueden tener árboles profundos. Considerar limites.
- Cálculos Recursivos:
getRootProducts()es O(n) en profundidad del árbol.
🚀 Ejemplos de Uso
Crear Producto Terminado Compuesto
// POST /master-data/products
{
"name": "Kit Herramientas Completo",
"sku": "KIT-TOOLS-PRO",
"product_type": "KIT",
"is_composed": true,
"auto_calculate_cost": true,
"manages_stock": false, // Kit, no se gestiona stock del padre
"active": true
}
Agregar Componentes
// POST /master-data/products/:id/components
[
{
"child_product_id": "destornillador-id",
"quantity": 1,
"unit_id": "unit-pcs",
"order": 1
},
{
"child_product_id": "cinta-aislante-id",
"quantity": 2,
"unit_id": "unit-roll",
"waste_percentage": 10,
"order": 2
}
]
Obtener Producto Completo
// GET /master-data/products/kit-id
→ Retorna producto con todas las variantes, precios, componentes, costos, etc.
📚 Referencias
- Prisma Models:
/prisma/schema.prisma(buscarmodel products,model product_components, etc.) - Enums:
/generated/prisma/enums(ProductType, CalculationType, ProductCostSource, etc.) - Testing:
/test/products.*.spec.ts(si existen)
Última Actualización: 2024 Versión: 1.0