feat(purchase): 物料搜索分页+价格半联动+图片必填校验
This commit is contained in:
@ -74,6 +74,11 @@ def create_purchase_request():
|
||||
if field not in data or str(data.get(field, '')).strip() == '':
|
||||
return jsonify({'code': 400, 'msg': f'缺少必填字段: {field}'}), 400
|
||||
|
||||
# 图片必填强校验
|
||||
images = data.get('images')
|
||||
if not images or (isinstance(images, list) and len(images) == 0):
|
||||
return jsonify({'code': 400, 'msg': '请上传采购凭证/物品图片'}), 400
|
||||
|
||||
purchase = PurchaseService.create_purchase_request(data, requester_id=user_id)
|
||||
|
||||
return jsonify({
|
||||
@ -200,3 +205,29 @@ def auto_fill_purchase():
|
||||
return jsonify({'code': 200, 'msg': 'ok', 'data': result}), 200
|
||||
except Exception as e:
|
||||
return jsonify({'code': 500, 'msg': str(e)}), 500
|
||||
|
||||
|
||||
# --------------------------------------------------------
|
||||
# 7. 物料基础信息搜索(分页)
|
||||
# GET /api/v1/purchase/search-material?keyword=xxx&page=1
|
||||
# --------------------------------------------------------
|
||||
@purchase_bp.route('/search-material', methods=['GET'])
|
||||
@jwt_required()
|
||||
def search_material_for_purchase():
|
||||
"""物料基础信息搜索接口,支持分页,用于采购申请弹窗"""
|
||||
try:
|
||||
keyword = request.args.get('keyword', '')
|
||||
page = request.args.get('page', 1, type=int)
|
||||
limit = 20
|
||||
|
||||
result = PurchaseService.search_base_material(keyword, page, limit)
|
||||
return jsonify({
|
||||
'code': 200,
|
||||
'msg': 'success',
|
||||
'data': result['items'],
|
||||
'total': result['total'],
|
||||
'has_next': result['has_next']
|
||||
}), 200
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return jsonify({'code': 500, 'msg': str(e)}), 500
|
||||
|
||||
@ -138,6 +138,49 @@ class PurchaseService:
|
||||
purchase = db.session.get(PurchaseRequest, purchase_id)
|
||||
return purchase.to_dict() if purchase else None
|
||||
|
||||
@staticmethod
|
||||
def search_base_material(keyword: str, page: int = 1, limit: int = 20):
|
||||
"""
|
||||
物料基础信息搜索,支持 name/spec_model/company_name 模糊匹配,返回分页结果
|
||||
用于采购申请弹窗的物料远程搜索
|
||||
"""
|
||||
from sqlalchemy import and_, or_
|
||||
|
||||
query = MaterialBase.query.filter(MaterialBase.is_enabled == True)
|
||||
|
||||
if keyword:
|
||||
k = keyword.strip()
|
||||
k_str = f'%{k}%'
|
||||
query = query.filter(or_(
|
||||
MaterialBase.name.ilike(k_str),
|
||||
MaterialBase.spec_model.ilike(k_str),
|
||||
MaterialBase.company_name.ilike(k_str)
|
||||
))
|
||||
|
||||
query = query.order_by(MaterialBase.id.desc())
|
||||
pagination = query.paginate(page=page, per_page=limit, error_out=False)
|
||||
|
||||
items = []
|
||||
for item in pagination.items:
|
||||
items.append({
|
||||
'id': item.id,
|
||||
'company_name': item.company_name,
|
||||
'name': item.name,
|
||||
'spec_model': item.spec_model,
|
||||
'category': item.category,
|
||||
'unit': item.unit,
|
||||
'type': item.material_type,
|
||||
'pinyin': getattr(item, 'pinyin', ''),
|
||||
'status': '启用'
|
||||
})
|
||||
|
||||
return {
|
||||
'items': items,
|
||||
'total': pagination.total,
|
||||
'page': page,
|
||||
'has_next': pagination.has_next
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _notify_new_request(purchase):
|
||||
"""发送新申请邮件给审批人"""
|
||||
|
||||
Reference in New Issue
Block a user