Écrans du back-office
Le back-office (/manage) expose sept surfaces autour du stock et du réappro fournisseur.
Chacune lit les mêmes tables (Product.stockLedger, StockAction, SupplierOrder), mais
elles n'écrivent pas toutes — et quand elles écrivent, chaque mouvement passe par les
chemins d'écriture canoniques (recordPhysicalMovement,
manageStock vérifié, StockAction émise dans la même transaction). Cette page dit quel
écran ouvrir pour quel besoin, et ce que chaque bouton fait réellement en base.
Quel écran ouvrir ?
flowchart TD
Q0{"Que voulez-vous faire ?"}
Q0 --> A["Corriger un stock"]
Q0 --> B["Comprendre un stock"]
Q0 --> C["Piloter le réappro"]
A --> A1{"Ampleur ?"}
A1 -->|"Quelques produits"| S1["vendors/stocks<br/>édition inline"]
A1 -->|"Comptage entrepôt"| S2["inventory/counts<br/>inventaire physique"]
B --> B1{"Quel angle ?"}
B1 -->|"Journal ligne à ligne"| S3["inventory/stock-actions"]
B1 -->|"Chronologie + KPIs"| S4["inventory/stock-ledger"]
C --> C1{"Quoi ?"}
C1 -->|"Qu'est-ce qui va manquer ?"| S5["inventory/mon-stock"]
C1 -->|"Relire les emails fournisseurs"| S6["vendors/orders/email-review"]
C1 -->|"Stock gonflé suspect"| S7["vendors/orders/phantom-stock"]
Et si la question est « est-ce que tout va bien ? » : /manage/inventory/health
(tableau de bord en lecture seule) et la puce cron-5 de l'écran stock-ledger.
Vue d'ensemble
| Écran | Route | Lit | Écrit | Permission clé |
|---|---|---|---|---|
| Stocks fournisseurs | /manage/ecommerce/vendors/stocks |
grille produits × fournisseur, valorisation | stock inline, import en masse, snapshots | vendors:read + stock:write |
| État de stock (ledger) | /manage/ecommerce/inventory/stock-ledger |
KPIs, chronologie produit, divergences | mouvements manuels, réconciliation, confirmations | stock:read / stock:write |
| Mon stock | /manage/ecommerce/inventory/mon-stock |
projection « va manquer » à 30 jours | suppression de brouillons fournisseurs, commande ad hoc | stock:read + vendors:write (écritures) |
| Inventaires physiques | /manage/ecommerce/inventory/counts |
sessions de comptage | ajustements au finalize | stock:write |
| Actions de stock | /manage/ecommerce/inventory/stock-actions |
le journal StockAction complet |
suppression avec/sans effet stock, bulk | stock:read / stock:write |
| Commandes fournisseurs | /manage/ecommerce/vendors/orders |
calendrier, stats, file email | envoi/report/annulation d'emails, actions de confiance | vendors:*, stock:write |
| Santé de l'inventaire | /manage/inventory/health |
alertes + indicateurs | — (lecture seule) | stock:read |
Stocks fournisseurs — /manage/ecommerce/vendors/stocks
La grille de travail : tous les produits avec leur fournisseur par défaut, le stock courant, les quantités en commande et en brouillon. C'est l'écran de correction ponctuelle.
Lit : vendors.getVendorsStocks (filtre par fournisseur via autocomplete). La requête
déclenche aussi la valorisation du stock (#2796) côté serveur — valeur totale en euros
au prix de gros, avec le compteur missingPriceCount des produits en stock sans prix
d'achat renseigné (donc non valorisés).
Écrit :
| Action | Procédure TRPC | Permission | Effet ledger |
|---|---|---|---|
| Édition inline d'un stock | vendors.updateProductStock |
stock:write |
StockAction COMPLETED source MANUAL (INCREMENT/DECREMENT/ADJUSTMENT selon le delta), raison optionnelle |
| Import CSV/XLSX en masse | vendors.bulkUpdateProductStock |
inventory:write |
une transaction, une SA par produit modifié |
| Snapshot d'inventaire | vendors.createStockSnapshot / deleteStockSnapshot |
stock:write |
fige la valorisation datée (items + valeur), aucun mouvement de stock |
L'export (dialog dédié) produit un fichier CSV/XLSX de l'état courant ; l'import le réinjecte. Les snapshots (#2796) gardent un historique daté de la valorisation, avec un détail par fournisseur dérivé des items figés — utile pour la comptabilité de fin de mois.
L'édition inline valide côté serveur (entier ≥ 0) et ne réindexe pas le moteur de recherche produit — elle est faite pour la correction rapide en rafale. Entrée = sauver, Échap = annuler.
État de stock — /manage/ecommerce/inventory/stock-ledger
L'écran le plus dense : la vérité du ledger rendue lisible. Quatre zones :
- Cartes KPI — Stock physique (avec sous-titre précommande quand le ledger signé est négatif), Réservé, Disponible, Sorties en attente (commandes non livrées).
- Tableau d'aperçu (
stockActions.getProductStockOverview) — un produit par ligne, filtres URL persistants : catégorie, fournisseur, critiques seuls, santé, divergences seules (stock ≠ stockLedger), produits non suivis inclus/exclus, tri, fenêtre de dates (défaut aujourd'hui → +14 jours). - Chronologie produit (
stockActions.getStockLedger) — le film ligne à ligne des entrées/sorties d'un produit sélectionné, avec les modales de détail commande client et bon fournisseur. - Puce santé cron-5 — sépare liveness et activité. Son titre est la liveness
(« le cron a-t-il tourné ? »), lue sur le heartbeat que la tâche estampille dans
bext-KV à la fin de chaque run (
health:auto-validate:{tenantId}:{siteId}, TTL 7 jours) : ≤ 5 h vert, ≤ 12 h ambre, > 12 h rouge — au regard de la cadence attendue (12:01 canonique + balayage toutes les 2 h via/etc/cron.d). L'horodatage de la dernière auto-validation (côté réceptions fournisseurs ⇣ et livraisons clients ⇡) est rétrogradé en info secondaire, « dernière activité » : un jour calme ne produit légitimement aucune ligne, donc l'âge du dernier scellé seul ne signifie jamais que le cron est mort. Si bext est injoignable, la puce retombe sur le statut par âge d'activité et le signale dans le popover. Voir Filets de sécurité.
Écrit (trois modales en stock:write — sauf l'aperçu de réconciliation
previewLedgerReconciliation, qui est en stock:read ; seul son apply
applyLedgerReconciliation écrit) :
| Modale | Procédure | Ce qui se passe |
|---|---|---|
| Mouvement manuel | stockActions.createManualStockMovement |
mouvement physique via recordPhysicalMovement — SA émise, miroir Product.stock recalculé |
| Réconciliation ledger | previewLedgerReconciliation (aperçu) puis applyLedgerReconciliation |
liste chaque produit où Product.stock ≠ Product.stockLedger ; chaque « réparation » émet une SA COMPLETED INVENTORY_ADJUSTMENT de (stock − ledger) pour réaligner en préservant l'invariant stockLedger == Σ SA |
| Confirmations en attente | stockActions.confirmStockAction |
passe une SA PENDING (projection de livraison fournisseur) en COMPLETED — applique le mouvement |
La réconciliation est un outil de réparation de divergence, pas un raccourci d'édition. Un aperçu (dry-run) s'affiche toujours avant application, et on peut ne réparer qu'une sélection. Si des divergences réapparaissent régulièrement, c'est qu'un chemin d'écriture contourne le contrat — voir Chemins d'écriture.
Mon stock — /manage/ecommerce/inventory/mon-stock
La vue marchand du quotidien : « Qu'est-ce qui va manquer, et qu'est-ce qui arrive ? ».
Projection à 30 jours glissants de la même source que l'aperçu ledger
(stockActions.getProductStockOverview), présentée en cartes produit avec :
- le panneau demande non couverte (commandes clients sans bon fournisseur en face) ;
- le groupage par fournisseur (
?group=true, deep-linkable) ; - la création de commande fournisseur ad-hoc depuis la carte (modales pilotées par l'URL :
?newOrder=1pour un composeur vierge,?orderVendor=<id>+?orderSlot=yyyy-MM-ddpour cibler un fournisseur/créneau, #2832).
Écrit très peu : la suppression d'un brouillon fournisseur
(vendors.deleteSupplierOrder) depuis le panneau de demande, et la création de commandes
via la modale — qui passe par la même chaîne gardée que le
réappro événementiel. Aucun mouvement de stock direct.
C'est l'écran à donner à un opérateur non technique : il lit la projection, il ne touche pas au ledger.
Inventaires physiques — /manage/ecommerce/inventory/counts
Le flux de comptage entrepôt en trois temps :
inventoryCount.create — choisir le périmètre (recherche produits, ajout/retrait
d'items possibles tant que la session n'est pas finalisée via addItems / removeItem).
La session est un brouillon : rien n'est écrit au stock.
inventoryCount.setCountedQty — saisir la quantité réellement comptée pour chaque ligne.
On peut s'arrêter, reprendre, corriger. Toujours aucune écriture stock.
inventoryCount.finalize (stock:write) — le seul moment qui écrit. Pour chaque ligne, le
delta est calculé contre le ledger signé (Product.stockLedger, jamais contre le miroir
bridé — règle 17) et un mouvement INCREMENT/DECREMENT est émis selon le signe de l'écart.
Les produits manageStock=false sont entièrement ignorés (ni écriture, ni SA). Un écart
nul avec miroir divergent répare le miroir sans émettre de SA. La finalisation date aussi un
snapshot de valorisation.
Sous backorder (STOCK_LEDGER_ALLOW_BACKORDER=true, prod depuis le 2026-07-09), un produit
peut avoir stockLedger = −3 et stock = 0. Compter « 5 » sur ce produit crée un mouvement
de +8, pas +5 — c'est le ledger signé qui fait foi. C'est le comportement attendu.
inventoryCount.cancel abandonne une session non finalisée sans trace au stock.
Actions de stock — /manage/ecommerce/inventory/stock-actions
Le journal. Chaque ligne est une StockAction : type (INCREMENT/DECREMENT/ADJUSTMENT),
source (MANUAL, ORDER_UPDATE, SUPPLIER_DELIVERY, INVENTORY_ADJUSTMENT…), statut
(PENDING/COMPLETED/CANCELLED), quantités avant/delta/après, auteur, raison. Lecture via
stockActions.getAll (stock:read), filtres et détail par ligne.
C'est la page canonique du journal. L'ancienne route /manage/dashboard/stock-actions
redirige ici (consolidation du 2026-07-11) en préservant les paramètres d'URL.
Écrit — uniquement de la suppression, et c'est le point délicat :
- Suppression bidirectionnelle (
stockActions.deleteWithOptionalStockEffect,stock:write) : le dialogue demande explicitement siProduct.stock/stockLedgerdoivent être réajustés en retour (le mouvement est annulé physiquement) ou si on supprime seulement la ligne de journal (le stock reste tel quel). Il n'y a pas de confirmation générique — ce choix est la confirmation. - Suppression en masse (
stockActions.bulkDelete) : un seul aller-retour serveur qui traite les ids dans l'ordre, côté serveur — deux suppressions avec effet stock sur le même produit ne doivent jamais se courser.
Supprimer avec effet stock est une réversion physique : elle passe par les mêmes
chemins d'écriture et, sous backorder, restaure
stock = max(0, ledger reversé). Supprimer sans effet stock casse volontairement le lien
journal↔compteur — réservez-le au nettoyage de lignes déjà connues comme erronées (doublons
d'import, etc.), sinon l'audit I5 signalera la dérive.
Commandes fournisseurs — /manage/ecommerce/vendors/orders
Le hub du réappro. Onglets : commandes, historique,
vue par site, par fournisseur, et calendrier (VendorOrderCalendarView, avec
glisser-déposer des bons entre créneaux). Stats de tête via
vendors.getSupplierOrdersStats.
File de relecture email — /manage/ecommerce/vendors/orders/email-review
La garantie « ce qui est relu est ce qui part » du pipeline
stage → review → send. Liste
vendors.listStagedSupplierOrders (filtres fournisseur, dates, aujourd'hui seulement,
reportés, modifiés, groupage par fournisseur) ; chaque ligne montre les valeurs
effectives (override fusionné) et une puce de dérive quand les items ont changé
depuis le stage.
| Action | Procédure | Effet |
|---|---|---|
| Envoyer | vendors.sendStagedSupplierOrderEmail |
re-rend depuis les items courants, bloque en CONFLICT si l'empreinte d'items a dérivé depuis le stage (« Re-préparez puis renvoyez ») |
| Re-préparer | vendors.stageSupplierOrderEmail |
régénère l'aperçu + l'empreinte |
| Reporter / annuler le report | vendors.deferSupplierOrderEmail |
décale deferUntil |
| Annuler la commande | vendors.cancelSupplierOrderFromReview |
annule + tague la provenance ; une commande CANCELLED ne s'emailera jamais |
Stock fantôme — /manage/ecommerce/vendors/orders/phantom-stock
Chasse au stock gonflé (typiquement du stock fictif hérité de la boutique en ligne —
voir Synchronisation PrestaShop). Lecture
vendors.getStockCoveragePage (products:read) avec recherche côté serveur
(debounce 350 ms) et filtre « suspects seulement » ; la colonne trusted montre ce que le
moteur de réappro utilisera réellement — plafonné par les
8 semaines de couverture quand le stock brut paraît
fantôme.
Deux actions de confiance (stock:write) :
vendors.setProductStock— corriger le chiffre (mouvement physique, SA émise) ;vendors.setProductStockTrusted— basculer le drapeau de confiance sans toucher au chiffre (le moteur ignore ou réutilise le stock brut).
À traiter avant d'activer une commande davantage pilotée par le stock : chaque suspect non résolu se traduit en sous-commande.
Santé de l'inventaire — /manage/inventory/health
Tableau de bord en lecture seule (inventory.getDashboardData) : alertes de stock,
indicateurs agrégés. Aucune action d'écriture — c'est le complément visuel des
filets de sécurité (audit stock-ledger-audit, sonde
quotidienne 06:30, puce cron-5 de l'écran ledger).
Permissions
Toutes les procédures sont protégées par RBAC (permissionProtectedProcedure) :
| Permission | Ouvre |
|---|---|
stock:read |
journal, ledger, aperçus, mon-stock, santé |
stock:write |
édition inline, mouvements manuels, réconciliation, confirmations, finalize d'inventaire, suppressions du journal, snapshots, actions de confiance phantom-stock |
vendors:read / vendors:write |
grille stocks fournisseurs, commandes fournisseurs, file email |
inventory:write |
import de stocks en masse (bulkUpdateProductStock) |
products:read |
lecture de la page stock fantôme |
Un profil « opérateur du quotidien » en lecture vit très bien avec stock:read +
vendors:read + l'accès à mon-stock et à la file email ; les actions d'écriture de mon-stock
(supprimer un brouillon fournisseur, créer une commande ad hoc) exigent toutefois
vendors:write. Réservez stock:write aux personnes qui comprennent la différence entre
supprimer une ligne de journal avec et sans effet stock.
Voir aussi
- Vue d'ensemble — le modèle complet en une page
- Le ledger de stock — autorité signée, miroir bridé, invariants
- Chemins d'écriture — le contrat que chaque bouton d'écriture respecte
- Commandes fournisseurs — réappro événementiel, créneaux, verrou
- La journée type — crons 06:00 / 06:30 / 07:15 / 12:01
- Filets de sécurité — audit, sonde, tests épinglés
- Configuration — les gates (
STOCK_LEDGER_ALLOW_BACKORDER,STOCK_TRUST_MAX_COVER_WEEKS…)