βοΈ Transformation Methodology
Systematic and rigorous 8-phase process to transform each Dolibarr module from traditional PHP to PostgreSQL client/server model, ensuring maximum quality and consistency at every step.
Systematized Process
8 clearly defined and documented phases for each module
Rigorous Testing
Comparative tests that guarantee 100% functional parity
Iterative and Reproducible
Each module follows exactly the same proven process
π Module Migration Process
Systematic methodology developed to transform each Dolibarr module from traditional PHP model to PostgreSQL client/server model.
Analysis and Preparation
π Component Identification
- Map all module tables
- Identify relationships and dependencies
- Analyze existing PHP methods (create, update, validate)
- Document current business rules
- Verify relationship tables expected by code
Analysis command:
# Search all table references in PHP code
grep -r "llx_tabla" htdocs/modulo/ | grep -E "(DELETE|INSERT|UPDATE)"
# Verify structure in PostgreSQL
\d llx_tabla*
Tests de Estructura
ποΈ Infrastructure Validation
CRITICAL: Before writing any function, verify that all tables and fields exist.
Structure test example:
-- test-modulo-estructura.sql
BEGIN;
SELECT plan(15);
-- Verify table existence
SELECT has_table('llx_tabla_principal', 'Main table must exist');
SELECT has_table('llx_tabla_det', 'Detail table must exist');
-- Verify required fields
SELECT has_column('llx_tabla', 'rowid', 'rowid field must exist');
SELECT has_column('llx_tabla', 'entity', 'entity field must exist');
-- Verify data types
SELECT col_type_is('llx_tabla', 'amount', 'double precision', 'amount must be double precision');
-- Verify constraints
SELECT col_not_null('llx_tabla', 'fk_user_author', 'fk_user_author must be NOT NULL');
SELECT * FROM finish();
ROLLBACK;
ImplementaciΓ³n PostgreSQL
βοΈ Development of Functions and Triggers
- Create specific validation functions
- Implement BEFORE INSERT/UPDATE triggers
- Develop automatic calculation functions
- Establish unique code generation
- Configure audit and logging
Standard function pattern:
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 'Required field cannot be empty';
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. Automatic code generation
IF NEW.ref IS NULL OR NEW.ref = '' THEN
NEW.ref := llx_tabla_get_next_ref();
END IF;
RETURN NEW;
END;
$$;
Unit Tests pgTAP
π§ͺ Complete Functional Validation
- Tests de existencia de functions y triggers
- Tests de validaciones (casos exitosos y fallidos)
- Automatic calculation tests
- Code generation tests
- Tests de casos extremos y errores
π Testing Categories
- Structure: Existence of functions/triggers
- Validations: Required fields, formats
- Calculations: Totals, prices, discounts
- Codes: Automatic generation and uniqueness
- States: Valid and invalid transitions
- Relations: Referential integrity
PHP Modification
π§ Code Simplification
Objective: Convert PHP into a thin client that only executes SQL.
β Before: PHP with Logic
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;
}
// More validations...
return 0;
}
β After: Simplified PHP
public function verify() {
return 0; // PostgreSQL triggers handle ALL validation
}
ποΈ Elements to REMOVE from PHP:
- Methods verify(), validate(), check()
- Code and reference generation
- Total and subtotal calculations
- Format and required field validations
- MySQL compatibility conditionals
- State logic and transitions
Comparative Tests
βοΈ Functional Parity Validation
Rigorous process to ensure PostgreSQL behavior is identical to original PHP.
1. PHP Capture
Execute operations in database without triggers
2. PostgreSQL Execution
Same operations with active triggers
3. Comparison
Validate identical results
Comparative test script:
-- Temporary table for results
CREATE TEMP TABLE test_results (
test_name varchar(100),
php_result text,
pgsql_result text,
match boolean
);
-- Example comparative test
DO $$
DECLARE
v_error text;
BEGIN
-- Attempt invalid insertion in PostgreSQL
BEGIN
INSERT INTO llx_tabla (campo_obligatorio) VALUES ('');
PERFORM fail('Should have failed');
EXCEPTION WHEN OTHERS THEN
GET STACKED DIAGNOSTICS v_error = MESSAGE_TEXT;
INSERT INTO test_results VALUES
('empty_field', 'ErrorFieldRequired', v_error, v_error LIKE '%required%');
END;
END $$;
-- Show results
SELECT * FROM test_results WHERE NOT match;
Documentation
π Complete Process Record
- MIGRATION_PLAN.md: Detailed pre-implementation plan
- MIGRATION_LOG.md: Chronological change log
- functions-module.sql: Commented PostgreSQL code
- test-module.sql: Complete pgTAP tests
- Screenshots: Evidence of passing tests
π Documentation Structure:
htdocs/modulo/
βββ MIGRATION_PLAN.md # Migration plan
βββ MIGRATION_LOG.md # Change log
βββ class/modulo.class.php # Simplified PHP
βββ class/modulo.class.php.orig # Backup original
htdocs/install/pgsql/
βββ functions/
β βββ functions-modulo.sql # PostgreSQL Functions
βββ tests/
βββ test-modulo.sql # Functional tests
βββ test-modulo-estructura.sql # Structure tests
π§ͺ Testing Strategy
Implemented Testing Pyramid
Comparative Tests
PHP β PostgreSQL parity validation
68 testsUnit Tests pgTAP
Functions, triggers y validaciones
1,477 testsTests de Estructura
Tablas, campos y constraints
56 testsπ§ Herramientas de Testing
pgTAP
Framework de testing unitario para PostgreSQL
- Tests de existencia de functions
- Behavior validation
- Exception verification
- Tests de tipos de datos
Comparative Tests
PHP scripts to capture original behavior
- Controlled execution of operations
- Error and exception capture
- Automated comparison
- Discrepancy reporting
Test Databases
Isolated environment for validation
- dolibarr_test_php (without triggers)
- dolibarr_test_postgresql (with triggers)
- Automated synchronization
- Controlled test data
β Migration Checklist
Mandatory checklist to ensure each migrated module meets all quality standards.
π Preparation
ποΈ Structure Tests
βοΈ PostgreSQL Implementation
π§ͺ Testing
π§ PHP Modification
βοΈ Comparative Tests
π Documentation
π Templates and Patterns
π MIGRATION_PLAN.md
Standard template for planning each module migration
# MIGRATION_PLAN - Module X
## Initial Analysis
- Identified tables: X
- Relations: Y
- Business rules: Z
## PostgreSQL Implementation
- [ ] Validation function
- [ ] Automatic triggers
- [ ] Code generation
## Testing
- [ ] Structure tests
- [ ] pgTAP tests
- [ ] Comparative tests
## PHP Modification
- [ ] Simplify verify()
- [ ] Remove calculations
- [ ] Remove validations
π§ PostgreSQL Function
Standard pattern for validation functions
CREATE OR REPLACE FUNCTION llx_tabla_before_insert()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
-- Required validations
IF NEW.campo IS NULL OR trim(NEW.campo) = '' THEN
RAISE EXCEPTION 'ErrorFieldRequired: campo';
END IF;
-- Data cleanup
NEW.campo_texto = trim(NEW.campo_texto);
-- Default values
NEW.entity = COALESCE(NEW.entity, 1);
NEW.datec = COALESCE(NEW.datec, NOW());
RETURN NEW;
END;
$$;
π§ͺ Test pgTAP
Standard structure for unit tests
BEGIN;
SELECT plan(X);
-- Existence tests
SELECT has_function('llx_tabla_validate');
SELECT has_trigger('llx_tabla', 'trigger_name');
-- Validation tests
PREPARE test_invalid AS
INSERT INTO llx_tabla (campo) VALUES ('');
SELECT throws_ok(
'test_invalid',
'P0001',
'ErrorFieldRequired',
'Must fail with empty field'
);
SELECT * FROM finish();
ROLLBACK;