GLO-4002 2026

Carnet · Iter 1 · aubergiste

GARD — Le garde-manger

Description

En tant qu’aubergiste, je peux approvisionner mon garde-manger et faire passer les nuits.

L’auberge ouvre bientôt et mes réserves sont vides. Rien n’arrive vite au Carrefour : mes fournisseurs viennent d’ailleurs. Chaque commande met donc un certain nombre de nuits à me parvenir. Je le sais quand je la passe, et je ne peux rien y faire. Pour ne plus y penser, je peux aussi souscrire un abonnement : la même quantité, à intervalle régulier, jusqu’à ce que je le résilie.

Au sous-sol, trois réserves. Elles ne se prêtent rien : une montagne de viande ne nourrira jamais un Plantagenêt, et je le découvrirai à mes dépens.

Réserve Une portion périme après La réserve contient au plus
Végétale 3 nuits 40 portions
Carnée 4 nuits 30 portions
Minérale 20 nuits 100 portions

Deux choses me coûtent cher, et je veux pouvoir les distinguer.

Ce qui pourrit. Une portion sert pendant sa durée de vie, en comptant la nuit où elle a été rangée, puis on la jette. Une portion végétale rangée ce soir servira donc ce soir et les deux nuits suivantes ; c’est au tri de la quatrième nuit qu’elle finira à la poubelle (voir critère 5 et l’exemple 1).

Ce qui déborde. Une réserve ne s’étire pas. Quand la livraison du soir ne rentre plus, je ne vais pas redéfaire des tablettes déjà garnies : je range ce qui rentre, et le reste disparaît dans la nuit (voir critère 7).

⚠️ Je range avant de trier. Une réserve peut être pleine de portions qui seront jetées cinq minutes plus tard et refuser quand même la livraison du soir. C’est arrivé. C’est comme ça (voir l’exemple 2).

Les commandes

Une commande précise une denrée, une quantité et un délai en nuits. Un délai de deux nuits signifie qu’elle sera rangée deux nuits après celle où je l’ai passée ; un délai nul, la nuit même.

Les abonnements

Un abonnement porte un nom que je choisis, une denrée, une quantité et une période en nuits. Il ne livre jamais la nuit où je le souscris : la première livraison arrive une période plus tard, et ainsi de suite.

Deux abonnements ne peuvent pas porter le même nom. Comme un nom peut être libéré par une résiliation déposée la même nuit, c’est à l’exécution que le conflit est constaté (voir critère 10).

Résilier un abonnement l’arrête pour de bon. Comme les actions passent avant les conséquences, une résiliation exécutée pendant une nuit tue la livraison prévue pour cette nuit-là (voir l’exemple 3).

Déroulement d’une nuit

Actions possibles
Commander des denrées.
Souscrire un abonnement.
Résilier un abonnement.
Conséquences d’une nuit (en ordre)
1. Ranger les livraisons attendues.
2. Jeter les denrées périmées.

Conditions de succès

# Description
1 Avant toute exécution, l’auberge se trouve à la nuit 0. Chaque exécution incrémente le numéro de nuit de 1 et le retourne.
2 Rappel: Les actions accumulées sont exécutées en ordre d’arrivée, puis la pile est vide.
3 Une commande est rangée après le nombre de nuits de son délai ; un délai de 0 la range pendant la nuit qui l’exécute.
4 Un abonnement livre pour la première fois après la nuit qui l’a exécuté (selon sa périodicité), jamais la nuit même de l’exécution.
5 Une portion sert pendant un nombre de nuits égal à la durée de vie de sa denrée, en comptant la nuit de son rangement, et elle est jetée au tri de la nuit suivante.
7 On range ce qui rentre ; le reste est perdu sans faire de rotation de dates ou de gestion de l’inventaire.
8 Les pertes sont consignées séparément selon leur cause : jetées ou débordées.
9 Chaque réserve est indépendante des deux autres : ni sa contenance, ni son contenu, ni ses pertes n’influencent les autres.
10 Un abonnement dont le nom est déjà porté par un abonnement actif est rejeté à l’exécution avec le motif SUBSCRIPTION_NAME_ALREADY_USED, et reste consultable.
11 Une résiliation portant sur un abonnement inexistant ou déjà résilié ne fait rien (idempotent)
12 Une résiliation exécutée pendant une nuit empêche la livraison prévue pour cette nuit-là.
13 Les événements (ex.: abonnement) qui n’ont pas encore été traités ne sont pas retournés par l’API

🖥️ À l’écran

  • Les arrivages de chacune des réserves séparément : combien, rangé quelle nuit, jeté quelle nuit.
  • Pour chaque réserve, les pertes consignées avec leur cause. C’est la seule information qui me dise si je commande trop, ou trop tôt.
  • Les abonnements, leur état, et la nuit de leur prochaine livraison.
  • Les commandes déjà exécutées dont la livraison est encore à venir, avec la nuit où elles seront rangées. Une commande déposée mais pas encore exécutée n’apparaît pas.

API

Dans le texte Dans l’API
végétale, carnée, minérale PLANT, MEAT, MINERAL

✅ Exécuter une nuit

POST /nights

➡️ HTTP 200 Ok

{
  "number": 1::int
}

🧑‍🏫 Normalement l’API devrait logiquement retourner 201 Created et pointer vers /nights/<number> mais pour simplifier le projet, nous n’utiliserons que l’information sur la dernière nuit via ../last et donc nous retournons 200 OK ici.


✅ Connaître l’état de l’auberge

GET /inn

➡️ HTTP 200 Ok

{
  "currentNight": 0::int
}

✅ Commander des denrées

POST /orders

{
  "foodType": "PLANT"::string(PLANT | MEAT | MINERAL),
  "quantity": 12::int,
  "deliveryDelay": 2::int
}

➡️ HTTP 202 Accepted


✅ Consulter les commandes en chemin

GET /orders

➡️ HTTP 200 Ok

[
  {
    "foodType": "PLANT"::string(PLANT | MEAT | MINERAL),
    "quantity": 12::int,
    "orderedAtNight": 3::int,
    "deliveryNight": 5::int
  }, ...
]

Ne figurent ici que les commandes déjà exécutées dont la livraison n’a pas encore eu lieu. Une commande déposée mais pas encore exécutée n’y est pas (voir critère 13), et une commande rangée en sort le soir même de son rangement, qu’elle soit entrée en entier, en partie ou pas du tout.

Une commande de délai nul n’y apparaît donc jamais : elle est rangée pendant la nuit qui l’exécute.

Les commandes sont retournées par nuit de livraison croissante, puis dans l’ordre où elles ont été exécutées. Les livraisons d’abonnement n’y figurent pas : elles se lisent dans GET /subscriptions, à nextDeliveryNight.


✅ Souscrire un abonnement

POST /subscriptions

{
  "subscriptionName": "Verger"::string,
  "foodType": "PLANT"::string(PLANT | MEAT | MINERAL),
  "quantity": 5::int,
  "period": 2::int
}

➡️ HTTP 202 Accepted


✅ Résilier un abonnement

DELETE /subscriptions/<subscriptionName::string>

➡️ HTTP 202 Accepted — que l’abonnement existe ou non.


✅ Consulter les abonnements

GET /subscriptions

➡️ HTTP 200 Ok

[
  {
    "subscriptionName": "Verger"::string,
    "foodType": "PLANT"::string,
    "quantity": 5::int,
    "period": 2::int,
    "status": "ACTIVE"::string(ACTIVE | CANCELLED | REFUSED),
    "refusalReason": "SUBSCRIPTION_NAME_ALREADY_USED"::string(SUBSCRIPTION_NAME_ALREADY_USED) || null,
    "nextDeliveryNight": 7::int
  }, ...
]

nextDeliveryNight est null dès que l’abonnement n’est plus actif.


✅ Consulter le garde-manger

GET /pantry

➡️ HTTP 200 Ok

{
  "reserves": [
    {
      "foodType": "PLANT"::string,
      "maxCapacity": 40::int,
      "stockedQuantity": 12::int,
      "shipments": [
        {
          "quantity": 12::int,
          "stockedAtNight": 4::int,
          "discardedAtNight": 7::int || null
        }, ...
      ],
      "totalDiscarded": 40::int,
      "totalOverflowed": 10::int
    }, ...
  ]
}

Les shipments n’incluent que ce qui a été stocké suite à la réception d’une commande ou d’un abonnement. Cela n’inclut donc pas ce qui est prévu pour arriver plus tard ou qui n’a pas encore été rangé.

Les trois réserves sont toujours présentes, même vides.

💡 Exemple 1 — combien de nuits sert une portion

Nous sommes à la nuit 0.

  1. POST /orders{ "foodType": "PLANT", "quantity": 12, "deliveryDelay": 0 }
  2. POST /nights{ "number": 1 }
Nuit Réserve végétale totalDiscarded
1 12 0
2 12 0
3 12 0
4 0 12

Les portions ont servi trois nuits, soit les nuits 1, 2 et 3, puis elles ont été jetées au tri de la nuit 4.

💡 Exemple 2 — la réserve pleine de portions condamnées

Nous sommes à la nuit 0.

  1. POST /orders{ "foodType": "PLANT", "quantity": 40, "deliveryDelay": 0 }
  2. POST /orders{ "foodType": "PLANT", "quantity": 10, "deliveryDelay": 3 }
  3. POST /nights{ "number": 1 }

Nuit 1 — 40 portions rangées, la réserve est pleine. Elles serviront les nuits 1, 2 et 3.

Nuit 4 :

  • on range d’abord : les 10 portions arrivent, la réserve est encore pleine à 40 sur 40. Les 10 portions sont perdues → totalOverflowed = 10 ;
  • on trie ensuite : les 40 portions de la nuit 1 sont jetées → totalDiscarded = 40.

Après la nuit 4, la réserve végétale est vide. J’ai perdu 50 portions et je n’ai rien à servir. Trier avant de ranger m’aurait laissé 10 portions, mais ce n’est pas ainsi que fonctionne mon sous-sol.

💡 Exemple 3 — la résiliation coupe la livraison du soir

L’abonnement Boucherie, de période 4, a été souscrit pendant la nuit 1. Il livre donc aux nuits 5, 9, 13, …

Nous sommes à la nuit 8.

  1. DELETE /subscriptions/Boucherie → 202
  2. POST /nights{ "number": 9 }

Pendant la nuit 9, la résiliation s’exécute avec les autres actions, donc avant qu’on range quoi que ce soit. La livraison prévue pour la nuit 9 n’a pas lieu.