Оформление заказа в Telegram-магазине (Часть 5)

Оформление заказа в Telegram-магазине (Часть 5)

Теперь, когда пользователи могут добавлять товары в корзину, нам нужно сделать процесс оформления заказа максимально удобным. В этой статье мы:
✅ Добавим кнопку «Оформить заказ».
✅ Запросим номер телефона перед оформлением.
✅ Добавим возможность увеличивать и уменьшать количество товаров в корзине.
✅ Сделаем кнопку «🔄 Обновить корзину», чтобы сразу видеть изменения.

Обновляем базу данных

Перед добавлением новых функций нам нужно удалить старую базу данных, так как мы изменяем структуру таблиц.

Удаляем старую базу

Запусти команду в терминале или удалите её в ручную:

rm shop.db  # Для Linux и Mac
del shop.db  # Для Windows

Теперь база данных удалена, и мы можем пересоздать её с нужными таблицами.

Создаём обновленную базу данных (database.py)

Теперь создадим базу данных с корзиной, заказами и возможностью изменять количество товаров.

import sqlite3

conn = sqlite3.connect("shop.db")
cursor = conn.cursor()

# Таблица товаров
cursor.execute("""
CREATE TABLE IF NOT EXISTS products (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    price INTEGER NOT NULL
)
""")

# Таблица корзины
cursor.execute("""
CREATE TABLE IF NOT EXISTS cart (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    product_id INTEGER NOT NULL,
    quantity INTEGER NOT NULL,
    FOREIGN KEY (product_id) REFERENCES products (id)
)
""")

# Таблица заказов
cursor.execute("""
CREATE TABLE IF NOT EXISTS orders (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    total_price INTEGER NOT NULL,
    status TEXT NOT NULL DEFAULT 'pending'
)
""")

conn.commit()
conn.close()

✅ Что изменилось?

  • Теперь корзина поддерживает изменение количества товаров.
  • Полностью удаляем старую таблицу orders и создаем новую.

Теперь запусти database.py, чтобы создать новую базу данных: python database.py

Так как мы удаляли полностью базу, то и тестовые товары у нас удалились.

Чтобы добавить несколько тестовых товары, можно запустить Python-скрипт вручную.

✅ Открой терминал и запусти Python:

Далее в интерактивном режиме выполни команды:

from database import add_product

add_product("📦 Товар A", 100)
add_product("📦 Товар B", 200)
add_product("📦 Товар C", 300)

print("Товары добавлены!")

После этого товары появятся в базе.

Проверка списка товаров

Чтобы убедиться, что товары записаны, в терминале введи:

from database import get_products

print(get_products())

Ты увидишь список товаров в виде:

Функции управления корзиной

Сделаем возможность добавлять товары, изменять их количество и удалять из корзины.

def add_to_cart(user_id, product_id, quantity=1):
    """Добавляет товар в корзину или увеличивает его количество"""
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()

    cursor.execute("SELECT quantity FROM cart WHERE user_id=? AND product_id=?", (user_id, product_id))
    result = cursor.fetchone()

    if result:
        new_quantity = result[0] + quantity
        cursor.execute("UPDATE cart SET quantity=? WHERE user_id=? AND product_id=?", (new_quantity, user_id, product_id))
    else:
        cursor.execute("INSERT INTO cart (user_id, product_id, quantity) VALUES (?, ?, ?)", (user_id, product_id, quantity))

    conn.commit()
    conn.close()


def remove_from_cart(user_id, product_id):
    """Удаляет 1 шт. товара из корзины, если количество > 1, иначе удаляет полностью"""
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()

    cursor.execute("SELECT quantity FROM cart WHERE user_id=? AND product_id=?", (user_id, product_id))
    result = cursor.fetchone()

    if result:
        if result[0] > 1:
            new_quantity = result[0] - 1
            cursor.execute("UPDATE cart SET quantity=? WHERE user_id=? AND product_id=?", (new_quantity, user_id, product_id))
        else:
            cursor.execute("DELETE FROM cart WHERE user_id=? AND product_id=?", (user_id, product_id))

    conn.commit()
    conn.close()

✅ Что изменилось?

  • Теперь можно увеличить или уменьшить количество товаров в корзине.
  • Если товар удаляется полностью, он исчезает из корзины.

В итоге полный код database.py должен быть такой:

import sqlite3

# Подключаемся к базе данных (если файла нет, он создастся автоматически)
conn = sqlite3.connect("shop.db")
cursor = conn.cursor()

# Таблица товаров
cursor.execute("""
CREATE TABLE IF NOT EXISTS products (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    price INTEGER NOT NULL
)
""")

# Таблица корзины
cursor.execute("""
CREATE TABLE IF NOT EXISTS cart (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    product_id INTEGER NOT NULL,
    quantity INTEGER NOT NULL,
    FOREIGN KEY (product_id) REFERENCES products (id)
)
""")

# Таблица заказов
cursor.execute("""
CREATE TABLE IF NOT EXISTS orders (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    total_price INTEGER NOT NULL,
    status TEXT NOT NULL DEFAULT 'pending'
)
""")

conn.commit()
conn.close()


def add_product(name, price):
    """Добавляет товар в базу данных"""
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()
    cursor.execute("INSERT INTO products (name, price) VALUES (?, ?)", (name, price))
    conn.commit()
    conn.close()


def get_products():
    """Получает список всех товаров"""
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM products")
    products = cursor.fetchall()
    conn.close()
    return products


def add_to_cart(user_id, product_id, quantity=1):
    """Добавляет товар в корзину пользователя с указанием количества"""
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()

    cursor.execute("SELECT quantity FROM cart WHERE user_id=? AND product_id=?", (user_id, product_id))
    result = cursor.fetchone()

    if result:
        new_quantity = result[0] + quantity
        cursor.execute("UPDATE cart SET quantity=? WHERE user_id=? AND product_id=?", (new_quantity, user_id, product_id))
    else:
        cursor.execute("INSERT INTO cart (user_id, product_id, quantity) VALUES (?, ?, ?)", (user_id, product_id, quantity))

    conn.commit()
    conn.close()


def remove_from_cart(user_id, product_id):
    """Удаляет 1 шт. товара из корзины, если количество > 1, иначе удаляет полностью"""
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()

    cursor.execute("SELECT quantity FROM cart WHERE user_id=? AND product_id=?", (user_id, product_id))
    result = cursor.fetchone()

    if result:
        if result[0] > 1:
            new_quantity = result[0] - 1
            cursor.execute("UPDATE cart SET quantity=? WHERE user_id=? AND product_id=?", (new_quantity, user_id, product_id))
        else:
            cursor.execute("DELETE FROM cart WHERE user_id=? AND product_id=?", (user_id, product_id))

    conn.commit()
    conn.close()


def get_cart(user_id):
    """Возвращает список товаров в корзине пользователя"""
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()
    cursor.execute("""
        SELECT products.name, cart.quantity, products.price, products.id 
        FROM cart 
        JOIN products ON cart.product_id = products.id 
        WHERE cart.user_id=?
    """, (user_id,))
    cart_items = cursor.fetchall()
    conn.close()
    return cart_items


def create_order(user_id):
    """Создает заказ из корзины пользователя"""
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()

    cursor.execute("""
        SELECT products.id, products.price, cart.quantity
        FROM cart
        JOIN products ON cart.product_id = products.id
        WHERE cart.user_id=?
    """, (user_id,))
    cart_items = cursor.fetchall()

    if not cart_items:
        conn.close()
        return None

    total_price = sum(item[1] * item[2] for item in cart_items)

    cursor.execute("INSERT INTO orders (user_id, total_price) VALUES (?, ?)", (user_id, total_price))
    order_id = cursor.lastrowid

    cursor.execute("DELETE FROM cart WHERE user_id=?", (user_id,))

    conn.commit()
    conn.close()
    return order_id

Обновляем обработку корзины в handlers.py

Сделаем возможность корзины обновляться в реальном времени, а также у пользователя появятся кнопки «+» и «-» возле каждого товара.

@router.message(F.text == "🛒 Корзина")
@router.callback_query(F.data == "refresh_cart")
async def cart_command(event):
    user_id = event.from_user.id if isinstance(event, CallbackQuery) else event.from_user.id
    chat_id = event.message.chat.id if isinstance(event, CallbackQuery) else event.chat.id

    cart_items = get_cart(user_id)

    if not cart_items:
        await event.message.answer("Ваша корзина пуста.")
    else:
        cart_text = "🛒 Ваша корзина:\n"
        total_price = 0
        keyboard_buttons = []

        for name, quantity, price, product_id in cart_items:
            total_price += quantity * price
            cart_text += f"{name} — {quantity} шт. × {price}₽ = {quantity * price}₽\n"

            keyboard_buttons.append([
                InlineKeyboardButton(text="➕", callback_data=f"add_{product_id}"),
                InlineKeyboardButton(text="➖", callback_data=f"remove_{product_id}")
            ])

        cart_text += f"\n💰 Итоговая сумма: {total_price}₽"

        keyboard_buttons.append([
            InlineKeyboardButton(text="🔄 Обновить корзину", callback_data="refresh_cart")
        ])
        keyboard_buttons.append([
            InlineKeyboardButton(text="✅ Оформить заказ", callback_data="checkout")
        ])

        keyboard = InlineKeyboardMarkup(inline_keyboard=keyboard_buttons)

        if isinstance(event, CallbackQuery):
            await event.message.edit_text(cart_text, reply_markup=keyboard)
            await event.answer()
        else:
            await event.answer(cart_text, reply_markup=keyboard)

✅ Что изменилось?

  • Теперь можно изменять количество товаров прямо в корзине.
  • Кнопка «🔄 Обновить корзину» сразу обновляет список товаров.

Запрашиваем номер телефона перед заказом

Когда пользователь нажимает «Оформить заказ», бот просит отправить номер телефона.

@router.callback_query(F.data == "checkout")
async def request_contact(callback: CallbackQuery):
    contact_keyboard = ReplyKeyboardMarkup(
        keyboard=[[KeyboardButton(text="📱 Отправить номер", request_contact=True)]],
        resize_keyboard=True,
        one_time_keyboard=True
    )
    await callback.message.answer("📱 Отправьте свой номер телефона для оформления заказа:", reply_markup=contact_keyboard)
    await callback.answer()

@router.message(F.contact | F.text.regexp(r'^\+?\d{10,15}$'))
async def receive_contact(message: Message):
    phone_number = message.contact.phone_number if message.contact else message.text
    order_id = create_order(message.from_user.id)

    if order_id:
        await message.answer(f"✅ Заказ #{order_id} оформлен!\n📞 Мы свяжемся с вами по номеру: {phone_number}.")
    else:
        await message.answer("❌ Ваша корзина пуста, добавьте товары перед заказом.")

✅ Теперь бот принимает номер телефона как через кнопку, так и вручную.

В итоге полный код handlers.pyдолжен быть такой:

from aiogram import Router, F
from aiogram.types import Message, CallbackQuery, ReplyKeyboardMarkup, KeyboardButton, InlineKeyboardMarkup, InlineKeyboardButton
from keyboards import main_menu
from database import get_products, add_to_cart, get_cart, create_order, remove_from_cart

router = Router()

# Обработчик команды /start
@router.message(F.text == "/start")
async def start_command(message: Message):
    await message.answer("Добро пожаловать в магазин! Выберите действие:", reply_markup=main_menu)

# Обработчик кнопки "Каталог"
@router.message(F.text == "🛍 Каталог")
async def catalog_command(message: Message):
    await message.answer("Вот наш каталог. Выберите товар и укажите его количество:")

    products = get_products()
    for product in products:
        product_id, name, price = product
        keyboard = InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="➕ Добавить", callback_data=f"add_{product_id}")],
            [InlineKeyboardButton(text="➖ Убрать", callback_data=f"remove_{product_id}")]
        ])
        await message.answer(f"{name} - {price}₽", reply_markup=keyboard)

# Обработчик кнопок "Добавить" и "Удалить"
@router.callback_query(F.data.startswith("add_") | F.data.startswith("remove_"))
async def update_cart_handler(callback: CallbackQuery):
    action, product_id = callback.data.split("_")
    product_id = int(product_id)

    if action == "add":
        add_to_cart(callback.from_user.id, product_id)
        await callback.answer("✅ Товар добавлен!")
    elif action == "remove":
        remove_from_cart(callback.from_user.id, product_id)
        await callback.answer("❌ Товар убран!")

# Обработчик кнопки "Корзина" и "🔄 Обновить корзину"
@router.message(F.text == "🛒 Корзина")
@router.callback_query(F.data == "refresh_cart")  # Теперь корзину можно обновлять кнопкой
async def cart_command(event):
    user_id = event.from_user.id if isinstance(event, CallbackQuery) else event.from_user.id
    chat_id = event.message.chat.id if isinstance(event, CallbackQuery) else event.chat.id

    cart_items = get_cart(user_id)

    if not cart_items:
        await event.message.answer("Ваша корзина пуста.")
    else:
        cart_text = "🛒 Ваша корзина:\n"
        total_price = 0
        keyboard_buttons = []

        for name, quantity, price, product_id in cart_items:
            total_price += quantity * price
            cart_text += f"{name} — {quantity} шт. × {price}₽ = {quantity * price}₽\n"

            keyboard_buttons.append([
                InlineKeyboardButton(text="➕", callback_data=f"add_{product_id}"),
                InlineKeyboardButton(text="➖", callback_data=f"remove_{product_id}")
            ])

        cart_text += f"\n💰 Итоговая сумма: {total_price}₽"

        # Добавляем кнопку "🔄 Обновить корзину" и "✅ Оформить заказ"
        keyboard_buttons.append([
            InlineKeyboardButton(text="🔄 Обновить корзину", callback_data="refresh_cart")
        ])
        keyboard_buttons.append([
            InlineKeyboardButton(text="✅ Оформить заказ", callback_data="checkout")
        ])

        keyboard = InlineKeyboardMarkup(inline_keyboard=keyboard_buttons)

        if isinstance(event, CallbackQuery):  # Если обновление через кнопку
            await event.message.edit_text(cart_text, reply_markup=keyboard)
            await event.answer()
        else:  # Если обычный текст "Корзина"
            await event.answer(cart_text, reply_markup=keyboard)

# Обработчик кнопки "Оформить заказ"
@router.callback_query(F.data == "checkout")
async def request_contact(callback: CallbackQuery):
    contact_keyboard = ReplyKeyboardMarkup(
        keyboard=[[KeyboardButton(text="📱 Отправить номер", request_contact=True)]],
        resize_keyboard=True,
        one_time_keyboard=True
    )
    await callback.message.answer("📱 Отправьте свой номер телефона для оформления заказа:", reply_markup=contact_keyboard)
    await callback.answer()

# Обработчик получения номера телефона
@router.message(F.contact | F.text.regexp(r'^\+?\d{10,15}$'))
async def receive_contact(message: Message):
    phone_number = message.contact.phone_number if message.contact else message.text
    order_id = create_order(message.from_user.id)

    if order_id:
        await message.answer(f"✅ Заказ #{order_id} оформлен!\n📞 Мы свяжемся с вами по номеру: {phone_number}.")
    else:
        await message.answer("❌ Ваша корзина пуста, добавьте товары перед заказом.")

Тестируем новые функции

1️⃣ Удаляем старую базу и создаём новую.
2️⃣ Запускаем бота: python bot.py

3️⃣ Добавляем товары в корзину.
4️⃣ Пробуем «➕» и «➖» для изменения количества.
5️⃣ Нажимаем «🔄 Обновить корзину» и видим изменения сразу.
6️⃣ Оформляем заказ, отправляем номер телефона.

Заключение

Теперь Telegram-магазин полностью поддерживает:
✅ Управление количеством товаров в корзине.
✅ Автоматическое обновление корзины.
✅ Оформление заказа с подтверждением номера телефона.

🎉 В следующей статье мы создадим панель администратора! 🚀

Комментарии

Пока нет комментариев. Будьте первым!

Оставить комментарий

Чтобы оставить комментарий, пожалуйста, войдите или зарегистрируйтесь.