La semaine type

Le cycle quotidien décrit ce que fait le moteur heure par heure. Cette page prend l'échelle du dessus — la semaine — parce que c'est là que naissent la plupart des « je ne comprends plus mon stock » : deux calendriers tournent en parallèle, le physique (collectes, tournées, comptages) et celui de l'application (créneaux d'envoi, dates de réception prévues, scellés automatiques), et ils ne coïncident pas exactement.

Astuce

La seule idée à retenir. L'application date le stock à l'heure prévue (envoi + délai de réception), pas à l'heure réelle (la collecte du matin). Tout le reste — le décalage, les fenêtres à risque, quand compter, quoi régler — découle de cet écart.

La semaine décrite ici est la configuration de production réelle d'un marchand type (épicerie de producteurs, ~100 fournisseurs), mais la mécanique — créneau d'envoi + délai de réception

  • grâce d'auto-validation — est la même pour tout tenant.

La règle, tout de suite

Comptez ce qui est physiquement sur l'étagère, au moment où vous comptez — l'appli aligne le reste. Une seule chose à éviter : les deux courtes fenêtres où la marchandise est déjà sur l'étal mais pas encore « reçue » côté application. Un comptage qui y tombe est doublé : l'appli rajoute la marchandise par-dessus au scellé suivant.

Fenêtre Statut Pourquoi
Mardi, collecte → 12:15 (~3 h) ⚠️ à risque Vague 1 sur l'étal mais « pas encore reçue » pour l'appli
Jeudi, collecte → 22:15 (~13 h) ⚠️ à risque Vague 2 sur l'étal mais « pas encore reçue » pour l'appli
Tout le reste de la semaine ✅ sûr Y compris le vendredi après-midi

Et le raccourci qui rend ces fenêtres sans objet : confirmer la réception (ou valider la tournée) au moment physique, dans l'UI. Le geste manuel date l'arrivée à l'instant vrai et court-circuite toute supposition — le scellé automatique n'est qu'un filet. Le reste de la page explique pourquoi ces fenêtres existent et elles tombent.

Les deux vagues fournisseurs

Chaque fournisseur porte des créneaux d'envoi (sendSchedules), un délai de commande (deliveryDays, pilote l'affectation des créneaux — 2 par défaut) et un délai de réception (receptionLeadDays, pilote la date d'arrivée attendue — 1 jour pour tous les fournisseurs amisferme). En production, l'essentiel du volume tient en deux vagues :

Vague Créneau d'envoi Fournisseurs Réception attendue (délai de réception 1 j) Scellé auto si non confirmée
Vague 1 lundi 12:15 ~95 mardi 12:15 mercredi ~02:01
Vague 2 mercredi 22:15 ~87 jeudi 22:15 vendredi ~12:01

La date de réception attendue est mécanique : expectedDeliveryDate = envoi + (receptionLeadDays ?? deliveryDays) (voir Commandes fournisseur). C'est cette date — pas la date d'arrivée réelle — que lit l'auto-validation pour supposer la réception, et que lit la loi de l'ancre pour décider si un comptage a déjà absorbé la marchandise.

La semaine, sur les deux calendriers

Jour Côté marchand (physique) Côté application (dates prévues)
Lundi Préparation ; l'appli envoie la vague 1 à 12:15 Emails vague 1 partis ; brouillons figés
Mardi Collecte vague 1 (matin) — l'étal est réassorti 12:15 : date de réception attendue vague 1 atteinte (envoi + 1 j)
Mercredi Tournées client ; l'appli envoie la vague 2 à 22:15 ~02:01 : réception vague 1 supposée (grâce 12 h écoulée, balayage 2-horaire) ; emails vague 2 partis
Jeudi Collecte vague 2 (matin) ; tournées client 22:15 : date de réception attendue vague 2 atteinte
Vendredi Tournées client ~12:01 : réception vague 2 supposée
Samedi Tournées client Scellés des tournées de la veille seulement
Dimanche Régime de fond seulement (syncs, files)

L'animation ci-dessous rejoue la semaine avec des quantités illustratives. Suivez les deux compteurs : l'étal réel (trait plein vert) prend la marchandise à la collecte ; le stock vu par l'appli (pointillé violet) ne la prend qu'au scellé automatique, quelques heures plus tard (délai de réception 1 j + grâce 12 h). La bande ambrée entre les deux courbes matérialise l'écart, et les zones rouges sont les fenêtres où un comptage physique serait doublé. Cliquez n'importe où sur la frise pour tester un moment de comptage : le verdict vous dit s'il serait sûr ou doublé, et de combien.

Note

Les heures de scellé (02:01, 12:01) sont des heures serveur (UTC) — comme sur la page cycle quotidien. Les créneaux d'envoi (12:15, 22:15) sont ceux affichés dans la configuration des fournisseurs.

Le décalage résiduel : collecté le matin, « reçu » à l'heure prévue

Même avec le délai de réception à 1 jour, un petit décalage subsiste : la marchandise est physiquement sur l'étal le matin de la collecte (mardi ~09:00, jeudi ~09:00), mais l'application ne la date qu'à l'heure prévue (mardi 12:15, jeudi 22:15) et ne la scelle qu'après la grâce de 12 h (mercredi ~02:01, vendredi ~12:01). D'où deux conséquences, désormais bornées à quelques heures :

  1. Le stock apparaît avec quelques heures de retard dans l'appli — sauf confirmation manuelle de la réception, qui date l'arrivée à l'instant vrai.
  2. Un comptage fait dans la fenêtre à risque est doublé. La loi de l'ancre n'absorbe que les événements dont la date prévue est antérieure au comptage. Un comptage fait entre la collecte réelle et l'heure prévue (mardi 09:00 → 12:15, ou jeudi 09:00 → 22:15) contient déjà la marchandise… que l'appli croit « pas encore reçue » et rajoute par-dessus au scellé suivant : +N unités fantômes sur un comptage pourtant juste.
Attention

Pourquoi ces fenêtres sont désormais courtes. Jusqu'au 2026-07-16, le délai de réception n'existait pas comme champ propre : la réception était datée envoi + deliveryDays (soit + 2 j), donc vague 1 mercredi 12:15 et vague 2 vendredi 22:15, scellées jeudi 02:01 / samedi 12:01 — un à deux jours après l'arrivée réelle. Les fenêtres à risque duraient alors 26 h et 36 h : c'est la signature du ticket #2936, « j'ai compté vendredi après-midi, et samedi mon stock avait grossi tout seul ». Le comptage tombait avant la date prévue (vendredi 22:15) d'une marchandise collectée le jeudi ; ni le comptage ni la réception supposée n'étaient faux isolément — la date de réception attendue mentait d'un jour. Depuis que receptionLeadDays est un champ dédié (livré par la PR #848, backfillé à 1 j pour tous les fournisseurs amisferme), les fenêtres tombent à ~3 h et ~13 h, et un comptage du vendredi après-midi est sûr.

Quand compter le stock

La règle ci-dessus suffit pour les réceptions fournisseurs. Une seule précision, côté commandes client du jour : leur deliveryDate porte une heure. Une commande dont l'heure de livraison est déjà passée est considérée comme partie (pas de second décrément — ne comptez pas ces produits) ; une commande prévue plus tard dans la journée sera décomptée à son heure (comptez ces produits s'ils sont encore là). Le plus simple : compter le soir après les tournées, ou le matin avant les départs.

Ajuster la semaine

Trois leviers, du plus efficace au plus ponctuel :

  1. Régler le « Délai de réception » du fournisseur (fiche fournisseur, carte Paramètres de livraison — champ receptionLeadDays, livré le 2026-07-16 et backfillé à 1 jour pour tous les fournisseurs amisferme). Vide = hérite du délai de commande. ⚠️ Ne jamais réduire deliveryDays à la place : ce champ sert aussi à choisir quel email couvre quelle commande client (fenêtre d'envoi = livraison client − délai − 1 j de marge). Avec les deux créneaux hebdomadaires, la valeur 2 est précisément ce qui met les livraisons de mercredi/jeudi sur l'email du lundi et celles de vendredi/samedi sur celui du mercredi — la passer à 1 enverrait les commandes du jeudi sur le mauvais email et laisserait celles du samedi sans créneau (voir la règle épinglée par reception-lead-days.test.ts).
  2. Confirmer la réception à la collecte. Le geste UI au moment physique est le chemin primaire — il date la réception à l'instant vrai, et l'auto-validation n'a alors rien à supposer. Le scellé automatique n'est qu'un filet de sécurité, pas le mode nominal.
  3. Annuler une réception supposée à tort. Sur la page Stock (/manage/ecommerce/inventory/stock-ledger), le panneau « Réceptions supposées » liste tout ce que le filet a scellé, avec un bouton « Annuler la réception » (retire le stock, restaure les projections, rouvre la commande — et la commande ne sera pas re-scellée par le balayage suivant). Voir Écrans du back-office.

La grâce globale (AUTO_VALIDATE_GRACE_HOURS, 12 h) est volontairement hors de cette liste : la raccourcir rapproche les scellés des dates prévues mais ré-ouvre le risque de sceller des livraisons pas encore advenues — c'est le délai par fournisseur qu'il faut régler, pas la grâce. Voir Flags & configuration.

Et les commandes client, dans tout ça ?

Symétriquement aux réceptions, les livraisons client sont supposées effectuées à deliveryDate + 12 h : une tournée du vendredi 11:00 non confirmée est scellée dans la nuit de vendredi à samedi (balayage de ~00:01), avec le décrément de stock correspondant. Là aussi le scellé UI (« Valider la tournée du jour ») au retour de tournée est le geste primaire ; le filet ne fait que rattraper l'oubli, environ 12 à 14 h après l'heure prévue.

En résumé

  • Deux calendriers : le physique (collectes mardi/jeudi, tournées mercredi→samedi) et le prévu (envoi + délai, scellé + grâce). Ils divergent d'autant plus que la date de réception attendue s'écarte de l'arrivée réelle.
  • Un comptage est sûr après l'heure de réception attendue de la dernière vague, ou avant sa collecte — jamais entre les deux. Aujourd'hui, les fenêtres à éviter font ~3 h (mardi) et ~13 h (jeudi).
  • Le geste manuel (confirmer réception / valider tournée) au moment physique court-circuite toutes les suppositions : c'est toujours la meilleure option.