thecodex.expert · The Codex Family of Knowledge
Tier 3 · Upper-Intermediate · Python Project

Inventory System

Track stock items with quantities, restock and sell, and flag low stock — saved to disk. A real business-style system.

🧠 Teaches how to think spoonfed, every age Last verified:

1 The Problem

We want an inventory system: track products and quantities, restock (add stock), sell (remove stock, but never below zero), and report which items are low. It teaches modelling a collection of items with business rules and reporting on them — the core of any stock, warehouse, or store system.

Where this shows up: retail point-of-sale, warehouse management, e-commerce stock, supply chains, even game item systems. Tracking quantities with rules (no negative stock) and reporting (what is low) is a classic business need.

2 How to Think About It

Think about the operations and rules, before any code:

The plan — in plain English
1. Inventory maps each product to its quantity. → 2. Restock adds quantity. → 3. Sell removes quantity — but refuses if not enough in stock. → 4. Low-stock report lists items below a threshold. → 5. Persist to a file.

Restock

Sell

Yes

No

Report

Inventory: item to quantity

Action?

Add quantity

Enough stock?

Subtract quantity

Reject

List items below threshold

3 The Build — explained part by part

Here is the complete inventory system as a class. Each part is explained below.

Pythoninventory.py
import json
import os

class Inventory:
    def __init__(self, filename="inventory.json"):
        self.filename = filename
        self.items = self._load()

    def _load(self):
        if os.path.exists(self.filename):
            return json.load(open(self.filename))
        return {}

    def _save(self):
        json.dump(self.items, open(self.filename, "w"))

    def restock(self, item, quantity):
        self.items[item] = self.items.get(item, 0) + quantity
        self._save()

    def sell(self, item, quantity):
        if self.items.get(item, 0) < quantity:
            raise ValueError(f&quot;Not enough {item} in stock&quot;)
        self.items[item] -= quantity
        self._save()

    def low_stock(self, threshold=5):
        return [item for item, qty in self.items.items() if qty < threshold]

if __name__ == &quot;__main__&quot;:
    inv = Inventory()
    inv.restock(&quot;apples&quot;, 10)
    inv.sell(&quot;apples&quot;, 7)
    print(&quot;Low stock:&quot;, inv.low_stock())
What each part does — in plain words
class Inventory: — bundles the stock data with the operations that change it and the file it persists to.

self.items.get(item, 0) — look up a quantity, defaulting to 0 for items not yet stocked. This avoids errors on new products.

restock — add to the current quantity (or start from 0), then save.

sell — the business rule: refuse to sell more than is in stock by raising an error. Stock can never go negative.

low_stock(threshold=5) — a report: return every item whose quantity is below the threshold, using a list comprehension. Default threshold of 5, overridable.

_load / _save — the leading underscore signals these are internal helpers, not meant to be called from outside. Every change is saved immediately.
Common mistakes — and how to avoid them
✗ Allowing negative stock — selling more than available corrupts the count.
✓ Check stock in sell and raise if there is not enough.
✗ Using self.items[item] directly for a new item — KeyError.
✓ Use self.items.get(item, 0) so new items default to 0.
✗ Forgetting to save after each change — data lost on restart.
✓ Call _save() after restock and sell.

4 Test & Prove Each Part

We test restocking, the no-oversell rule, and the low-stock report.

Restocking increases quantity
Selling within stock reduces quantity
Overselling is refused
Low-stock report flags items below the threshold
Pythontest_inventory.py
import pytest


class Inventory:
    def __init__(self):
        self.items = {}

    def restock(self, item, quantity):
        self.items[item] = self.items.get(item, 0) + quantity

    def sell(self, item, quantity):
        if self.items.get(item, 0) < quantity:
            raise ValueError(&quot;Not enough stock&quot;)
        self.items[item] -= quantity

    def low_stock(self, threshold=5):
        return [i for i, q in self.items.items() if q < threshold]


def test_restock():
    &quot;&quot;&quot;Restocking adds quantity.&quot;&quot;&quot;
    inv = Inventory()
    inv.restock(&quot;apples&quot;, 10)
    assert inv.items[&quot;apples&quot;] == 10


def test_sell():
    &quot;&quot;&quot;Selling reduces quantity.&quot;&quot;&quot;
    inv = Inventory()
    inv.restock(&quot;apples&quot;, 10)
    inv.sell(&quot;apples&quot;, 3)
    assert inv.items[&quot;apples&quot;] == 7


def test_oversell_refused():
    &quot;&quot;&quot;Selling more than stock raises an error.&quot;&quot;&quot;
    inv = Inventory()
    inv.restock(&quot;apples&quot;, 2)
    with pytest.raises(ValueError):
        inv.sell(&quot;apples&quot;, 5)


def test_low_stock():
    &quot;&quot;&quot;Items below threshold are reported.&quot;&quot;&quot;
    inv = Inventory()
    inv.restock(&quot;apples&quot;, 2)
    inv.restock(&quot;bread&quot;, 20)
    assert inv.low_stock(5) == [&quot;apples&quot;]

Run with pytest -v. As with the bank, we test that bad actions (overselling) are refused, not just that good ones work. The low-stock test proves the report logic is correct.

5 The Interface

restockinv.restock(item, qty)add stock
sellinv.sell(item, qty)remove stock (refuses oversell)
reportinv.low_stock(threshold)items running low
Returns
["apples", "milk"]

6 Run It & Automate It

Run it locally
python3 inventory.py
Restock and sell items, then check what is low. Stock is saved in inventory.json.

Jenkins runs the tests automatically — each line explained below.

What you should see when it works
Terminala real run
Low stock: [&#x27;apples&#x27;]
If it breaks — how to fix it
🚨 KeyError when selling a new item.
Use .get(item, 0) so unknown items are treated as zero stock.
🚨 Stock went negative.
Your sell check is wrong — it must raise before subtracting when stock is short.
GroovyJenkinsfile
// Jenkinsfile — automated tests on every change.
pipeline {
    agent any
    stages {
        stage(&#x27;Get the code&#x27;) { steps { checkout scm } }       // download the code
        stage(&#x27;Install&#x27;) { steps { sh &#x27;python3 -m venv venv && . venv/bin/activate && pip install pytest&#x27; } }  // test tool
        stage(&#x27;Test&#x27;) { steps { sh &#x27;. venv/bin/activate && pytest -v&#x27; } }   // run every test
    }
    post {
        success { echo &#x27;All tests passed.&#x27; }
        failure { echo &#x27;A test failed — look above.&#x27; }
    }
}
🎯 Try this next — make it yours
  1. Prices and value. Track price per item and report total inventory value. (Teaches: richer records.)
  2. Auto-reorder. Flag items to reorder and suggest quantities. (Teaches: business logic.)
  3. Sales log. Record every sale with a timestamp. (Teaches: history tracking.)
What you learned
You modelled a business system: a class holding stock with rules (no negative inventory), reporting (low stock), and persistence. Bundling data, rules, and reports into one self-protecting object is exactly how real systems are built. Related: Object-Oriented Python, Comprehensions.