You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
259 lines
7.7 KiB
259 lines
7.7 KiB
|
8 months ago
|
# CPQ Engine - Motor de Configuración de Producto
|
||
|
|
|
||
|
|
Sistema TypeScript type-safe para configuración de productos complejos (Configure-Price-Quote) con soporte i18n, reglas de validación declarativas y renderizado visual condicional.
|
||
|
|
|
||
|
|
## 🎯 Características principales
|
||
|
|
|
||
|
|
- **Type-safe**: Sistema de tipos robusto con IDs con prefijos branded
|
||
|
|
- **i18n nativo**: Soporte multiidioma en todos los elementos del modelo
|
||
|
|
- **Reglas declarativas**: Motor de validación basado en JsonLogic
|
||
|
|
- **Visual rendering**: Generación dinámica de imágenes basada en selecciones
|
||
|
|
- **Arquitectura modular**: Separación clara entre modelo, motor y utilidades
|
||
|
|
- **Testing completo**: >180 tests unitarios con Vitest
|
||
|
|
|
||
|
|
## 📦 Estructura del proyecto
|
||
|
|
|
||
|
|
```
|
||
|
|
src/
|
||
|
|
├── types/ # Definiciones de tipos TypeScript
|
||
|
|
│ ├── core/ # Tipos base (Value, ID, JsonLogic)
|
||
|
|
│ ├── model/ # Tipos del modelo (Attribute, Section, Rule)
|
||
|
|
│ └── i18n/ # Sistema de internacionalización
|
||
|
|
├── engine/ # Motores de evaluación
|
||
|
|
│ ├── json-logic/ # Evaluador JsonLogic
|
||
|
|
│ └── rule-engine.ts # Motor de reglas de validación
|
||
|
|
├── utils/ # Utilidades
|
||
|
|
│ ├── ids/ # Sistema de IDs con prefijos
|
||
|
|
│ └── i18n/ # Funciones de traducción
|
||
|
|
├── messages/ # Sistema de mensajes centralizados
|
||
|
|
│ ├── errors.ts # Mensajes de error
|
||
|
|
│ ├── warnings.ts # Mensajes de advertencia
|
||
|
|
│ └── logger.ts # Logger estructurado
|
||
|
|
└── constants/ # Constantes globales
|
||
|
|
└── defaults.ts # Valores por defecto
|
||
|
|
```
|
||
|
|
|
||
|
|
## 🚀 Inicio rápido
|
||
|
|
|
||
|
|
### Instalación
|
||
|
|
|
||
|
|
```bash
|
||
|
|
npm install
|
||
|
|
```
|
||
|
|
|
||
|
|
### Ejecutar tests
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Todos los tests
|
||
|
|
npm test
|
||
|
|
|
||
|
|
# Con coverage
|
||
|
|
npm test -- --coverage
|
||
|
|
|
||
|
|
# Watch mode
|
||
|
|
npm test -- --watch
|
||
|
|
```
|
||
|
|
|
||
|
|
### Ejemplo básico
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { CATALOGO_VIVIENDAS } from './fixtures/housing-catalog';
|
||
|
|
import { RuleEngine } from './engine/rule-engine';
|
||
|
|
|
||
|
|
// Obtener un objeto configurable
|
||
|
|
const apartamento = CATALOGO_VIVIENDAS.objects['ob:apartamento'];
|
||
|
|
|
||
|
|
// Crear estado de selección
|
||
|
|
const seleccion = {
|
||
|
|
'at:calidad': 'op:calidad_premium',
|
||
|
|
'at:suelo': 'op:suelo_parquet',
|
||
|
|
'at:sanitario': 'op:sanit_negro'
|
||
|
|
};
|
||
|
|
|
||
|
|
// Validar con reglas
|
||
|
|
const engine = new RuleEngine();
|
||
|
|
const reglas = Object.values(CATALOGO_VIVIENDAS.rules);
|
||
|
|
|
||
|
|
const esValido = engine.isValid(reglas, seleccion);
|
||
|
|
const violaciones = engine.getViolations(reglas, seleccion);
|
||
|
|
|
||
|
|
console.log('¿Configuración válida?', esValido);
|
||
|
|
console.log('Violaciones:', violaciones);
|
||
|
|
```
|
||
|
|
|
||
|
|
## 📚 Documentación
|
||
|
|
|
||
|
|
- [**Modelo de datos**](./docs/MODEL.md) - Estructura completa del modelo
|
||
|
|
- [**Sistema de tipos**](./docs/TYPES.md) - Tipos TypeScript y branded IDs
|
||
|
|
- [**Reglas de validación**](./docs/RULES.md) - Cómo crear y usar reglas
|
||
|
|
- [**i18n**](./docs/I18N.md) - Sistema de internacionalización
|
||
|
|
- [**Motor de renderizado**](./docs/RENDERING.md) - Generación de imágenes
|
||
|
|
- [**Guía de creación**](./docs/CATALOG_GUIDE.md) - Cómo crear un catálogo completo
|
||
|
|
|
||
|
|
## 🏗️ Arquitectura
|
||
|
|
|
||
|
|
### Flujo de datos
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────┐
|
||
|
|
│ Catalog JSON │
|
||
|
|
│ (Definición) │
|
||
|
|
└────────┬────────┘
|
||
|
|
│
|
||
|
|
▼
|
||
|
|
┌─────────────────┐ ┌──────────────┐
|
||
|
|
│ Type System │────▶│ Validation │
|
||
|
|
│ (Compile-time) │ │ (Runtime) │
|
||
|
|
└────────┬────────┘ └──────┬───────┘
|
||
|
|
│ │
|
||
|
|
▼ ▼
|
||
|
|
┌─────────────────┐ ┌──────────────┐
|
||
|
|
│ User Selection │────▶│ Rule Engine │
|
||
|
|
│ (State) │ │ (JsonLogic) │
|
||
|
|
└────────┬────────┘ └──────┬───────┘
|
||
|
|
│ │
|
||
|
|
▼ ▼
|
||
|
|
┌─────────────────┐ ┌──────────────┐
|
||
|
|
│ Visual Render │ │ Pricing │
|
||
|
|
│ (Images) │ │ (Calculate) │
|
||
|
|
└─────────────────┘ └──────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
### Capas
|
||
|
|
|
||
|
|
1. **Types Layer** - Definiciones de tipos, contratos
|
||
|
|
2. **Model Layer** - Estructura del catálogo (Objects, Sections, Attributes)
|
||
|
|
3. **Engine Layer** - Lógica de negocio (Rules, JsonLogic, Rendering)
|
||
|
|
4. **Utils Layer** - Funciones auxiliares (IDs, i18n, logging)
|
||
|
|
|
||
|
|
## 🧪 Testing
|
||
|
|
|
||
|
|
El proyecto tiene >180 tests organizados en:
|
||
|
|
|
||
|
|
- **Unit tests** - Utilidades individuales (IDs, i18n, JsonLogic)
|
||
|
|
- **Integration tests** - Motor de reglas completo
|
||
|
|
- **Fixture tests** - Catálogo de viviendas real
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Ver coverage
|
||
|
|
npm test -- --coverage
|
||
|
|
|
||
|
|
# Coverage actual: ~95%
|
||
|
|
```
|
||
|
|
|
||
|
|
## 🌍 i18n
|
||
|
|
|
||
|
|
Sistema de internacionalización con 9 idiomas soportados:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const mensaje: I18nString = {
|
||
|
|
es: 'Hola mundo',
|
||
|
|
en: 'Hello world',
|
||
|
|
de: 'Hallo Welt',
|
||
|
|
fr: 'Bonjour le monde',
|
||
|
|
it: 'Ciao mondo',
|
||
|
|
pt: 'Olá mundo',
|
||
|
|
ca: 'Hola món',
|
||
|
|
eu: 'Kaixo mundua',
|
||
|
|
gl: 'Ola mundo'
|
||
|
|
};
|
||
|
|
|
||
|
|
// Uso
|
||
|
|
const texto = translate(mensaje, 'es'); // → "Hola mundo"
|
||
|
|
```
|
||
|
|
|
||
|
|
**Strings simples** también son válidos:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const simple: I18nString = 'OK'; // Válido en todos los idiomas
|
||
|
|
```
|
||
|
|
|
||
|
|
## 🎨 Ejemplo de modelo real
|
||
|
|
|
||
|
|
El proyecto incluye un **catálogo completo de viviendas** como fixture:
|
||
|
|
|
||
|
|
- 2 modelos: Apartamento (4 secciones) y Dúplex (5 secciones)
|
||
|
|
- 24 opciones de acabados (suelos, paredes, sanitarios, etc.)
|
||
|
|
- 6 reglas de validación con prioridades
|
||
|
|
- Generación de imágenes con templates
|
||
|
|
- Pricing dinámico
|
||
|
|
|
||
|
|
Ver: [`housing-catalog.fixture.ts`](./tests/fixtures/housing-catalog.fixture.ts)
|
||
|
|
|
||
|
|
## 🔧 Tecnologías
|
||
|
|
|
||
|
|
- **TypeScript 5.x** - Lenguaje principal
|
||
|
|
- **Vitest** - Framework de testing
|
||
|
|
- **JsonLogic** - Motor de reglas declarativas
|
||
|
|
- **Branded Types** - IDs type-safe
|
||
|
|
|
||
|
|
## 📝 Convenciones
|
||
|
|
|
||
|
|
### IDs con prefijos
|
||
|
|
|
||
|
|
Todos los IDs usan prefijos branded:
|
||
|
|
|
||
|
|
| Tipo | Prefijo | Ejemplo |
|
||
|
|
|------|---------|---------|
|
||
|
|
| Attribute | `at:` | `at:calidad` |
|
||
|
|
| Section | `sc:` | `sc:salon` |
|
||
|
|
| Option | `op:` | `op:premium` |
|
||
|
|
| Object | `ob:` | `ob:apartamento` |
|
||
|
|
| Rule | `rl:` | `rl:negro_premium` |
|
||
|
|
| View | `vw:` | `vw:front` |
|
||
|
|
| Hotspot | `hs:` | `hs:punto1` |
|
||
|
|
|
||
|
|
### Mensajes
|
||
|
|
|
||
|
|
Todos los mensajes de error/warning/info están **centralizados** en `src/messages/`:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { ERRORS, logger } from '@/messages';
|
||
|
|
|
||
|
|
// ❌ NO hacer
|
||
|
|
throw new Error('Invalid ID format');
|
||
|
|
|
||
|
|
// ✅ Hacer
|
||
|
|
const errorMsg = ERRORS.INVALID_ID_FORMAT(id);
|
||
|
|
logger.error(MESSAGE_CATEGORIES.ID, errorMsg, { id });
|
||
|
|
throw new Error(JSON.stringify(errorMsg));
|
||
|
|
```
|
||
|
|
|
||
|
|
### Naming
|
||
|
|
|
||
|
|
- **Types**: PascalCase (`AttributeDisplay`, `ValidationRule`)
|
||
|
|
- **Interfaces**: PascalCase con prefijo `I` opcional
|
||
|
|
- **Functions**: camelCase (`extractIdPart`, `isValid`)
|
||
|
|
- **Constants**: UPPER_SNAKE_CASE (`DEFAULTS`, `MESSAGE_CATEGORIES`)
|
||
|
|
- **Files**: kebab-case (`rule-engine.ts`, `id-parser.ts`)
|
||
|
|
|
||
|
|
## 🤝 Contribuir
|
||
|
|
|
||
|
|
1. Fork el proyecto
|
||
|
|
2. Crea una rama (`git checkout -b feature/amazing`)
|
||
|
|
3. Commit cambios (`git commit -m 'Add amazing feature'`)
|
||
|
|
4. Push a la rama (`git push origin feature/amazing`)
|
||
|
|
5. Abre un Pull Request
|
||
|
|
|
||
|
|
### Requisitos para PR
|
||
|
|
|
||
|
|
- ✅ Todos los tests pasan
|
||
|
|
- ✅ Coverage >90%
|
||
|
|
- ✅ Sin errores de TypeScript
|
||
|
|
- ✅ Mensajes centralizados (no inline)
|
||
|
|
- ✅ Documentación actualizada
|
||
|
|
|
||
|
|
## 📄 Licencia
|
||
|
|
|
||
|
|
MIT License - ver [LICENSE](LICENSE)
|
||
|
|
|
||
|
|
## 🙏 Agradecimientos
|
||
|
|
|
||
|
|
- [JsonLogic](http://jsonlogic.com/) - Motor de reglas declarativas
|
||
|
|
- [Vitest](https://vitest.dev/) - Framework de testing
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Versión**: 1.0.0
|
||
|
|
**Última actualización**: Febrero 2026
|