Version : 2025 – destiné aux administrateurs système, aux développeurs et aux chefs de projet qui utilisent Dolibarr en production.
1. Pourquoi un ETL sur Dolibarr ?
| Besoin | Solution ETL | Bénéfice attendu |
|---|---|---|
| Exploitation des données | Extraction des tables Banque, Facture, Produit, Client, etc. | Rapports historisés, analyses de ventes, tableaux de bord BI |
| Migration ou synchronisation | Consolidation vers un entrepôt de données (Snowflake, PostgreSQL, MySQL, etc.) | Uniformisation des sources, réduction de la duplication |
| Archivage et conformité | Copie des enregistrements dans un bucket S3/Blob | Conformité RGPD, rétention légale |
| Intégration avec des outils tiers | Chargement dans un data‑lake ou un moteur de recherche (Elasticsearch) | Recherche full‑text, scoring de leads, etc. |
Le défi principal : ajouter ce processus sans impacter la disponibilité et la performance du ERP de production.
2. Principes de conception « sans casser l’existant »
| Principe | Description | Implémentation concrète |
|---|---|---|
| Isolation | Le flux ETL doit être décorrélé du core Dolibarr. | Utiliser des schémas de tables en lecture seule (read‑only view) ou des réplicas de la base. |
| Idempotence | Rejouer le même job ne doit pas créer de doublons. | Design de la charge incrémentale (timestamp updated_at, last_update). |
| Non‑bloquant | Pas de lock durable sur les tables de production. | Scans de petites sections, utilisation de READ UNCOMMITTED ou de snapshots (MySQL 8+). |
| Retry & Resilience | Gérer les pannes réseau ou serveur sans perte de données. | Bibliothèques retry (ex. tenacity en Python), logs détaillés. |
| Observabilité | Suivi temps réel des volumes, erreurs, latence. | Métriques Prometheus + Grafana, alertes Slack/Email. |
| Versionning & Tests | Tous les scripts sont versionnés (Git) et testés en environnement sandbox. | CI/CD (GitHub Actions, GitLab CI) avec jeux de données de test. |
3. Architecture proposée (schéma simplifié)
+-------------------+ +-------------------+ +---------------------+
| Dolibarr (Prod) | ---> | Extraction | ---> | Staging Area |
| (MySQL/PSQL) | | (logical dump) | | (Parquet/CSV) |
+-------------------+ +-------------------+ +---------------------+
|
v
+--------------------+
| Transformation |
| (SQL / Python) |
+--------------------+
|
v
+--------------------+
| Chargement final |
| (Data Warehouse |
| / Data Lake) |
+--------------------+
Points clés
- Extraction → utilisation d’un snapshot (ex.
mysqldump --single-transaction) ou d’un reader via les API REST de Dolibarr (si le module d’API est activé). - Staging → stockage temporaire dans un répertoire
/etl/staging/ou dans un bucket S3. Les fichiers sont immutables (namingyyyyMMdd_HHmmss_<table>.parquet). - Transformation → scripts Python (pandas/SQLAlchemy) ou SQL stored procedures dans le DW.
- Chargement →
COPYvers Snowflake/Redshift,INSERTbulk vers PostgreSQL, ouINSERT INTO … SELECT …vers un entrepôt analytique. —
4. Étapes détaillées pour un premier pilote (exemple: sauvegarde des Factures)
Objectif : Extraire les factures created/updated depuis les 24 h précédentes, les transformer en format parquet et les charger dans un entrepôt PostgreSQL
dwh.billing.
4.1. Pré‑requis
| Élément | Version / Note |
|---|---|
| Dolibarr | 22.x ou supérieur (API disponible) |
| DBMS | MySQL 8 ou PostgreSQL 13 |
| Outils Python | pandas, sqlalchemy, psycopg2-binary, pymysql, pyarrow |
| Croissance | Crontab ou Scheduler (Airflow, Prefect) |
| Accès réseau | IP autorisée du serveur ETL vers le serveur DB cible |
4.2. Extraction (read‑only)
-- 1. Créez une vue READ‑ONLY (MySQL example)
CREATE OR REPLACE VIEW v_facture_incr AS
SELECT
f.rowid,
f.facnumero,
f.fadate,
f.total_ht,
f.status,
f.date_modif -- champ datetime « updated_at »
FROM llx_facture f
WHERE f.date_modif >= NOW() - INTERVAL 1 DAY;
# extract_factures.py
import pandas as pd
from sqlalchemy import create_engine
# connexion read‑only (MySQL)
engine_src = create_engine(
"mysql+pymysql://etl_user:secure_pwd@db-prod.dolibarr.local:3306/dolibarr",
isolation_level="READ UNCOMMITTED"
)
sql = """
SELECT rowid, facnumero, fadate, total_ht, status, date_modifFROM v_facture_incr
WHERE date_modif >= %(start)s
"""
df = pd.read_sql(
sql,
con=engine_src,
params={"start": (pd.Timestamp.utcnow() - pd.Timedelta(days=1)).strftime("%Y-%m-%d %H:%M:%S")}
)
df.to_parquet("/etl/staging/factures_{{ts}}.parquet", index=False)
Pas de lock durable sur la table llx_facture grâce à READ UNCOMMITTED et à la fenêtre temporelle très courte.
4.3. Transformation
# transform_factures.py
import pyarrow.parquet as pq
import pandas as pd
def clean(df: pd.DataFrame) -> pd.DataFrame:
# Normaliser les types et supprimer les doublons éventuels
df["total_ht"] = df["total_ht"].astype(float)
df["status"] = df["status"].astype("category")
df = df.drop_duplicates(subset=["rowid"])
return df
raw = pd.read_parquet("/etl/staging/factures_*.parquet")
clean_df = clean(raw)
# Ajout d’une clé de chargement et d’une partition par date
clean_df["etl_ts"] = pd.Timestamp.utcnow()
clean_df["load_date"] = clean_df["etl_ts"].dt.date
clean_df["year_month"] = clean_df["etl_ts"].dt.to_period("M")
clean_df.to_parquet(
f"/etl/staging/factures_clean_{clean_df['load_date'].iloc[0]}.parquet",
index=False,
compression="snappy"
)
4.4. Chargement (bulk load) dans le DW « `sql
— 4. Chargement dans PostgreSQL (exemple)
CREATE TABLE IF NOT EXISTS dwh.billing_fact (
rowid bigint PRIMARY KEY,
facnumero varchar(30),
fadate date,
total_ht numeric(15,2),
status varchar(20),
date_modif timestamp,
etl_ts timestamp,
load_date date,
year_month varchar(7)
);
COPY dwh.billing_fact
FROM ‘/etl/staging/factures_clean_20251102.parquet’
WITH (FORMAT parquet);
> **Astuce** : Si votre DW accepte le format `parquet` natif (ex. Snowflake `COPY INTO ... FROM @stage/file.parquet`), vous n’avez pas besoin de passer par CSV intermédiaire.
### 4.5. Nettoyage & archivage
* Déplacer les fichiers `staging/*.parquet` vers `/etl/archive/yyyyMMdd/` après validation (checksum MD5).
* Supprimer les anciens fichiers après 7 jours (politique de rétention).
---
## 5. Intégration dans un scheduler (exemple : Apache Airflow)
```python
# dag_etl_dolibarr_factures.pyfrom airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime, timedelta
default_args = {
"owner": "etl-team",
"retries": 2,
"retry_delay": timedelta(minutes=5),
"depends_on_past": False,
}
dag = DAG(
"dolibarr_etl_factures",
default_args=default_args,
description="ETL quotidien des factures Dolibarr",
schedule_interval="0 2 * * *", # 02h00 chaque jour
start_date=datetime(2025, 1, 1),
catchup=False,
)
extract_task = PythonOperator(
task_id="extract_factures",
python_callable=lambda: __import__("extract_factures").extract_factures.main()
)
transform_task = PythonOperator(
task_id="transform_factures",
python_callable=lambda: __import__("transform_factures").transform_factures.main()
)
load_task = PythonOperator(
task_id="load_into_dw",
python_callable=lambda: __import__("load_factures").load_factures.main()
)
extract_task >> transform_task >> load_task
Airflow assure le retry, la visibilité des logs, et le déclenchement à heure fixe, tout en gardant l’isolation du moteur de production.
6. Bonnes pratiques & check‑list de non‑impact
| ✅ Action | Pourquoi | Comment vérifier |
|---|---|---|
| Planifier pendant les créneaux de basse charge | Réduction de la contrainte I/O sur la base source | Monitorer SHOW ENGINE INNODB STATUS ou les métriques CPU/DB pendant 1‑2 jours de pilotage |
| Utiliser des vues ou des snapshots | Pas de lock logique sur les tables de production | SELECT COUNT(*) FROM llx_facture WHERE updated_at > NOW() - INTERVAL 1 DAY doit rester constant |
| Limiter la taille du « slice » | Éviter un flood de scans complets | |
Mettre en place un replay‑proof** (ex. ON CONFLICT DO NOTHING) |
Garantir l’idempotence du chargement | Tester plusieurs exécutions du même ETL sur un jeu de données fixe |
| Sauvegarder les logs de chaque étape | Déboguer facilement en cas d’erreur | logging avec rotation de fichiers (logging.handlers.RotatingFileHandler) |
| Tester en environnement sandbox | Valider les transformations avant le prod | Jeu de données de 10 000 enregistrements aléatoires, comparatif de checksum après charge |
| Versionner les scripts | Traçabilité et rollback | Git‑repo dédié etl-dolibarr/, tags v1.2.0 |
| Informer les parties prenantes | Transparence sur les fenêtres d’exécution | Mail + tableau partagé des fenêtres d’ETL |
7. Solutions de contournement selon les contraintes
| Contrainte | Solution alternative | Pro / Con |
|---|---|---|
| Aucun accès réseau direct à la base source | Replication logique via binlog (MySQL) ou log‑decoder (PostgreSQL) → canaliser dans Kafka → ETL depuis Kafka | + Sécurité renforcée, – Complexité supplémentaire |
Pas de READ UNCOMMITTED autorisé |
Snapshot via mysqldump --single-transaction toutes les tables pertinentes toutes les heures |
+ Simple, – Consommation I/O plus élevée |
| Besoin d’un chargement en temps réel (< 5 min) | Change Data Capture (CDC) avec Debezium → topic Kafka → micro‑service de transformation → upsert dans le DW | + Latence faible, – Nécessite infrastructure Kafka |
| Budget limité (pas de licence) | Utiliser uniquement des outils opensource (Python + PostgreSQL) et des jobs cron |
+ Rapide à mettre en place, – Manque de dashboard native |
8. Monitoring & alerting (exemple)
| Métrique | Seuil d’alerte | Source de collecte |
|---|---|---|
| Durée d’extraction | > 10 min | Prometheus etl_extraction_duration_seconds |
| Taux d’erreurs de transformation | > 1 % | Logs transform_factures.py → Loki |
| Volumes de lignes chargés | < expected‑volume * 0.9 | SELECT COUNT(*) FROM staging.table → Grafana |
| Latence de montée en charge | > 5 min | Heapster / kube‑state‑metrics |
| Espace disque du staging | > 80 % du quota | du -sh /etl/staging → alertes Slack |
Exemple d’alerte Slack
# alertmanager.yml – example rule
- alert: ETLStagingFull
expr: job_disk_bytes{mount="/etl/staging"} > 8e+9
for: 5m
labels:
severity: critical
annotations:
summary: "Space de staging ETL plein>"
description: "Le disque /etl/staging dépasse 80 % de sa capacité."
9. Checklist finale avant le basculement
- Tests unitaires : chaque script passe sur le jeu de données de test (ex. 5 k factures).
- Tests d’intégration : exécution complète du pipeline dans l’environnement staging pendant 2 cycles complets.
- Revue de code : audit de sécurité (pas de
DROP TABLEouDELETEen écriture). - Plan de rollback : script de restauration du state de la base source (ex. copie de la réplique avant le run).
- Documentation : README avec diagrammes, variables d’environnement, secrets (Vault ou AWS Secrets Manager). 6. Communication : diffuser le planning aux équipes support, aux équipes BI et aux auditors.
10. Conclusion
Mettre en place un processus ETL sur Dolibarr tout en préservant la disponibilité de l’ERP repose sur trois piliers :
| Pilier | Action clé |
|---|---|
| Isolement technique | Snapshot / read‑only view + scheduling hors‑pic |
| Fiabilité mechanismnelle | Idempotence, retries, logs détaillés |
| Observabilité | Métriques, alertes, audit des chargements |
En suivant le plan ci‑dessus – extraction via vues ou snapshots, stockage en format columnar (Parquet), transformation avec Python/SQL, chargement bulk dans un entrepôt dédié, puis automatisation via Airflow (ou tout autre orchestrateur) – vous obtiendrez un flux de données fiable, scalable et sans impact perceptible sur votre Dolibarr de production.
"Le secret n’est pas de toucher la base de production, mais de la lire sans qu’elle Notice que vous êtes là." – Proverbe des data‑engineers Dolibarr.
Bonne implémentation ! 🚀