Comment exploiter la puissance d’un ERP/CRM léger comme Dolibarr pour des flux de données automatisés, scalables et compatibles avec les architectures cloud‑native.
1. Introduction
Dolibarr est un ERP/CRM open‑source très léger, idéal pour les PME ou les départements qui souhaitent une solution « tout‑en‑un » sans complexité.
Cependant, dans les environnements modernes, il est souvent nécessaire d’alimenter d’autres outils (BI, data‑warehouse, plateformes de marketing, systèmes de paiement, etc.) via des ETL (Extract‑Transform‑Load).
Cet article décrit comment déployer des pipelines ETL avancés à partir de Dolibarr, en s’appuyant sur des outils contemporains : API REST, connecteurs Kafka, sauvegardes dans un data‑lake, orchestration serverless, et gouvernance des données (metadata‑driven).
Objectif : passer de “exporter un CSV à la main” à “orchestrer des flux de données fiables, réutilisables et gouvernés”, le tout grâce aux capacités d’extension et aux API exposées par Dolibarr.
2. Architecture de Base : Layers d’Intégration
+-------------------+ +----------------------+ +-------------------+
| Dolibarr (ERP) | API | Connecteurs Externe| ETL | Orchestration |
| (Core) +-----> (Kafka, REST, JDBC) +-----> (Airflow, Prefect) |
+-------------------+ +----------------------+ +-------------------+
| | |
v v v
+-------------------+ +----------------------+ +-------------------+
| Cloud Storage | <----> | Transformation | <----> | BI / Data Lake |
| (S3, Blob) | | (dbt, Spark) | | (PowerBI, Looker)|
+-------------------+ +----------------------+ +-------------------+
2.1. Points d’attache (entry points)
| Point d’attache | Méthode | Cas d’usage typique |
|---|---|---|
| API REST native | GET/POST/PUT via /dolibarr/api/index.php (v10+) |
Export de factures, synchronisation des contacts |
| Webhooks | POST (payload JSON) déclenché par évènements Dolibarr | Réaction en temps réel à une commande payée |
| Export CSV/Excel | Scripts PHP ou CLI | Extrait ponctuel de gros jeux de données |
| BD relationnelle | connexion directe à MySQL/PostgreSQL | Chargement massifs dans un entrepôt cloud |
| Messages Kafka | Connecteur Debezium → Kafka topic | Streaming change‑data‑capture (CDC) |
3. Extraction (E)
3.1. Exemple : Exporter les factures avec l’API REST
curl -X GET "https://example.com/dolibarr/api/index.php?module=admin&route=commande&filter='{\"status\": \"paid\"}' \
-H "Authorization: Bearer $TOKEN"
-
Gestion de l’authentification
- Token d’accès : généré dans
Configuration > Sécurité > Token API. - Rate‑limit : header
X-RateLimit-Limità gérer côté client. - Paginations : paramètres
rangeoulimit/offsetà itérer jusqu’à épuisement.
- Token d’accès : généré dans
- Bibliothèques recommandées
python-requests+pytestpour les tests unitaires.apify-clientpour orchestration serverless (AWS Lambda, Azure Functions).
3.2. Streaming changelog via Debezium + Kafka
{
"name": "dolibarr-connector",
"config": {
"connector.class":"io.debezium.connector.mysql.MySqlConnector",
"tasks.max":"1",
"database.hostname":"db.dolibarr.local",
"database.port":"3306",
"database.user":"dolibarr_user",
"database.password":"****",
"database.server.id":"184054",
"database.server.name":"dolibarr_cdc",
"database.whitelist":"llx_commande,llx_facture,llx_client",
"snapshot.mode":"when_needed",
"include.column.names":"true"
}
}
- Avantages : aucune requête supplémentaire sur Dolibarr, latence quasi‑temps réel, décodage des modifications à la volée.
- Tip : ajouter un schema registry (Confluent) pour garder les messages sérialisés en Avro/Protobuf et éviter les ruptures de version.
3.3. Export direct depuis la base
SELECT
c.id as client_id,
f.id as facture_id,
f.facture_date,
f.total_ht,
f.status
FROM llx_facture f
JOIN llx_client c ON f.id_client = c.id
WHERE f.facture_date >= CURRENT_DATE - INTERVAL 30 DAY;
- Export vers S3 : utilisation de
SELECT ... INTO OUTFILE '/tmp/factures_$(date +%F).csv'puisaws s3 cp. - Optimisation : créer une vue matérialisée
vw_factures_recentespré‑indexée pour accélérer les scans.
4. Transformation (T)
4.1. Normalisation des types de données
| Champ Source | Type Dolibarr | Transformation recommandée |
|---|---|---|
date |
DATE |
Convertir en ISO‑8601 UTC (2025-10-31T14:20:00Z) |
montant |
DECIMAL(12,2) |
Multiplier par 100 pour passer en centimes avant d’ingérer dans un data‑warehouse (Snowflake/Redshift) |
status |
"paid" | "unpaid" |
Mapper vers enum PAID|PENDING|OVERDUE |
4.2. Enrichissement (lookup)
- Client → Segment Marketing : jointure avec un table
dim_segmentstockée dans Snowflake. - Produit → Prix Currency : enrichir avec taux de change du jour (API ExchageRate).
Extrait d’un script d’enrichissement (Python + dbt) :
import dbt.utils
import pandas as pd
def enrich_factures(df: pd.DataFrame) -> pd.DataFrame:
# Lookup segment
df['segment'] = df['client_id'].map(segment_map)
# Rate‑currency
df['price_usd'] = (df['total_ht'] * exchange_rate_usd(df['currency'])).round(2)
return df
Astuce dbt : créez un model
stg_factures_raw→fct_factures_enrichiqui regroupe toutes les transformations structurées.
4.3. Nettoyage et déduplication
- Déduplication : créez une clé composite
(facture_id, client_id). - Gestion des doublons : utilisez
DROP DUPLICATESdans SparkSQL ouGROUP BY+MAX(updated_at)en SQL.
5. Chargement (L)
5.1. Chargement Batch vers un Data‑Lake (S3 + Glue)
aws s3 cp factures_2025-10-31.parquet s3://my-bucket/datalake/factures/
- Glue Crawler : déclenché par l’événement
ObjectCreated:*.parquetpour mettre à jour le Glue Data Catalog. - Partitionnement : structure
s3://bucket/datalake/factures/year=2025/month=10/.
5.2. Chargement en temps réel (Kafka → Snowflake)
COPY INTO snowflake_table
FROM 'kafka://my_topic?wait_for=5s'
FILE_FORMAT = (TYPE = PARQUET);
- Source : Connecteur Snowflake Kafka Connector (confluent) ou Kafka Connect avec sink Snowflake.
- Mode exactement‑une‑fois : configurer
isolation.level=read_committedettransactional.idpour éviter les pertes de données.
5.3. Chargement vers un Data‑Warehouse (Snowflake/BigQuery)
INSERT INTO dw.factures
SELECT * FROM staging.factures_enriched;
- Chargement incrémental : utilisez la colonne
updated_atpour ne charger que les lignes modifiées depuis la dernière exécution.
6. Orchestration Moderne
| Orchestrateur | Pourquoi le choisir pour Dolibarr ? |
|---|---|
| Apache Airflow | DAGs explicites, UI riche, opérateurs natifs REST, Kafka, Snowflake. |
| Prefect 2.0 | Architecture serverless, excellent support de dynamic mapping (batch‑per‑client). |
| Dagster | Gouvernance des métadonnées + tests de data‑quality intégrés. |
| Dagger (CI/CD‑native) | Déploiement « Infrastructure as Code » via Docker + GitOps. |
Exemple de DAG Airflow pour extraire les factures et les pousser dans S3
from airflow import DAG
from airflow.providers.http.operators.http import SimpleHttpOperator
from airflow.providers.amazon.aws.operators.s3 import S3CreateObjectOperator
from airflow.utils.dates import days_ago
default_args = {
'owner': 'dolibarr_etl',
'retries': 2,
'retry_delay': timedelta(minutes=5)
}
with DAG(
dag_id='dolibarr_factures_to_s3',
default_args=default_args,
schedule_interval='@daily',
start_date=days_ago(1),
catchup=False,
) as dag:
extract = SimpleHttpOperator(
method='GET',
http_conn_id='dolibarr_api',
endpoint='/index.php?module=admin&route=commande&filter={"status":"paid"}',
headers={"Authorization": "Bearer {{ var.value.dolibarr_token }}"},
response_filter=lambda response: response.json(),
log_response=True,
)
load_to_s3 = S3CreateObjectOperator(
task_id='load_to_s3',
bucket_name='dolibarr-etl',
object_name='factures_{{ ds_nodash }}.json',
data=extract.xcom_pull(key='response', task_ids='extract'),
storage_class='STANDARD',
)
7. Gouvernance & Qualité des Données
| Aspect | Action concrète |
|---|---|
| Catalogue de métadonnées | Utilisez DataHub ou Amundsen pour enregistrer les tables Dolibarr → transformations → cibles. |
| Tests de data‑quality | dbt data_quality tests (not_null, unique, accepted_values) exécutés à chaque run Airflow. |
| Audits de sécurité | Masquez les champs sensibles (numéro de compte bancaire) via Dynamic Data Masking avant le transfert. |
| Versionnage des pipelines | Stockez les DAG/Airflow/prefixes dans un repo Git ; versionnez les schémas SQL (Flyway) et les modèles dbt. |
8. Bonnes Pratiques Spécifiques à Dolibarr
| Pratique | Description | Impact |
|---|---|---|
| Ne jamais toucher la base en production | Utilisez toujours une replication read‑only (ou un dump filtré) pour les extractions. | Garantit la continuité du service ERP. |
| Limiter la taille du payload | Découpez les réponses > 10 Mo en chunks (pagination + stream). | Réduit les time‑outs API et le risque de MemoryError côté client. |
| Garder la logique métier hors du script ETL | Utilisez les hooks et modules de Dolibarr (ex : llx_facture, llx_client) uniquement pour la lecture, jamais pour modifier les données dans le pipeline. |
Évite les effets de bord inattendus (ordres modifiés, stocks incohérents). |
| Cache des référentiels | Mise en cache locale (Redis / Memcached) des listes de prix ou de devis pour limiter les appels API redondants. | Diminution du temps de transformation. |
| Gestion des conflits de langues | Si votre instance est multilingue, normalisez les libellés en français (ou langue de référence) avant le chargement. | Simplifie l’analyse BI. |
9. Cas d’Usage Avancé : Pipeline « Zero‑Code » avec Prefect Cloud
- Déclencheur : Webhook Dolibarr →Prefect Serverless → crée un flow
facture_paid. - Extract :
prefect.http.client→ appel/api/factures?status=paid&since=2025-10-30. - Transform : Utilisation de Pydantic pour valider le schéma, puis conversion en Parquet via
pyarrow. - Load :
prefect.AWSS3BucketWrite→ écriture danss3://dolibarr-pipeline/factures/YYYY/MM/DD/. - Notify : Slack webhook + création d’un ticket JIRA si le nombre de lignes est > 10 000 (détection d’anomalie).
from prefect import flow, task
import pydantic_models as pm
@task
def fetch_factures():
resp = requests.get(
"https://erp.example.com/api/factures",
headers={"Authorization": f"Bearer {os.getenv('DOLIBARR_TOKEN')}"},
params={"status":"paid", "date_from":"2025-10-01"},
)
resp.raise_for_status()
return resp.json()
@task
def to_parquet(data: List[dict]):
df = pd.DataFrame(data)
df['date'] = pd.to_datetime(df['facture_date']).dt.strftime("%Y-%m-%d")
return df.to_parquet('s3://my-bucket/datalake/factures/', engine='pyarrow', compression='snappy')
@flow(name="dolibarr-facture-etl")
def facture_etl():
raw = fetch_factures()
to_parquet(raw)
if __name__ == "__main__":
facture_etl()
Ce flux peut être déclenché chaque jour via le schedule de Prefect Cloud, sans serveur dédié.
10. Conclusion
Dolibarr ne se limite pas à un simple ERP « lightweight ». En exploitant :
- ses API REST et webhooks pour l’extraction fine‑grained,
- les connecteurs change‑data‑capture (Debezium/Kafka) pour le streaming,
- des pipelines de transformation modernes (dbt, PySpark, Python + Pydantic),
- une orchestration serverless ou DAG‑based (Airflow, Prefect, Dagster),
- une gouvernance centralisée (DataHub, tests dbt, audits de sécurité),
on obtient un ETL avancé, scalable et gouverné qui s’intègre parfaitement aux architectures cloud‑native.
Le mot de la fin : la clé du succès réside dans la séparation stricte des responsabilités (extraction → transformation → chargement), la détection proactive des changements dans Dolibarr (pas de modifications directes en prod), et l’utilisation d’outils déclaratifs et versionnés pour que chaque flux soit reproductible, auditable et prêt à évoluer avec les besoins métier futurs.
Bon ETL ! 🚀
Sources & références supplémentaires
- Documentation officielle de Dolibarr : https://www.dolibarr.org
- Debezium Connector : https://debezium.io
- dbt Documentation : https://docs.getdbt.com
- Airflow Provider HTTP : https://airflow.apache.org/docs/airflow-providers-http/index.html
- Prefect 2.0 : https://docs.prefect.io
Auteur :
[Nom du Consultant] – Architecte Data & Engineer ETL spécialisé Dolibarr, certifié Snowflake & AWS Glue.
Date : 2 novembre 2025.