تمرین و پروژه‌های پایتون

ساخت برنامه مدیریت موجودی فروشگاه با پایتون؛ پروژه کامل با 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 دوباره ذخیره شود.

محاسبه ارزش انبار فقط با جمع قیمت‌ها

ارزش انبار برابر جمع قیمت واحد کالاها نیست؛ باید قیمت هر کالا در تعداد موجود آن ضرب شود.

چطور پروژه را توسعه دهیم؟

  1. SKU: برای هر محصول کد انبار یا SKU مستقل تعریف کنید.
  2. ثبت ورود و خروج: تاریخچه هر تغییر موجودی را در یک فایل جداگانه نگه دارید.
  3. تأمین‌کننده: نام و اطلاعات Supplier را به کالا اضافه کنید.
  4. حد سفارش مجدد: برای هر کالا حد کمبود مستقل ذخیره کنید.
  5. سفارش خرید: برای کالاهای کم‌موجود یک Purchase Order ساده ایجاد کنید.
  6. گزارش دسته‌ها: ارزش موجودی را بر اساس دسته‌بندی محاسبه کنید.
  7. ویرایش کامل: نام، دسته، قیمت و سایر مشخصات را ویرایش کنید.
  8. CSV: گزارش انبار را برای Excel صادر کنید.
  9. SQLite: JSON را با دیتابیس جایگزین کنید.
  10. OOP: کلاس‌های Product و Inventory بسازید. برای مرور مفاهیم شی‌گرایی، آموزش سازنده، متدها و وراثت در پایتون مفید است.
  11. رابط گرافیکی: با 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 و ساخت ربات با پایتون

می‌خواهی پیشرفتت ذخیره شود؟با ورود یا ثبت‌نام رایگان، درس‌های تمام‌شده را علامت بزن و درصد پیشرفتت را در حساب کاربری ببین.ورود / ثبت‌نام برای ذخیره پیشرفت
درس بعدی · پروژه‌های پایتون (16 از 19)ساخت برنامه حضور و غیاب با پایتون؛ پروژه کامل با JSON و گزارش حضور ←
آموزش‌های تازه، کد آماده و رفع خطاهر هفته در کانال Dev Source؛ رایگان.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *