2026-02-04 17:22:20 +08:00
|
|
|
|
from flask import Blueprint, request, jsonify
|
|
|
|
|
|
from app.services.outbound_service import OutboundService
|
2026-02-27 13:54:06 +08:00
|
|
|
|
from flask_jwt_extended import jwt_required, get_jwt_identity, get_jwt
|
2026-02-27 13:45:49 +08:00
|
|
|
|
from app.utils.decorators import permission_required
|
2026-02-27 13:54:06 +08:00
|
|
|
|
from app.services.auth_service import AuthService
|
2026-02-05 10:20:52 +08:00
|
|
|
|
import traceback
|
2026-02-04 17:22:20 +08:00
|
|
|
|
|
|
|
|
|
|
outbound_bp = Blueprint('outbound', __name__, url_prefix='/outbound')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --------------------------------------------------------
|
2026-02-05 10:20:52 +08:00
|
|
|
|
# 1. 扫码查询库存接口 (关联三个库存表)
|
2026-02-04 17:22:20 +08:00
|
|
|
|
# GET /api/v1/outbound/scan?barcode=...
|
|
|
|
|
|
# --------------------------------------------------------
|
|
|
|
|
|
@outbound_bp.route('/scan', methods=['GET'])
|
|
|
|
|
|
@jwt_required()
|
2026-02-27 13:45:49 +08:00
|
|
|
|
@permission_required('outbound_selection')
|
2026-02-04 17:22:20 +08:00
|
|
|
|
def scan_barcode():
|
|
|
|
|
|
barcode = request.args.get('barcode')
|
|
|
|
|
|
if not barcode:
|
|
|
|
|
|
return jsonify({'code': 400, 'msg': '请提供条码'}), 400
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
2026-02-05 16:54:11 +08:00
|
|
|
|
# 调用 Service 层去三个表中查找 (Service已更新,会返回价格)
|
2026-02-04 17:22:20 +08:00
|
|
|
|
result = OutboundService.get_stock_by_barcode(barcode)
|
2026-02-05 10:20:52 +08:00
|
|
|
|
|
2026-02-04 17:22:20 +08:00
|
|
|
|
if result:
|
2026-02-05 10:20:52 +08:00
|
|
|
|
return jsonify({
|
|
|
|
|
|
'code': 200,
|
|
|
|
|
|
'msg': '扫描成功',
|
|
|
|
|
|
'data': result
|
|
|
|
|
|
})
|
2026-02-04 17:22:20 +08:00
|
|
|
|
else:
|
2026-02-05 10:20:52 +08:00
|
|
|
|
return jsonify({
|
|
|
|
|
|
'code': 404,
|
|
|
|
|
|
'msg': '未找到对应的库存记录,请确认条码是否正确'
|
|
|
|
|
|
}), 404
|
|
|
|
|
|
|
2026-02-04 17:22:20 +08:00
|
|
|
|
except Exception as e:
|
2026-02-05 10:20:52 +08:00
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
return jsonify({'code': 500, 'msg': f'扫描查询出错: {str(e)}'}), 500
|
2026-02-04 17:22:20 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --------------------------------------------------------
|
2026-02-05 16:54:11 +08:00
|
|
|
|
# 2. 提交出库单接口 (批量)
|
2026-02-04 17:22:20 +08:00
|
|
|
|
# POST /api/v1/outbound
|
|
|
|
|
|
# --------------------------------------------------------
|
|
|
|
|
|
@outbound_bp.route('', methods=['POST'])
|
|
|
|
|
|
@jwt_required()
|
|
|
|
|
|
def create_outbound():
|
2026-02-27 13:54:06 +08:00
|
|
|
|
# 权限检查:需要 outbound_create:operation 或 outbound_selection:operation 之一
|
|
|
|
|
|
claims = get_jwt()
|
|
|
|
|
|
user_role = claims.get('role')
|
|
|
|
|
|
if not user_role:
|
|
|
|
|
|
return jsonify({'code': 403, 'msg': '未授权'}), 403
|
|
|
|
|
|
|
|
|
|
|
|
# 超级管理员直接放行
|
|
|
|
|
|
if user_role != 'super_admin':
|
|
|
|
|
|
perm_dict = AuthService.get_user_permissions(user_role)
|
|
|
|
|
|
perms = perm_dict.get('menus', []) + perm_dict.get('elements', [])
|
|
|
|
|
|
if ('outbound_create:operation' not in perms) and ('outbound_selection:operation' not in perms):
|
|
|
|
|
|
return jsonify({'code': 403, 'msg': '权限不足'}), 403
|
|
|
|
|
|
|
2026-02-04 17:22:20 +08:00
|
|
|
|
data = request.get_json()
|
|
|
|
|
|
if not data:
|
|
|
|
|
|
return jsonify({'code': 400, 'msg': '无有效数据'}), 400
|
|
|
|
|
|
|
2026-02-05 10:20:52 +08:00
|
|
|
|
# 获取当前登录用户名 (JWT identity)
|
|
|
|
|
|
current_user_name = get_jwt_identity()
|
|
|
|
|
|
if not current_user_name:
|
|
|
|
|
|
current_user_name = 'Unknown'
|
|
|
|
|
|
|
2026-02-05 16:54:11 +08:00
|
|
|
|
# 获取最终的操作员名称
|
2026-02-05 10:20:52 +08:00
|
|
|
|
final_operator = data.get('operator_name')
|
|
|
|
|
|
if not final_operator:
|
|
|
|
|
|
final_operator = current_user_name
|
2026-02-04 17:22:20 +08:00
|
|
|
|
|
2026-02-05 16:54:11 +08:00
|
|
|
|
# 必填校验 (针对整个单据)
|
|
|
|
|
|
# items 必须是列表且不为空,consumer_name 和 signature_path 必填
|
|
|
|
|
|
if 'items' not in data or not data['items']:
|
|
|
|
|
|
return jsonify({'code': 400, 'msg': '出库商品列表不能为空'}), 400
|
|
|
|
|
|
|
|
|
|
|
|
if not data.get('consumer_name') or not data.get('signature_path'):
|
|
|
|
|
|
return jsonify({'code': 400, 'msg': '领用人及签名信息缺失'}), 400
|
2026-02-04 17:22:20 +08:00
|
|
|
|
|
|
|
|
|
|
try:
|
2026-02-05 16:54:11 +08:00
|
|
|
|
# ★ [修改] 调用批量创建服务
|
|
|
|
|
|
outbound_no = OutboundService.create_outbound_batch(data, operator_name=final_operator)
|
2026-02-04 17:22:20 +08:00
|
|
|
|
return jsonify({
|
|
|
|
|
|
'code': 200,
|
|
|
|
|
|
'msg': '出库成功',
|
2026-02-05 16:54:11 +08:00
|
|
|
|
'data': {'outbound_no': outbound_no}
|
2026-02-04 17:22:20 +08:00
|
|
|
|
})
|
|
|
|
|
|
except ValueError as e:
|
2026-02-05 10:20:52 +08:00
|
|
|
|
# 业务逻辑错误 (如库存不足)
|
2026-02-04 17:22:20 +08:00
|
|
|
|
return jsonify({'code': 400, 'msg': str(e)}), 400
|
|
|
|
|
|
except Exception as e:
|
2026-02-05 10:20:52 +08:00
|
|
|
|
traceback.print_exc()
|
2026-02-04 17:22:20 +08:00
|
|
|
|
return jsonify({'code': 500, 'msg': f'服务器内部错误: {str(e)}'}), 500
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --------------------------------------------------------
|
2026-02-05 16:54:11 +08:00
|
|
|
|
# 3. 获取出库记录列表 (分组展示)
|
2026-02-04 17:22:20 +08:00
|
|
|
|
# GET /api/v1/outbound
|
|
|
|
|
|
# --------------------------------------------------------
|
|
|
|
|
|
@outbound_bp.route('', methods=['GET'])
|
|
|
|
|
|
@jwt_required()
|
2026-02-27 13:45:49 +08:00
|
|
|
|
@permission_required('outbound_selection')
|
2026-02-04 17:22:20 +08:00
|
|
|
|
def get_outbound_list():
|
|
|
|
|
|
try:
|
|
|
|
|
|
page = int(request.args.get('page', 1))
|
2026-02-05 10:20:52 +08:00
|
|
|
|
limit = int(request.args.get('limit', 10))
|
2026-02-04 17:22:20 +08:00
|
|
|
|
keyword = request.args.get('keyword', '')
|
2026-02-05 16:54:11 +08:00
|
|
|
|
# 如果前端传了日期范围,可以解析处理,这里暂略
|
2026-02-04 17:22:20 +08:00
|
|
|
|
|
2026-02-05 16:54:11 +08:00
|
|
|
|
# ★ [修改] 调用分组查询服务
|
|
|
|
|
|
result = OutboundService.get_grouped_list(page, limit, keyword)
|
2026-02-04 17:22:20 +08:00
|
|
|
|
|
|
|
|
|
|
return jsonify({
|
|
|
|
|
|
'code': 200,
|
|
|
|
|
|
'msg': '获取成功',
|
|
|
|
|
|
'data': result
|
|
|
|
|
|
})
|
|
|
|
|
|
except Exception as e:
|
2026-02-05 10:20:52 +08:00
|
|
|
|
traceback.print_exc()
|
2026-02-27 13:45:49 +08:00
|
|
|
|
return jsonify({'code': 500, 'msg': str(e)}), 500
|