294 lines
12 KiB
Python
294 lines
12 KiB
Python
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)
|