ساخت برنامه مدیریت موجودی فروشگاه با پایتون؛ پروژه کامل با JSON و گزارش انبار

🗓 آخرین بهروزرسانی:
ساخت برنامه مدیریت موجودی فروشگاه با پایتون یکی از پروژههای کاربردی برای تمرین همزمان دیکشنری، لیست، توابع، حلقهها، شرطها، فایل JSON، اعتبارسنجی ورودی و گزارشگیری است. در این پروژه فقط چند کالا را داخل یک لیست نگه نمیداریم؛ یک سیستم کوچک اما واقعی میسازیم که بتواند کالا ثبت کند، موجودی را افزایش یا کاهش دهد، قیمت را نگه دارد، کالا را جستوجو کند، هشدار کمبود موجودی بدهد و ارزش کل انبار را محاسبه کند.
این مقاله پانزدهمین پروژه فاز اول دسته تمرین و پروژههای پایتون است. اگر پروژههای مدیریت هزینههای شخصی با پایتون و مدیریت کتابخانه با پایتون را انجام داده باشید، با الگوی ثبت داده، تغییر وضعیت و ذخیره دائمی اطلاعات آشنا هستید. در این پروژه تمرکز اصلی روی کنترل Quantity و ساخت گزارش انبار است.
در این پروژه چه چیزی میسازیم؟
- ثبت کالای جدید با نام، دسته، قیمت و موجودی اولیه
- ایجاد شناسه یکتا برای هر کالا
- جستوجوی کالا بر اساس نام یا شناسه
- افزایش موجودی کالا
- کاهش موجودی کالا با کنترل مقدار کافی
- جلوگیری از منفی شدن موجودی
- ویرایش قیمت کالا
- نمایش همه کالاها
- نمایش کالاهای کمموجود
- گزارش تعداد کل کالاها
- محاسبه تعداد کل واحدهای موجود در انبار
- محاسبه ارزش کل انبار
- حذف کالا
- ذخیره اطلاعات در فایل JSON
- بارگذاری خودکار دادهها هنگام اجرای برنامه
- مدیریت فایل JSON خراب یا نامعتبر
ساختار داده هر کالا
برای هر محصول یک Dictionary ذخیره میکنیم:
{
"id": 1,
"name": "کیبورد مکانیکی",
"category": "لوازم جانبی",
"price": 750000,
"quantity": 18
}تمام کالاها داخل یک لیست قرار میگیرند و در فایل JSON ذخیره میشوند. این مدل برای پروژه آموزشی بسیار مناسب است، چون هم خواناست و هم در آینده میتوان آن را به SQLite یا یک دیتابیس واقعی منتقل کرد.
مرحله اول: ساخت فایل پروژه
یک فایل جدید با نام زیر بسازید:
inventory_manager.pyدادههای فروشگاه در فایل زیر ذخیره خواهند شد:
inventory.jsonمرحله دوم: ماژولهای مورد نیاز
import json
from pathlib import Pathدر این نسخه فقط از کتابخانه استاندارد پایتون استفاده میکنیم. json برای ذخیره اطلاعات و Path برای مدیریت مسیر فایل استفاده میشود. اگر با JSON آشنا نیستید، مقاله کار با فایلهای JSON در پایتون را قبل از ادامه مرور کنید.
مرحله سوم: تعریف تنظیمات برنامه
DATA_FILE = Path("inventory.json")
LOW_STOCK_LIMIT = 5متغیر LOW_STOCK_LIMIT مشخص میکند از چه موجودیای به پایین، کالا در گزارش کمبود موجودی نمایش داده شود. قرار دادن این عدد در ابتدای برنامه باعث میشود بعداً بتوانیم آن را بدون تغییر منطق اصلی عوض کنیم.
مرحله چهارم: بارگذاری اطلاعات از JSON
def load_products():
if not DATA_FILE.exists():
return []
try:
with DATA_FILE.open(
"r",
encoding="utf-8",
) as file:
data = json.load(file)
except (
json.JSONDecodeError,
OSError,
):
print(
"فایل موجودی قابل خواندن نیست. "
"برنامه با لیست خالی شروع شد."
)
return []
return data if isinstance(data, list) else []اگر فایل وجود نداشته باشد، برنامه با لیست خالی شروع میشود. اگر JSON خراب باشد نیز برنامه Crash نمیکند و پیام مناسب نمایش میدهد.
مرحله پنجم: ذخیره اطلاعات
def save_products(products):
with DATA_FILE.open(
"w",
encoding="utf-8",
) as file:
json.dump(
products,
file,
ensure_ascii=False,
indent=2,
)استفاده از ensure_ascii=False باعث میشود نام فارسی کالاها داخل فایل بهشکل خوانا ذخیره شوند.
مرحله ششم: تولید شناسه یکتا
def next_product_id(products):
return max(
(
product.get("id", 0)
for product in products
),
default=0,
) + 1بهتر است برای ID از len(products) + 1 استفاده نکنیم، چون بعد از حذف یک کالا ممکن است شناسه تکراری ایجاد شود.
مرحله هفتم: دریافت عدد مثبت
قیمت و مقدار موجودی باید اعتبارسنجی شوند. برای قیمت از عدد اعشاری و برای موجودی از عدد صحیح استفاده میکنیم.
def get_positive_price():
value = input(
"قیمت کالا: "
).strip()
try:
price = float(value)
except ValueError:
print("قیمت باید عدد باشد.")
return None
if price < 0:
print(
"قیمت نمیتواند منفی باشد."
)
return None
return price
def get_non_negative_quantity(
prompt="تعداد: ",
):
value = input(prompt).strip()
try:
quantity = int(value)
except ValueError:
print(
"تعداد باید عدد صحیح باشد."
)
return None
if quantity < 0:
print(
"تعداد نمیتواند منفی باشد."
)
return None
return quantityمرحله هشتم: ثبت کالای جدید
def add_product(products):
name = input(
"نام کالا: "
).strip()
if not name:
print(
"نام کالا نمیتواند خالی باشد."
)
return
category = input(
"دستهبندی: "
).strip()
if not category:
category = "سایر"
price = get_positive_price()
if price is None:
return
quantity = get_non_negative_quantity(
"موجودی اولیه: "
)
if quantity is None:
return
product = {
"id": next_product_id(products),
"name": name,
"category": category,
"price": price,
"quantity": quantity,
}
products.append(product)
save_products(products)
print(
f"کالا با شناسه {product['id']} ثبت شد."
)مرحله نهم: پیدا کردن کالا با شناسه
def find_product_by_id(
products,
product_id,
):
for product in products:
if product.get("id") == product_id:
return product
return Noneاین تابع در افزایش موجودی، کاهش موجودی، تغییر قیمت و حذف کالا دوباره استفاده میشود.
مرحله دهم: نمایش فهرست کالاها
def show_products(products):
if not products:
print(
"هنوز کالایی ثبت نشده است."
)
return
print("\n=== فهرست کالاها ===")
for product in products:
print(
f"#{product.get('id')} | "
f"{product.get('name')} | "
f"{product.get('category')} | "
f"موجودی: {product.get('quantity', 0)} | "
f"قیمت: {product.get('price', 0):,.2f}"
)مرحله یازدهم: جستوجوی کالا
def search_products(products):
query = input(
"نام یا دسته کالا: "
).strip().casefold()
if not query:
print(
"عبارت جستوجو خالی است."
)
return
results = [
product
for product in products
if (
query
in product.get(
"name",
"",
).casefold()
or query
in product.get(
"category",
"",
).casefold()
)
]
if not results:
print("کالایی پیدا نشد.")
return
show_products(results)مرحله دوازدهم: افزایش موجودی
def increase_stock(products):
product = select_product(products)
if product is None:
return
amount = get_non_negative_quantity(
"مقدار افزایش: "
)
if amount is None or amount == 0:
print(
"مقدار افزایش باید بیشتر از صفر باشد."
)
return
product["quantity"] += amount
save_products(products)
print(
f"موجودی جدید: {product['quantity']}"
)برای انتخاب کالا از یک تابع کمکی استفاده میکنیم:
def select_product(products):
value = input(
"شناسه کالا: "
).strip()
try:
product_id = int(value)
except ValueError:
print(
"شناسه باید عدد صحیح باشد."
)
return None
product = find_product_by_id(
products,
product_id,
)
if product is None:
print("کالا پیدا نشد.")
return productمرحله سیزدهم: کاهش موجودی
در این بخش مهمترین قانون سیستم را داریم: موجودی کالا نباید منفی شود.
def decrease_stock(products):
product = select_product(products)
if product is None:
return
amount = get_non_negative_quantity(
"مقدار کاهش: "
)
if amount is None or amount == 0:
print(
"مقدار کاهش باید بیشتر از صفر باشد."
)
return
current = product.get(
"quantity",
0,
)
if amount > current:
print(
"موجودی کافی نیست. "
f"موجودی فعلی: {current}"
)
return
product["quantity"] = (
current - amount
)
save_products(products)
print(
f"موجودی جدید: {product['quantity']}"
)مرحله چهاردهم: ویرایش قیمت کالا
def update_price(products):
product = select_product(products)
if product is None:
return
price = get_positive_price()
if price is None:
return
product["price"] = price
save_products(products)
print("قیمت کالا بهروزرسانی شد.")مرحله پانزدهم: گزارش کمبود موجودی
def low_stock_report(products):
rows = [
product
for product in products
if product.get(
"quantity",
0,
) <= LOW_STOCK_LIMIT
]
if not rows:
print(
"هیچ کالای کمموجودی وجود ندارد."
)
return
print(
"\n=== هشدار کمبود موجودی ==="
)
show_products(rows)با تغییر LOW_STOCK_LIMIT میتوانید حساسیت این گزارش را تنظیم کنید.
مرحله شانزدهم: محاسبه ارزش کل انبار
ارزش هر محصول برابر است با قیمت واحد ضربدر تعداد موجود:
product_value = price * quantityتابع گزارش کلی را اینطور مینویسیم:
def inventory_report(products):
total_products = len(products)
total_units = sum(
product.get("quantity", 0)
for product in products
)
total_value = sum(
product.get("price", 0)
* product.get("quantity", 0)
for product in products
)
low_stock_count = sum(
1
for product in products
if product.get(
"quantity",
0,
) <= LOW_STOCK_LIMIT
)
print("\n=== گزارش انبار ===")
print(
f"تعداد رکورد کالا: {total_products}"
)
print(
f"تعداد کل واحدها: {total_units}"
)
print(
f"ارزش کل انبار: {total_value:,.2f}"
)
print(
f"کالاهای کمموجود: {low_stock_count}"
)این بخش یک نمونه مناسب از Aggregation دادههاست؛ همان الگویی که در داشبوردهای فروش، گزارش مالی و سیستمهای مدیریتی استفاده میشود.
مرحله هفدهم: حذف کالا
def delete_product(products):
product = select_product(products)
if product is None:
return
confirm = input(
f"حذف {product['name']}؟ (بله/خیر): "
).strip().lower()
if confirm not in {
"بله",
"yes",
"y",
}:
print("حذف لغو شد.")
return
products.remove(product)
save_products(products)
print("کالا حذف شد.")کد کامل برنامه مدیریت موجودی فروشگاه با پایتون
import json
from pathlib import Path
DATA_FILE = Path("inventory.json")
LOW_STOCK_LIMIT = 5
def load_products():
if not DATA_FILE.exists():
return []
try:
with DATA_FILE.open(
"r",
encoding="utf-8",
) as file:
data = json.load(file)
except (
json.JSONDecodeError,
OSError,
):
print(
"فایل موجودی قابل خواندن نیست."
)
return []
return data if isinstance(data, list) else []
def save_products(products):
with DATA_FILE.open(
"w",
encoding="utf-8",
) as file:
json.dump(
products,
file,
ensure_ascii=False,
indent=2,
)
def next_product_id(products):
return max(
(
product.get("id", 0)
for product in products
),
default=0,
) + 1
def get_positive_price():
value = input("قیمت کالا: ").strip()
try:
price = float(value)
except ValueError:
print("قیمت باید عدد باشد.")
return None
if price < 0:
print("قیمت نمیتواند منفی باشد.")
return None
return price
def get_non_negative_quantity(prompt="تعداد: "):
value = input(prompt).strip()
try:
quantity = int(value)
except ValueError:
print("تعداد باید عدد صحیح باشد.")
return None
if quantity < 0:
print("تعداد نمیتواند منفی باشد.")
return None
return quantity
def find_product_by_id(products, product_id):
for product in products:
if product.get("id") == product_id:
return product
return None
def select_product(products):
value = input("شناسه کالا: ").strip()
try:
product_id = int(value)
except ValueError:
print("شناسه باید عدد صحیح باشد.")
return None
product = find_product_by_id(
products,
product_id,
)
if product is None:
print("کالا پیدا نشد.")
return product
def add_product(products):
name = input("نام کالا: ").strip()
if not name:
print("نام کالا نمیتواند خالی باشد.")
return
category = input("دستهبندی: ").strip()
if not category:
category = "سایر"
price = get_positive_price()
if price is None:
return
quantity = get_non_negative_quantity(
"موجودی اولیه: "
)
if quantity is None:
return
product = {
"id": next_product_id(products),
"name": name,
"category": category,
"price": price,
"quantity": quantity,
}
products.append(product)
save_products(products)
print(
f"کالا با شناسه {product['id']} ثبت شد."
)
def show_products(products):
if not products:
print("کالایی برای نمایش وجود ندارد.")
return
print("\n=== فهرست کالاها ===")
for product in products:
print(
f"#{product.get('id')} | "
f"{product.get('name')} | "
f"{product.get('category')} | "
f"موجودی: {product.get('quantity', 0)} | "
f"قیمت: {product.get('price', 0):,.2f}"
)
def search_products(products):
query = input(
"نام یا دسته کالا: "
).strip().casefold()
if not query:
print("عبارت جستوجو خالی است.")
return
results = [
product
for product in products
if (
query
in product.get("name", "").casefold()
or query
in product.get(
"category",
"",
).casefold()
)
]
if not results:
print("کالایی پیدا نشد.")
return
show_products(results)
def increase_stock(products):
product = select_product(products)
if product is None:
return
amount = get_non_negative_quantity(
"مقدار افزایش: "
)
if amount is None or amount == 0:
print(
"مقدار افزایش باید بیشتر از صفر باشد."
)
return
product["quantity"] += amount
save_products(products)
print(
f"موجودی جدید: {product['quantity']}"
)
def decrease_stock(products):
product = select_product(products)
if product is None:
return
amount = get_non_negative_quantity(
"مقدار کاهش: "
)
if amount is None or amount == 0:
print(
"مقدار کاهش باید بیشتر از صفر باشد."
)
return
current = product.get("quantity", 0)
if amount > current:
print(
"موجودی کافی نیست. "
f"موجودی فعلی: {current}"
)
return
product["quantity"] = current - amount
save_products(products)
print(
f"موجودی جدید: {product['quantity']}"
)
def update_price(products):
product = select_product(products)
if product is None:
return
price = get_positive_price()
if price is None:
return
product["price"] = price
save_products(products)
print("قیمت کالا بهروزرسانی شد.")
def low_stock_report(products):
rows = [
product
for product in products
if product.get("quantity", 0)
<= LOW_STOCK_LIMIT
]
if not rows:
print(
"هیچ کالای کمموجودی وجود ندارد."
)
return
print("\n=== هشدار کمبود موجودی ===")
show_products(rows)
def inventory_report(products):
total_products = len(products)
total_units = sum(
product.get("quantity", 0)
for product in products
)
total_value = sum(
product.get("price", 0)
* product.get("quantity", 0)
for product in products
)
low_stock_count = sum(
1
for product in products
if product.get("quantity", 0)
<= LOW_STOCK_LIMIT
)
print("\n=== گزارش انبار ===")
print(
f"تعداد رکورد کالا: {total_products}"
)
print(f"تعداد کل واحدها: {total_units}")
print(
f"ارزش کل انبار: {total_value:,.2f}"
)
print(
f"کالاهای کمموجود: {low_stock_count}"
)
def delete_product(products):
product = select_product(products)
if product is None:
return
confirm = input(
f"حذف {product['name']}؟ (بله/خیر): "
).strip().lower()
if confirm not in {"بله", "yes", "y"}:
print("حذف لغو شد.")
return
products.remove(product)
save_products(products)
print("کالا حذف شد.")
def show_menu():
print("\n=== مدیریت موجودی فروشگاه ===")
print("1) ثبت کالا")
print("2) نمایش همه کالاها")
print("3) جستوجو")
print("4) افزایش موجودی")
print("5) کاهش موجودی")
print("6) تغییر قیمت")
print("7) گزارش کمبود موجودی")
print("8) گزارش کلی انبار")
print("9) حذف کالا")
print("0) خروج")
def main():
products = load_products()
while True:
show_menu()
choice = input(
"انتخاب شما: "
).strip()
if choice == "1":
add_product(products)
elif choice == "2":
show_products(products)
elif choice == "3":
search_products(products)
elif choice == "4":
increase_stock(products)
elif choice == "5":
decrease_stock(products)
elif choice == "6":
update_price(products)
elif choice == "7":
low_stock_report(products)
elif choice == "8":
inventory_report(products)
elif choice == "9":
delete_product(products)
elif choice == "0":
print("برنامه پایان یافت.")
break
else:
print("گزینه نامعتبر است.")
if __name__ == "__main__":
main()نمونه اجرای برنامه
=== مدیریت موجودی فروشگاه ===
1) ثبت کالا
2) نمایش همه کالاها
3) جستوجو
4) افزایش موجودی
5) کاهش موجودی
6) تغییر قیمت
7) گزارش کمبود موجودی
8) گزارش کلی انبار
9) حذف کالا
0) خروج
انتخاب شما: 1
نام کالا: کیبورد مکانیکی
دستهبندی: لوازم جانبی
قیمت کالا: 750000
موجودی اولیه: 18
کالا با شناسه 1 ثبت شد.نمونه گزارش انبار
=== گزارش انبار ===
تعداد رکورد کالا: 12
تعداد کل واحدها: 146
ارزش کل انبار: 98,750,000.00
کالاهای کمموجود: 3چرا جلوگیری از موجودی منفی مهم است؟
در یک سیستم واقعی، Quantity فقط یک عدد نیست؛ نشاندهنده وضعیت فیزیکی کالا در انبار است. اگر برنامه اجازه دهد مقدار موجودی از صفر کمتر شود، دادهها از واقعیت جدا میشوند و گزارش فروش یا خرید بعدی دیگر قابل اعتماد نخواهد بود. به همین دلیل قبل از هر خروج کالا باید مقدار درخواستشده با موجودی فعلی مقایسه شود.
اشتباههای رایج در پروژه مدیریت موجودی
ذخیره قیمت به شکل رشته
قیمت باید قبل از ذخیره به عدد تبدیل شود تا محاسبه ارزش انبار بدون تبدیلهای مکرر انجام شود.
استفاده از نام کالا بهعنوان شناسه
دو کالا میتوانند نام مشابه داشته باشند. ID عددی مدیریت رکوردها را دقیقتر میکند.
کاهش موجودی بدون کنترل
این کار باعث منفی شدن Quantity و خراب شدن گزارشها میشود.
ذخیره نکردن بعد از تغییر
بعد از ثبت کالا، تغییر قیمت، افزایش، کاهش یا حذف باید فایل JSON دوباره ذخیره شود.
محاسبه ارزش انبار فقط با جمع قیمتها
ارزش انبار برابر جمع قیمت واحد کالاها نیست؛ باید قیمت هر کالا در تعداد موجود آن ضرب شود.
چطور پروژه را توسعه دهیم؟
- SKU: برای هر محصول کد انبار یا SKU مستقل تعریف کنید.
- ثبت ورود و خروج: تاریخچه هر تغییر موجودی را در یک فایل جداگانه نگه دارید.
- تأمینکننده: نام و اطلاعات Supplier را به کالا اضافه کنید.
- حد سفارش مجدد: برای هر کالا حد کمبود مستقل ذخیره کنید.
- سفارش خرید: برای کالاهای کمموجود یک Purchase Order ساده ایجاد کنید.
- گزارش دستهها: ارزش موجودی را بر اساس دستهبندی محاسبه کنید.
- ویرایش کامل: نام، دسته، قیمت و سایر مشخصات را ویرایش کنید.
- CSV: گزارش انبار را برای Excel صادر کنید.
- SQLite: JSON را با دیتابیس جایگزین کنید.
- OOP: کلاسهای Product و Inventory بسازید. برای مرور مفاهیم شیگرایی، آموزش سازنده، متدها و وراثت در پایتون مفید است.
- رابط گرافیکی: با Tkinter جدول کالا، فرم ثبت و هشدار کمبود موجودی طراحی کنید.
تمرین پیشنهادی: تاریخچه تراکنش موجودی
هر بار موجودی زیاد یا کم میشود، یک رکورد تراکنش ذخیره کنید:
{
"product_id": 1,
"type": "out",
"quantity": 2,
"date": "2026-08-13"
}با این تغییر میتوانید بعداً گزارش دهید چه مقدار کالا در یک بازه زمانی وارد یا خارج شده است. این قابلیت پروژه را به یک سیستم انبارداری واقعیتر نزدیک میکند.
این پروژه چه مفاهیمی را تمرین میدهد؟
- List و Dictionary
- JSON و File I/O
- Pathlib
- توابع و مقدار بازگشتی
- حلقه for و while
- شرطها
- Exception Handling
- Generator Expression
- List Comprehension
- جستوجو و فیلتر دادهها
- اعتبارسنجی عددی
- Aggregation و گزارشگیری
- مدیریت موجودی و State
- قوانین کسبوکار
- ماندگاری اطلاعات
جمعبندی
در این آموزش یک برنامه کامل مدیریت موجودی فروشگاه با پایتون ساختیم. سیستم میتواند کالا ثبت کند، موجودی را افزایش یا کاهش دهد، قیمت را تغییر دهد، کالاها را جستوجو کند، هشدار کمبود موجودی نمایش دهد و ارزش کل انبار را محاسبه کند. تمام اطلاعات نیز داخل JSON ذخیره میشوند و در اجرای بعدی دوباره بارگذاری خواهند شد.
مهمترین بخش این پروژه مدیریت صحیح Quantity و جلوگیری از ناسازگاری داده است. وقتی این نسخه را کامل اجرا کردید، افزودن تاریخچه ورود و خروج و سپس مهاجرت به SQLite بهترین مراحل بعدی برای تبدیل آن به یک پروژه حرفهایتر هستند.
📌 همچنین بخوانید: تمرین پایتون با جواب، ساخت بازی با پایتون و Pygame و ساخت ربات با پایتون