Le stock, de l’argent qui dort
Un stock sert à répondre à la demande sans attendre le fournisseur. Mais chaque article en rayon ou en entrepôt est de l’argent immobilisé : il a été payé, il occupe de la place, il peut se périmer ou devenir obsolète. Le pilotage des stocks cherche l’équilibre entre deux risques : la rupture (perdre des ventes) et le surstock (immobiliser de l’argent inutilement).
Valeur, couverture et rotation
items["stock_value"] = items["stock_units"] * items["unit_cost"]
items["daily_usage"] = items["annual_usage"] / 365
items["coverage_days"] = items["stock_units"] / items["daily_usage"]
items["usage_value"] = items["annual_usage"] * items["unit_cost"]
items["rotation"] = items["usage_value"] / items["stock_value"]
- La couverture répond à la question : « si plus rien n’arrive, combien de jours puis-je tenir ? ». P1 : 400 unités pour 13,7 unités par jour, soit environ 29 jours.
- La rotation dit combien de fois le stock « tourne » dans l’année. P1 : 50 000 euros consommés pour 4 000 euros de stock, soit une rotation de 12,5.
Les deux indicateurs sont liés : couverture en jours ≈ 365 / rotation. Au global, on calcule la rotation sur les totaux (consommation totale / stock total), comme tout ratio. Idéalement, le dénominateur est un stock moyen sur la période (moyenne de relevés mensuels) plutôt qu’une photo à une date.
Repérer les surstocks
Un seuil simple, fixé avec les achats, signale les articles trop couverts :
overstock = items.index[items["coverage_days"] > 120].tolist()
Un article couvert pour six mois alors que le fournisseur livre en deux semaines immobilise de l’argent sans raison. À l’inverse, une couverture inférieure au délai fournisseur annonce une rupture (leçon 2 et leçon 4).
La classification ABC
Tous les articles ne méritent pas la même attention. Le principe de Pareto s’observe souvent : une petite partie des articles représente l’essentiel de la valeur consommée. La classification ABC l’exploite :
abc = items.sort_values("usage_value", ascending=False)
abc["cum_share"] = abc["usage_value"].cumsum() / abc["usage_value"].sum()
abc["abc_class"] = "C"
abc.loc[abc["cum_share"] <= 0.95, "abc_class"] = "B"
abc.loc[abc["cum_share"] <= 0.80, "abc_class"] = "A"
- On trie les articles par valeur de consommation décroissante.
- On calcule la part cumulée.
- On classe : A jusqu’à 80 %, B jusqu’à 95 %, C au-delà.
Ici, P1 et P2 (2 articles sur 8) font 75 % de la consommation : ce sont les A. Les seuils 80 / 95 sont une convention courante, pas une règle : certaines entreprises utilisent 70 / 90, ou classent sur la marge plutôt que sur la consommation. Écrivez votre choix.
Croiser ABC et stock
La classification devient utile quand on la croise avec le stock :
summary = abc.groupby("abc_class").agg(skus=("abc_class", "size"),
usage_value=("usage_value", "sum"),
stock_value=("stock_value", "sum"))
Les articles C ne pèsent que 6 % de la consommation, mais 21 % de la valeur du stock (3 000 euros sur 14 000) : c’est là que dort l’argent. Politique typique :
- A : prévisions soignées, révision fréquente des paramètres, suivi fournisseur rapproché ;
- B : règles standard ;
- C : commandes groupées, réduction des références, déstockage des articles dormants.
À retenir
- Couverture = stock / consommation journalière ; rotation = consommation / stock moyen.
- La classification ABC trie par valeur et classe selon la part cumulée, avec des seuils écrits.
- Croisez ABC et valeur de stock pour savoir où agir.
Exercices
Les solutions et les tests restent sur le serveur. Dans l'espace étudiant, ton code est exécuté et corrigé, et le tuteur donne des indices avant la solution.
Couverture et rotation
Ajoutez à
itemsles colonnesstock_value,coverage_daysetrotation(consommation annuelle en valeur / valeur du stock). Calculezglobal_rotationsur les totaux etoverstock, la liste des articles couverts plus de 120 jours.import pandas as pd items = pd.DataFrame({ "sku": ["P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8"], "stock_units": [400, 300, 200, 100, 150, 300, 50, 100], "unit_cost": [10.0, 10.0, 10.0, 5.0, 10.0, 5.0, 20.0, 5.0], "annual_usage": [5000, 2500, 800, 1400, 400, 600, 100, 200], # unités consommées par an }).set_index("sku") # global_rotation = ... # overstock = ...Classification ABC
Construisez
abc:itemstrié par valeur de consommation décroissante, avecusage_value,cum_shareetabc_class(A si part cumulée ≤ 0,80, B si ≤ 0,95, sinon C). Construisezsummary(index = classe) avecskus,usage_valueetstock_value, puisc_stock_share: la part de la valeur du stock détenue par les articles C.import pandas as pd items = pd.DataFrame({ "sku": ["P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8"], "stock_units": [400, 300, 200, 100, 150, 300, 50, 100], "unit_cost": [10.0, 10.0, 10.0, 5.0, 10.0, 5.0, 20.0, 5.0], "annual_usage": [5000, 2500, 800, 1400, 400, 600, 100, 200], # unités consommées par an }).set_index("sku") # abc = ... # summary = ... # c_stock_share = ...Lire la rotation
Un article a une rotation annuelle de 4. Quelle est sa couverture approximative ?
- 4 jours
- Environ 91 jours (trois mois)
- Environ 365 jours
- 4 mois
Quiz de fin de leçon
Stock de 600 unités, consommation de 20 unités par jour. Couverture ?
- 12 jours
- 30 jours
- 620 jours
Dans une classification ABC, les articles A sont :
- Les plus nombreux
- Ceux qui pèsent le plus dans la valeur consommée
- Ceux qui ont le plus de stock
Pourquoi classer sur la valeur plutôt que sur les quantités ?
- Parce que les quantités sont inconnues
- Parce qu'une unité d'un article cher pèse plus qu'une unité d'un article bon marché
- C'est indifférent
La suite du cours
- Ruptures et taux de service15 min
- Prévoir la demande : naïve, moyenne mobile et lissage exponentiel18 min
- Délais fournisseurs, stock de sécurité et point de commande18 min
- Optimisation simple : quantité économique et politique de réapprovisionnement18 min