Dolibarr et import-export : erreurs fréquentes et solutions orienté performance

Version Dolibarr ≥ 17 (les principes restent valables pour les versions précédentes)


1. Introduction

Dolibarr est un ERP/CRM open‑source très apprécié pour sa simplicité d’utilisation et son extensibilité. L’une de ses forces réside dans les modules Import/Export qui permettent de charger massivement des données (clients, fournisseurs, articles, factures, etc.) ou de les extraire vers des formats compatibles (CSV, Excel, ODS, PDF…).

Cependant, lorsqu’on traite des volumes importants ou que l’on exploite des champs personnalisés, plusieurs erreurs récurrentes peuvent ralentir voire bloquer le processus. Ce guide recensent les problèmes les plus fréquents et propose des solutions orientées performance pour les éviter ou les remédier rapidement.


2. Architecture de l’import‑export dans Dolibarr

Étape Description Point sensible (performance)
1. Lecture du fichier source Le module parcourt le fichier ligne par ligne. Taille du fichier, encodage, nombre de colonnes.
2. Validation des données Contrôle des formats (date, nombre, email) et des contraintes (unicité, clés étrangères). Validation lente si effectuée règle par règle.
3. Transformation Mapping des colonnes du fichier vers les champs Dolibarr. Mapping complexe → surcharge de code PHP.
4. Insertion en base Insertion en batch ou transaction unique. Verrouillage de la table, recomposition d’index, logs de requêtes.
5. Post‑traitement Génération des log, notifications ou déclenchement d’évènements. Processus supplémentaire (ex. génération PDF).

Les goulots d’étranglement se situent généralement aux étapes 2‑4.


3. Erreurs fréquentes et pourquoi elles nuisent à la performance

Erreur rencontrée Conséquence sur la performance
E1 Encodage UTF‑8 non détecté (fichier encodé en ISO‑8859‑1 ou Windows‑1252). Le parser lit chaque caractère comme un octet supplémentaire, ce qui ralentit le traitement et crée des caractères «  ».
E2 Colonnes mal orthographiées ou manquantes (ex. client_id au lieu de client_id). Dolibarr tente de mapper chaque colonne, déclenchant des erreurs et des retours en arrière qui immobilisent les requêtes.
E3 Valeurs de type incorrect (ex. un champ numérique contenant du texte). La validation lève une exception, tout le lot est interrompu, et la transaction est annulée → perte de temps et besoin de relancer le processus.
E4 Insertion ligne‑par‑ligne sans batch. Chaque INSERT ouvre une connexion, un verrou et un log de transaction → overhead réseau et serveur DB.
E5 Absence d’index ou d’optimisation DB sur les colonnes clés (ex. fk_user, status). Les inserts provoquent des reconstructions d’index à chaque ligne, ce qui multiplie le temps d’écriture de 3‑5×.
E6 Table de log ou de sauvegarde très volumineuse activée par défaut. Chaque ligne d’import crée une entrée de log, saturant le disque I/O et ralentissant l’opération globale.
E7 Permissions serveur mal configurées (ex. le répertoire files/ ou blob/ non accessible). Dolibarr bloque le traitement dès la première erreur d’accès, requiring manual correction.
E8 Champs personnalisés (user fields) non pris en charge lors de l’import. Le script ne les ignore pas, générant des appels PHP inutiles et des warnings qui polluent le log.
E9 Mémoire PHP dépassée (scripts longs avec beaucoup de lignes). memory_limit atteint → fatal error, interruption brutale du batch.
E10 Conflit de clé étrangère quand les références sont insérées après leurs dépendances. Dolibarr arrête l’import dès la première violation – il faut ré‑ordonner les lots ou désactiver temporairement les FK.


4. Solutions orientées performance

4.1. Pré‑traitement du fichier source

Action Implémentation recommandée Gains
Détection et conversion d’encodage Utilisez iconv -f ISO-8859-1 -t UTF-8 fichier.csv > fichier_utf8.csv en phase de préparation (script Bash/PHP). Évite les erreurs de lecture, accélère le parsing.
Normalisation des en‑têtes Supprimez les espaces, assurez‑vous que tous les en‑têtes sont identiques à la table ($field_map[$header] = $field_name). Réduit le domaine de recherche du mapping, évite les retours en erreur.
Conversion de type Créez une fonction prepareValue($value, $type) (int, float, date) : intval(), floatval(), strtotime() + format Y-m-d. Prévention des erreurs de validation, supprime les étapes d’exception.
Élagage des lignes vides ou commentaires Filtrez dès le début (if (trim($line) === '' || $line[0] === '#') continue;). Diminution du nombre d’itérations inutiles.

4.2. Optimisation du parsing PHP

// Exemple de boucle d’import batch
$batchSize = 500; // taille idéale à ajuster selon le serveur
$handle = fopen($filePath, 'r');
while (($data = fgetcsv($handle, 0, ';')) !== false) {
$values = $this->prepareRow($data); // conversion des types
$insertSql[] = $this->buildInsert($values);
// Insert batch lorsque la taille maximale est atteinte
if (count($insertSql) >= $batchSize) {
$this->executeBatch($insertSql);
$insertSql = []; // reset
}
}
// Insert le dernier batch
if (!empty($insertSql)) {
$this->executeBatch($insertSql);
}

  • Batch size : 500 – 2000 lignes selon votre RAM et la capacité du serveur DB.
  • Utilisation de LOAD DATA INFILE (MySQL) : si le serveur DB a accès au fichier, il est 5‑10× plus rapide que des inserts PHP.

4.3. Réglages DB pour les imports massifs

Paramètre Valeur recommandée (MySQL) Impact
autocommit = 0 Désactive l’autocommit pendant le batch. Un seul gros COMMIT à la fin, évite des dizaines de rollback.
unique_checks = 0 Diminue les vérifications d’unicité pendant le chargement. Réduction du temps d’insertion (à remettre à 1 après le import).
foreign_key_checks = 0 Décoche les contrôles FK (réactiver après). Supprime les contrôles de dépendance qui ralentissent l’insertion.
bulk_insert_buffer_size = 1G Augmente le buffer de chargement. Réduit le nombre de flushes disque.
Indexes temporaires ALTER TABLE xxx DISABLE KEYS; (MyISAM) ou ALTER TABLE xxx DROP INDEX idx_name; Supprime la mise à jour d’index à chaque ligne.
max_allowed_packet Augmenter (ex. 1G) pour accepter des paquets de plusieurs mégaoctets. Permet d’utiliser LOAD DATA INFILE avec de gros fichiers.

Workflow recommandé

SET autocommit=0;
SET unique_checks=0;
SET foreign_key_checks=0;
-- (Optionnel) ALTER TABLE ... DISABLE KEYS;
-- Exécution du chargement (LOAD DATA INFILE ou INSERT multi‑valeurs)
-- ...
-- Ré‑activer les contrôles
SET foreign_key_checks=1;
SET unique_checks=1;
COMMIT;

Attention : Ne pas oublier de remettre les index et les contrôles à 1 après l’opération, sous peine de corruption de données.

4.4. Limiter les logs et les déclencheurs

  • Désactiver temporairement dolibarr_log_... : ajoutez dans le fichier conf/conf.php

    $conf['log_file'] = '/dev/null'; // ou désactivez le fichier log dans l UI

  • Désactiver les module “hooks” qui génèrent des actions (ex. génération PDF, envoi d’emails).

  • Post‑process usuel : effectuez l’enrichissement (PDF, pièces jointes) en batch séparé après l’import complet, afin de ne pas ralentir le chargement initial.

4.5. Gestion de la mémoire

ini_set('memory_limit', '512M');   // ou plus selon le volume
set_time_limit(0); // désactive le timeout PHP

  • Utilisez generation en mode « CLI » (executed via php -f import.php) pour éviter les limites d’Apache/PHP‑FPM.
  • Pré‑allouer le tableau des $insertSql avec array_fill ou splFixedArray si vous avez besoin d’un contrôle strict.

4.6. Gestion des clés étrangères et des référentiels

  1. Exportez les tables de référence (ex. llx_user, llx_categorie) au préalable.
  2. Insérez d’abord les données « maîtres », puis les tables dépendantes.
  3. Si vous avez besoin d’un chargement aléatoire, créez un script qui génère un fichier d’import trié par dépendance ou utilisez la fonction dol_import_completed pour ré‑ordonner les transactions.

4.7. Utilisation de LOAD DATA INFILE (MySQL)

Dolibarr possède un wrapper import.php qui accepte le paramètre --sql. On peut contourner l’interface UI et appeler directement :

mysql -u dolibarr -p dolibarr_db <<SQL
USE dolibarr_db;
LOAD DATA LOCAL INFILE '/var/www/htdocs/files/import/2023_articles.csv'
INTO TABLE llx_product
FIELDS TERMINATED BY ';'
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 LINES
(product_code, label, price, fk_category, qty, unit, ...)
SET active = 1;
SQL

  • Avantage : Le moteur MySQL gère la lecture du fichier, la conversion d’encodage et la mise en cache des requêtes.
  • Limitation : Nécessite un accès file‑system au serveur DB (souvent possible en environnement Docker ou sur serveur dédié).


5. Checklist « À faire avant chaque import »

Action
1 Vérifier l’encodage du fichier (UTF‑8, BOM éventuel).
2 Normaliser les headers avec les noms de champ de la table ($field['label']).
3 Pré‑valider le type de chaque colonne (int, float, date).
4 Déterminer la taille du batch adaptée au serveur (500‑2000).
5 Désactiver temporairement : foreign_key_checks, unique_checks, logs.
6 Vérifier les index : ALTER TABLE … DISABLE KEYS (MyISAM) ou DROP INDEX ….
7 Faire un test sur un sous‑ensemble (ex. 1 % du fichier) pour détecter les erreurs.
8 Surveiller les logs du serveur DB (SHOW ENGINE INNODB STATUS) pendant le batch.
9 Commit unique à la fin du batch, puis ré‑activer les contrôles.
10 Post‑process (PDF, mail) en lot séparé après validation du import.


6. Exemple complet d’un script d’import batch (PHP)

<?php
require_once '../../htdocs/config.php';
require_once '../../dolibarr/dol.php';
$filePath = '/var/www/htdocs/files/import/articles_2023.csv';
$batchSize = 1000;
$db->begin(); // désactive autocommit
$db->query('SET unique_checks = 0');
$db->query('SET foreign_key_checks = 0');
$handle = fopen($filePath, 'r');
$batchInsert = [];
while (($row = fgetcsv($handle, 0, ';')) !== false) {
// 1️⃣ Nettoyage / conversion
$code = trim($row[0]);
$label = trim($row[1]);
$price = floatval(trim($row[2]));
$categoryId = intval(trim($row[3]));
// 2️⃣ Construction de la requête INSERT multi‑valeurs
$sql = "INSERT INTO llx_product (rowid, entity, fc, label, price, fk_category, tkl_origin,
code, note, barcode, active, sku) VALUES
(NULL, 'product', 0, '$label', $price, $categoryId, 0, '$code', '', '', 1, 0)";
$batchInsert[] = $sql;
// 3️⃣ Ajouter au batch
if (count($batchInsert) >= $batchSize) {
foreach ($batchInsert as $stmt) {
$db->query($stmt);
}
$batchInsert = []; // reset
}
}
// Insert le dernier groupe
if ($batchInsert) {
foreach ($batchInsert as $stmt) {
$db->query($stmt);
}
}
// 4️⃣ Ré‑activer les contrôles et valider
$db->query('SET foreign_key_checks = 1');
$db->query('SET unique_checks = 1');
$db->commit();
fclose($handle);
echo "Import terminé avec succès.\n";
?>

  • Points forts :

    • Batch = 1000 lignes → charge DB compatible avec la plupart des serveurs.
    • Utilisation de SET … = 0 avant le batch pour gagner en rapidité.
    • Aucun appel à l’UI Dolibarr, donc aucune surcharge de hooks.
    • Possibilité de redémarrer le script en cas d’interruption (les lignes déjà insérées restentvalidées grâce au COMMIT).


7. Bonnes pratiques supplémentaires

  1. Versionner les scripts d’import (Git) et garder un historique des jeux de données test.
  2. Faire un dry‑run avec l’option --dry-run (si disponible) pour vérifier la correspondance colonne ⇢ champ.
  3. Sauvegarder la base avant tout import massif (mysqldump --single-transaction).
  4. Tester sur un environnement de pré‑production avec un sous‑ensemble représentatif (ex. 10 000 lignes).
  5. Surveiller les ressources pendant le chargement (top, iostat, vmstat).
  6. Archiver les fichiers importés (date/heure) pour éviter de les retraiter accidentellement.
  7. Documenter les contraintes spécifiques à chaque type d’objet (ex. llx_delta nécessite les champs date au format Y-m-d).


8. Conclusion

L’import‑export dans Dolibarr est un levier puissant pour synchroniser rapidement des bases de données externes ou des CSV générés par d’autres systèmes. Mais la performance de ces opérations dépend fortement de :

  • Préparation rigoureuse du fichier source (encodage, colonnes, valeurs).
  • Batch‑processing et désactivation temporaire des contrôles de base de données.
  • Optimisation du moteur de stockage (index, autocommit, paramètres MySQL).
  • Limitation des logs / hooks pendant le chargement massif.

En appliquant les corrections présentées dans cet article, vous transformerez des importations qui prenaient plusieurs minutes (voire des heures) en processus rapides (quelques secondes à quelques minutes) pour plusieurs dizaines de milliers de lignes, tout en garantissant l’intégrité des données.

*« L’import n’est jamais qu’une série d’étapes bien orchestrées ; chaque micro‑optimisation se répercute sur l’ensemble du flux ».

N’hésitez pas à adapter les paramètres de batch, les réglages DB et les vérifications pré‑import à votre architecture précise (Docker, serveur dédié, bases PostgreSQL vs MySQL). Vous disposez ainsi d’un plan d’action complet pour exploiter pleinement les capacités d’import‑export de Dolibarr, sans sacrifier la vitesse ni la fiabilité.


Bon import ! 🚀

Publications similaires