sport_inventary/app/routes.py

366 lines
14 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify
from . import db, csrf
from .models import Partners, Inventory, Categories, IssueOrders, IssueOrderItems, ReturnOrders, ReturnOrderItems
from .utils import calculate_item_price, get_current_season_name
from datetime import datetime
bp = Blueprint('main', __name__)
# Главная
@bp.route('/')
def index():
active_issues = IssueOrders.query.filter(~IssueOrders.return_order.has()).count()
partners_count = Partners.query.count()
expected_total = 0
for issue in IssueOrders.query.filter(~IssueOrders.return_order.has()).all():
for item in issue.items:
expected_total += float(item.inventory.base_price) * item.quantity
recent_issues = IssueOrders.query.order_by(IssueOrders.issue_date.desc()).limit(5).all()
recent_returns = ReturnOrders.query.order_by(ReturnOrders.return_date.desc()).limit(5).all()
return render_template('index.html',
active_issues=active_issues,
partners_count=partners_count,
expected_total=round(expected_total, 2),
recent_issues=recent_issues,
recent_returns=recent_returns)
# Партнёры
@bp.route('/partners')
def partners():
search = request.args.get('search', '')
query = Partners.query
if search:
query = query.filter(
(Partners.name.contains(search)) | (Partners.phone.contains(search))
)
all_partners = query.all()
return render_template('partners.html', partners=all_partners)
@bp.route('/partners/new', methods=['GET', 'POST'])
def partner_new():
if request.method == 'POST':
name = request.form['name']
phone = request.form['phone'].strip()
email = request.form['email']
address = request.form['address']
# Валидация телефона
if phone:
digits = ''.join(filter(str.isdigit, phone))
if len(digits) < 10 or len(digits) > 15:
flash('Телефон должен содержать от 10 до 15 цифр (например, +7(999)123-45-67)', 'danger')
# Создаём временный объект для возврата в форму
temp_partner = Partners(name=name, phone=phone, email=email, address=address)
return render_template('partner_form.html', partner=temp_partner)
p = Partners(name=name, phone=phone, email=email, address=address)
db.session.add(p)
db.session.commit()
flash('Партнёр добавлен', 'success')
return redirect(url_for('main.partners'))
return render_template('partner_form.html')
@bp.route('/partners/<int:id>')
def partner_detail(id):
partner = Partners.query.get_or_404(id)
return render_template('partner_detail.html', partner=partner)
@bp.route('/partners/<int:id>/edit', methods=['GET', 'POST'])
def partner_edit(id):
partner = Partners.query.get_or_404(id)
if request.method == 'POST':
name = request.form['name']
phone = request.form['phone'].strip()
email = request.form['email']
address = request.form['address']
# Сначала обновляем поля (ещё не коммитим)
partner.name = name
partner.phone = phone
partner.email = email
partner.address = address
# Валидация телефона
if phone:
digits = ''.join(filter(str.isdigit, phone))
if len(digits) < 10 or len(digits) > 15:
flash('Телефон должен содержать от 10 до 15 цифр (например, +7(999)123-45-67)', 'danger')
return render_template('partner_form.html', partner=partner)
db.session.commit()
flash('Данные обновлены', 'success')
return redirect(url_for('main.partner_detail', id=id))
return render_template('partner_form.html', partner=partner)
# Инвентарь
@bp.route('/inventory')
def inventory():
category_id = request.args.get('category', type=int)
season = request.args.get('season', '')
query = Inventory.query
if category_id:
query = query.filter_by(category_id=category_id)
if season in ['winter', 'summer']:
query = query.filter_by(season_type=season)
items = query.all()
categories = Categories.query.all()
return render_template('inventory.html',
items=items,
categories=categories,
selected_category=category_id,
selected_season=season)
@bp.route('/inventory/new', methods=['GET', 'POST'])
def inventory_new():
if request.method == 'POST':
name = request.form['name']
category_id = request.form['category_id']
base_price = request.form['base_price']
season_type = request.form['season_type']
inv = Inventory(name=name, category_id=category_id, base_price=base_price, season_type=season_type)
db.session.add(inv)
db.session.commit()
flash('Инвентарь добавлен', 'success')
return redirect(url_for('main.inventory'))
categories = Categories.query.all()
return render_template('inventory_form.html', categories=categories)
@bp.route('/inventory/<int:id>/edit', methods=['GET', 'POST'])
def inventory_edit(id):
inv = Inventory.query.get_or_404(id)
if request.method == 'POST':
inv.name = request.form['name']
inv.category_id = request.form['category_id']
inv.base_price = request.form['base_price']
inv.season_type = request.form['season_type']
db.session.commit()
flash('Изменения сохранены', 'success')
return redirect(url_for('main.inventory'))
categories = Categories.query.all()
return render_template('inventory_form.html', item=inv, categories=categories)
@bp.route('/inventory/<int:id>/delete', methods=['POST'])
def inventory_delete(id):
inv = Inventory.query.get_or_404(id)
if IssueOrderItems.query.filter_by(inventory_id=id).first() or ReturnOrderItems.query.filter_by(
inventory_id=id).first():
flash('Нельзя удалить инвентарь, который был выдан', 'danger')
else:
db.session.delete(inv)
db.session.commit()
flash('Инвентарь удалён', 'success')
return redirect(url_for('main.inventory'))
# Категории
@bp.route('/categories')
def categories():
cats = Categories.query.all()
return render_template('categories.html', categories=cats)
@bp.route('/categories/new', methods=['GET', 'POST'])
def category_new():
if request.method == 'POST':
name = request.form['name']
description = request.form['description']
cat = Categories(name=name, description=description)
db.session.add(cat)
db.session.commit()
flash('Категория добавлена', 'success')
return redirect(url_for('main.categories'))
return render_template('category_form.html')
@bp.route('/categories/<int:id>/edit', methods=['GET', 'POST'])
def category_edit(id):
cat = Categories.query.get_or_404(id)
if request.method == 'POST':
cat.name = request.form['name']
cat.description = request.form['description']
db.session.commit()
flash('Категория обновлена', 'success')
return redirect(url_for('main.categories'))
return render_template('category_form.html', category=cat)
@bp.route('/categories/<int:id>/delete', methods=['POST'])
def category_delete(id):
cat = Categories.query.get_or_404(id)
if Inventory.query.filter_by(category_id=id).first():
flash('Нельзя удалить категорию, в которой есть инвентарь', 'danger')
else:
db.session.delete(cat)
db.session.commit()
flash('Категория удалена', 'success')
return redirect(url_for('main.categories'))
# Выдачи
@bp.route('/issues')
def issues():
issues = IssueOrders.query.all()
return render_template('issues.html', issues=issues)
@bp.route('/issues/new', methods=['GET', 'POST'])
def issue_new():
if request.method == 'POST':
partner_id = request.form['partner_id']
notes = request.form.get('notes', '')
issue = IssueOrders(partner_id=partner_id, notes=notes)
db.session.add(issue)
db.session.commit()
inventory_ids = request.form.getlist('inventory_id[]')
quantities = request.form.getlist('quantity[]')
for inv_id, qty in zip(inventory_ids, quantities):
if inv_id and qty:
item = IssueOrderItems(issue_order_id=issue.id, inventory_id=int(inv_id), quantity=int(qty))
db.session.add(item)
db.session.commit()
flash('Выдача оформлена', 'success')
return redirect(url_for('main.issues'))
partners = Partners.query.all()
inventory = Inventory.query.all()
return render_template('issue_form.html', partners=partners, inventory=inventory)
@bp.route('/issues/<int:id>')
def issue_detail(id):
issue = IssueOrders.query.get_or_404(id)
return render_template('issue_detail.html', issue=issue)
@bp.route('/issues/<int:id>/edit', methods=['GET', 'POST'])
def issue_edit(id):
issue = IssueOrders.query.get_or_404(id)
if issue.return_order:
flash('Нельзя редактировать закрытую выдачу', 'danger')
return redirect(url_for('main.issue_detail', id=id))
if request.method == 'POST':
issue.notes = request.form.get('notes', '')
IssueOrderItems.query.filter_by(issue_order_id=id).delete()
inventory_ids = request.form.getlist('inventory_id[]')
quantities = request.form.getlist('quantity[]')
for inv_id, qty in zip(inventory_ids, quantities):
if inv_id and qty:
item = IssueOrderItems(issue_order_id=id, inventory_id=int(inv_id), quantity=int(qty))
db.session.add(item)
db.session.commit()
flash('Выдача обновлена', 'success')
return redirect(url_for('main.issue_detail', id=id))
partners = Partners.query.all()
inventory = Inventory.query.all()
return render_template('issue_form.html', issue=issue, partners=partners, inventory=inventory)
# Возвраты
@bp.route('/returns')
def returns():
returns = ReturnOrders.query.all()
return render_template('returns.html', returns=returns)
@bp.route('/returns/new', methods=['GET', 'POST'])
def return_new():
if request.method == 'POST':
issue_id = request.form['issue_id']
# Проверка на существующий возврат
existing_return = ReturnOrders.query.filter_by(issue_order_id=issue_id).first()
if existing_return:
flash('Возврат по этой выдаче уже оформлен', 'danger')
return redirect(url_for('main.return_new'))
# Получаем список ID позиций
items_data = request.form.getlist('item_id[]')
if not items_data:
flash('Нет позиций для возврата', 'danger')
return redirect(url_for('main.return_new'))
# Собираем степени износа для каждой позиции
wear_dict = {}
for item_id in items_data:
wear = request.form.get(f'wear_{item_id}')
if not wear:
flash(f'Не указана степень износа для позиции', 'danger')
return redirect(url_for('main.return_new'))
wear_dict[item_id] = wear
return_date = datetime.now()
notes = request.form.get('notes', '')
# Создаём возврат (получаем ID, но не коммитим)
ret = ReturnOrders(issue_order_id=issue_id, return_date=return_date, notes=notes)
db.session.add(ret)
db.session.flush() # чтобы получить ID возврата
total = 0
for item_id in items_data:
issue_item = IssueOrderItems.query.get(int(item_id))
wear = wear_dict[item_id]
price = calculate_item_price(
base_price=issue_item.inventory.base_price,
quantity=issue_item.quantity,
wear_level=wear,
season_type=issue_item.inventory.season_type,
return_date=return_date
)
ret_item = ReturnOrderItems(
return_order_id=ret.id,
inventory_id=issue_item.inventory_id,
quantity=issue_item.quantity,
wear_level=wear,
calculated_price=price
)
db.session.add(ret_item)
total += price
ret.total_amount = round(total, 2)
db.session.commit()
flash('Возврат оформлен', 'success')
return redirect(url_for('main.returns'))
open_issues = IssueOrders.query.filter(~IssueOrders.return_order.has()).all()
current_season = get_current_season_name()
return render_template('return_select_issue.html', issues=open_issues, current_season=current_season)
@bp.route('/returns/get_issue_items/<int:issue_id>')
def get_issue_items(issue_id):
issue = IssueOrders.query.get_or_404(issue_id)
items = []
for item in issue.items:
items.append({
'id': item.id,
'inventory_id': item.inventory_id,
'inventory_name': item.inventory.name,
'quantity': item.quantity,
'base_price': float(item.inventory.base_price),
'season_type': item.inventory.season_type
})
return jsonify(items)
@csrf.exempt
@bp.route('/returns/calculate_preview', methods=['POST'])
def calculate_preview():
data = request.get_json()
base_price = float(data['base_price'])
quantity = int(data['quantity'])
wear_level = data['wear_level']
season_type = data['season_type']
return_date = datetime.now()
price = calculate_item_price(base_price, quantity, wear_level, season_type, return_date)
return jsonify({'calculated_price': price})