Un bug courant dans les systèmes de devis faits maison : le PDF affiche le prix au moment où le client l'ouvre. Vous ajustez le catalogue le mardi, le client ouvre le devis le jeudi, le prix imprimé est faux, le client est mécontent, et personne ne peut expliquer ce qui s'est passé.
Ce bug est insidieux parce qu'il ne ressemble pas à un bug. Le système fonctionne correctement — il affiche le prix actuel. Le problème, c'est que le « prix actuel » est la mauvaise réponse pour un document créé la semaine dernière. Le devis est un snapshot de prix d'un accord commercial à un moment précis. Le catalogue est un document vivant. Confondre les deux est une erreur de catégorie, et c'en est une qui coûte vraiment de l'argent lorsqu'un client exige le respect d'un devis que vous ne pouvez pas honorer au prix d'origine.
Snapshot à la création de la ligne
Chaque QuoteItem porte un unit_price capturé à partir du Product au moment de la création de la ligne. Les mises à jour du catalogue ne réécrivent jamais l'historique rétroactivement. D2 et D11 dans le journal des décisions sont explicites : le snapshot est le contrat.
L'implémentation est directe : lorsqu'une ligne de devis est créée, le service lit le unit_price actuel du produit et l'écrit dans QuoteItem.unit_price. À partir de ce moment, les deux valeurs sont indépendantes. Le prix du produit peut changer cent fois — la ligne du devis conserve le prix qui était en vigueur lorsque la ligne a été créée. Si le commercial souhaite utiliser le prix mis à jour, il doit explicitement re-tarifer la ligne.
Pourquoi ne pas simplement re-consulter ?
Re-consulter signifie que chaque rechargement affiche un total légèrement différent. Votre PDF imprimé et la base de données se contredisent. Deux clients différents peuvent se retrouver avec des prix différents pour le même devis historique. Le snapshot élimine tout cela.
La dimension juridique rend cela non négociable. Dans la plupart des juridictions, un devis est une offre. Une fois accepté, il devient un contrat au prix indiqué. Si votre système change silencieusement le prix entre le moment où vous avez envoyé le devis et celui où le client l'a accepté, vous absorbez la différence ou vous vous disputez avec un client qui possède un PDF comportant un numéro différent. Aucun de ces deux résultats n'est bon pour l'entreprise.
Les remises vivent aussi sur la ligne
discount_pct est par ligne, capturé en même temps. line_total est une propriété calculée — quantité × unit_price × (1 − discount_pct / 100). Toute la mathématique est reproductible à partir de la ligne.
Comme chaque donnée d'entrée du total de ligne est stockée sur la ligne, le total est déterministe. Deux personnes différentes qui consultent le même devis, à six mois d'écart, verront les mêmes chiffres. Un auditeur peut recalculer chaque ligne à partir des champs stockés et vérifier que le total correspond. Il n'y a ni formule cachée, ni recherche dynamique, ni données de référence qui auraient pu changer depuis la création du devis. La ligne est autonome et auto-démontrable.
