Aller au contenu principal
Making DigitalMaking Digital
Note technique

Dolibarr : corriger un prix d'achat resté à 0 sur les commandes et les factures

En bref

Dolibarr copie le prix d'achat dans chaque ligne de devis, de commande et de facture au moment de sa création : corriger la fiche produit ne change pas les lignes existantes. Les documents en brouillon se corrigent dans l'interface ou par l'API (champ pa_ht) ; les documents validés demandent une mise à jour SQL de buy_price_ht, factures comprises, car le rapport des marges lit les lignes de facture.

Un produit a été vendu avant que son prix d'achat soit connu. Les lignes de devis, de commande et de facture ont été enregistrées avec un prix d'achat à 0, et le rapport des marges affiche 100 % de marge sur ce produit. Corriger la fiche produit ne suffit pas : Dolibarr copie le prix d'achat dans chaque ligne au moment où elle est créée, les lignes existantes ne bougent pas. Il faut donc corriger le produit pour l'avenir, puis les lignes déjà enregistrées : par l'interface ou l'API tant que le document est en brouillon, en SQL pour les documents validés.

D'où vient le prix d'achat d'une ligne ?

Quand une ligne est ajoutée sans prix d'achat, Dolibarr en propose un selon le réglage du module Marges, "Prix d'achat/revient suggéré par défaut pour le calcul de la marge" : prix de revient du produit, prix moyen pondéré (PMP) ou meilleur prix fournisseur. Si rien n'est renseigné, la ligne garde 0. Une option du même module, "Forcer le prix d'achat au prix de vente si non renseigné", change ce comportement : dans le code (defineBuyPrice), elle passe avant la recherche du prix de revient et prend le prix de vente remisé dès qu'une ligne arrive sans prix d'achat. La marge devient nulle au lieu de 100 %, ce qui est tout aussi faux.

Dans notre test, avec le réglage sur le prix de revient : une facture créée avant de renseigner le produit a gardé 0 ; après avoir saisi un prix de revient de 8 sur la fiche, une nouvelle ligne a reçu 8 automatiquement, sans toucher aux anciennes.

Première étape, donc : renseigner le prix de revient ou le prix fournisseur sur la fiche produit, pour que les prochaines lignes soient justes.

Quelles tables contiennent ce prix ?

Document Table des lignes Colonne
Devis (proposition commerciale) llx_propaldet buy_price_ht
Commande client llx_commandedet buy_price_ht
Facture client llx_facturedet buy_price_ht

L'ancienne méthode ne corrigeait que les commandes. Or le rapport Marges par produit lit les lignes de facture : sa requête additionne d.qty * d.buy_price_ht sur llx_facturedet. Corriger les commandes sans les factures laisse le rapport faux. Les commandes et les devis gardent leur importance : leurs lignes alimentent la marge affichée sur leur fiche, et une facture créée depuis une commande reprend le prix d'achat de ses lignes.

Peut-on corriger sans SQL ?

Oui, tant que le document est en brouillon. Avec le module Marges actif, le formulaire de modification d'une ligne affiche un champ de prix d'achat : il suffit de le corriger. Par l'API REST, le champ pa_ht fait la même chose :

Terminal
curl -X PUT "https://votre-dolibarr/api/index.php/invoices/485/lines/2120" \
  -H "DOLAPIKEY: votre_cle_api" -H "Content-Type: application/json" \
  -d '{"desc":"","subprice":20,"qty":2,"tva_tx":20,"pa_ht":8,"product_type":0}'

485 et 2120 sont l'identifiant de la facture et celui de la ligne, renvoyés par GET /invoices/{id}. Sur une facture brouillon, la ligne est passée à 8. Sur une facture validée, la même requête a répondu HTTP 304 sans rien modifier : la mise à jour d'une ligne n'est acceptée qu'en brouillon (le code de updateline le vérifie, pour les factures comme pour les commandes).

Quelle requête SQL pour les documents validés ?

D'abord, lister les lignes concernées. Le produit est désigné par sa référence, jamais par son identifiant, et seules les lignes encore à 0 sont visées :

SQL
SELECT 'propal' AS document, x.ref, x.fk_statut AS statut, d.rowid AS ligne, d.qty, d.subprice, d.buy_price_ht
FROM llx_propaldet AS d
JOIN llx_propal AS x ON x.rowid = d.fk_propal
JOIN llx_product AS p ON p.rowid = d.fk_product
WHERE p.ref = 'TESTPA' AND d.buy_price_ht = 0
UNION ALL
SELECT 'commande', x.ref, x.fk_statut, d.rowid, d.qty, d.subprice, d.buy_price_ht
FROM llx_commandedet AS d
JOIN llx_commande AS x ON x.rowid = d.fk_commande
JOIN llx_product AS p ON p.rowid = d.fk_product
WHERE p.ref = 'TESTPA' AND d.buy_price_ht = 0
UNION ALL
SELECT 'facture', x.ref, x.fk_statut, d.rowid, d.qty, d.subprice, d.buy_price_ht
FROM llx_facturedet AS d
JOIN llx_facture AS x ON x.rowid = d.fk_facture
JOIN llx_product AS p ON p.rowid = d.fk_product
WHERE p.ref = 'TESTPA' AND d.buy_price_ht = 0;

Puis, après une sauvegarde de la base, corriger les trois tables dans une même transaction (TESTPA est notre produit de test et 8 son prix de revient : remplacez les deux) :

SQL
START TRANSACTION;
UPDATE llx_propaldet AS d
JOIN llx_product AS p ON p.rowid = d.fk_product
SET d.buy_price_ht = 8
WHERE p.ref = 'TESTPA' AND d.buy_price_ht = 0;
UPDATE llx_commandedet AS d
JOIN llx_product AS p ON p.rowid = d.fk_product
SET d.buy_price_ht = 8
WHERE p.ref = 'TESTPA' AND d.buy_price_ht = 0;
UPDATE llx_facturedet AS d
JOIN llx_product AS p ON p.rowid = d.fk_product
SET d.buy_price_ht = 8
WHERE p.ref = 'TESTPA' AND d.buy_price_ht = 0;
SELECT ROW_COUNT() AS lignes_facture_modifiees;
COMMIT;

Résultat dans notre test, sur une facture validée de 3 unités vendues 20 € HT : le rapport Marges par produit est passé de "prix de revient 0,00 / marge 60,00" à "prix de revient 24,00 / marge 36,00", à l'actualisation de la page. Le total HT de la ligne (60) n'a pas changé : le prix d'achat n'entre dans aucun total du document.

Quels sont les risques ?

  • Les marges des mois passés changent. C'est le but, mais un chiffre déjà communiqué (bilan commercial, prime indexée sur la marge) ne correspondra plus.
  • La condition buy_price_ht = 0 vise aussi les zéros voulus (échantillon, geste commercial facturé au prix coûtant nul). Relisez la liste de la première requête avant de lancer la mise à jour.
  • Le prix choisi s'applique à toutes les dates. Si le prix d'achat a varié, ajoutez une condition sur la date ou la référence des documents, ou faites plusieurs passages.
  • SQL contourne les contrôles de Dolibarr : aucun événement n'est enregistré dans l'historique des documents. Gardez la sauvegarde et notez la modification. D'autres requêtes testées sont réunies dans Requêtes SQL utiles pour Dolibarr.

Ce genre de correction en masse est souvent le signe qu'une information arrive trop tard dans l'ERP. Faire remonter automatiquement les prix fournisseurs fait partie de ce que nous mettons en place avec nos connecteurs et nos automatisations. Vos marges Dolibarr ne vous semblent pas fiables ? Parlons-en.

À retenir
  • Corriger d'abord la fiche produit : seules les lignes créées ensuite en profitent.
  • Le rapport de marges lit llx_facturedet.buy_price_ht : corriger les commandes seules ne suffit pas.
  • Document brouillon : interface ou API (pa_ht) ; document validé : SQL, en transaction, sur les lignes à 0.
  • Désigner le produit par sa référence et relire la liste avant la mise à jour.

Testé avec Dolibarr 24.0.2 et MariaDB 11.4.13.

Questions fréquentes

Faut-il régénérer les PDF après la correction ?

Le prix d'achat ne change aucun total du document : le total HT de la ligne est resté identique dans notre test. La correction sert aux marges, pas aux montants facturés.

Quel réglage choisir pour éviter le problème ?

Dans la configuration du module Marges, le prix suggéré peut être le prix de revient, le PMP ou le meilleur prix fournisseur. L'essentiel est de renseigner l'une de ces valeurs sur la fiche produit avant la première vente.

Les commandes fournisseur sont-elles concernées ?

Non. Le prix d'achat des lignes de vente (buy_price_ht) sert au calcul de la marge. Les lignes de commandes et de factures fournisseur sont dans d'autres tables, que ces requêtes ne touchent pas.

Making Digital

Nous connectons les logiciels des PME, automatisons leurs tâches répétitives et créons des assistants IA, depuis 2010. Nos articles partent de nos projets réels.

Notre parcours
Une question sur ce sujet ?

Parlons-en 30 minutes.

Vous saurez si votre problème se règle, comment, et en combien de temps. Sans engagement.

Un échange de 30 minutes, en visio ou par téléphone

Gratuit, sans engagement. On parle de votre problème, pas de technique.