Learn how to manage medicines, stock, and customers with clean, beginner-friendly Object-Oriented Python.
Summary: In this tutorial, you’ll build a simple yet practical Medical Store App using Python OOP. You’ll create classes for Medicine, Customer, and MedicalStore, connect them with clear methods, and finish with a fully working script you can run immediately. Perfect for beginners who want a hands-on OOP project.
Table of Contents
- Why OOP for a Medical Store?
- What You Need
- Design the Data Model (Classes)
- Quick Start: Minimal Working Example
- Step-by-Step Build
- Full Copy-Paste Code
- How to Run
- Next Steps & Enhancements
- FAQ
1) Why OOP for a Medical Store?
Object-Oriented Programming maps naturally to the real world. In a pharmacy, you deal with:
- Medicines — items with a name, price, and stock quantity.
- Customers — people who buy medicines and have purchase history.
- Store — a system that manages inventory and sales.
By turning each concept into a class, your code becomes cleaner, reusable, and easy to extend later (e.g., adding expiry dates, invoices, GUI, or a web API).
2) What You Need
- Python 3.8+ installed (download).
- A code editor (VS Code, PyCharm, or any text editor).
- Basic familiarity with running Python files.
3) Design the Data Model (Classes)
We’ll use three core classes:
- Medicine: name, price, quantity; methods to restock and format output.
- Customer: name, purchase list; methods to record purchases and compute totals.
- MedicalStore: a dictionary of medicines; methods to add/show stock and sell items.
4) Quick Start: Minimal Working Example
If you want the fastest path to results, copy this snippet first. It creates a store, adds stock, sells items, and prints results.
class Medicine:
def __init__(self, name: str, price: float, quantity: int):
self.name = name
self.price = float(price)
self.quantity = int(quantity)
def restock(self, amount: int):
self.quantity += int(amount)
def __str__(self):
return f"{self.name} - ${self.price:.2f} (Stock: {self.quantity})"
class Customer:
def __init__(self, name: str):
self.name = name
self.purchases = [] # list of (medicine_name, qty, line_total)
def add_purchase(self, med_name: str, qty: int, line_total: float):
self.purchases.append((med_name, qty, round(line_total, 2)))
def total_spent(self) -> float:
return round(sum(p[2] for p in self.purchases), 2)
def __str__(self):
return f"Customer: {self.name}, Purchases: {len(self.purchases)}"
class MedicalStore:
def __init__(self, name: str):
self.name = name
self.medicines = {} # {name: Medicine}
def add_medicine(self, medicine: Medicine):
self.medicines[medicine.name] = medicine
def show_stock(self):
print(f"--- {self.name} Stock ---")
if not self.medicines:
print("(no items yet)")
for med in self.medicines.values():
print(med)
def sell_medicine(self, customer: Customer, med_name: str, qty: int):
if med_name not in self.medicines:
print(f"❌ {med_name} not available")
return False
med = self.medicines[med_name]
if qty <= 0:
print("❌ Quantity must be positive")
return False
if med.quantity < qty:
print(f"❌ Not enough stock for {med_name} (have {med.quantity})")
return False
med.quantity -= qty
line_total = med.price * qty
customer.add_purchase(med_name, qty, line_total)
print(f"✅ Sold {qty} x {med_name} to {customer.name} (${line_total:.2f})")
return True
# Demo run
if __name__ == "__main__":
store = MedicalStore("HealthPlus Pharmacy")
store.add_medicine(Medicine("Paracetamol", 2.5, 100))
store.add_medicine(Medicine("Amoxicillin", 5.0, 50))
store.add_medicine(Medicine("Vitamin C", 1.0, 200))
store.show_stock()
john = Customer("John Doe")
store.sell_medicine(john, "Paracetamol", 5)
store.sell_medicine(john, "Vitamin C", 10)
print(f"Total spent by {john.name}: ${john.total_spent():.2f}")
store.show_stock()
5) Step-by-Step Build
5.1) Create the Medicine class
Each medicine has a name, price, and stock quantity. Add a restock() helper to increase inventory safely.
class Medicine:
def __init__(self, name: str, price: float, quantity: int):
self.name = name
self.price = float(price)
self.quantity = int(quantity)
def restock(self, amount: int):
if amount <= 0:
raise ValueError("Restock amount must be positive")
self.quantity += int(amount)
def __str__(self):
return f"{self.name} - ${self.price:.2f} (Stock: {self.quantity})"
5.2) Create the Customer class
Customers keep a simple purchase history that stores what they bought, how many, and the line total.
class Customer:
def __init__(self, name: str):
self.name = name
self.purchases = [] # list[tuple[str, int, float]]
def add_purchase(self, med_name: str, qty: int, line_total: float):
self.purchases.append((med_name, int(qty), float(line_total)))
def total_spent(self) -> float:
return round(sum(total for _, _, total in self.purchases), 2)
def __str__(self):
return f"Customer: {self.name}, Purchases: {len(self.purchases)}"
5.3) Create the MedicalStore class
The store manages the catalog (dictionary by name), shows stock, and handles sales with validation.
class MedicalStore:
def __init__(self, name: str):
self.name = name
self.medicines = {}
def add_medicine(self, medicine: Medicine):
self.medicines[medicine.name] = medicine
def show_stock(self):
print(f"--- {self.name} Stock ---")
if not self.medicines:
print("(no items yet)")
for med in self.medicines.values():
print(med)
def sell_medicine(self, customer: Customer, med_name: str, qty: int):
if med_name not in self.medicines:
print(f"❌ {med_name} not available")
return False
med = self.medicines[med_name]
if qty <= 0:
print("❌ Quantity must be positive")
return False
if med.quantity < qty:
print(f"❌ Not enough stock for {med_name} (have {med.quantity})")
return False
med.quantity -= qty
line_total = med.price * qty
customer.add_purchase(med.name, qty, line_total)
print(f"✅ Sold {qty} x {med.name} to {customer.name} (${line_total:.2f})")
return True
5.4) Wire it up (demo)
if __name__ == "__main__":
store = MedicalStore("HealthPlus Pharmacy")
store.add_medicine(Medicine("Paracetamol", 2.50, 100))
store.add_medicine(Medicine("Ibuprofen", 3.00, 80))
store.add_medicine(Medicine("Vitamin C", 1.00, 200))
store.show_stock()
alice = Customer("Alice")
store.sell_medicine(alice, "Ibuprofen", 3)
store.sell_medicine(alice, "Vitamin C", 12)
print(f"Total spent by {alice.name}: ${alice.total_spent():.2f}")
store.show_stock()
6) Full Copy-Paste Code (Single File)
Save the following as medical_store.py:
"""
Medical Store App (Python OOP)
- Manage medicines (name, price, stock)
- Record customer purchases
- Sell with validation and auto stock updates
"""
from typing import Dict, List, Tuple
class Medicine:
def __init__(self, name: str, price: float, quantity: int):
if price < 0:
raise ValueError("Price cannot be negative")
if quantity < 0:
raise ValueError("Quantity cannot be negative")
self.name = name.strip()
self.price = float(price)
self.quantity = int(quantity)
def restock(self, amount: int):
if amount <= 0:
raise ValueError("Restock amount must be positive")
self.quantity += int(amount)
def __str__(self) -> str:
return f"{self.name} - ${self.price:.2f} (Stock: {self.quantity})"
class Customer:
def __init__(self, name: str):
self.name = name.strip()
self.purchases: List[Tuple[str, int, float]] = []
def add_purchase(self, med_name: str, qty: int, line_total: float):
self.purchases.append((med_name, int(qty), round(float(line_total), 2)))
def total_spent(self) -> float:
return round(sum(total for _, _, total in self.purchases), 2)
def receipt(self) -> str:
if not self.purchases:
return f"{self.name} — no purchases yet."
lines = [f"Receipt for {self.name}:"]
for med, qty, total in self.purchases:
lines.append(f" - {med} x{qty}: ${total:.2f}")
lines.append(f"TOTAL: ${self.total_spent():.2f}")
return "\n".join(lines)
def __str__(self) -> str:
return f"Customer: {self.name}, Purchases: {len(self.purchases)}"
class MedicalStore:
def __init__(self, name: str):
self.name = name.strip()
self.medicines: Dict[str, Medicine] = {}
def add_medicine(self, medicine: Medicine):
"""Add or replace a medicine by name."""
self.medicines[medicine.name] = medicine
def show_stock(self):
print(f"--- {self.name} Stock ---")
if not self.medicines:
print("(no items yet)")
return
for med in self.medicines.values():
print(med)
def find(self, med_name: str) -> Medicine | None:
return self.medicines.get(med_name)
def sell_medicine(self, customer: Customer, med_name: str, qty: int) -> bool:
med = self.find(med_name)
if not med:
print(f"❌ {med_name} not available")
return False
if qty <= 0:
print("❌ Quantity must be positive")
return False
if med.quantity < qty:
print(f"❌ Not enough stock for {med.name} (have {med.quantity})")
return False
med.quantity -= qty
line_total = med.price * qty
customer.add_purchase(med.name, qty, line_total)
print(f"✅ Sold {qty} x {med.name} to {customer.name} (${line_total:.2f})")
return True
def demo():
store = MedicalStore("HealthPlus Pharmacy")
store.add_medicine(Medicine("Paracetamol", 2.50, 100))
store.add_medicine(Medicine("Ibuprofen", 3.00, 80))
store.add_medicine(Medicine("Vitamin C", 1.00, 200))
store.add_medicine(Medicine("Amoxicillin", 5.00, 50))
store.show_stock()
john = Customer("John Doe")
store.sell_medicine(john, "Paracetamol", 5)
store.sell_medicine(john, "Vitamin C", 10)
store.sell_medicine(john, "Ibuprofen", 3)
print()
print(john.receipt())
print()
store.show_stock()
if __name__ == "__main__":
demo()
7) How to Run
- Copy the full code above into a file named
medical_store.py. - Open a terminal in the same folder.
- Run:
python3 medical_store.py - Check the console output for stock, sales, and the customer receipt.
8) Next Steps & Enhancements
- Persistence: Save/load stock and purchases using
jsonorsqlite3. - Expiry & Batches: Track expiry dates and batch numbers per medicine.
- Discounts/Taxes: Add coupon logic and tax calculation in
sell_medicine(). - Receipts: Export a receipt to
.txtor.pdffor each sale. - GUI: Use
Tkinterfor a desktop app (buttons for add/sell/show). - Web App: Convert into Flask/Django routes (inventory page, sell endpoint).
Bonus: Tiny JSON persistence example
import json
def save_stock(store: MedicalStore, path="stock.json"):
data = [
{"name": m.name, "price": m.price, "quantity": m.quantity}
for m in store.medicines.values()
]
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
def load_stock(store: MedicalStore, path="stock.json"):
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
for item in data:
store.add_medicine(Medicine(item["name"], item["price"], item["quantity"]))
except FileNotFoundError:
pass
9) FAQ
Q: Can I add input prompts to sell interactively?
A: Yes! Wrap input() in a simple loop that asks for a medicine name and quantity, then calls sell_medicine().
Q: How do I prevent negative prices or invalid quantities?
A: Validation is already included in the Medicine constructor and sell_medicine(). Extend as needed (e.g., min/max price caps).
Q: Can I support multiple stores?
A: Absolutely. Instantiate multiple MedicalStore objects and persist each store’s catalog separately.

Leave a Reply