⚙️ 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.

1

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*
2

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;
3

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;
$$;
4

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
5

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
6

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;
7

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 tests

Tests Unitarios pgTAP

Funciones, triggers y validaciones

1,215 tests

Tests 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;