⚙️ Metodología de Transformación
Proceso sistemático y riguroso de 8 fases para transformar cada módulo de Dolibarr de PHP tradicional a modelo cliente/servidor PostgreSQL, garantizando la máxima calidad y consistencia en cada paso.
Proceso Sistematizado
8 fases claramente definidas y documentadas para cada módulo
Testing Riguroso
Tests comparativos que garantizan paridad funcional 100%
Iterativo y Reproducible
Cada módulo sigue exactamente el mismo proceso probado
🔄 Proceso de Migración de un Módulo
Metodología sistemática desarrollada para transformar cada módulo de Dolibarr del modelo tradicional PHP al modelo cliente/servidor PostgreSQL.
Análisis y Preparación
🔍 Identificación de Componentes
- Mapear todas las tablas del módulo
- Identificar relaciones y dependencias
- Analizar métodos PHP existentes (create, update, validate)
- Documentar reglas de negocio actuales
- Verificar tablas de relación esperadas por el código
Comando de análisis:
# Buscar todas las referencias a tablas en el código PHP
grep -r "llx_tabla" htdocs/modulo/ | grep -E "(DELETE|INSERT|UPDATE)"
# Verificar estructura en PostgreSQL
\d llx_tabla*
Tests de Estructura
🏗️ Validación de Infraestructura
CRÍTICO: Antes de escribir cualquier función, verificar que todas las tablas y campos existen.
Test de estructura ejemplo:
-- test-modulo-estructura.sql
BEGIN;
SELECT plan(15);
-- Verificar existencia de tablas
SELECT has_table('llx_tabla_principal', 'Tabla principal debe existir');
SELECT has_table('llx_tabla_det', 'Tabla de detalle debe existir');
-- Verificar campos obligatorios
SELECT has_column('llx_tabla', 'rowid', 'Campo rowid debe existir');
SELECT has_column('llx_tabla', 'entity', 'Campo entity debe existir');
-- Verificar tipos de datos
SELECT col_type_is('llx_tabla', 'amount', 'double precision', 'amount debe ser double precision');
-- Verificar constraints
SELECT col_not_null('llx_tabla', 'fk_user_author', 'fk_user_author debe ser NOT NULL');
SELECT * FROM finish();
ROLLBACK;
Implementación PostgreSQL
⚙️ Desarrollo de Funciones y Triggers
- Crear funciones de validación específicas
- Implementar triggers BEFORE INSERT/UPDATE
- Desarrollar funciones de cálculo automático
- Establecer generación de códigos únicos
- Configurar auditoría y logging
Patrón estándar de función:
CREATE OR REPLACE FUNCTION llx_tabla_before_insert()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
-- 1. Validaciones obligatorias
IF NEW.campo_obligatorio IS NULL OR trim(NEW.campo_obligatorio) = '' THEN
RAISE EXCEPTION 'Campo obligatorio no puede estar vacío';
END IF;
-- 2. Limpieza de datos
NEW.campo_texto = trim(NEW.campo_texto);
-- 3. Valores por defecto
NEW.entity = COALESCE(NEW.entity, 1);
NEW.datec = COALESCE(NEW.datec, NOW());
-- 4. Generación automática de códigos
IF NEW.ref IS NULL OR NEW.ref = '' THEN
NEW.ref := llx_tabla_get_next_ref();
END IF;
RETURN NEW;
END;
$$;
Tests Unitarios pgTAP
🧪 Validación Funcional Completa
- Tests de existencia de funciones y triggers
- Tests de validaciones (casos exitosos y fallidos)
- Tests de cálculos automáticos
- Tests de generación de códigos
- Tests de casos extremos y errores
📋 Categorías de Testing
- Estructura: Existencia de funciones/triggers
- Validaciones: Campos obligatorios, formatos
- Cálculos: Totales, precios, descuentos
- Códigos: Generación automática y unicidad
- Estados: Transiciones válidas e inválidas
- Relaciones: Integridad referencial
Modificación PHP
🔧 Simplificación de Código
Objetivo: Convertir PHP en un cliente tonto que solo ejecuta SQL.
❌ Antes: PHP con Lógica
public function verify() {
if (empty($this->nom)) {
$this->errors[] = 'Nom obligatoire';
return -1;
}
if (!empty($this->email) && !filter_var($this->email, FILTER_VALIDATE_EMAIL)) {
$this->errors[] = 'Email invalide';
return -1;
}
// Más validaciones...
return 0;
}
✅ Después: PHP Simplificado
public function verify() {
return 0; // Los triggers PostgreSQL manejan TODA la validación
}
🗑️ Elementos a ELIMINAR del PHP:
- Métodos verify(), validate(), check()
- Generación de códigos y referencias
- Cálculos de totales y subtotales
- Validaciones de formato y obligatoriedad
- Condicionales de compatibilidad MySQL
- Lógica de estado y transiciones
Tests Comparativos
⚖️ Validación de Paridad Funcional
Proceso riguroso para garantizar que el comportamiento PostgreSQL es idéntico al PHP original.
1. Captura PHP
Ejecutar operaciones en base sin triggers
2. Ejecución PostgreSQL
Mismas operaciones con triggers activos
3. Comparación
Validar resultados idénticos
Script de test comparativo:
-- Tabla temporal para resultados
CREATE TEMP TABLE test_results (
test_name varchar(100),
php_result text,
pgsql_result text,
match boolean
);
-- Test comparativo ejemplo
DO $$
DECLARE
v_error text;
BEGIN
-- Intentar inserción inválida en PostgreSQL
BEGIN
INSERT INTO llx_tabla (campo_obligatorio) VALUES ('');
PERFORM fail('Debería haber fallado');
EXCEPTION WHEN OTHERS THEN
GET STACKED DIAGNOSTICS v_error = MESSAGE_TEXT;
INSERT INTO test_results VALUES
('campo_vacio', 'ErrorFieldRequired', v_error, v_error LIKE '%obligatorio%');
END;
END $$;
-- Mostrar resultados
SELECT * FROM test_results WHERE NOT match;
Documentación
📚 Registro Completo del Proceso
- MIGRATION_PLAN.md: Plan detallado pre-implementación
- MIGRATION_LOG.md: Registro cronológico de cambios
- functions-modulo.sql: Código PostgreSQL comentado
- test-modulo.sql: Tests pgTAP completos
- Capturas de pantalla: Evidencia de tests pasando
📁 Estructura de Documentación:
htdocs/modulo/
├── MIGRATION_PLAN.md # Plan de migración
├── MIGRATION_LOG.md # Log de cambios
├── class/modulo.class.php # PHP simplificado
└── class/modulo.class.php.orig # Backup original
htdocs/install/pgsql/
├── functions/
│ └── functions-modulo.sql # Funciones PostgreSQL
└── tests/
├── test-modulo.sql # Tests funcionales
└── test-modulo-estructura.sql # Tests estructura
🧪 Estrategia de Testing
Pirámide de Testing Implementada
Tests Comparativos
Validación de paridad PHP ↔ PostgreSQL
68 testsTests Unitarios pgTAP
Funciones, triggers y validaciones
1,215 testsTests de Estructura
Tablas, campos y constraints
56 tests🔧 Herramientas de Testing
pgTAP
Framework de testing unitario para PostgreSQL
- Tests de existencia de funciones
- Validación de comportamiento
- Verificación de excepciones
- Tests de tipos de datos
Tests Comparativos
Scripts PHP para capturar comportamiento original
- Ejecución controlada de operaciones
- Captura de errores y excepciones
- Comparación automatizada
- Reporting de discrepancias
Bases de Datos de Test
Entorno aislado para validación
- dolibarr_test_php (sin triggers)
- dolibarr_test_postgresql (con triggers)
- Sincronización automatizada
- Datos de prueba controlados
✅ Checklist de Migración
Lista de verificación obligatoria para garantizar que cada módulo migrado cumple todos los estándares de calidad.
🔍 Preparación
🏗️ Tests de Estructura
⚙️ Implementación PostgreSQL
🧪 Testing
🔧 Modificación PHP
⚖️ Tests Comparativos
📚 Documentación
📋 Templates y Patrones
📄 MIGRATION_PLAN.md
Template estándar para planificar la migración de cada módulo
# MIGRATION_PLAN - Módulo X
## Análisis Inicial
- Tablas identificadas: X
- Relaciones: Y
- Reglas de negocio: Z
## Implementación PostgreSQL
- [ ] Función validación
- [ ] Triggers automáticos
- [ ] Generación códigos
## Testing
- [ ] Tests estructura
- [ ] Tests pgTAP
- [ ] Tests comparativos
## Modificación PHP
- [ ] Simplificar verify()
- [ ] Eliminar cálculos
- [ ] Quitar validaciones
🔧 Función PostgreSQL
Patrón estándar para funciones de validación
CREATE OR REPLACE FUNCTION llx_tabla_before_insert()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
-- Validaciones obligatorias
IF NEW.campo IS NULL OR trim(NEW.campo) = '' THEN
RAISE EXCEPTION 'ErrorFieldRequired: campo';
END IF;
-- Limpieza de datos
NEW.campo_texto = trim(NEW.campo_texto);
-- Valores por defecto
NEW.entity = COALESCE(NEW.entity, 1);
NEW.datec = COALESCE(NEW.datec, NOW());
RETURN NEW;
END;
$$;
🧪 Test pgTAP
Estructura estándar para tests unitarios
BEGIN;
SELECT plan(X);
-- Tests de existencia
SELECT has_function('llx_tabla_validate');
SELECT has_trigger('llx_tabla', 'trigger_name');
-- Tests de validación
PREPARE test_invalid AS
INSERT INTO llx_tabla (campo) VALUES ('');
SELECT throws_ok(
'test_invalid',
'P0001',
'ErrorFieldRequired',
'Debe fallar con campo vacío'
);
SELECT * FROM finish();
ROLLBACK;