É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.

Astuce

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 :

  1. 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).
  2. 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).
  3. 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.
  4. 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
Attention

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=1 pour un composeur vierge, ?orderVendor=<id> + ?orderSlot=yyyy-MM-dd pour 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.

Note

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.

Note

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 si Product.stock/stockLedger doivent ê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.
Attention

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