This commit is contained in:
commit 5f22af0ec0
47 changed files with 2137 additions and 0 deletions

3
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

View file

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

4
.idea/misc.xml Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (zxc)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/project.iml" filepath="$PROJECT_DIR$/.idea/project.iml" />
</modules>
</component>
</project>

8
.idea/project.iml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

BIN
TZ.odt Normal file

Binary file not shown.

293
app.py Normal file
View file

@ -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/<int:product_id>')
def delete_product(product_id):
db.delete_product(product_id)
flash('Товар удален', 'warning')
return redirect(url_for('products'))
@app.route('/products/edit/<int:product_id>', 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/<int:supplier_id>', 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/<int:supplier_id>')
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)

6
categories.json Normal file
View file

@ -0,0 +1,6 @@
[
{"id":1,"name":"Фотобумага"},
{"id":2,"name":"Чернила"},
{"id":3,"name":"Фотопленка"},
{"id":4,"name":"Аксессуары"}
]

2
create_tables.py Normal file
View file

@ -0,0 +1,2 @@
Flask==2.3.3
Flask-SQLAlchemy==3.1.1

140
database.py Normal file
View file

@ -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()

47
incoming_orders.json Normal file
View file

@ -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"
}
]

1
orders.json Normal file
View file

@ -0,0 +1 @@
[]

1
outgoing_orders.json Normal file
View file

@ -0,0 +1 @@
[]

20
partners.json Normal file
View file

@ -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
}
]

52
products.json Normal file
View file

@ -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
}
]

1
requirements.txt Normal file
View file

@ -0,0 +1 @@
Flask==2.3.3

2
requirements.txt.txt Normal file
View file

@ -0,0 +1,2 @@
Flask==2.3.3
Flask-SQLAlchemy==3.1.1

11
reviews.json Normal file
View file

@ -0,0 +1,11 @@
[
{
"user_id": 1,
"author_name": "RoTacy",
"rating": 5,
"text": "ееееееееееееееееееееееееее",
"id": 1,
"created_at": "27.03.2026",
"is_approved": true
}
]

5
static/image_names.txt Normal file
View file

@ -0,0 +1,5 @@
Портретная съемка
Пейзажная фотография
Предметная съемка
Свадебная фотография
Уличная съемка

BIN
static/images/photo1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

BIN
static/images/photo2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
static/images/photo3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

BIN
static/images/photo4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

BIN
static/images/photo5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

231
static/styles.css Normal file
View file

@ -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;
}
}

0
static/styles.css.txt Normal file
View file

4
suppliers.json Normal file
View file

@ -0,0 +1,4 @@
[
{"id":1,"name":"ООО КанцТорг","contact":"Иванов Иван","phone":"+7 (495) 123-45-67"},
{"id":2,"name":"ИП ФотоСнаб","contact":"Петров Петр","phone":"+7 (495) 987-65-43"}
]

8
templates/about.html Normal file
View file

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block content %}
<h1>О нас</h1>
<p>Мы занимаемся продажей фотоматериалов с 2010 года.</p>
<p>В нашем ассортименте представлены фотобумага, чернила, фотопленка и аксессуары.</p>
<p>Мы сотрудничаем с проверенными производителями: Canon, Epson, Kodak, Lomond.</p>
{% endblock %}

38
templates/base.html Normal file
View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Фотоматериалы - Склад</title>
<link href="https://fonts.googleapis.com/css2?family=Old+Standard+TT:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
<nav>
<ul>
<li><a href="/">Главная</a></li>
<li><a href="/products">Товары</a></li>
<li><a href="/suppliers">Поставщики</a></li>
<li><a href="/incoming">Поступление</a></li>
<li><a href="/incoming-orders">Приходные</a></li>
</ul>
</nav>
<div class="container">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="flash {{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
<footer>
<p>&copy; 2025 Фотоматериалы. Система учета склада</p>
<p>📞 Телефон: +7 (999) 123-45-67 | ✉️ Email: info@photomaterials.ru</p>
</footer>
</body>
</html>

View file

@ -0,0 +1,56 @@
{% extends "base.html" %}
{% block content %}
<h1>Сертификаты качества</h1>
<p>Вся наша продукция имеет официальные сертификаты и соответствует высоким стандартам качества.</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 2rem; margin-top: 2rem;">
<div style="background: linear-gradient(135deg, #FDF5E6 0%, #fff 100%); border-radius: 15px; padding: 1.5rem; border: 1px solid #CD853F; text-align: center;">
<div style="font-size: 3rem; margin-bottom: 1rem;">📜</div>
<h3 style="color: #CD853F;">Сертификат ISO 9001</h3>
<p>Международный стандарт качества менеджмента</p>
<p style="font-size: 0.9rem; color: #666;">Действителен до 2026 года</p>
</div>
<div style="background: linear-gradient(135deg, #FDF5E6 0%, #fff 100%); border-radius: 15px; padding: 1.5rem; border: 1px solid #CD853F; text-align: center;">
<div style="font-size: 3rem; margin-bottom: 1rem;">🏆</div>
<h3 style="color: #CD853F;">Гарантия производителя</h3>
<p>Официальная гарантия от Canon, Epson, Kodak</p>
<p style="font-size: 0.9rem; color: #666;">Срок гарантии 12 месяцев</p>
</div>
<div style="background: linear-gradient(135deg, #FDF5E6 0%, #fff 100%); border-radius: 15px; padding: 1.5rem; border: 1px solid #CD853F; text-align: center;">
<div style="font-size: 3rem; margin-bottom: 1rem;">🌿</div>
<h3 style="color: #CD853F;">Экологический сертификат</h3>
<p>Продукция безопасна для окружающей среды</p>
<p style="font-size: 0.9rem; color: #666;">Соответствует стандартам RoHS</p>
</div>
<div style="background: linear-gradient(135deg, #FDF5E6 0%, #fff 100%); border-radius: 15px; padding: 1.5rem; border: 1px solid #CD853F; text-align: center;">
<div style="font-size: 3rem; margin-bottom: 1rem;">🔬</div>
<h3 style="color: #CD853F;">Сертификат качества</h3>
<p>Лабораторные испытания продукции</p>
<p style="font-size: 0.9rem; color: #666;">Соответствие ГОСТ Р</p>
</div>
<div style="background: linear-gradient(135deg, #FDF5E6 0%, #fff 100%); border-radius: 15px; padding: 1.5rem; border: 1px solid #CD853F; text-align: center;">
<div style="font-size: 3rem; margin-bottom: 1rem;">📸</div>
<h3 style="color: #CD853F;">Сертификат соответствия</h3>
<p>Профессиональная фотопродукция</p>
<p style="font-size: 0.9rem; color: #666;">Рекомендовано профессиональными фотографами</p>
</div>
<div style="background: linear-gradient(135deg, #FDF5E6 0%, #fff 100%); border-radius: 15px; padding: 1.5rem; border: 1px solid #CD853F; text-align: center;">
<div style="font-size: 3rem; margin-bottom: 1rem;">📋</div>
<h3 style="color: #CD853F;">Декларация о соответствии</h3>
<p>Таможенного союза ЕАЭС</p>
<p style="font-size: 0.9rem; color: #666;">Действительна до 2027 года</p>
</div>
</div>
<div style="margin-top: 2rem; padding: 1.5rem; background: #CD853F10; border-radius: 10px; text-align: center; border-left: 4px solid #CD853F;">
<p style="margin: 0;">Все сертификаты можно запросить по электронной почте или посмотреть в нашем магазине</p>
<p style="margin-top: 0.5rem; color: #CD853F;">✉️ rotacyzxc@gmail.com | 📞 +7 (995) 947-00-42</p>
</div>
{% endblock %}

56
templates/contacts.html Normal file
View file

@ -0,0 +1,56 @@
{% extends "base.html" %}
{% block content %}
<h1>Контакты</h1>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem;">
<div>
<h3 style="color: #CD853F;">Наши контакты</h3>
<p><strong>📞 Телефон:</strong><br>
<span style="font-size: 1.3rem;">+7 (995) 947-00-42</span></p>
<p><strong>💬 WhatsApp / Telegram:</strong><br>
+7 (995) 947-00-42</p>
<p><strong>✉️ Email: rotacyzxc@gmail.com</strong><br>
</p>
<p><strong>📍 Адрес:</strong><br>
г. Белорецк, ул. Точисского, д. 16/2</p>
<p><strong>🕒 Режим работы:</strong><br>
Понедельник - Пятница: 10:00 - 20:00<br>
Суббота: 11:00 - 18:00<br>
Воскресенье: выходной</p>
<p><strong>🚗 Как добраться:</strong><br>
на такси или пешочком</p>
</div>
<div>
<h3 style="color: #CD853F;">Свяжитесь с нами</h3>
<form method="POST" style="background: #FDF5E6; padding: 1.5rem; border-radius: 10px;">
<div style="margin-bottom: 1rem;">
<label>Ваше имя:</label><br>
<input type="text" name="name" style="width: 100%; padding: 0.5rem; border: 1px solid #CD853F; border-radius: 5px;">
</div>
<div style="margin-bottom: 1rem;">
<label>Email:</label><br>
<input type="email" name="email" style="width: 100%; padding: 0.5rem; border: 1px solid #CD853F; border-radius: 5px;">
</div>
<div style="margin-bottom: 1rem;">
<label>Сообщение:</label><br>
<textarea name="message" rows="4" style="width: 100%; padding: 0.5rem; border: 1px solid #CD853F; border-radius: 5px;"></textarea>
</div>
<button type="submit" class="btn-primary">Отправить</button>
</form>
<div style="margin-top: 2rem; background: #CD853F10; padding: 1rem; border-radius: 10px; text-align: center;">
<p>📱 Мы в соцсетях:</p>
<p>Instagram | VK | Telegram</p>
<p style="font-size: 0.9rem; color: #666;">@RoTacy</p>
</div>
</div>
</div>
{% endblock %}

29
templates/discounts.html Normal file
View file

@ -0,0 +1,29 @@
{% extends "base.html" %}
{% block content %}
<h1>Акции и скидки</h1>
<div class="discount-card">
<h3>🎉 Скидка 10% на первый заказ</h3>
<p>Для новых клиентов при первом заказе от 1000 ₽</p>
<p>Акция действует до 31.12.2024</p>
</div>
<div class="discount-card">
<h3>🎁 Подарок при заказе</h3>
<p>При покупке фотобумаги - чернила со скидкой 15%</p>
<p>Акция действует до 31.03.2024</p>
</div>
<div class="discount-card">
<h3>🏆 Оптовым покупателям</h3>
<p>Индивидуальные условия для постоянных клиентов и оптовых закупок от 5000 ₽</p>
<p>Скидка до 20% в зависимости от объема</p>
</div>
<div class="discount-card">
<h3>📸 Спецпредложение месяца</h3>
<p>Фотобумага Lomond глянцевая + чернила Epson = 1200 ₽ (экономия 140 ₽)</p>
<p>Предложение ограничено!</p>
</div>
{% endblock %}

View file

@ -0,0 +1,62 @@
{% extends "base.html" %}
{% block content %}
<h1>✏️ Редактировать товар</h1>
<div style="max-width: 600px; margin: 0 auto;">
<form method="POST" style="background: #FDF5E6; padding: 2rem; border-radius: 10px;">
<div class="form-group">
<label>Название *</label>
<input type="text" name="name" value="{{ product.name }}" required style="width: 100%; padding: 0.5rem;">
</div>
<div class="form-group">
<label>Производитель</label>
<input type="text" name="manufacturer" value="{{ product.manufacturer }}" style="width: 100%; padding: 0.5rem;">
</div>
<div class="form-group">
<label>Категория</label>
<input type="text" name="product_type" value="{{ product.product_type }}" style="width: 100%; padding: 0.5rem;">
</div>
<div class="form-group">
<label>Цена закупки (₽)</label>
<input type="number" name="purchase_price" value="{{ product.purchase_price }}" step="0.01" style="width: 100%; padding: 0.5rem;">
</div>
<div class="form-group">
<label>Цена продажи (₽)</label>
<input type="number" name="selling_price" value="{{ product.selling_price }}" step="0.01" style="width: 100%; padding: 0.5rem;">
</div>
<div class="form-group">
<label>Срок годности</label>
<input type="text" name="expiry_date" value="{{ product.expiry_date }}" placeholder="дд.мм.гггг" style="width: 100%; padding: 0.5rem;">
<small style="color: #666;">📅 Формат: 31.12.2026</small>
</div>
<div class="form-group">
<label>Остаток на складе</label>
<input type="number" name="stock_quantity" value="{{ product.stock_quantity }}" style="width: 100%; padding: 0.5rem;">
</div>
<div style="display: flex; gap: 1rem;">
<button type="submit" style="background: #CD853F; color: white; padding: 0.5rem 1rem; border: none; border-radius: 5px;">💾 Сохранить</button>
<a href="/products" style="background: #666; color: white; padding: 0.5rem 1rem; text-decoration: none; border-radius: 5px;">❌ Отмена</a>
</div>
</form>
</div>
<style>
.form-group {
margin-bottom: 1rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: bold;
color: #CD853F;
}
</style>
{% endblock %}

View file

@ -0,0 +1,44 @@
{% extends "base.html" %}
{% block content %}
<h1>✏️ Редактировать поставщика</h1>
<div style="max-width: 600px; margin: 0 auto;">
<form method="POST" style="background: #FDF5E6; padding: 2rem; border-radius: 10px;">
<div class="form-group">
<label>Название *</label>
<input type="text" name="name" value="{{ supplier.name }}" required style="width: 100%; padding: 0.5rem;">
</div>
<div class="form-group">
<label>Контактное лицо</label>
<input type="text" name="contact" value="{{ supplier.contact }}" style="width: 100%; padding: 0.5rem;">
</div>
<div class="form-group">
<label>Телефон</label>
<input type="text" name="phone" value="{{ supplier.phone }}" id="phone" style="width: 100%; padding: 0.5rem;">
<small style="color: #666; display: block; margin-top: 0.25rem;">
📞 Форматы: +7 (999) 123-45-67, 8-999-123-45-67, 9991234567
</small>
</div>
<div style="display: flex; gap: 1rem;">
<button type="submit" style="background: #CD853F; color: white; padding: 0.5rem 1rem; border: none; border-radius: 5px;">💾 Сохранить</button>
<a href="/suppliers" style="background: #666; color: white; padding: 0.5rem 1rem; text-decoration: none; border-radius: 5px;">❌ Отмена</a>
</div>
</form>
</div>
<style>
.form-group {
margin-bottom: 1rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: bold;
color: #CD853F;
}
</style>
{% endblock %}

53
templates/gallery.html Normal file
View file

@ -0,0 +1,53 @@
{% extends "base.html" %}
{% block content %}
<h1>Галерея работ</h1>
<p>Примеры работ, напечатанных на наших материалах:</p>
{% if gallery_items %}
<div class="gallery-grid">
{% for item in gallery_items %}
<div class="gallery-item">
<img src="{{ url_for('static', filename='images/' + item.file) }}"
alt="{{ item.name }}"
style="width: 100%; height: 250px; object-fit: cover; border-radius: 10px;">
<p style="text-align: center; margin-top: 10px; font-weight: bold; color: #CD853F;">
{{ item.name }}
</p>
</div>
{% endfor %}
</div>
{% else %}
<p style="text-align: center; color: #999;">Фотографии пока не добавлены. Добавьте изображения в папку static/images/</p>
{% endif %}
<style>
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-top: 2rem;
}
.gallery-item {
background: #FDF5E6;
border-radius: 10px;
padding: 1rem;
transition: transform 0.3s;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.gallery-item:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(205, 133, 63, 0.2);
}
.gallery-item img {
transition: opacity 0.3s;
}
.gallery-item:hover img {
opacity: 0.9;
}
</style>
{% endblock %}

140
templates/incoming.html Normal file
View file

@ -0,0 +1,140 @@
{% extends "base.html" %}
{% block content %}
<h1>📥 Поступление товара на склад</h1>
<div style="max-width: 600px; margin: 0 auto;">
<form method="POST" style="background: #FDF5E6; padding: 2rem; border-radius: 10px;" oninput="calculatePrice()">
<div class="form-group">
<label>Поставщик *</label>
<select name="supplier_id" required style="width: 100%; padding: 0.5rem;">
<option value="">Выберите поставщика</option>
{% for supplier in suppliers %}
<option value="{{ supplier.id }}">{{ supplier.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label>Товар *</label>
<select name="product_id" id="product_id" required style="width: 100%; padding: 0.5rem;" onchange="updateProductInfo()">
<option value="">Выберите товар</option>
{% for product in products %}
<option value="{{ product.id }}"
data-price="{{ product.purchase_price }}"
data-expiry="{{ product.expiry_date }}">
{{ product.name }} (закупка: {{ product.purchase_price }} ₽, срок: {{ product.expiry_date or 'не указан' }})
</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label>Количество *</label>
<input type="number" name="quantity" id="quantity" min="1" value="1" required style="width: 100%; padding: 0.5rem;">
</div>
<div class="form-group">
<label>Цена закупки (₽) *</label>
<input type="number" name="purchase_price" id="purchase_price" step="0.01" required style="width: 100%; padding: 0.5rem;">
</div>
<div style="background: white; padding: 1rem; border-radius: 5px; margin: 1rem 0;">
<p><strong>💰 Расчет стоимости:</strong></p>
<p>Сумма: <span id="total">0</span> ₽</p>
<p>Скидка по сроку годности: <span id="discount_percent">0</span>% (<span id="discount_amount">0</span> ₽)</p>
<p style="font-size: 1.2rem;"><strong>Итого к оплате: <span id="final_total">0</span> ₽</strong></p>
</div>
<div style="background: #FFF3E0; padding: 0.5rem; border-radius: 5px; margin-bottom: 1rem;">
<small>📅 <strong>Условия скидки по сроку годности:</strong></small>
<ul style="margin: 0.5rem 0 0 1rem; font-size: 0.85rem;">
<li>Менее 30 дней → 50% скидка</li>
<li>30-90 дней → 30% скидка</li>
<li>90-180 дней → 15% скидка</li>
<li>Более 180 дней → 0% скидка</li>
<li>Просрочен → 100% скидка (не принимается)</li>
</ul>
</div>
<button type="submit" class="btn-submit">✅ Оприходовать на склад</button>
</form>
</div>
<script>
function updateProductInfo() {
let select = document.getElementById('product_id');
let selectedOption = select.options[select.selectedIndex];
let price = selectedOption.getAttribute('data-price') || 0;
let expiry = selectedOption.getAttribute('data-expiry') || '';
document.getElementById('purchase_price').value = price;
calculatePrice();
}
function calculatePrice() {
let quantity = parseInt(document.getElementById('quantity').value) || 0;
let price = parseFloat(document.getElementById('purchase_price').value) || 0;
let total = quantity * price;
// Расчет скидки по сроку годности
let select = document.getElementById('product_id');
let selectedOption = select.options[select.selectedIndex];
let expiry = selectedOption.getAttribute('data-expiry') || '';
let discountPercent = 0;
if (expiry) {
let parts = expiry.split('.');
if (parts.length === 3) {
let expiryDate = new Date(parts[2], parts[1] - 1, parts[0]);
let today = new Date();
let daysUntil = Math.ceil((expiryDate - today) / (1000 * 60 * 60 * 24));
if (daysUntil < 0) discountPercent = 100;
else if (daysUntil < 30) discountPercent = 50;
else if (daysUntil < 90) discountPercent = 30;
else if (daysUntil < 180) discountPercent = 15;
}
}
let discountAmount = total * discountPercent / 100;
let finalTotal = total - discountAmount;
document.getElementById('total').innerText = total.toFixed(2);
document.getElementById('discount_percent').innerText = discountPercent;
document.getElementById('discount_amount').innerText = discountAmount.toFixed(2);
document.getElementById('final_total').innerText = finalTotal.toFixed(2);
}
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('product_id').addEventListener('change', updateProductInfo);
document.getElementById('quantity').addEventListener('input', calculatePrice);
updateProductInfo();
});
</script>
<style>
.form-group {
margin-bottom: 1rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: bold;
color: #CD853F;
}
.btn-submit {
background: #CD853F;
color: white;
padding: 0.75rem;
border: none;
border-radius: 5px;
width: 100%;
cursor: pointer;
font-size: 1rem;
}
.btn-submit:hover {
background: #B87333;
}
</style>
{% endblock %}

View file

@ -0,0 +1,64 @@
{% extends "base.html" %}
{% block content %}
<h1>📋 Приходные накладные</h1>
<!-- Сортировка -->
<div style="margin-bottom: 1rem; padding: 0.5rem; background: #FDF5E6; border-radius: 5px;">
<span>Сортировать по: </span>
<a href="/incoming-orders?sort=date&order={% if sort_by=='date' and order=='desc' %}asc{% else %}desc{% endif %}" style="color: #CD853F; text-decoration: none;">
📅 Дата {% if sort_by=='date' %}{% if order=='desc' %}↓{% else %}↑{% endif %}{% endif %}
</a> |
<a href="/incoming-orders?sort=total&order={% if sort_by=='total' and order=='desc' %}asc{% else %}desc{% endif %}" style="color: #CD853F; text-decoration: none;">
💰 Сумма {% if sort_by=='total' %}{% if order=='desc' %}↓{% else %}↑{% endif %}{% endif %}
</a> |
<a href="/incoming-orders?sort=supplier&order={% if sort_by=='supplier' and order=='desc' %}asc{% else %}desc{% endif %}" style="color: #CD853F; text-decoration: none;">
🚚 Поставщик {% if sort_by=='supplier' %}{% if order=='desc' %}↓{% else %}↑{% endif %}{% endif %}
</a>
</div>
{% if orders %}
{% for order in orders %}
<div style="background: #FDF5E6; padding: 1rem; margin-bottom: 1rem; border-radius: 10px; border-left: 4px solid
{% if order.expiry_discount_percent and order.expiry_discount_percent > 0 %}#FFA500{% else %}#CD853F{% endif %};">
<div style="display: flex; justify-content: space-between;">
<strong>Поступление #{{ order.id }}</strong>
<span style="color: #666;">{{ order.date }}</span>
</div>
<p><strong>🚚 Поставщик:</strong> {{ order.supplier_name }}</p>
<p><strong>📦 Товар:</strong> {{ order.product_name }}</p>
<p><strong>📅 Срок годности:</strong> {{ order.expiry_date or 'не указан' }}</p>
<p><strong>📦 Количество:</strong> {{ order.quantity }} шт.</p>
<p><strong>💰 Цена закупки:</strong> {{ order.purchase_price }} ₽</p>
{% if order.expiry_discount_percent and order.expiry_discount_percent > 0 %}
<div style="background: #FFF3E0; padding: 0.5rem; border-radius: 5px; margin-top: 0.5rem;">
<p><strong>💰 Стоимость со скидкой:</strong></p>
<p style="margin-left: 1rem;">
Сумма: {{ order.total }} ₽<br>
Скидка по сроку годности: {{ order.expiry_discount_percent }}% ({{ order.discount_amount }} ₽)<br>
<span style="font-size: 1.1rem; color: #CD853F;"><strong>Итого: {{ order.final_total }} ₽</strong></span>
</p>
</div>
{% else %}
<p><strong>💰 Итого:</strong> {{ order.total }} ₽</p>
{% endif %}
</div>
{% endfor %}
{% else %}
<p>Приходных накладных пока нет.</p>
<p><a href="/incoming" class="btn-primary">➕ Оформить поступление</a></p>
{% endif %}
<style>
.btn-primary {
display: inline-block;
background: #CD853F;
color: white;
padding: 0.5rem 1rem;
text-decoration: none;
border-radius: 5px;
}
</style>
{% endblock %}

113
templates/index.html Normal file
View file

@ -0,0 +1,113 @@
{% extends "base.html" %}
{% block content %}
<div class="hero">
<h1>📦 Система учета склада</h1>
<p>Фотоматериалы — управление товарами, поставщиками и поступлениями</p>
</div>
<div class="stats-grid">
<div class="stat-card">
<h3>📦 Товаров на складе</h3>
<p class="stat-number">{{ total_items }}</p>
<p>единиц</p>
</div>
<div class="stat-card">
<h3>💰 Общая стоимость</h3>
<p class="stat-number">{{ "{:,.0f}".format(total_value) }} ₽</p>
<p>по розничным ценам</p>
</div>
</div>
<div class="features-grid">
<div class="feature-card">
<h3>📦 Управление товарами</h3>
<p>Добавление, редактирование, удаление товаров. Контроль остатков на складе.</p>
<a href="/products" class="btn-small">Перейти →</a>
</div>
<div class="feature-card">
<h3>🚚 Поставщики</h3>
<p>Ведение базы поставщиков для оперативного пополнения склада.</p>
<a href="/suppliers" class="btn-small">Перейти →</a>
</div>
<div class="feature-card">
<h3>📥 Поступление товаров</h3>
<p>Оприходование товара на склад. Автоматическое увеличение остатков.</p>
<a href="/incoming" class="btn-small">Перейти →</a>
</div>
<div class="feature-card">
<h3>📋 Приходные накладные</h3>
<p>История всех поступлений товара на склад. Сортировка по дате и сумме.</p>
<a href="/incoming-orders" class="btn-small">Перейти →</a>
</div>
</div>
<style>
.hero {
text-align: center;
padding: 2rem;
background: #FDF5E6;
border-radius: 10px;
margin-bottom: 2rem;
}
.hero h1 {
color: #CD853F;
margin-bottom: 0.5rem;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.stat-card {
background: #CD853F;
color: white;
padding: 1.5rem;
border-radius: 10px;
text-align: center;
}
.stat-card h3 {
margin-bottom: 0.5rem;
font-size: 1rem;
}
.stat-number {
font-size: 2rem;
font-weight: bold;
margin: 0.5rem 0;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
}
.feature-card {
background: white;
padding: 1.5rem;
border-radius: 10px;
border: 1px solid #CD853F30;
transition: transform 0.3s;
}
.feature-card:hover {
transform: translateY(-5px);
border-color: #CD853F;
}
.feature-card h3 {
color: #CD853F;
margin-bottom: 0.5rem;
}
.btn-small {
display: inline-block;
margin-top: 1rem;
background: #CD853F;
color: white;
padding: 0.3rem 0.8rem;
text-decoration: none;
border-radius: 5px;
font-size: 0.9rem;
}
.btn-small:hover {
background: #B87333;
}
</style>
{% endblock %}

70
templates/my_orders.html Normal file
View file

@ -0,0 +1,70 @@
{% extends "base.html" %}
{% block content %}
<h1>Все заказы</h1>
<!-- Сортировка -->
<div style="margin-bottom: 1rem; padding: 0.5rem; background: #FDF5E6; border-radius: 5px;">
<span>Сортировать по: </span>
<a href="/my-orders?sort=date&order={% if sort_by=='date' and order=='desc' %}asc{% else %}desc{% endif %}" style="color: #CD853F; text-decoration: none;">
📅 Дата {% if sort_by=='date' %}{% if order=='desc' %}↓{% else %}↑{% endif %}{% endif %}
</a> |
<a href="/my-orders?sort=total&order={% if sort_by=='total' and order=='desc' %}asc{% else %}desc{% endif %}" style="color: #CD853F; text-decoration: none;">
💰 Сумма {% if sort_by=='total' %}{% if order=='desc' %}↓{% else %}↑{% endif %}{% endif %}
</a> |
<a href="/my-orders?sort=quantity&order={% if sort_by=='quantity' and order=='desc' %}asc{% else %}desc{% endif %}" style="color: #CD853F; text-decoration: none;">
📦 Количество {% if sort_by=='quantity' %}{% if order=='desc' %}↓{% else %}↑{% endif %}{% endif %}
</a> |
<a href="/my-orders?sort=name&order={% if sort_by=='name' and order=='desc' %}asc{% else %}desc{% endif %}" style="color: #CD853F; text-decoration: none;">
👤 Имя {% if sort_by=='name' %}{% if order=='desc' %}↓{% else %}↑{% endif %}{% endif %}
</a>
</div>
{% if orders %}
{% for order in orders %}
<div style="background: #FDF5E6; padding: 1rem; margin-bottom: 1rem; border-radius: 10px; border-left: 4px solid #CD853F;">
<div style="display: flex; justify-content: space-between; flex-wrap: wrap;">
<strong>Заказ #{{ order.id }}</strong>
<div>
<span style="color: #666;">{{ order.order_date }}</span>
<a href="/orders/delete/{{ order.id }}" onclick="return confirm('Удалить заказ #{{ order.id }}?')" style="background: #f44336; color: white; padding: 0.2rem 0.5rem; text-decoration: none; border-radius: 3px; margin-left: 0.5rem;">🗑️ Удалить</a>
</div>
</div>
<p><strong>👤 Клиент:</strong> {{ order.client_name }} ({{ order.phone }})</p>
<p><strong>📦 Товар:</strong> {{ order.product_name }}</p>
<p><strong>📋 Категория:</strong> {{ order.category }}</p>
<p><strong>🤝 Партнер:</strong> {{ order.partner }}</p>
<p><strong>📦 Количество:</strong> {{ order.quantity }} шт. × {{ order.price_per_unit }} ₽</p>
<div style="background: white; padding: 0.5rem; border-radius: 5px; margin-top: 0.5rem;">
<p><strong>💰 Стоимость:</strong></p>
<p style="margin-left: 1rem;">
Сумма: {{ order.total }} ₽<br>
Скидка: {{ order.discount_percent }}% ({{ order.discount_amount }} ₽)<br>
<span style="font-size: 1.2rem; color: #CD853F;"><strong>Итого: {{ order.final_total }} ₽</strong></span>
</p>
</div>
{% if order.notes %}
<p style="margin-top: 0.5rem;"><strong>📝 Примечание:</strong> {{ order.notes }}</p>
{% endif %}
</div>
{% endfor %}
{% else %}
<p style="text-align: center;">Заказов пока нет.</p>
<p style="text-align: center;"><a href="/order" class="btn-primary">Сделать первый заказ</a></p>
{% endif %}
<style>
.btn-primary {
display: inline-block;
background: #CD853F;
color: white;
padding: 0.5rem 1rem;
text-decoration: none;
border-radius: 5px;
}
</style>
{% endblock %}

130
templates/order.html Normal file
View file

@ -0,0 +1,130 @@
{% extends "base.html" %}
{% block content %}
<h1>Оформление заказа</h1>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem;">
<div>
<form method="POST" class="order-form" oninput="calculateTotal()">
<div class="form-group">
<label>Ваше имя *</label>
<input type="text" name="name" required>
</div>
<div class="form-group">
<label>Телефон *</label>
<input type="tel" name="phone" required placeholder="+7 (___) ___-__-__">
</div>
<div class="form-group">
<label>Email *</label>
<input type="email" name="email" required>
</div>
<div class="form-group">
<label>Выберите товар *</label>
<select name="product_id" id="product_id" required onchange="updatePrice()">
<option value="">Выберите товар</option>
{% for product in products %}
<option value="{{ product.id }}" data-price="{{ product.price }}" data-category="{{ product.product_type }}">
{{ product.name }} - {{ product.price }} ₽ ({{ product.product_type }})
</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label>Партнер *</label>
<select name="partner" required>
<option value="">Выберите партнера</option>
{% for partner in partners %}
<option value="{{ partner.name }}">{{ partner.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label>Количество</label>
<input type="number" name="quantity" id="quantity" min="1" value="1" onchange="calculateTotal()">
</div>
<div class="form-group">
<label>Примечание</label>
<textarea name="notes" rows="4"></textarea>
</div>
<button type="submit" class="btn-submit">Отправить заказ</button>
</form>
</div>
<div style="background: #FDF5E6; padding: 1.5rem; border-radius: 10px; border: 1px solid #CD853F;">
<h3>💰 Расчет стоимости</h3>
<div id="calculation">
<p><strong>Товар:</strong> <span id="selected_product">-</span></p>
<p><strong>Цена за шт:</strong> <span id="price">0</span> ₽</p>
<p><strong>Сумма:</strong> <span id="total">0</span> ₽</p>
<p><strong>Скидка:</strong> <span id="discount_percent">0</span>% (<span id="discount_amount">0</span> ₽)</p>
<p style="font-size: 1.2rem;"><strong>Итого:</strong> <span id="final_total">0</span> ₽</p>
</div>
<hr>
<p><strong>Условия скидки:</strong></p>
<ul style="font-size: 0.9rem;">
<li>от 5 шт. → 5% скидка</li>
<li>от 10 шт. → 10% скидка</li>
<li>от 20 шт. → 15% скидка</li>
<li>от 50 шт. → 20% скидка</li>
</ul>
<hr>
<p><strong>📞 Телефон:</strong> +7 (999) 123-45-67</p>
<p><strong>✉️ Email:</strong> info@photomaterials.ru</p>
</div>
</div>
<script>
function updatePrice() {
let select = document.getElementById('product_id');
let selectedOption = select.options[select.selectedIndex];
let price = selectedOption.getAttribute('data-price') || 0;
document.getElementById('price').innerText = price;
document.getElementById('selected_product').innerText = selectedOption.text.split(' - ')[0];
calculateTotal();
}
function calculateTotal() {
let quantity = parseInt(document.getElementById('quantity').value) || 0;
let select = document.getElementById('product_id');
let selectedOption = select.options[select.selectedIndex];
let price = parseInt(selectedOption.getAttribute('data-price')) || 0;
let total = quantity * price;
let discountPercent = 0;
if (quantity >= 50) discountPercent = 20;
else if (quantity >= 20) discountPercent = 15;
else if (quantity >= 10) discountPercent = 10;
else if (quantity >= 5) discountPercent = 5;
let discountAmount = total * discountPercent / 100;
let finalTotal = total - discountAmount;
document.getElementById('total').innerText = total.toFixed(2);
document.getElementById('discount_percent').innerText = discountPercent;
document.getElementById('discount_amount').innerText = discountAmount.toFixed(2);
document.getElementById('final_total').innerText = finalTotal.toFixed(2);
}
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('product_id').addEventListener('change', updatePrice);
updatePrice();
});
</script>
<style>
.order-form { max-width: 100%; }
.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 #ddd; border-radius: 5px; }
.btn-submit { background: #CD853F; color: white; padding: 0.75rem; border: none; border-radius: 5px; width: 100%; cursor: pointer; }
.btn-submit:hover { background: #B87333; }
#calculation { background: white; padding: 1rem; border-radius: 5px; margin-top: 1rem; }
</style>
{% endblock %}

45
templates/partners.html Normal file
View file

@ -0,0 +1,45 @@
{% extends "base.html" %}
{% block content %}
<h1>Наши партнеры</h1>
<div style="background: #FDF5E6; padding: 1rem; margin-bottom: 2rem; border-radius: 10px;">
<h3>➕ Добавить партнера</h3>
<form method="POST">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 0.5rem;">
<input type="text" name="name" placeholder="Название" required style="padding: 0.5rem;">
<input type="text" name="contact" placeholder="Контакт" style="padding: 0.5rem;">
<input type="text" name="phone" placeholder="Телефон" style="padding: 0.5rem;">
<button type="submit" style="background: #CD853F; color: white; padding: 0.5rem;">Добавить</button>
</div>
</form>
</div>
<div class="partners-grid">
{% for partner in partners %}
<div class="partner-card">
<h3>{{ partner.name }}</h3>
<p>📧 {{ partner.contact }}</p>
<p>📞 {{ partner.phone }}</p>
<a href="/partners/delete/{{ partner.id }}" onclick="return confirm('Удалить партнера {{ partner.name }}?')" style="color: #f44336; text-decoration: none;">🗑️ Удалить</a>
</div>
{% endfor %}
</div>
<style>
.partners-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
margin-top: 1rem;
}
.partner-card {
background: #FDF5E6;
padding: 1rem;
border-radius: 10px;
border: 1px solid #CD853F;
text-align: center;
}
.partner-card h3 { color: #CD853F; margin-bottom: 0.5rem; }
</style>
{% endblock %}

78
templates/products.html Normal file
View file

@ -0,0 +1,78 @@
{% extends "base.html" %}
{% block content %}
<h1>📦 Управление товарами</h1>
<!-- Форма добавления -->
<div style="background: #FDF5E6; padding: 1rem; margin-bottom: 2rem; border-radius: 10px;">
<h3>➕ Добавить товар</h3>
<form method="POST">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0.5rem;">
<input type="text" name="name" placeholder="Название" required style="padding: 0.5rem;">
<input type="text" name="manufacturer" placeholder="Производитель" style="padding: 0.5rem;">
<select name="product_type" style="padding: 0.5rem;">
<option value="">Выберите категорию</option>
{% for cat in categories %}
<option value="{{ cat.name }}">{{ cat.name }}</option>
{% endfor %}
</select>
<input type="number" name="purchase_price" placeholder="Цена закупки" step="0.01" style="padding: 0.5rem;">
<input type="number" name="selling_price" placeholder="Цена продажи" step="0.01" style="padding: 0.5rem;">
<input type="text" name="expiry_date" placeholder="Срок годности" style="padding: 0.5rem;">
<input type="number" name="stock_quantity" placeholder="Начальный остаток" style="padding: 0.5rem;">
<button type="submit" style="background: #CD853F; color: white; padding: 0.5rem;">Добавить</button>
</div>
</form>
</div>
<!-- Список товаров -->
<div class="products-grid">
{% if products %}
{% for product in products %}
<div class="product-card">
<h3>{{ product.name }}</h3>
<p>🏭 Производитель: {{ product.manufacturer }}</p>
<p>📋 Категория: {{ product.product_type }}</p>
<p>💰 Закупка: {{ product.purchase_price }} ₽ | Продажа: {{ product.selling_price }} ₽</p>
<p>📅 Срок годности: {{ product.expiry_date if product.expiry_date else 'не указан' }}</p>
<p class="stock {% if product.stock_quantity > 0 %}in-stock{% else %}out-of-stock{% endif %}">
📦 Остаток: {{ product.stock_quantity }} шт.
</p>
<div style="margin-top: 0.5rem; display: flex; gap: 0.5rem;">
<a href="/products/edit/{{ product.id }}" style="background: #2196F3; color: white; padding: 0.2rem 0.5rem; text-decoration: none; border-radius: 3px;">✏️ Редактировать</a>
<a href="/products/delete/{{ product.id }}" onclick="return confirm('Удалить товар?')" style="background: #f44336; color: white; padding: 0.2rem 0.5rem; text-decoration: none; border-radius: 3px;">🗑️ Удалить</a>
</div>
</div>
{% endfor %}
{% else %}
<p>Товаров пока нет</p>
{% endif %}
</div>
<style>
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
margin-top: 1rem;
}
.product-card {
background: white;
padding: 1rem;
border-radius: 10px;
border: 1px solid #CD853F30;
}
.product-card h3 {
color: #CD853F;
margin-bottom: 0.5rem;
}
.in-stock {
color: #228B22;
font-weight: bold;
}
.out-of-stock {
color: #DC143C;
font-weight: bold;
}
</style>
{% endblock %}

63
templates/reviews.html Normal file
View file

@ -0,0 +1,63 @@
{% extends "base.html" %}
{% block content %}
<h1>Отзывы наших клиентов</h1>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem;">
<!-- Форма отзыва -->
<div style="background: #FDF5E6; padding: 1.5rem; border-radius: 10px; border: 1px solid #CD853F;">
<h3 style="color: #CD853F;">Оставить отзыв</h3>
{% if session.get('user_id') %}
<form method="POST">
<div style="margin-bottom: 1rem;">
<label style="display: block; margin-bottom: 0.5rem;">Оценка</label>
<select name="rating" required style="width: 100%; padding: 0.5rem; border: 1px solid #ddd; border-radius: 5px;">
<option value="5">★★★★★ (5 звезд)</option>
<option value="4">★★★★☆ (4 звезды)</option>
<option value="3">★★★☆☆ (3 звезды)</option>
<option value="2">★★☆☆☆ (2 звезды)</option>
<option value="1">★☆☆☆☆ (1 звезда)</option>
</select>
</div>
<div style="margin-bottom: 1rem;">
<label style="display: block; margin-bottom: 0.5rem;">Ваш отзыв</label>
<textarea name="text" rows="4" required style="width: 100%; padding: 0.5rem; border: 1px solid #ddd; border-radius: 5px;"></textarea>
</div>
<button type="submit" class="btn-primary">Отправить отзыв</button>
</form>
{% else %}
<p>Чтобы оставить отзыв, <a href="/login" style="color: #CD853F;">войдите в систему</a></p>
{% endif %}
</div>
<!-- Список отзывов -->
<div>
<h3 style="color: #CD853F;">Отзывы</h3>
{% if reviews %}
{% for review in reviews %}
<div style="background: white; padding: 1rem; margin-bottom: 1rem; border-radius: 10px; border: 1px solid #eee;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<div>
<strong style="color: #CD853F;">{{ review.author_name }}</strong>
<span style="margin-left: 0.5rem;">
{% for i in range(review.rating) %}★{% endfor %}
{% for i in range(5 - review.rating) %}☆{% endfor %}
</span>
</div>
<div style="color: #999; font-size: 0.8rem;">
{{ review.created_at }}
</div>
</div>
<p style="margin-top: 0.5rem;">{{ review.text }}</p>
</div>
{% endfor %}
{% else %}
<p style="color: #999;">Пока нет отзывов. Будьте первым!</p>
{% endif %}
</div>
</div>
{% endblock %}

98
templates/services.html Normal file
View file

@ -0,0 +1,98 @@
{% extends "base.html" %}
{% block content %}
<h1>Наши услуги</h1>
<p>Мы предлагаем полный спектр услуг для фотографов и любителей:</p>
<div class="services-grid">
<div class="service-card">
<h3>🖨️ Печать фотографий</h3>
<p><strong>Форматы:</strong> 10x15, 15x21, 20x30, А4, А3+</p>
<p><strong>Материалы:</strong> глянцевая, матовая, перламутровая, холст</p>
<p><strong>Цены:</strong> от 30 ₽ за фото 10x15</p>
<p><strong>Срок:</strong> от 30 минут</p>
</div>
<div class="service-card">
<h3>🎨 Фотообработка</h3>
<p><strong>Услуги:</strong> цветокоррекция, ретушь, кадрирование</p>
<p><strong>Удаление дефектов:</strong> исправление кожи, удаление объектов</p>
<p><strong>Цены:</strong> от 100 ₽ за фото</p>
<p><strong>Срок:</strong> от 1 часа</p>
</div>
<div class="service-card">
<h3>🖼️ Печать на холсте</h3>
<p><strong>Форматы:</strong> любой размер до 90x120 см</p>
<p><strong>Особенности:</strong> натяжка на подрамник, покрытие лаком</p>
<p><strong>Цены:</strong> от 1500 ₽ за холст 30x40</p>
<p><strong>Срок:</strong> 2-3 дня</p>
</div>
<div class="service-card">
<h3>📚 Фотокниги и альбомы</h3>
<p><strong>Форматы:</strong> 20x20, 20x30, 30x30 см</p>
<p><strong>Варианты:</strong> твердая обложка, кожаный переплет</p>
<p><strong>Цены:</strong> от 2000 ₽ за фотокнигу 20 страниц</p>
<p><strong>Срок:</strong> 5-7 дней</p>
</div>
<div class="service-card">
<h3>💬 Консультации</h3>
<p><strong>Помощь в выборе:</strong> фотобумаги, чернил, принтеров</p>
<p><strong>Настройка оборудования:</strong> калибровка монитора, профилирование</p>
<p><strong>Консультация:</strong> бесплатно по телефону или в магазине</p>
</div>
<div class="service-card">
<h3>🚚 Доставка</h3>
<p><strong>По Москве:</strong> курьером - 300 ₽, бесплатно от 2000 ₽</p>
<p><strong>По России:</strong> Почта России, СДЭК от 350 ₽</p>
<p><strong>Самовывоз:</strong> бесплатно из магазина</p>
<p><strong>Срок:</strong> 1-2 дня по Москве, 3-7 дней по России</p>
</div>
</div>
<div style="margin-top: 2rem; padding: 1.5rem; background: #FDF5E6; border-radius: 10px; border-left: 4px solid #CD853F;">
<h3>✨ Специальное предложение</h3>
<p>При заказе печати от 20 фотографий - скидка 10% на обработку!</p>
<p>Акция действует до 31.05.2024</p>
</div>
<style>
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 1.5rem;
margin-top: 1.5rem;
}
.service-card {
background: white;
padding: 1.5rem;
border-radius: 10px;
border: 1px solid #CD853F20;
transition: all 0.3s;
}
.service-card:hover {
border-color: #CD853F;
box-shadow: 0 5px 15px rgba(205, 133, 63, 0.1);
}
.service-card h3 {
color: #CD853F;
margin-bottom: 1rem;
font-size: 1.3rem;
}
.service-card p {
margin: 0.5rem 0;
line-height: 1.5;
}
.service-card strong {
color: #8B5A2B;
}
</style>
{% endblock %}

View file

@ -0,0 +1,85 @@
{% extends "base.html" %}
{% block content %}
<h1>📋 Заказы поставщика: {{ supplier.name }}</h1>
<div style="background: #FDF5E6; padding: 1rem; margin-bottom: 2rem; border-radius: 10px;">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;">
<div>
<strong>📞 Контакт:</strong> {{ supplier.contact }}
</div>
<div>
<strong>📱 Телефон:</strong> {{ supplier.phone }}
</div>
<div>
<strong>📦 Всего поставок:</strong> {{ orders|length }}
</div>
<div>
<strong>📦 Всего товара:</strong> {{ total_items }} шт.
</div>
<div>
<strong>💰 Общая сумма:</strong> {{ "{:,.0f}".format(total_sum) }} ₽
</div>
</div>
<div style="margin-top: 1rem;">
<a href="/suppliers" class="btn-back">← Назад к поставщикам</a>
</div>
</div>
{% if orders %}
<div class="orders-list">
{% for order in orders %}
<div class="order-card">
<div style="display: flex; justify-content: space-between; flex-wrap: wrap;">
<strong>📄 Поступление #{{ order.id }}</strong>
<span style="color: #666;">{{ order.date }}</span>
</div>
<p><strong>📦 Товар:</strong> {{ order.product_name }}</p>
<p><strong>📦 Количество:</strong> {{ order.quantity }} шт.</p>
<p><strong>💰 Цена закупки:</strong> {{ order.purchase_price }} ₽</p>
<p><strong>💰 Общая сумма:</strong> {{ order.total }} ₽</p>
</div>
{% endfor %}
</div>
{% else %}
<div style="text-align: center; padding: 3rem; background: #FDF5E6; border-radius: 10px;">
<p>📭 У этого поставщика пока нет заказов.</p>
<p><a href="/incoming" class="btn-primary">➕ Оформить поступление</a></p>
</div>
{% endif %}
<style>
.order-card {
background: #FDF5E6;
padding: 1rem;
margin-bottom: 1rem;
border-radius: 10px;
border-left: 4px solid #CD853F;
}
.btn-back {
display: inline-block;
background: #666;
color: white;
padding: 0.5rem 1rem;
text-decoration: none;
border-radius: 5px;
}
.btn-back:hover {
background: #555;
}
.btn-primary {
display: inline-block;
background: #CD853F;
color: white;
padding: 0.5rem 1rem;
text-decoration: none;
border-radius: 5px;
}
.btn-primary:hover {
background: #B87333;
}
.orders-list {
margin-top: 1rem;
}
</style>
{% endblock %}

54
templates/suppliers.html Normal file
View file

@ -0,0 +1,54 @@
{% extends "base.html" %}
{% block content %}
<h1>🚚 Управление поставщиками</h1>
<div style="background: #FDF5E6; padding: 1rem; margin-bottom: 2rem; border-radius: 10px;">
<h3>➕ Добавить поставщика</h3>
<form method="POST">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 0.5rem;">
<input type="text" name="name" placeholder="Название" required style="padding: 0.5rem;">
<input type="text" name="contact" placeholder="Контактное лицо" style="padding: 0.5rem;">
<input type="text" name="phone" placeholder="Телефон" style="padding: 0.5rem;">
<button type="submit" style="background: #CD853F; color: white; padding: 0.5rem;">Добавить</button>
</div>
<small style="color: #666; display: block; margin-top: 0.5rem;">
📞 Форматы телефона: +7 (999) 123-45-67, 8-999-123-45-67, 9991234567
</small>
</form>
</div>
<div class="suppliers-grid">
{% for supplier in suppliers %}
<div class="supplier-card">
<h3>{{ supplier.name }}</h3>
<p>📧 {{ supplier.contact }}</p>
<p>📞 {{ supplier.phone }}</p>
<div style="margin-top: 0.5rem; display: flex; gap: 0.5rem; justify-content: center;">
<a href="/suppliers/edit/{{ supplier.id }}" style="background: #2196F3; color: white; padding: 0.3rem 0.8rem; text-decoration: none; border-radius: 5px;">✏️ Редактировать</a>
<a href="/supplier/orders/{{ supplier.id }}" style="background: #4CAF50; color: white; padding: 0.3rem 0.8rem; text-decoration: none; border-radius: 5px;">📋 Заказы</a>
</div>
</div>
{% endfor %}
</div>
<style>
.suppliers-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
margin-top: 1rem;
}
.supplier-card {
background: #FDF5E6;
padding: 1rem;
border-radius: 10px;
border: 1px solid #CD853F;
text-align: center;
}
.supplier-card h3 {
color: #CD853F;
margin-bottom: 0.5rem;
}
</style>
{% endblock %}