Intégration d'un référentiel de prix reconnu
Spécification technique pour adosser le chiffrage Conceptuo à une bibliothèque d'ouvrages du marché (Batiprix ou Batichiffrage), la répliquer en base, la mapper à notre base de prestations, en dériver les trois gammes de prix, et l'utiliser en repli quand une prestation demandée n'existe pas chez nous.
provider).
00Principe directeur
On ne consomme jamais l'API du fournisseur en direct au moment de générer un devis.
On réplique sa bibliothèque en local (PostgreSQL), on l'indexe, et le moteur de chiffrage ne parle qu'à notre base. Le fournisseur est une source amont synchronisée périodiquement, jamais une dépendance temps réel.
Raisons : latence, quotas d'API, disponibilité, et surtout le besoin de joindre, mapper et recalculer sur ces données (composition d'ouvrages, dérivation des gammes, indexation) — impossible à faire à la volée et de façon fiable.
0Architecture d'ensemble
Flux nominal : l'éditeur expose ses données → un job planifié les réplique dans le miroir local → la table de mapping relie chaque prestation Conceptuo aux ouvrages amont → le moteur de prix compose le Standard et en dérive Éco/Premium → le devis ne lit que cette sortie.
1·2Interfaçage API & réplication en base
Le mode partenaire de ces éditeurs est prévu pour ça : une clé d'API (licence éditeur) donne accès à un export des nomenclatures que le partenaire peut recopier en local. On implémente exactement ce pattern.
Job de synchronisation
- Service
ReferentialSyncService+ job planifié (Laravel scheduler). Cadence : à chaque nouveau millésime, et/ou mensuel pour les mises à jour de prix. - Le job pagine l'API, mappe les champs amont → schéma Conceptuo, et fait un upsert dans la table miroir.
- Versionnement par millésime : on n'écrase pas l'existant, on ajoute une version (audit + rollback possibles).
- En fin de sync : contrôle d'intégrité (cf. §3, détection des ouvrages orphelins) et journal de la passe (nb d'ouvrages créés / mis à jour / disparus).
Schéma de la table miroir
Le sous-détail mo_ht / fourniture_ht est indispensable — c'est lui qui rend possible la dérivation des gammes (§5). Si l'API le fournit, on le stocke ; sinon, on le reconstitue via le temps de pose × taux horaire.
-- Réplique locale du référentiel, versionnée par millésime TABLE referential_ouvrage ( id bigserial PK, provider text, -- 'batiprix' | 'batichiffrage' millesime int, -- 2026 code_provider text, -- code natif de l'éditeur libelle text, descriptif text, -- descriptif détaillé (conforme DTU) unite text, -- m2 | ml | u | forfait... prix_moyen_ht numeric, -- prix de référence (le pivot) mo_ht numeric, -- part main-d'oeuvre (pose) fourniture_ht numeric, -- part matériaux/fourniture temps_pose_h numeric, -- h / unité (si fourni) lot, famille text, -- nomenclature amont region text, -- si prix régionalisés raw_json jsonb, -- payload brut, on ne perd rien imported_at timestamptz, UNIQUE (provider, millesime, code_provider, region) );
ReferentialSyncService ; le reste du système ne voit que notre schéma normalisé ci-dessus.3Mapping de notre base vers le référentiel
C'est le cœur fragile du système. À traiter comme un actif vivant, pas comme un import unique.
Pas de correspondance codée en dur : une table de mapping versionnée et administrable.
TABLE prestation_referential_map ( prestation_id text, -- nos codes : PRE-001, SOL-008... referential_ouvrage_id bigint, -- l'ouvrage amont millesime int, coefficient numeric, -- quantité d'ouvrage amont / unité de notre presta confidence numeric, -- 0..1 : qualité du rapprochement validated_by text, -- humain ayant validé status text -- mapped | review | unmapped );
Deux contraintes à expliciter aux devs
Le mapping est rarement 1:1 → relation N..N avec composition
Notre base est au niveau « prestation de rénovation » et bundle (ex. WC suspendu fourni-posé = bâti + cuvette + plaque + raccordement). Le référentiel est souvent plus atomique. Une prestation Conceptuo peut donc valoir la somme de k ouvrages amont × coefficients. Le champ coefficient gère la composition.
Le mapping dérive à chaque millésime
Codes amont qui changent, ouvrages ajoutés/supprimés. À chaque sync, un contrôle doit détecter les referential_ouvrage_id orphelins (mappés mais disparus du nouveau millésime) et les passer en status = review. Sans ce garde-fou, le mapping pourrit silencieusement.
Amorçage du mapping
On réutilise le travail déjà fait (base de prestations propre + libellés connus). Rapprochement automatique par similarité (mots-clés métier + embeddings sur les libellés) qui propose des candidats avec un score de confiance ; un humain valide en back-office.
confidence sert à prioriser la file de validation, pas à valider automatiquement.4Le prix récupéré devient le prix Standard
Une fois la prestation mappée :
C'est le prix citable et opposable — celui qui porte la crédibilité. On stocke la provenance sur la ligne (provider + millésime + codes) pour pouvoir afficher « prix issu de Batiprix/Batichiffrage millésime 2026 » et tenir un audit.
prix_standard à la médiane observée. Un écart fort = mapping à revoir ou positionnement à documenter.5Définir Éco / Standard / Premium
Raisonnement métier
Pour une tâche donnée, la pose (main-d'œuvre) varie peu ; ce qui fait l'Éco vs Premium, c'est surtout le produit. On fige donc la pose et on fait varier la fourniture — ce qui s'aligne exactement avec l'architecture existante (typage POSE / F+P / FOURNITURE + base produits scrapée taguée Éco/Standard/Premium).
Règle par type de prestation
| pricing_type | Exemples | Comment varient les gammes |
|---|---|---|
| avec produit | carrelage, parquet, vasque, robinetterie, porte | Pose constante ; fourniture remplacée par le prix produit réel scrapé au tag de gamme (Éco/Std/Premium) de la linked_product_category |
| pose seule | dépose, ragréage, démolition | Pas de produit → coefficient de finition par famille (pas global), assumé comme un vrai écart de niveau de service |
Formules
Prestation avec produit — on éclate le Standard amont en pose + fourniture, la pose reste constante :
prix_standard = mo_ht + fourniture_standard // ≈ prix amont (calage)
prix_premium = mo_ht + fourniture_premium // produit tagué Premium
Prestation de pure main-d'œuvre — coefficient de finition par lot, paramétrable et documenté :
prix_standard = mo_ht
prix_premium = mo_ht × k_premium // ex. 1,25
Exemple chiffré — SOL-008 « Pose carrelage sol » (avec produit)
| Composant | Éco | Standard | Premium |
|---|---|---|---|
| Pose (mo_ht, constante) | 45 € | 45 € | 45 € |
| Fourniture (produit scrapé selon gamme) | 30 € | 45 € | 75 € |
| Prix /m² HT | 75 € | 90 € | 120 € |
Avantage : Éco et Premium ne sont plus inventés, ils sont adossés à des prix produits réels et sourcés (IKEA / Castorama / Leroy Merlin). Le Standard reste calé sur le référentiel, ce qui permet le contrôle mo_ht + fourniture_standard ≈ prix_moyen amont.
6Fallback : prestation absente de notre base
Quand le client demande une prestation hors de la base curée, on cherche dans le miroir local du référentiel (recherche plein-texte / sémantique sur les 28 000–80 000 ouvrages).
- Jamais de substitution silencieuse. On présente les 3–5 ouvrages les plus proches avec leur score ; le pro (ou le client) choisit.
- La ligne créée est marquée
source = referential,is_assumption = true, et tarifée selon la même logique qu'au §5 (le prix amont devient son Standard, Éco/Premium dérivés). - Ces lignes ad hoc alimentent une file de revue en back-office. Si une prestation revient souvent, un admin la promeut dans la base canonique.
7Indexation régionale & temporelle
Régional : si Batichiffrage est retenu, les prix sont déjà régionalisés (champ region). Sinon, on applique un coef_region Conceptuo documenté.
Temporel : on indexe entre la date du millésime et la date du devis via les indices officiels INSEE BT01 (national tous corps d'état) et BT50 (rénovation – entretien). Gratuits, publiés au Journal officiel — donc opposables.
8Hiérarchie des sources de prix
Trois sources vont coexister. La précédence doit être fixée une fois pour toutes :
| Priorité | Source | Rôle |
|---|---|---|
| 1 (override) | Prix custom TCE | Chaque entreprise sur sa propre base : écrase tout pour ses devis |
| 2 (défaut générique) | Référentiel (Batiprix/Batichiffrage) | Prix par défaut Agences / Particuliers — l'ancre opposable |
| 3 (calage interne) | Médianes Bernadette | Contrôle de réalisme et calibrage des coefficients — pas affiché |
9Contrats d'API internes (Conceptuo)
Ce que notre backend expose au front et au microservice de génération de devis :
// Prix d'une prestation, toutes gammes, avec provenance GET /api/prestations/{id}/pricing?region=IDF&date=2026-06-15 → { "prestation_id": "SOL-008", "unit": "m2", "eco": 75, "standard": 90, "premium": 120, "source": "batiprix", "millesime": 2026, "provider_codes": ["..."], "derived": { "eco": true, "premium": true } // gammes dérivées, pas sourcées } // Recherche fallback dans le miroir référentiel POST /api/referential/search { "query": "pose receveur extra-plat 90x90", "limit": 5 } → [ { "ouvrage_id": ..., "libelle": "...", "unite": "u", "prix_moyen_ht": ..., "confidence": 0.82 }, ... ] // État du mapping (back-office) GET /api/admin/mapping?status=review&millesime=2026
10Points de vigilance
11Prérequis & séquencement
Ne pas lancer l'intégration API tant que :
- (1) la licence autorisant l'affichage B2B2C + stockage local n'est pas confirmée par écrit ;
- (2) le contrat de mapping (composition N..N, coefficients, gestion du millésime) n'est pas tranché.
Ces deux décisions conditionnent tout le schéma de données. Une fois actées : sync miroir → amorçage + revue du mapping → moteur de prix Standard → dérivation gammes (lot pilote : Sols, où pose/fourniture se séparent proprement) → fallback → indexation.
Conceptuo — spécification de travail. Contenu à valider avant implémentation (notamment le préalable licence).