commit 5f22af0ec0c43fb5cf6d79041d14a802be989dc2 Author: kosarewmihail Date: Mon Apr 20 14:37:54 2026 +0500 20260420 diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..09a6d45 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..a0733a5 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/project.iml b/.idea/project.iml new file mode 100644 index 0000000..d0876a7 --- /dev/null +++ b/.idea/project.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/TZ.odt b/TZ.odt new file mode 100644 index 0000000..74f9a3c Binary files /dev/null and b/TZ.odt differ diff --git a/app.py b/app.py new file mode 100644 index 0000000..ab979f1 --- /dev/null +++ b/app.py @@ -0,0 +1,293 @@ +from flask import Flask, render_template, request, redirect, url_for, flash +from database import db +import os +import re +from datetime import datetime + +app = Flask(__name__) +app.config['SECRET_KEY'] = 'your-secret-key-2026' + +# Функция валидации телефона +def validate_phone(phone): + phone_clean = re.sub(r'[^\d+]', '', phone) + + if phone_clean.startswith('+7') and len(phone_clean) == 12: + return True + elif phone_clean.startswith('8') and len(phone_clean) == 11: + return True + elif len(phone_clean) == 10 and phone_clean.isdigit(): + return True + elif len(phone_clean) == 11 and phone_clean.isdigit(): + return True + return False + +def format_phone(phone): + phone_clean = re.sub(r'[^\d+]', '', phone) + + if phone_clean.startswith('+7') and len(phone_clean) == 12: + return f"+7 ({phone_clean[2:5]}) {phone_clean[5:8]}-{phone_clean[8:10]}-{phone_clean[10:12]}" + elif phone_clean.startswith('8') and len(phone_clean) == 11: + return f"+7 ({phone_clean[1:4]}) {phone_clean[4:7]}-{phone_clean[7:9]}-{phone_clean[9:11]}" + elif len(phone_clean) == 10: + return f"+7 ({phone_clean[0:3]}) {phone_clean[3:6]}-{phone_clean[6:8]}-{phone_clean[8:10]}" + elif len(phone_clean) == 11: + return f"+7 ({phone_clean[1:4]}) {phone_clean[4:7]}-{phone_clean[7:9]}-{phone_clean[9:11]}" + return phone + +# Функция расчета коэффициента срока годности +def get_expiry_coefficient(expiry_date): + """ + Расчет коэффициента скидки в зависимости от срока годности + < 30 дней -> 50% скидка + 30-90 дней -> 30% скидка + 90-180 дней -> 15% скидка + > 180 дней -> 0% скидка + """ + if not expiry_date: + return 0 + + try: + # Преобразуем строку в дату (формат дд.мм.гггг) + expiry = datetime.strptime(expiry_date, '%d.%m.%Y') + today = datetime.now() + + days_until_expiry = (expiry - today).days + + if days_until_expiry < 0: + return 100 # Просрочен - 100% скидка (не продаем) + elif days_until_expiry < 30: + return 50 # 50% скидка + elif days_until_expiry < 90: + return 30 # 30% скидка + elif days_until_expiry < 180: + return 15 # 15% скидка + else: + return 0 + except: + return 0 + +# Функция расчета итоговой цены с учетом скидки и срока годности +def calculate_final_price(price, quantity, expiry_date, quantity_discount_percent=0): + """ + Расчет итоговой цены: + 1. Базовая сумма = цена * количество + 2. Скидка по сроку годности + 3. Скидка по количеству + """ + total = price * quantity + + # Скидка по сроку годности + expiry_discount_percent = get_expiry_coefficient(expiry_date) + expiry_discount_amount = total * expiry_discount_percent / 100 + + # Скидка по количеству + quantity_discount_amount = total * quantity_discount_percent / 100 + + # Общая скидка + total_discount = expiry_discount_amount + quantity_discount_amount + final_total = total - total_discount + + return { + 'total': total, + 'expiry_discount_percent': expiry_discount_percent, + 'expiry_discount_amount': expiry_discount_amount, + 'quantity_discount_percent': quantity_discount_percent, + 'quantity_discount_amount': quantity_discount_amount, + 'total_discount': total_discount, + 'final_total': final_total + } + +# ============ ГЛАВНАЯ ============ +@app.route('/') +def index(): + products = db.get_products() + total_items = sum(p.get('stock_quantity', 0) for p in products) + total_value = sum(p.get('stock_quantity', 0) * p.get('selling_price', 0) for p in products) + return render_template('index.html', total_items=total_items, total_value=total_value) + +# ============ ТОВАРЫ ============ +@app.route('/products', methods=['GET', 'POST']) +def products(): + if request.method == 'POST': + product = { + 'name': request.form.get('name'), + 'manufacturer': request.form.get('manufacturer'), + 'product_type': request.form.get('product_type'), + 'purchase_price': float(request.form.get('purchase_price', 0)), + 'selling_price': float(request.form.get('selling_price', 0)), + 'expiry_date': request.form.get('expiry_date', ''), + 'stock_quantity': int(request.form.get('stock_quantity', 0)) + } + db.save_product(product) + flash('Товар добавлен', 'success') + return redirect(url_for('products')) + + products = db.get_products() + categories = db.get_categories() + return render_template('products.html', products=products, categories=categories) + +@app.route('/products/delete/') +def delete_product(product_id): + db.delete_product(product_id) + flash('Товар удален', 'warning') + return redirect(url_for('products')) + +@app.route('/products/edit/', methods=['GET', 'POST']) +def edit_product(product_id): + product = db.get_product_by_id(product_id) + if not product: + flash('Товар не найден', 'danger') + return redirect(url_for('products')) + + if request.method == 'POST': + updated_data = { + 'name': request.form.get('name'), + 'manufacturer': request.form.get('manufacturer'), + 'product_type': request.form.get('product_type'), + 'purchase_price': float(request.form.get('purchase_price', 0)), + 'selling_price': float(request.form.get('selling_price', 0)), + 'expiry_date': request.form.get('expiry_date', ''), + 'stock_quantity': int(request.form.get('stock_quantity', 0)) + } + db.update_product(product_id, updated_data) + flash('Товар обновлен', 'success') + return redirect(url_for('products')) + + return render_template('edit_product.html', product=product) + +# ============ ПОСТАВЩИКИ ============ +@app.route('/suppliers', methods=['GET', 'POST']) +def suppliers(): + if request.method == 'POST': + name = request.form.get('name') + contact = request.form.get('contact') + phone = request.form.get('phone') + + if phone and not validate_phone(phone): + flash('Неверный формат телефона', 'danger') + suppliers = db.get_suppliers() + return render_template('suppliers.html', suppliers=suppliers) + + supplier = { + 'name': name, + 'contact': contact, + 'phone': format_phone(phone) if phone else '' + } + db.save_supplier(supplier) + flash('Поставщик добавлен', 'success') + return redirect(url_for('suppliers')) + + suppliers = db.get_suppliers() + return render_template('suppliers.html', suppliers=suppliers) + +@app.route('/suppliers/edit/', methods=['GET', 'POST']) +def edit_supplier(supplier_id): + supplier = db.get_supplier_by_id(supplier_id) + if not supplier: + flash('Поставщик не найден', 'danger') + return redirect(url_for('suppliers')) + + if request.method == 'POST': + name = request.form.get('name') + contact = request.form.get('contact') + phone = request.form.get('phone') + + if phone and not validate_phone(phone): + flash('Неверный формат телефона', 'danger') + return render_template('edit_supplier.html', supplier=supplier) + + updated_data = { + 'name': name, + 'contact': contact, + 'phone': format_phone(phone) if phone else '' + } + db.update_supplier(supplier_id, updated_data) + flash('Поставщик обновлен', 'success') + return redirect(url_for('suppliers')) + + return render_template('edit_supplier.html', supplier=supplier) + +@app.route('/supplier/orders/') +def supplier_orders(supplier_id): + supplier = db.get_supplier_by_id(supplier_id) + if not supplier: + flash('Поставщик не найден', 'danger') + return redirect(url_for('suppliers')) + + all_orders = db.get_incoming_orders() + supplier_orders = [o for o in all_orders if o.get('supplier_id') == supplier_id] + + total_sum = sum(o.get('total', 0) for o in supplier_orders) + total_items = sum(o.get('quantity', 0) for o in supplier_orders) + + return render_template('supplier_orders.html', + supplier=supplier, + orders=supplier_orders, + total_sum=total_sum, + total_items=total_items) + +# ============ ПОСТУПЛЕНИЕ ТОВАРА (С УЧЕТОМ СРОКА ГОДНОСТИ) ============ +@app.route('/incoming', methods=['GET', 'POST']) +def incoming(): + if request.method == 'POST': + product_id = int(request.form.get('product_id')) + supplier_id = int(request.form.get('supplier_id')) + quantity = int(request.form.get('quantity')) + purchase_price = float(request.form.get('purchase_price')) + + product = db.get_product_by_id(product_id) + supplier = db.get_supplier_by_id(supplier_id) + + if product and supplier: + # Получаем срок годности товара + expiry_date = product.get('expiry_date', '') + + # Рассчитываем коэффициенты + expiry_discount_percent = get_expiry_coefficient(expiry_date) + + # Базовая сумма + total = quantity * purchase_price + + # Скидка по сроку годности + discount_amount = total * expiry_discount_percent / 100 + final_total = total - discount_amount + + incoming_order = { + 'supplier_id': supplier_id, + 'supplier_name': supplier['name'], + 'product_id': product_id, + 'product_name': product['name'], + 'expiry_date': expiry_date, + 'quantity': quantity, + 'purchase_price': purchase_price, + 'total': total, + 'expiry_discount_percent': expiry_discount_percent, + 'discount_amount': discount_amount, + 'final_total': final_total + } + db.save_incoming_order(incoming_order) + db.update_stock(product_id, quantity) + + if expiry_discount_percent > 0: + flash(f'Товар "{product["name"]}" поступил на склад. Применена скидка {expiry_discount_percent}% по сроку годности!', 'success') + else: + flash(f'Товар "{product["name"]}" поступил на склад в количестве {quantity} шт.', 'success') + else: + flash('Ошибка при оформлении поступления', 'danger') + return redirect(url_for('incoming')) + + products = db.get_products() + suppliers = db.get_suppliers() + return render_template('incoming.html', products=products, suppliers=suppliers) + +# ============ ПРИХОДНЫЕ НАКЛАДНЫЕ ============ +@app.route('/incoming-orders') +def incoming_orders(): + sort_by = request.args.get('sort', 'date') + order = request.args.get('order', 'desc') + orders = db.get_incoming_orders(sort_by=sort_by, order=order) + return render_template('incoming_orders.html', orders=orders, sort_by=sort_by, order=order) + +# ============ ЗАПУСК ============ +if __name__ == '__main__': + app.run(debug=True) diff --git a/categories.json b/categories.json new file mode 100644 index 0000000..922d6cb --- /dev/null +++ b/categories.json @@ -0,0 +1,6 @@ +[ + {"id":1,"name":"Фотобумага"}, + {"id":2,"name":"Чернила"}, + {"id":3,"name":"Фотопленка"}, + {"id":4,"name":"Аксессуары"} +] diff --git a/create_tables.py b/create_tables.py new file mode 100644 index 0000000..9cc5759 --- /dev/null +++ b/create_tables.py @@ -0,0 +1,2 @@ +Flask==2.3.3 +Flask-SQLAlchemy==3.1.1 \ No newline at end of file diff --git a/database.py b/database.py new file mode 100644 index 0000000..daa7caa --- /dev/null +++ b/database.py @@ -0,0 +1,140 @@ +import json +import os +from datetime import datetime + +class SimpleDB: + def __init__(self): + self.products_file = 'products.json' + self.suppliers_file = 'suppliers.json' + self.categories_file = 'categories.json' + self.incoming_file = 'incoming_orders.json' + self._init_files() + + def _init_files(self): + files = [self.products_file, self.suppliers_file, self.categories_file, self.incoming_file] + for file in files: + if not os.path.exists(file): + with open(file, 'w', encoding='utf-8') as f: + json.dump([], f) + + # ==================== ТОВАРЫ ==================== + def get_products(self): + try: + with open(self.products_file, 'r', encoding='utf-8') as f: + return json.load(f) + except: + return [] + + def get_product_by_id(self, product_id): + products = self.get_products() + for product in products: + if product.get('id') == product_id: + return product + return None + + def save_product(self, product): + products = self.get_products() + product['id'] = len(products) + 1 + products.append(product) + with open(self.products_file, 'w', encoding='utf-8') as f: + json.dump(products, f, ensure_ascii=False, indent=2) + return product + + def update_product(self, product_id, updated_data): + products = self.get_products() + for i, product in enumerate(products): + if product.get('id') == product_id: + products[i].update(updated_data) + break + with open(self.products_file, 'w', encoding='utf-8') as f: + json.dump(products, f, ensure_ascii=False, indent=2) + return True + + def delete_product(self, product_id): + products = self.get_products() + products = [p for p in products if p.get('id') != product_id] + with open(self.products_file, 'w', encoding='utf-8') as f: + json.dump(products, f, ensure_ascii=False, indent=2) + + def update_stock(self, product_id, quantity_change): + products = self.get_products() + for product in products: + if product.get('id') == product_id: + product['stock_quantity'] = product.get('stock_quantity', 0) + quantity_change + break + with open(self.products_file, 'w', encoding='utf-8') as f: + json.dump(products, f, ensure_ascii=False, indent=2) + + # ==================== ПОСТАВЩИКИ ==================== + def get_suppliers(self): + try: + with open(self.suppliers_file, 'r', encoding='utf-8') as f: + return json.load(f) + except: + return [] + + def get_supplier_by_id(self, supplier_id): + suppliers = self.get_suppliers() + for supplier in suppliers: + if supplier.get('id') == supplier_id: + return supplier + return None + + def save_supplier(self, supplier): + suppliers = self.get_suppliers() + supplier['id'] = len(suppliers) + 1 + suppliers.append(supplier) + with open(self.suppliers_file, 'w', encoding='utf-8') as f: + json.dump(suppliers, f, ensure_ascii=False, indent=2) + return supplier + + def update_supplier(self, supplier_id, updated_data): + suppliers = self.get_suppliers() + for i, supplier in enumerate(suppliers): + if supplier.get('id') == supplier_id: + suppliers[i].update(updated_data) + break + with open(self.suppliers_file, 'w', encoding='utf-8') as f: + json.dump(suppliers, f, ensure_ascii=False, indent=2) + return True + + def delete_supplier(self, supplier_id): + suppliers = self.get_suppliers() + suppliers = [s for s in suppliers if s.get('id') != supplier_id] + with open(self.suppliers_file, 'w', encoding='utf-8') as f: + json.dump(suppliers, f, ensure_ascii=False, indent=2) + + # ==================== КАТЕГОРИИ ==================== + def get_categories(self): + try: + with open(self.categories_file, 'r', encoding='utf-8') as f: + return json.load(f) + except: + return [] + + # ==================== ПРИХОДНЫЕ НАКЛАДНЫЕ ==================== + def get_incoming_orders(self, sort_by='date', order='desc'): + try: + with open(self.incoming_file, 'r', encoding='utf-8') as f: + orders = json.load(f) + + if sort_by == 'date': + orders.sort(key=lambda x: x.get('date', ''), reverse=(order == 'desc')) + elif sort_by == 'total': + orders.sort(key=lambda x: x.get('final_total', x.get('total', 0)), reverse=(order == 'desc')) + elif sort_by == 'supplier': + orders.sort(key=lambda x: x.get('supplier_name', ''), reverse=(order == 'desc')) + return orders + except: + return [] + + def save_incoming_order(self, order): + orders = self.get_incoming_orders() + order['id'] = len(orders) + 1 + order['date'] = datetime.now().strftime('%d.%m.%Y %H:%M') + orders.append(order) + with open(self.incoming_file, 'w', encoding='utf-8') as f: + json.dump(orders, f, ensure_ascii=False, indent=2) + return order + +db = SimpleDB() diff --git a/incoming_orders.json b/incoming_orders.json new file mode 100644 index 0000000..ec15fd3 --- /dev/null +++ b/incoming_orders.json @@ -0,0 +1,47 @@ +[ + { + "supplier_id": 2, + "supplier_name": "ИП ФотоСнаб", + "product_id": 4, + "product_name": "Фотопленка 35мм", + "expiry_date": "31.03.2026", + "quantity": 5000, + "purchase_price": 250.0, + "total": 1250000.0, + "expiry_discount_percent": 50, + "discount_amount": 625000.0, + "final_total": 625000.0, + "id": 2, + "date": "30.03.2026 11:38" + }, + { + "supplier_id": 1, + "supplier_name": "ООО КанцТорг", + "product_id": 4, + "product_name": "Фотопленка 35мм", + "expiry_date": "31.03.2026", + "quantity": 13, + "purchase_price": 250.0, + "total": 3250.0, + "expiry_discount_percent": 50, + "discount_amount": 1625.0, + "final_total": 1625.0, + "id": 1, + "date": "30.03.2026 11:33" + }, + { + "supplier_id": 1, + "supplier_name": "ООО КанцТорг", + "product_id": 4, + "product_name": "Фотопленка 35мм", + "expiry_date": "31.03.2026", + "quantity": 12, + "purchase_price": 250.0, + "total": 3000.0, + "expiry_discount_percent": 50, + "discount_amount": 1500.0, + "final_total": 1500.0, + "id": 3, + "date": "30.03.2026 11:39" + } +] \ No newline at end of file diff --git a/orders.json b/orders.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/orders.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/outgoing_orders.json b/outgoing_orders.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/outgoing_orders.json @@ -0,0 +1 @@ +[] diff --git a/partners.json b/partners.json new file mode 100644 index 0000000..ffbb1c7 --- /dev/null +++ b/partners.json @@ -0,0 +1,20 @@ +[ + { + "id": 1, + "name": "ООО ФотоМир", + "contact": "ivan@fotomir.ru", + "phone": "+7 (999) 111-22-33" + }, + { + "id": 2, + "name": "ТОО ФотоПлюс", + "contact": "petr@fotoplus.ru", + "phone": "+7 (999) 444-55-66" + }, + { + "name": "ячс", + "contact": "ячс", + "phone": "ячс", + "id": 3 + } +] \ No newline at end of file diff --git a/products.json b/products.json new file mode 100644 index 0000000..20d2f56 --- /dev/null +++ b/products.json @@ -0,0 +1,52 @@ +[ + { + "id": 1, + "name": "Фотобумага глянцевая", + "manufacturer": "Lomond", + "product_type": "Фотобумага", + "purchase_price": 300, + "selling_price": 450, + "expiry_date": "31.12.2026", + "stock_quantity": 50 + }, + { + "id": 2, + "name": "Фотобумага матовая", + "manufacturer": "Canon", + "product_type": "Фотобумага", + "purchase_price": 400, + "selling_price": 550, + "expiry_date": "31.12.2027", + "stock_quantity": 30 + }, + { + "id": 3, + "name": "Чернила для принтера", + "manufacturer": "Epson", + "product_type": "Чернила", + "purchase_price": 600, + "selling_price": 890, + "expiry_date": "30.06.2026", + "stock_quantity": 20 + }, + { + "id": 4, + "name": "Фотопленка 35мм", + "manufacturer": "Kodak", + "product_type": "Пленка", + "purchase_price": 250, + "selling_price": 350, + "expiry_date": "31.03.2026", + "stock_quantity": 5040 + }, + { + "name": "zxc", + "manufacturer": "zxc", + "product_type": "Фотопленка", + "purchase_price": 1289.0, + "selling_price": 3000.0, + "expiry_date": " 31.12.2027", + "stock_quantity": 10, + "id": 5 + } +] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ebb0c6d --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +Flask==2.3.3 diff --git a/requirements.txt.txt b/requirements.txt.txt new file mode 100644 index 0000000..9cc5759 --- /dev/null +++ b/requirements.txt.txt @@ -0,0 +1,2 @@ +Flask==2.3.3 +Flask-SQLAlchemy==3.1.1 \ No newline at end of file diff --git a/reviews.json b/reviews.json new file mode 100644 index 0000000..7ecc6f7 --- /dev/null +++ b/reviews.json @@ -0,0 +1,11 @@ +[ + { + "user_id": 1, + "author_name": "RoTacy", + "rating": 5, + "text": "ееееееееееееееееееееееееее", + "id": 1, + "created_at": "27.03.2026", + "is_approved": true + } +] \ No newline at end of file diff --git a/static/image_names.txt b/static/image_names.txt new file mode 100644 index 0000000..3ea3e33 --- /dev/null +++ b/static/image_names.txt @@ -0,0 +1,5 @@ +Портретная съемка +Пейзажная фотография +Предметная съемка +Свадебная фотография +Уличная съемка \ No newline at end of file diff --git a/static/images/photo1.jpg b/static/images/photo1.jpg new file mode 100644 index 0000000..582aa68 Binary files /dev/null and b/static/images/photo1.jpg differ diff --git a/static/images/photo2.jpg b/static/images/photo2.jpg new file mode 100644 index 0000000..cc3c81f Binary files /dev/null and b/static/images/photo2.jpg differ diff --git a/static/images/photo3.jpg b/static/images/photo3.jpg new file mode 100644 index 0000000..b34a5dc Binary files /dev/null and b/static/images/photo3.jpg differ diff --git a/static/images/photo4.jpg b/static/images/photo4.jpg new file mode 100644 index 0000000..a68dc0e Binary files /dev/null and b/static/images/photo4.jpg differ diff --git a/static/images/photo5.jpg b/static/images/photo5.jpg new file mode 100644 index 0000000..38f5ff7 Binary files /dev/null and b/static/images/photo5.jpg differ diff --git a/static/styles.css b/static/styles.css new file mode 100644 index 0000000..88bf334 --- /dev/null +++ b/static/styles.css @@ -0,0 +1,231 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Old Standard TT', 'Times New Roman', serif; + background-color: #FDF5E6; + color: #333; + line-height: 1.6; +} + +/* Шапка сайта */ +nav { + background-color: #CD853F; + padding: 1rem 0; + position: sticky; + top: 0; + z-index: 100; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); +} + +nav ul { + max-width: 1200px; + margin: 0 auto; + display: flex; + justify-content: center; + list-style: none; + gap: 1.5rem; + flex-wrap: wrap; +} + +nav a { + color: white; + text-decoration: none; + padding: 0.5rem 1rem; + transition: all 0.3s; + font-size: 1.1rem; +} + +nav a:hover { + background-color: #FDF5E6; + color: #CD853F; + border-radius: 5px; +} + +/* Основной контент */ +.container { + max-width: 1200px; + margin: 2rem auto; + padding: 2rem; + background: white; + border-radius: 10px; + box-shadow: 0 2px 10px rgba(0,0,0,0.05); + min-height: 500px; +} + +h1 { + color: #CD853F; + margin-bottom: 1.5rem; + font-size: 2rem; + border-left: 4px solid #CD853F; + padding-left: 1rem; +} + +h2, h3 { + color: #8B5A2B; + margin: 1rem 0; +} + +/* Герой-секция */ +.hero { + text-align: center; + padding: 3rem; + background: linear-gradient(135deg, #FDF5E6 0%, #E6D5B8 100%); + border-radius: 10px; + margin-bottom: 2rem; +} + +.hero h1 { + border-left: none; + font-size: 2.5rem; + margin-bottom: 1rem; +} + +/* Карточки */ +.features-grid, .services-grid, .products-grid, .gallery-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 2rem; + margin-top: 2rem; +} + +.feature-card, .service-card, .product-card, .discount-card { + background: #FDF5E6; + padding: 1.5rem; + border-radius: 10px; + border: 1px solid #CD853F; + transition: transform 0.3s, box-shadow 0.3s; +} + +.feature-card:hover, .service-card:hover, .product-card:hover { + transform: translateY(-5px); + box-shadow: 0 5px 15px rgba(205, 133, 63, 0.2); +} + +/* Товары */ +.product-card { + background: white; +} + +.product-card h3 { + color: #CD853F; + margin-bottom: 0.5rem; +} + +.price { + font-size: 1.3rem; + color: #CD853F; + font-weight: bold; + margin: 0.5rem 0; +} + +.manufacturer { + color: #666; + font-size: 0.9rem; + margin: 0.5rem 0; +} + +.expiry { + font-size: 0.85rem; + color: #888; + margin-top: 0.5rem; +} + +.stock { + display: inline-block; + padding: 0.2rem 0.5rem; + border-radius: 3px; + font-size: 0.8rem; + margin-top: 0.5rem; +} + +.in-stock { + background-color: #90EE90; + color: #228B22; +} + +.out-of-stock { + background-color: #FFB6C1; + color: #DC143C; +} + +/* Форма */ +.order-form { + max-width: 600px; + margin: 0 auto; +} + +.form-group { + margin-bottom: 1rem; +} + +.form-group label { + display: block; + margin-bottom: 0.5rem; + font-weight: bold; + color: #CD853F; +} + +.form-group input, +.form-group select, +.form-group textarea { + width: 100%; + padding: 0.75rem; + border: 1px solid #CD853F; + border-radius: 5px; + font-family: inherit; +} + +.btn-primary, .btn-submit { + background-color: #CD853F; + color: white; + padding: 0.75rem 1.5rem; + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 1rem; + font-family: inherit; + transition: background-color 0.3s; + text-decoration: none; + display: inline-block; +} + +.btn-primary:hover, .btn-submit:hover { + background-color: #B87333; +} + +/* Футер */ +footer { + background-color: #CD853F; + color: white; + text-align: center; + padding: 2rem; + margin-top: 2rem; +} + +/* Акции */ +.discount-card { + background: #FDF5E6; + border-left: 4px solid #CD853F; +} + +/* Адаптивность */ +@media (max-width: 768px) { + nav ul { + flex-direction: column; + align-items: center; + gap: 0.5rem; + } + + .container { + margin: 1rem; + padding: 1rem; + } + + .hero { + padding: 2rem; + } +} \ No newline at end of file diff --git a/static/styles.css.txt b/static/styles.css.txt new file mode 100644 index 0000000..e69de29 diff --git a/suppliers.json b/suppliers.json new file mode 100644 index 0000000..7d4ae09 --- /dev/null +++ b/suppliers.json @@ -0,0 +1,4 @@ +[ + {"id":1,"name":"ООО КанцТорг","contact":"Иванов Иван","phone":"+7 (495) 123-45-67"}, + {"id":2,"name":"ИП ФотоСнаб","contact":"Петров Петр","phone":"+7 (495) 987-65-43"} +] diff --git a/templates/about.html b/templates/about.html new file mode 100644 index 0000000..068dde6 --- /dev/null +++ b/templates/about.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block content %} +

О нас

+

Мы занимаемся продажей фотоматериалов с 2010 года.

+

В нашем ассортименте представлены фотобумага, чернила, фотопленка и аксессуары.

+

Мы сотрудничаем с проверенными производителями: Canon, Epson, Kodak, Lomond.

+{% endblock %} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..9eab94e --- /dev/null +++ b/templates/base.html @@ -0,0 +1,38 @@ + + + + + + Фотоматериалы - Склад + + + + + + +
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} +
{{ message }}
+ {% endfor %} + {% endif %} + {% endwith %} + + {% block content %}{% endblock %} +
+ +
+

© 2025 Фотоматериалы. Система учета склада

+

📞 Телефон: +7 (999) 123-45-67 | ✉️ Email: info@photomaterials.ru

+
+ + diff --git a/templates/certificates.html b/templates/certificates.html new file mode 100644 index 0000000..a261d0e --- /dev/null +++ b/templates/certificates.html @@ -0,0 +1,56 @@ +{% extends "base.html" %} + +{% block content %} +

Сертификаты качества

+

Вся наша продукция имеет официальные сертификаты и соответствует высоким стандартам качества.

+ +
+ +
+
📜
+

Сертификат ISO 9001

+

Международный стандарт качества менеджмента

+

Действителен до 2026 года

+
+ +
+
🏆
+

Гарантия производителя

+

Официальная гарантия от Canon, Epson, Kodak

+

Срок гарантии 12 месяцев

+
+ +
+
🌿
+

Экологический сертификат

+

Продукция безопасна для окружающей среды

+

Соответствует стандартам RoHS

+
+ +
+
🔬
+

Сертификат качества

+

Лабораторные испытания продукции

+

Соответствие ГОСТ Р

+
+ +
+
📸
+

Сертификат соответствия

+

Профессиональная фотопродукция

+

Рекомендовано профессиональными фотографами

+
+ +
+
📋
+

Декларация о соответствии

+

Таможенного союза ЕАЭС

+

Действительна до 2027 года

+
+
+ +
+

Все сертификаты можно запросить по электронной почте или посмотреть в нашем магазине

+

✉️ rotacyzxc@gmail.com | 📞 +7 (995) 947-00-42

+
+{% endblock %} \ No newline at end of file diff --git a/templates/contacts.html b/templates/contacts.html new file mode 100644 index 0000000..8ab43b4 --- /dev/null +++ b/templates/contacts.html @@ -0,0 +1,56 @@ +{% extends "base.html" %} + +{% block content %} +

Контакты

+ +
+
+

Наши контакты

+ +

📞 Телефон:
+ +7 (995) 947-00-42

+ +

💬 WhatsApp / Telegram:
+ +7 (995) 947-00-42

+ +

✉️ Email: rotacyzxc@gmail.com
+

+ +

📍 Адрес:
+ г. Белорецк, ул. Точисского, д. 16/2

+ +

🕒 Режим работы:
+ Понедельник - Пятница: 10:00 - 20:00
+ Суббота: 11:00 - 18:00
+ Воскресенье: выходной

+ +

🚗 Как добраться:
+ на такси или пешочком

+
+ +
+

Свяжитесь с нами

+
+
+
+ +
+
+
+ +
+
+
+ +
+ +
+ +
+

📱 Мы в соцсетях:

+

Instagram | VK | Telegram

+

@RoTacy

+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/discounts.html b/templates/discounts.html new file mode 100644 index 0000000..1b81765 --- /dev/null +++ b/templates/discounts.html @@ -0,0 +1,29 @@ +{% extends "base.html" %} + +{% block content %} +

Акции и скидки

+ +
+

🎉 Скидка 10% на первый заказ

+

Для новых клиентов при первом заказе от 1000 ₽

+

Акция действует до 31.12.2024

+
+ +
+

🎁 Подарок при заказе

+

При покупке фотобумаги - чернила со скидкой 15%

+

Акция действует до 31.03.2024

+
+ +
+

🏆 Оптовым покупателям

+

Индивидуальные условия для постоянных клиентов и оптовых закупок от 5000 ₽

+

Скидка до 20% в зависимости от объема

+
+ +
+

📸 Спецпредложение месяца

+

Фотобумага Lomond глянцевая + чернила Epson = 1200 ₽ (экономия 140 ₽)

+

Предложение ограничено!

+
+{% endblock %} \ No newline at end of file diff --git a/templates/edit_product.html b/templates/edit_product.html new file mode 100644 index 0000000..3b7f727 --- /dev/null +++ b/templates/edit_product.html @@ -0,0 +1,62 @@ +{% extends "base.html" %} + +{% block content %} +

✏️ Редактировать товар

+ +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + + 📅 Формат: 31.12.2026 +
+ +
+ + +
+ +
+ + ❌ Отмена +
+
+
+ + +{% endblock %} diff --git a/templates/edit_supplier.html b/templates/edit_supplier.html new file mode 100644 index 0000000..1dc40c0 --- /dev/null +++ b/templates/edit_supplier.html @@ -0,0 +1,44 @@ +{% extends "base.html" %} + +{% block content %} +

✏️ Редактировать поставщика

+ +
+
+
+ + +
+ +
+ + +
+ +
+ + + + 📞 Форматы: +7 (999) 123-45-67, 8-999-123-45-67, 9991234567 + +
+ +
+ + ❌ Отмена +
+
+
+ + +{% endblock %} diff --git a/templates/gallery.html b/templates/gallery.html new file mode 100644 index 0000000..e806f53 --- /dev/null +++ b/templates/gallery.html @@ -0,0 +1,53 @@ +{% extends "base.html" %} + +{% block content %} +

Галерея работ

+

Примеры работ, напечатанных на наших материалах:

+ +{% if gallery_items %} + +{% else %} +

Фотографии пока не добавлены. Добавьте изображения в папку static/images/

+{% endif %} + + +{% endblock %} \ No newline at end of file diff --git a/templates/incoming.html b/templates/incoming.html new file mode 100644 index 0000000..7929b45 --- /dev/null +++ b/templates/incoming.html @@ -0,0 +1,140 @@ +{% extends "base.html" %} + +{% block content %} +

📥 Поступление товара на склад

+ +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+

💰 Расчет стоимости:

+

Сумма: 0 ₽

+

Скидка по сроку годности: 0% (0 ₽)

+

Итого к оплате: 0 ₽

+
+ +
+ 📅 Условия скидки по сроку годности: +
    +
  • Менее 30 дней → 50% скидка
  • +
  • 30-90 дней → 30% скидка
  • +
  • 90-180 дней → 15% скидка
  • +
  • Более 180 дней → 0% скидка
  • +
  • Просрочен → 100% скидка (не принимается)
  • +
+
+ + +
+
+ + + + +{% endblock %} diff --git a/templates/incoming_orders.html b/templates/incoming_orders.html new file mode 100644 index 0000000..bdcc516 --- /dev/null +++ b/templates/incoming_orders.html @@ -0,0 +1,64 @@ +{% extends "base.html" %} + +{% block content %} +

📋 Приходные накладные

+ + + + +{% if orders %} + {% for order in orders %} +
+ +
+ Поступление #{{ order.id }} + {{ order.date }} +
+

🚚 Поставщик: {{ order.supplier_name }}

+

📦 Товар: {{ order.product_name }}

+

📅 Срок годности: {{ order.expiry_date or 'не указан' }}

+

📦 Количество: {{ order.quantity }} шт.

+

💰 Цена закупки: {{ order.purchase_price }} ₽

+ + {% if order.expiry_discount_percent and order.expiry_discount_percent > 0 %} +
+

💰 Стоимость со скидкой:

+

+ Сумма: {{ order.total }} ₽
+ Скидка по сроку годности: {{ order.expiry_discount_percent }}% ({{ order.discount_amount }} ₽)
+ Итого: {{ order.final_total }} ₽ +

+
+ {% else %} +

💰 Итого: {{ order.total }} ₽

+ {% endif %} +
+ {% endfor %} +{% else %} +

Приходных накладных пока нет.

+

➕ Оформить поступление

+{% endif %} + + +{% endblock %} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..951cd97 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,113 @@ +{% extends "base.html" %} + +{% block content %} +
+

📦 Система учета склада

+

Фотоматериалы — управление товарами, поставщиками и поступлениями

+
+ +
+
+

📦 Товаров на складе

+

{{ total_items }}

+

единиц

+
+
+

💰 Общая стоимость

+

{{ "{:,.0f}".format(total_value) }} ₽

+

по розничным ценам

+
+
+ +
+
+

📦 Управление товарами

+

Добавление, редактирование, удаление товаров. Контроль остатков на складе.

+ Перейти → +
+
+

🚚 Поставщики

+

Ведение базы поставщиков для оперативного пополнения склада.

+ Перейти → +
+
+

📥 Поступление товаров

+

Оприходование товара на склад. Автоматическое увеличение остатков.

+ Перейти → +
+
+

📋 Приходные накладные

+

История всех поступлений товара на склад. Сортировка по дате и сумме.

+ Перейти → +
+
+ + +{% endblock %} diff --git a/templates/my_orders.html b/templates/my_orders.html new file mode 100644 index 0000000..c5b2b33 --- /dev/null +++ b/templates/my_orders.html @@ -0,0 +1,70 @@ +{% extends "base.html" %} + +{% block content %} +

Все заказы

+ + + + +{% if orders %} + {% for order in orders %} +
+ +
+ Заказ #{{ order.id }} +
+ {{ order.order_date }} + 🗑️ Удалить +
+
+ +

👤 Клиент: {{ order.client_name }} ({{ order.phone }})

+

📦 Товар: {{ order.product_name }}

+

📋 Категория: {{ order.category }}

+

🤝 Партнер: {{ order.partner }}

+

📦 Количество: {{ order.quantity }} шт. × {{ order.price_per_unit }} ₽

+ +
+

💰 Стоимость:

+

+ Сумма: {{ order.total }} ₽
+ Скидка: {{ order.discount_percent }}% ({{ order.discount_amount }} ₽)
+ Итого: {{ order.final_total }} ₽ +

+
+ + {% if order.notes %} +

📝 Примечание: {{ order.notes }}

+ {% endif %} +
+ {% endfor %} +{% else %} +

Заказов пока нет.

+

Сделать первый заказ

+{% endif %} + + +{% endblock %} diff --git a/templates/order.html b/templates/order.html new file mode 100644 index 0000000..2cad0af --- /dev/null +++ b/templates/order.html @@ -0,0 +1,130 @@ +{% extends "base.html" %} + +{% block content %} +

Оформление заказа

+ +
+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+ +
+

💰 Расчет стоимости

+
+

Товар: -

+

Цена за шт: 0 ₽

+

Сумма: 0 ₽

+

Скидка: 0% (0 ₽)

+

Итого: 0 ₽

+
+
+

Условия скидки:

+
    +
  • от 5 шт. → 5% скидка
  • +
  • от 10 шт. → 10% скидка
  • +
  • от 20 шт. → 15% скидка
  • +
  • от 50 шт. → 20% скидка
  • +
+
+

📞 Телефон: +7 (999) 123-45-67

+

✉️ Email: info@photomaterials.ru

+
+
+ + + + +{% endblock %} diff --git a/templates/partners.html b/templates/partners.html new file mode 100644 index 0000000..66137b1 --- /dev/null +++ b/templates/partners.html @@ -0,0 +1,45 @@ +{% extends "base.html" %} + +{% block content %} +

Наши партнеры

+ +
+

➕ Добавить партнера

+
+
+ + + + +
+
+
+ +
+ {% for partner in partners %} +
+

{{ partner.name }}

+

📧 {{ partner.contact }}

+

📞 {{ partner.phone }}

+ 🗑️ Удалить +
+ {% endfor %} +
+ + +{% endblock %} diff --git a/templates/products.html b/templates/products.html new file mode 100644 index 0000000..0ef0bec --- /dev/null +++ b/templates/products.html @@ -0,0 +1,78 @@ +{% extends "base.html" %} + +{% block content %} +

📦 Управление товарами

+ + +
+

➕ Добавить товар

+
+
+ + + + + + + + +
+
+
+ + +
+ {% if products %} + {% for product in products %} +
+

{{ product.name }}

+

🏭 Производитель: {{ product.manufacturer }}

+

📋 Категория: {{ product.product_type }}

+

💰 Закупка: {{ product.purchase_price }} ₽ | Продажа: {{ product.selling_price }} ₽

+

📅 Срок годности: {{ product.expiry_date if product.expiry_date else 'не указан' }}

+

+ 📦 Остаток: {{ product.stock_quantity }} шт. +

+ +
+ {% endfor %} + {% else %} +

Товаров пока нет

+ {% endif %} +
+ + +{% endblock %} diff --git a/templates/reviews.html b/templates/reviews.html new file mode 100644 index 0000000..df261ed --- /dev/null +++ b/templates/reviews.html @@ -0,0 +1,63 @@ +{% extends "base.html" %} + +{% block content %} +

Отзывы наших клиентов

+ +
+ +
+

Оставить отзыв

+ + {% if session.get('user_id') %} +
+
+ + +
+ +
+ + +
+ + +
+ {% else %} +

Чтобы оставить отзыв, войдите в систему

+ {% endif %} +
+ + +
+

Отзывы

+ + {% if reviews %} + {% for review in reviews %} +
+
+
+ {{ review.author_name }} + + {% for i in range(review.rating) %}★{% endfor %} + {% for i in range(5 - review.rating) %}☆{% endfor %} + +
+
+ {{ review.created_at }} +
+
+

{{ review.text }}

+
+ {% endfor %} + {% else %} +

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

+ {% endif %} +
+
+{% endblock %} \ No newline at end of file diff --git a/templates/services.html b/templates/services.html new file mode 100644 index 0000000..120db26 --- /dev/null +++ b/templates/services.html @@ -0,0 +1,98 @@ +{% extends "base.html" %} + +{% block content %} +

Наши услуги

+

Мы предлагаем полный спектр услуг для фотографов и любителей:

+ +
+
+

🖨️ Печать фотографий

+

Форматы: 10x15, 15x21, 20x30, А4, А3+

+

Материалы: глянцевая, матовая, перламутровая, холст

+

Цены: от 30 ₽ за фото 10x15

+

Срок: от 30 минут

+
+ +
+

🎨 Фотообработка

+

Услуги: цветокоррекция, ретушь, кадрирование

+

Удаление дефектов: исправление кожи, удаление объектов

+

Цены: от 100 ₽ за фото

+

Срок: от 1 часа

+
+ +
+

🖼️ Печать на холсте

+

Форматы: любой размер до 90x120 см

+

Особенности: натяжка на подрамник, покрытие лаком

+

Цены: от 1500 ₽ за холст 30x40

+

Срок: 2-3 дня

+
+ +
+

📚 Фотокниги и альбомы

+

Форматы: 20x20, 20x30, 30x30 см

+

Варианты: твердая обложка, кожаный переплет

+

Цены: от 2000 ₽ за фотокнигу 20 страниц

+

Срок: 5-7 дней

+
+ +
+

💬 Консультации

+

Помощь в выборе: фотобумаги, чернил, принтеров

+

Настройка оборудования: калибровка монитора, профилирование

+

Консультация: бесплатно по телефону или в магазине

+
+ +
+

🚚 Доставка

+

По Москве: курьером - 300 ₽, бесплатно от 2000 ₽

+

По России: Почта России, СДЭК от 350 ₽

+

Самовывоз: бесплатно из магазина

+

Срок: 1-2 дня по Москве, 3-7 дней по России

+
+
+ +
+

✨ Специальное предложение

+

При заказе печати от 20 фотографий - скидка 10% на обработку!

+

Акция действует до 31.05.2024

+
+ + +{% endblock %} \ No newline at end of file diff --git a/templates/supplier_orders.html b/templates/supplier_orders.html new file mode 100644 index 0000000..493cfd8 --- /dev/null +++ b/templates/supplier_orders.html @@ -0,0 +1,85 @@ +{% extends "base.html" %} + +{% block content %} +

📋 Заказы поставщика: {{ supplier.name }}

+ +
+
+
+ 📞 Контакт: {{ supplier.contact }} +
+
+ 📱 Телефон: {{ supplier.phone }} +
+
+ 📦 Всего поставок: {{ orders|length }} +
+
+ 📦 Всего товара: {{ total_items }} шт. +
+
+ 💰 Общая сумма: {{ "{:,.0f}".format(total_sum) }} ₽ +
+
+ +
+ +{% if orders %} +
+ {% for order in orders %} +
+
+ 📄 Поступление #{{ order.id }} + {{ order.date }} +
+

📦 Товар: {{ order.product_name }}

+

📦 Количество: {{ order.quantity }} шт.

+

💰 Цена закупки: {{ order.purchase_price }} ₽

+

💰 Общая сумма: {{ order.total }} ₽

+
+ {% endfor %} +
+{% else %} +
+

📭 У этого поставщика пока нет заказов.

+

➕ Оформить поступление

+
+{% endif %} + + +{% endblock %} diff --git a/templates/suppliers.html b/templates/suppliers.html new file mode 100644 index 0000000..129d1d8 --- /dev/null +++ b/templates/suppliers.html @@ -0,0 +1,54 @@ +{% extends "base.html" %} + +{% block content %} +

🚚 Управление поставщиками

+ +
+

➕ Добавить поставщика

+
+
+ + + + +
+ + 📞 Форматы телефона: +7 (999) 123-45-67, 8-999-123-45-67, 9991234567 + +
+
+ +
+ {% for supplier in suppliers %} +
+

{{ supplier.name }}

+

📧 {{ supplier.contact }}

+

📞 {{ supplier.phone }}

+ +
+ {% endfor %} +
+ + +{% endblock %}