import os import sqlite3 from datetime import datetime, timedelta from flask import Flask, render_template, request, redirect, url_for, flash, session app = Flask(__name__) app.secret_key = 'super_secret_key_for_exam' DB_PATH = 'shoe_shop.db' # --- РАБОТА С БД --- def get_db_connection(): conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row return conn def init_db(): # Если базы нет, создаем её из файла database.sql if not os.path.exists(DB_PATH): conn = get_db_connection() with open('database.sql', 'r', encoding='utf-8') as f: conn.executescript(f.read()) # Наполняем базу тестовыми данными (Импорт данных) # Роли conn.execute("INSERT INTO roles (name) VALUES ('admin'), ('manager'), ('user')") # Пользователи (Логины без пароля) conn.execute("INSERT INTO users (login, last_name, first_name, middle_name, role_id) VALUES ('admin', 'Админов', 'Админ', 'Админович', 1)") conn.execute("INSERT INTO users (login, last_name, first_name, middle_name, role_id) VALUES ('manager', 'Менеджеров', 'Менеджер', 'Менеджерович', 2)") conn.execute("INSERT INTO users (login, last_name, first_name, middle_name, role_id) VALUES ('user', 'Петров', 'Петр', 'Петрович', 3)") # Категории и подкатегории conn.execute("INSERT INTO categories (name) VALUES ('Мужская'), ('Женская')") conn.execute("INSERT INTO subcategories (category_id, name) VALUES (1, 'Кроссовки'), (1, 'Ботинки'), (2, 'Кроссовки')") conn.execute("INSERT INTO manufacturers (name) VALUES ('Nike'), ('Adidas')") # Товары conn.execute("""INSERT INTO products (article, name, subcategory_id, manufacturer_id, description, composition, price, image_path) VALUES ('A001', 'Air Max', 1, 1, 'Спортивные', 'Кожа', 10000, '')""") conn.execute("""INSERT INTO products (article, name, subcategory_id, manufacturer_id, description, composition, price, image_path) VALUES ('A002', 'Superstar', 3, 2, 'Классика', 'Текстиль', 8000, '')""") # Размеры и остатки (для проверки "мало/много") conn.execute("INSERT INTO sizes (size_value) VALUES (40), (41), (42)") conn.execute("INSERT INTO product_stock (product_id, size_id, quantity) VALUES (1, 1, 1), (1, 2, 2), (2, 1, 10), (2, 2, 15)") conn.commit() conn.close() # --- БИЗНЕС-ЛОГИКА --- def calculate_discount(product_id, base_price): """Скидка 25%, если на товар не было заказов за последний месяц (30 дней)""" conn = get_db_connection() one_month_ago = (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d') has_recent_orders = conn.execute( "SELECT COUNT(*) FROM order_items oi JOIN orders o ON oi.order_id = o.id WHERE oi.product_id = ? AND o.order_date >= ?", (product_id, one_month_ago) ).fetchone()[0] conn.close() return base_price * 0.75 if has_recent_orders == 0 else base_price def get_user_role(): """Получение роли текущего пользователя""" if 'user_id' not in session: return 'guest' conn = get_db_connection() role = conn.execute("SELECT r.name FROM roles r JOIN users u ON u.role_id = r.id WHERE u.id = ?", (session['user_id'],)).fetchone() conn.close() return role['name'] if role else 'guest' # Передаем роль и имя во все шаблоны автоматически @app.context_processor def inject_globals(): return dict(current_role=get_user_role(), current_user=session.get('user_name')) # --- МАРШРУТЫ (АВТОРИЗАЦИЯ) --- @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': login_val = request.form.get('login') conn = get_db_connection() user = conn.execute("SELECT * FROM users WHERE login = ?", (login_val,)).fetchone() conn.close() if user: session['user_id'] = user['id'] session['user_name'] = f"{user['last_name']} {user['first_name']}" flash(f'Вы вошли как {user["last_name"]}', 'message') return redirect(url_for('index')) else: flash('Пользователь с таким логином не найден', 'error') return render_template('login.html') @app.route('/logout') def logout(): session.clear() flash('Вы вышли из системы', 'message') return redirect(url_for('index')) # --- КАТАЛОГ --- @app.route('/') def index(): conn = get_db_connection() # Фильтрация и поиск (доступна только не гостям) search = request.args.get('search', '') category_filter = request.args.get('category', '') query = """ SELECT p.*, sc.name as subcategory_name, c.name as category_name, m.name as manufacturer_name, (SELECT SUM(quantity) FROM product_stock WHERE product_id = p.id) as total_qty FROM products p JOIN subcategories sc ON p.subcategory_id = sc.id JOIN categories c ON sc.category_id = c.id JOIN manufacturers m ON p.manufacturer_id = m.id WHERE 1=1 """ params = [] # Гостю фильтры не применяем (по ТЗ) if get_user_role() != 'guest': if search: query += " AND (p.name LIKE ? OR p.description LIKE ?)" params.extend([f'%{search}%', f'%{search}%']) if category_filter: query += " AND c.id = ?" params.append(category_filter) products_raw = conn.execute(query, params).fetchall() categories = conn.execute("SELECT * FROM categories").fetchall() conn.close() products = [] for p in products_raw: p_dict = dict(p) # Расчет цены со скидкой p_dict['final_price'] = round(calculate_discount(p['id'], p['price']), 2) # Проверка количества (мало/много) if p_dict['total_qty'] is None or p_dict['total_qty'] <= 5: p_dict['qty_text'] = 'мало' p_dict['highlight'] = False else: p_dict['qty_text'] = 'много' p_dict['highlight'] = True # Заглушка для картинки if not p_dict['image_path']: p_dict['image_path'] = 'picture.png' products.append(p_dict) return render_template('index.html', products=products, categories=categories, search=search, cat_filter=category_filter) # --- КОРЗИНА И ЗАКАЗЫ --- @app.route('/add_to_cart/', methods=['POST']) def add_to_cart(product_id): if get_user_role() == 'guest': flash('Войдите, чтобы оформлять заказы', 'error') return redirect(url_for('index')) size_id = request.form.get('size_id') qty = int(request.form.get('quantity', 1)) if 'cart' not in session: session['cart'] = [] cart = session['cart'] found = False for item in cart: if item['product_id'] == product_id and item['size_id'] == int(size_id): item['quantity'] += qty found = True break if not found: cart.append({'product_id': product_id, 'size_id': int(size_id), 'quantity': qty}) session['cart'] = cart flash('Товар добавлен в корзину', 'message') return redirect(url_for('index')) @app.route('/cart') def cart(): if get_user_role() == 'guest': return redirect(url_for('login')) return render_template('cart.html', cart=session.get('cart', [])) @app.route('/checkout', methods=['POST']) def checkout(): if get_user_role() == 'guest' or not session.get('cart'): return redirect(url_for('index')) client_name = request.form.get('client_name') conn = get_db_connection() cursor = conn.cursor() # Создаем заказ cursor.execute("INSERT INTO orders (user_id, client_full_name, order_date) VALUES (?, ?, ?)", (session['user_id'], client_name, datetime.now().strftime('%Y-%m-%d'))) order_id = cursor.lastrowid # Добавляем позиции for item in session['cart']: price = conn.execute("SELECT price FROM products WHERE id = ?", (item['product_id'],)).fetchone()['price'] final_price = calculate_discount(item['product_id'], price) cursor.execute("INSERT INTO order_items (order_id, product_id, size_id, quantity, price_at_moment) VALUES (?, ?, ?, ?, ?)", (order_id, item['product_id'], item['size_id'], item['quantity'], final_price)) conn.commit() conn.close() session.pop('cart', None) flash('Заказ успешно оформлен!', 'message') return redirect(url_for('orders')) @app.route('/orders') def orders(): # Просмотр заказов только для менеджера и админа if get_user_role() in ['guest', 'user']: flash('Доступ запрещен', 'error') return redirect(url_for('index')) conn = get_db_connection() orders_list = conn.execute(""" SELECT o.id, o.order_date, o.client_full_name, u.last_name as manager_name FROM orders o LEFT JOIN users u ON o.user_id = u.id ORDER BY o.order_date DESC """).fetchall() conn.close() return render_template('orders.html', orders=orders_list) @app.route('/delete_order/') def delete_order(order_id): if get_user_role() not in ['admin', 'manager']: return redirect(url_for('index')) conn = get_db_connection() conn.execute("DELETE FROM orders WHERE id = ?", (order_id,)) conn.commit() conn.close() flash('Заказ удален', 'message') return redirect(url_for('orders')) # --- ЗАПУСК --- if __name__ == '__main__': init_db() # Проверяем/создаем базу при запуске app.run(debug=True)