233 lines
8.5 KiB
Python
233 lines
8.5 KiB
Python
# app/controllers/inventory.py
|
||
from flask import Blueprint, render_template, request, jsonify, flash, redirect, url_for
|
||
from flask_login import login_required, current_user
|
||
from app.models.book import Book
|
||
from app.models.inventory import InventoryLog
|
||
from app.models.log import Log # 导入日志模型
|
||
from app.models.user import db
|
||
from app.utils.auth import permission_required # 修改导入,使用permission_required替代admin_required
|
||
from datetime import datetime
|
||
|
||
inventory_bp = Blueprint('inventory', __name__, url_prefix='/inventory')
|
||
|
||
|
||
@inventory_bp.route('/')
|
||
@login_required
|
||
@permission_required('manage_inventory') # 替代 @admin_required
|
||
def inventory_list():
|
||
"""库存管理页面 - 只有拥有库存管理权限的用户有权限进入"""
|
||
page = request.args.get('page', 1, type=int)
|
||
per_page = request.args.get('per_page', 20, type=int)
|
||
|
||
# 搜索功能
|
||
search = request.args.get('search', '')
|
||
query = Book.query
|
||
|
||
if search:
|
||
query = query.filter(
|
||
(Book.title.contains(search)) |
|
||
(Book.author.contains(search)) |
|
||
(Book.isbn.contains(search))
|
||
)
|
||
|
||
# 排序
|
||
sort = request.args.get('sort', 'id')
|
||
order = request.args.get('order', 'asc')
|
||
if order == 'desc':
|
||
query = query.order_by(getattr(Book, sort).desc())
|
||
else:
|
||
query = query.order_by(getattr(Book, sort))
|
||
|
||
pagination = query.paginate(page=page, per_page=per_page)
|
||
books = pagination.items
|
||
|
||
# 记录系统日志 - 访问库存管理页面
|
||
Log.add_log(
|
||
action="访问库存管理",
|
||
user_id=current_user.id,
|
||
target_type="inventory",
|
||
ip_address=request.remote_addr,
|
||
description=f"用户访问库存管理页面,搜索条件:{search if search else '无'}"
|
||
)
|
||
|
||
return render_template('inventory/list.html',
|
||
books=books,
|
||
pagination=pagination,
|
||
search=search,
|
||
sort=sort,
|
||
order=order)
|
||
|
||
|
||
@inventory_bp.route('/adjust/<int:book_id>', methods=['GET', 'POST'])
|
||
@login_required
|
||
@permission_required('manage_inventory') # 替代 @admin_required
|
||
def adjust_inventory(book_id):
|
||
"""调整图书库存"""
|
||
book = Book.query.get_or_404(book_id)
|
||
|
||
# GET请求记录日志
|
||
if request.method == 'GET':
|
||
Log.add_log(
|
||
action="查看库存调整",
|
||
user_id=current_user.id,
|
||
target_type="book",
|
||
target_id=book.id,
|
||
ip_address=request.remote_addr,
|
||
description=f"用户查看图书《{book.title}》的库存调整页面"
|
||
)
|
||
|
||
if request.method == 'POST':
|
||
change_type = request.form.get('change_type')
|
||
change_amount = int(request.form.get('change_amount', 0))
|
||
remark = request.form.get('remark', '')
|
||
|
||
if change_amount <= 0:
|
||
flash('调整数量必须大于0', 'danger')
|
||
return redirect(url_for('inventory.adjust_inventory', book_id=book_id))
|
||
|
||
# 计算库存变化
|
||
original_stock = book.stock
|
||
if change_type == 'in':
|
||
book.stock += change_amount
|
||
after_stock = book.stock
|
||
operation_desc = "入库"
|
||
elif change_type == 'out':
|
||
if book.stock < change_amount:
|
||
flash('出库数量不能大于当前库存', 'danger')
|
||
return redirect(url_for('inventory.adjust_inventory', book_id=book_id))
|
||
book.stock -= change_amount
|
||
after_stock = book.stock
|
||
operation_desc = "出库"
|
||
else:
|
||
flash('无效的操作类型', 'danger')
|
||
return redirect(url_for('inventory.adjust_inventory', book_id=book_id))
|
||
|
||
# 创建库存日志
|
||
log = InventoryLog(
|
||
book_id=book.id,
|
||
change_type=change_type,
|
||
change_amount=change_amount,
|
||
after_stock=after_stock,
|
||
operator_id=current_user.id,
|
||
remark=remark,
|
||
changed_at=datetime.now()
|
||
)
|
||
|
||
try:
|
||
db.session.add(log)
|
||
|
||
# 记录系统日志 - 库存调整
|
||
Log.add_log(
|
||
action=f"库存{operation_desc}",
|
||
user_id=current_user.id,
|
||
target_type="book",
|
||
target_id=book.id,
|
||
ip_address=request.remote_addr,
|
||
description=f"用户对图书《{book.title}》进行{operation_desc}操作,数量:{change_amount},"
|
||
f"原库存:{original_stock},现库存:{after_stock},备注:{remark}"
|
||
)
|
||
|
||
db.session.commit()
|
||
flash(f'图书《{book.title}》库存调整成功!原库存:{original_stock},现库存:{after_stock}', 'success')
|
||
return redirect(url_for('inventory.inventory_list'))
|
||
except Exception as e:
|
||
db.session.rollback()
|
||
flash(f'操作失败:{str(e)}', 'danger')
|
||
return redirect(url_for('inventory.adjust_inventory', book_id=book_id))
|
||
|
||
return render_template('inventory/adjust.html', book=book)
|
||
|
||
|
||
@inventory_bp.route('/logs')
|
||
@login_required
|
||
@permission_required('manage_inventory') # 替代 @admin_required
|
||
def inventory_logs():
|
||
"""查看库存变动日志"""
|
||
page = request.args.get('page', 1, type=int)
|
||
per_page = request.args.get('per_page', 20, type=int)
|
||
# 搜索和筛选
|
||
book_id = request.args.get('book_id', type=int)
|
||
change_type = request.args.get('change_type', '')
|
||
date_from = request.args.get('date_from', '')
|
||
date_to = request.args.get('date_to', '')
|
||
query = InventoryLog.query
|
||
|
||
if book_id:
|
||
query = query.filter_by(book_id=book_id)
|
||
if change_type:
|
||
query = query.filter_by(change_type=change_type)
|
||
if date_from:
|
||
query = query.filter(InventoryLog.changed_at >= datetime.strptime(date_from, '%Y-%m-%d'))
|
||
if date_to:
|
||
query = query.filter(InventoryLog.changed_at <= datetime.strptime(date_to + ' 23:59:59', '%Y-%m-%d %H:%M:%S'))
|
||
|
||
# 默认按时间倒序
|
||
query = query.order_by(InventoryLog.changed_at.desc())
|
||
pagination = query.paginate(page=page, per_page=per_page)
|
||
logs = pagination.items
|
||
|
||
# 获取所有图书用于筛选
|
||
books = Book.query.all()
|
||
|
||
# 如果特定 book_id 被指定,也获取该书的详细信息
|
||
book = Book.query.get(book_id) if book_id else None
|
||
|
||
# 记录系统日志 - 查看库存日志
|
||
filter_desc = []
|
||
if book_id:
|
||
book_title = book.title if book else f"ID:{book_id}"
|
||
filter_desc.append(f"图书:{book_title}")
|
||
if change_type:
|
||
change_type_text = "入库" if change_type == "in" else "出库"
|
||
filter_desc.append(f"操作类型:{change_type_text}")
|
||
if date_from or date_to:
|
||
date_range = f"{date_from or '无限制'} 至 {date_to or '无限制'}"
|
||
filter_desc.append(f"日期范围:{date_range}")
|
||
|
||
Log.add_log(
|
||
action="查看库存日志",
|
||
user_id=current_user.id,
|
||
target_type="inventory_log",
|
||
ip_address=request.remote_addr,
|
||
description=f"用户查看库存变动日志,筛选条件:{', '.join(filter_desc) if filter_desc else '无'}"
|
||
)
|
||
|
||
return render_template('inventory/logs.html',
|
||
logs=logs,
|
||
pagination=pagination,
|
||
books=books,
|
||
book=book,
|
||
book_id=book_id,
|
||
change_type=change_type,
|
||
date_from=date_from,
|
||
date_to=date_to)
|
||
|
||
|
||
@inventory_bp.route('/book/<int:book_id>/logs')
|
||
@login_required
|
||
@permission_required('manage_inventory') # 替代 @admin_required
|
||
def book_inventory_logs(book_id):
|
||
"""查看特定图书的库存变动日志"""
|
||
book = Book.query.get_or_404(book_id)
|
||
page = request.args.get('page', 1, type=int)
|
||
per_page = request.args.get('per_page', 20, type=int)
|
||
|
||
logs = InventoryLog.query.filter_by(book_id=book_id) \
|
||
.order_by(InventoryLog.changed_at.desc()) \
|
||
.paginate(page=page, per_page=per_page)
|
||
|
||
# 记录系统日志 - 查看特定图书的库存日志
|
||
Log.add_log(
|
||
action="查看图书库存日志",
|
||
user_id=current_user.id,
|
||
target_type="book",
|
||
target_id=book.id,
|
||
ip_address=request.remote_addr,
|
||
description=f"用户查看图书《{book.title}》的库存变动日志"
|
||
)
|
||
|
||
return render_template('inventory/book_logs.html',
|
||
book=book,
|
||
logs=logs.items,
|
||
pagination=logs)
|