Clôturer un devis est le point de transition entre la vente et l'exécution. C'est aussi la transition la plus dangereuse de tout système de distribution lorsqu'on se trompe.
Dans un système maison, « clôturer » peut se limiter à modifier une cellule de statut dans une feuille de calcul. Dans un ERP, cela peut déclencher une cascade d'effets secondaires non documentés. Dans Quotery, ce sont cinq étapes explicites au sein d'une seule transaction de base de données, et chacune d'elles laisse une trace dans le registre. Voici la séquence exacte.
Les cinq étapes, atomiques
1. Verrouillez la ligne du devis avec select_for_update(). Cela sérialise les clôtures concurrentes sur le même devis — un seul appelant poursuit, les autres font la queue.
2. Pour chaque ligne, verrouillez le StockItem correspondant avec select_for_update() trié par (product_id, location_id). Le tri est essentiel : il garantit que deux devis concurrents clôturant sur des ensembles de produits qui se chevauchent acquièrent les verrous dans le même ordre, ce qui évite les interblocages.
3. Incrémentez reserved sur chaque StockItem et écrivez une ligne RESERVE dans le registre StockMovement pour chaque ligne. Chaque ligne RESERVE contient l'identifiant du devis, l'identifiant du produit, l'identifiant de l'emplacement, la quantité et l'horodatage.
4. Construisez une liste de ruptures pour toute ligne qui n'a pas pu être entièrement réservée. Cela relève de l'information — le devis se clôture quand même. L'appelant reçoit un tableau shortages avec les identifiants des produits et l'écart entre la quantité demandée et la quantité disponible.
5. Faites passer le statut du devis à closed et validez la transaction.
Les ruptures sont une information, pas un échec
Par conception, close_quote ne renvoie pas une erreur 409 en cas de stock faible. Vous recevez un tableau shortages avec des chiffres exacts. Les équipes commerciales peuvent décider de la marche à suivre — réserver quand même, ajuster ou négocier.
C'est l'inversion délibérée décrite dans l'article sur la sur-réservation. La plupart des systèmes traitent la rupture de stock comme une erreur bloquante. Nous la traitons comme un signal. Le commercial connaît son pipeline mieux que le système. Il sait ce qui arrive la semaine prochaine, quels clients sont flexibles et quelles lignes peuvent être réparties entre plusieurs expéditions. Le rôle de l'outil est de fournir les chiffres — pas de prendre la décision à sa place.
Ce qui est annulé Tout. La transaction est atomique. Une erreur de base de données à l'étape 3 annule les étapes 1 et 2. Vous ne vous retrouvez jamais avec une réservation incomplète.
En pratique, le principal mode de défaillance est un problème de connectivité avec la base de données ou un interblocage (résolu par l'ordre des verrous de l'étape 2). L'appelant relance et la transaction s'exécute proprement. Il n'existe pas d'état « devis bloqué », pas de situation où « le statut indique clôturé mais le stock n'a pas été réservé », pas de tâche de nettoyage qui répare un état cassé pendant la nuit. L'atomicité signifie que la machine à états ne connaît que deux issues : soit la transaction a été validée et tout est cohérent, soit elle ne l'a pas été et rien n'a changé.
