2026-02-06 10:16:37 +08:00
|
|
|
|
from flask import Blueprint, jsonify, request
|
2026-02-06 11:28:48 +08:00
|
|
|
|
from app.extensions import db
|
2026-02-06 14:30:14 +08:00
|
|
|
|
# ★★★ 修复点:必须引入 datetime,否则下方更新时间时会报错 500 ★★★
|
|
|
|
|
|
from datetime import datetime
|
2026-02-27 13:36:10 +08:00
|
|
|
|
from app.utils.decorators import permission_required
|
2026-02-06 14:30:14 +08:00
|
|
|
|
|
2026-02-06 11:28:48 +08:00
|
|
|
|
# 导入模型
|
2026-02-06 10:16:37 +08:00
|
|
|
|
from app.models.inbound.buy import StockBuy
|
2026-02-06 14:30:14 +08:00
|
|
|
|
from app.models.inbound.stocktake import StocktakeDraft
|
2026-02-06 10:16:37 +08:00
|
|
|
|
|
2026-02-06 11:28:48 +08:00
|
|
|
|
# 尝试导入半成品和成品
|
2026-02-06 10:16:37 +08:00
|
|
|
|
try:
|
|
|
|
|
|
from app.models.inbound.semi import StockSemi
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
|
StockSemi = None
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
from app.models.inbound.product import StockProduct
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
|
StockProduct = None
|
|
|
|
|
|
|
|
|
|
|
|
from app.services.print.network_print_service import NetworkPrintService
|
|
|
|
|
|
|
|
|
|
|
|
bp = Blueprint('stock_ops', __name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@bp.route('/all', methods=['GET'])
|
2026-02-27 13:36:10 +08:00
|
|
|
|
@permission_required('inventory_stocktake')
|
2026-02-06 10:16:37 +08:00
|
|
|
|
def get_all_stock():
|
|
|
|
|
|
"""
|
2026-02-06 14:30:14 +08:00
|
|
|
|
获取所有库存 > 0 的物品
|
2026-02-06 10:16:37 +08:00
|
|
|
|
"""
|
|
|
|
|
|
try:
|
2026-02-06 14:30:14 +08:00
|
|
|
|
# 1. 采购件
|
2026-02-06 10:16:37 +08:00
|
|
|
|
materials = []
|
|
|
|
|
|
if StockBuy:
|
2026-02-06 11:28:48 +08:00
|
|
|
|
materials = StockBuy.query.filter(StockBuy.stock_quantity > 0).all()
|
2026-02-06 10:16:37 +08:00
|
|
|
|
|
2026-02-06 11:28:48 +08:00
|
|
|
|
# 2. 半成品
|
2026-02-06 10:16:37 +08:00
|
|
|
|
semis = []
|
|
|
|
|
|
if StockSemi:
|
|
|
|
|
|
try:
|
2026-02-06 11:28:48 +08:00
|
|
|
|
semis = StockSemi.query.filter(StockSemi.stock_quantity > 0).all()
|
2026-02-06 10:16:37 +08:00
|
|
|
|
except Exception:
|
|
|
|
|
|
semis = []
|
|
|
|
|
|
|
2026-02-06 11:28:48 +08:00
|
|
|
|
# 3. 成品
|
2026-02-06 10:16:37 +08:00
|
|
|
|
products = []
|
|
|
|
|
|
if StockProduct:
|
|
|
|
|
|
try:
|
2026-02-06 11:28:48 +08:00
|
|
|
|
products = StockProduct.query.filter(StockProduct.stock_quantity > 0).all()
|
2026-02-06 10:16:37 +08:00
|
|
|
|
except Exception:
|
|
|
|
|
|
products = []
|
|
|
|
|
|
|
|
|
|
|
|
return jsonify({
|
|
|
|
|
|
"materials": [item.to_dict() for item in materials],
|
|
|
|
|
|
"semis": [item.to_dict() for item in semis],
|
|
|
|
|
|
"products": [item.to_dict() for item in products]
|
|
|
|
|
|
}), 200
|
|
|
|
|
|
except Exception as e:
|
2026-02-06 11:28:48 +08:00
|
|
|
|
print(f"Error: {e}")
|
2026-02-06 10:16:37 +08:00
|
|
|
|
return jsonify({"message": f"查询库存失败: {str(e)}"}), 500
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-02-06 14:30:14 +08:00
|
|
|
|
# --- 草稿箱接口 ---
|
2026-02-06 11:28:48 +08:00
|
|
|
|
|
|
|
|
|
|
@bp.route('/draft/list', methods=['GET'])
|
2026-02-27 13:36:10 +08:00
|
|
|
|
@permission_required('inventory_stocktake')
|
2026-02-06 11:28:48 +08:00
|
|
|
|
def get_drafts():
|
|
|
|
|
|
"""获取当前用户的盘点进度"""
|
|
|
|
|
|
user_id = request.args.get('user_id', 'admin')
|
|
|
|
|
|
drafts = StocktakeDraft.query.filter_by(user_id=user_id).all()
|
|
|
|
|
|
return jsonify([d.to_dict() for d in drafts]), 200
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@bp.route('/draft/add', methods=['POST'])
|
2026-02-27 13:36:10 +08:00
|
|
|
|
@permission_required('inventory_stocktake:operation')
|
2026-02-06 11:28:48 +08:00
|
|
|
|
def add_draft():
|
2026-02-06 14:30:14 +08:00
|
|
|
|
"""扫码同步 (支持更新数量)"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
data = request.json
|
|
|
|
|
|
user_id = data.get('user_id', 'admin')
|
|
|
|
|
|
uuid = data.get('uuid')
|
|
|
|
|
|
quantity = data.get('quantity', 1)
|
|
|
|
|
|
|
|
|
|
|
|
# 查找是否已存在
|
|
|
|
|
|
draft = StocktakeDraft.query.filter_by(user_id=user_id, uuid=uuid).first()
|
|
|
|
|
|
|
|
|
|
|
|
if draft:
|
|
|
|
|
|
# 如果已存在,更新数量和时间
|
|
|
|
|
|
draft.quantity = quantity
|
|
|
|
|
|
# ★ 修复点:这里需要 datetime 对象
|
|
|
|
|
|
draft.scan_time = datetime.now()
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 如果不存在,创建新的
|
|
|
|
|
|
draft = StocktakeDraft(user_id=user_id, uuid=uuid, quantity=quantity)
|
|
|
|
|
|
db.session.add(draft)
|
2026-02-06 11:28:48 +08:00
|
|
|
|
|
|
|
|
|
|
db.session.commit()
|
2026-02-06 14:30:14 +08:00
|
|
|
|
return jsonify({"message": "Saved"}), 200
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"Add Draft Error: {e}")
|
|
|
|
|
|
return jsonify({"message": str(e)}), 500
|
2026-02-06 11:28:48 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@bp.route('/draft/clear', methods=['POST'])
|
2026-02-27 13:36:10 +08:00
|
|
|
|
@permission_required('inventory_stocktake:operation')
|
2026-02-06 11:28:48 +08:00
|
|
|
|
def clear_draft():
|
2026-02-06 14:30:14 +08:00
|
|
|
|
"""清空进度"""
|
2026-02-06 11:28:48 +08:00
|
|
|
|
data = request.json
|
|
|
|
|
|
user_id = data.get('user_id', 'admin')
|
|
|
|
|
|
|
|
|
|
|
|
StocktakeDraft.query.filter_by(user_id=user_id).delete()
|
|
|
|
|
|
db.session.commit()
|
|
|
|
|
|
return jsonify({"message": "Cleared"}), 200
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-02-28 11:55:19 +08:00
|
|
|
|
@bp.route('/borrowed-quantities', methods=['POST'])
|
|
|
|
|
|
@permission_required('inventory_stocktake')
|
|
|
|
|
|
def get_borrowed_quantities():
|
|
|
|
|
|
"""批量获取借出未还数量"""
|
|
|
|
|
|
from app.models.transaction import TransBorrow
|
|
|
|
|
|
data = request.json.get('items', [])
|
|
|
|
|
|
result = {}
|
|
|
|
|
|
for item in data:
|
|
|
|
|
|
source = item.get('source_table')
|
|
|
|
|
|
stock_id = item.get('stock_id')
|
|
|
|
|
|
if source and stock_id is not None:
|
|
|
|
|
|
qty = TransBorrow.get_borrowed_quantity(source, stock_id)
|
|
|
|
|
|
result[f"{source}_{stock_id}"] = qty
|
|
|
|
|
|
return jsonify(result), 200
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-02-06 14:30:14 +08:00
|
|
|
|
# --- 打印接口 ---
|
2026-02-06 11:28:48 +08:00
|
|
|
|
|
2026-02-06 10:16:37 +08:00
|
|
|
|
@bp.route('/print/selection', methods=['POST'])
|
2026-02-27 13:36:10 +08:00
|
|
|
|
@permission_required('inventory_stocktake:operation')
|
2026-02-06 10:16:37 +08:00
|
|
|
|
def print_selection():
|
|
|
|
|
|
try:
|
|
|
|
|
|
data = request.json
|
|
|
|
|
|
items = data.get('items', [])
|
2026-02-06 11:28:48 +08:00
|
|
|
|
if not items: return jsonify({"message": "未选择任何物品"}), 400
|
|
|
|
|
|
printer = NetworkPrintService()
|
2026-02-06 10:16:37 +08:00
|
|
|
|
success, msg = printer.print_outbound_selection(items)
|
2026-02-06 11:28:48 +08:00
|
|
|
|
return jsonify({"message": "打印指令已发送" if success else msg}), 200 if success else 500
|
2026-02-06 10:16:37 +08:00
|
|
|
|
except Exception as e:
|
2026-02-06 11:28:48 +08:00
|
|
|
|
return jsonify({"message": str(e)}), 500
|
2026-02-06 10:16:37 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@bp.route('/print/stocktake', methods=['POST'])
|
2026-02-27 13:36:10 +08:00
|
|
|
|
@permission_required('inventory_stocktake:operation')
|
2026-02-06 10:16:37 +08:00
|
|
|
|
def print_stocktake():
|
|
|
|
|
|
try:
|
|
|
|
|
|
data = request.json
|
|
|
|
|
|
printer = NetworkPrintService()
|
|
|
|
|
|
success, msg = printer.print_stocktake_report(data)
|
2026-02-06 11:28:48 +08:00
|
|
|
|
return jsonify({"message": "盘点报告已发送" if success else msg}), 200 if success else 500
|
2026-02-06 10:16:37 +08:00
|
|
|
|
except Exception as e:
|
2026-02-27 13:36:10 +08:00
|
|
|
|
return jsonify({"message": str(e)}), 500
|