refactor(purchase): 批次概念改为基于 request_no 前缀推导,撤销数据库字段

按用户反馈:不需要新增数据库字段,现有 request_no (PUR-20260831-1021-0001)
本身就能推导批次——去掉末尾4位流水号即为批次前缀。

- 撤销 purchase_request.batch_no 字段(模型/数据库)
- 撤销 create 接口的 batch_no 参数与 generate_batch_no
- 批次标识 = request_no 去掉末尾 -XXXX 段
  getBatchKey('PUR-20260831-1021-0001') = 'PUR-20260831-1021'
- 按批次查询/批量审批接口改用 batch_key (request_no LIKE 前缀)
- 前端列表批次号列、整批弹窗、一键审批本批均基于前缀推导
- 零数据库迁移,历史数据立即可用
This commit is contained in:
yueli
2026-08-31 13:49:11 +08:00
parent 664127ee30
commit f499346f74
5 changed files with 40 additions and 63 deletions

View File

@ -158,18 +158,18 @@ def get_purchase_detail(purchase_id):
# --------------------------------------------------------
# 3.5 按批次查询采购申请(同一批提交的多条记录)
# GET /api/v1/purchase/batch/<batch_no>
# 3.5 按批次查询采购申请(同一批提交的多条记录,批次=request_no去掉末尾流水号)
# GET /api/v1/purchase/batch/<batch_key>
# --------------------------------------------------------
@purchase_bp.route('/batch/<path:batch_no>', methods=['GET'])
@purchase_bp.route('/batch/<path:batch_key>', methods=['GET'])
@jwt_required()
def get_purchase_by_batch(batch_no):
"""按批次号查询同一批提交的所有采购申请记录"""
def get_purchase_by_batch(batch_key):
"""按批次前缀(request_no 去掉末尾4位流水号)查询同一批的所有记录"""
try:
from app.models.purchase import PurchaseRequest
records = PurchaseRequest.query.filter(
PurchaseRequest.batch_no == batch_no
).order_by(PurchaseRequest.id.asc()).all()
PurchaseRequest.request_no.like(f"{batch_key}-%")
).order_by(PurchaseRequest.request_no.asc()).all()
user_id = get_current_user_id()
has_perm = _user_has_purchase_perm()
@ -183,7 +183,7 @@ def get_purchase_by_batch(batch_no):
d.pop(k, None)
items.append(d)
return jsonify({'code': 200, 'msg': '获取成功', 'data': {'batch_no': batch_no, 'items': items}}), 200
return jsonify({'code': 200, 'msg': '获取成功', 'data': {'batch_key': batch_key, 'items': items}}), 200
except Exception as e:
traceback.print_exc()
return jsonify({'code': 500, 'msg': str(e)}), 500
@ -248,7 +248,7 @@ def batch_approve_purchase():
{
"ids": [1, 2, 3], // 按 ID 列表审批
或
"batch_no": "BATCH-xxx", // 按批次号审批整批(同一批提交的记录)
"batch_key": "PUR-20260831-1021", // 按批次前缀审批整批(同批流水号连续)
"action": "approve" | "reject",
"reject_reason": "驳回原因(reject 时必填)"
}
@ -257,15 +257,15 @@ def batch_approve_purchase():
user_id = get_current_user_id()
data = request.get_json() or {}
ids = data.get('ids', [])
batch_no = data.get('batch_no')
batch_key = data.get('batch_key')
action = data.get('action', 'approve')
reject_reason = data.get('reject_reason')
# 若传 batch_no,则查出该批次的所有待审批记录 ID
if batch_no:
# 若传 batch_key(request_no 去掉末尾流水号的前缀),查出该批次所有待审批记录
if batch_key:
from app.models.purchase import PurchaseRequest
records = PurchaseRequest.query.filter(
PurchaseRequest.batch_no == batch_no,
PurchaseRequest.request_no.like(f"{batch_key}-%"),
PurchaseRequest.status == 0
).all()
ids = [r.id for r in records]
@ -273,7 +273,7 @@ def batch_approve_purchase():
return jsonify({'code': 400, 'msg': '该批次没有待审批的记录'}), 400
if not ids or not isinstance(ids, list):
return jsonify({'code': 400, 'msg': 'ids 或 batch_no 不能为空'}), 400
return jsonify({'code': 400, 'msg': 'ids 或 batch_key 不能为空'}), 400
if action not in ('approve', 'reject'):
return jsonify({'code': 400, 'msg': '无效的审批操作'}), 400
if action == 'reject' and not reject_reason:

View File

@ -11,8 +11,6 @@ class PurchaseRequest(db.Model):
id = db.Column(db.Integer, primary_key=True)
request_no = db.Column(db.String(100), unique=True, nullable=False, index=True)
# [新增] 采购批次号:同一批循环提交的多条记录共享,用于分组查看/统计
batch_no = db.Column(db.String(100), index=True, comment='采购批次号(同一批提交的多条记录共享)')
# [新增] 硬关联基础物料表,打通"按单入库"链路
base_id = db.Column(db.Integer, db.ForeignKey('material_base.id'), index=True, comment='关联基础物料ID')
name = db.Column(db.String(255), nullable=False, comment='采购名称')
@ -81,7 +79,6 @@ class PurchaseRequest(db.Model):
return {
'id': self.id,
'request_no': self.request_no,
'batch_no': self.batch_no or '',
'base_id': self.base_id,
'name': self.name,
'spec_model': self.spec_model or '',

View File

@ -20,18 +20,6 @@ class PurchaseService:
.filter(PurchaseRequest.request_no.like(f"{prefix}%")).scalar()
return f"{prefix}{(existing_count + 1):04d}"
@staticmethod
def generate_batch_no():
"""生成采购批次号: BATCH-yyyyMMdd-HHmm-当日流水(4位)
同一批循环提交的多条记录共享同一 batch_no"""
import uuid
beijing_tz = timezone(timedelta(hours=8))
now = datetime.now(beijing_tz)
date_str = now.strftime('%Y%m%d')
time_str = now.strftime('%H%M')
# 用时间戳 + 随机段确保唯一
return f"BATCH-{date_str}-{time_str}-{uuid.uuid4().hex[:4].upper()}"
@staticmethod
def auto_fill_from_material(keyword: str):
"""
@ -79,12 +67,8 @@ class PurchaseService:
if material:
base_id = material.id
# [新增] 批次号:前端同一批循环提交时传入同一 batch_no,无则自动生成
batch_no = data.get('batch_no') or PurchaseService.generate_batch_no()
purchase = PurchaseRequest(
request_no=request_no,
batch_no=batch_no, # [新增]
base_id=base_id, # [新增]
name=data['name'],
spec_model=data.get('spec_model', ''),