feat(purchase): 采购单号改为 批次-批内序号 格式,显式体现同批

按用户原始逻辑还原并扩展:
- 单号格式: PUR-日期-时间-批次-批内序号
  例: PUR-20260831-1021-0001-0001(今日第1次采购的第1条)
- generate_request_no 支持 batch_seq 参数生成批次+批内序号
- 新增 GET /purchase/next-batch-seq 返回今日下一个批次号
- 前端提交时获取批次号,同批所有行共享,循环提交生成连续批内序号
- 批次识别: 单号去掉末尾 -批内序号 即为批次前缀
- 保留旧格式兼容(无 batch_seq 时回退 PUR-日期-时间-流水)
This commit is contained in:
yueli
2026-08-31 13:54:03 +08:00
parent f499346f74
commit 1000deda2f
4 changed files with 80 additions and 8 deletions

View File

@ -9,16 +9,58 @@ from app.models.base import MaterialBase
class PurchaseService:
@staticmethod
def generate_request_no():
"""生成采购单号: PUR-yyyyMMdd-HHmm-当日流水(4位)"""
def generate_request_no(batch_seq: int = None):
"""
生成采购单号: PUR-yyyyMMdd-HHmm-批次-批内序号
Args:
batch_seq: 今日第几次采购批次(前端提交一批时传入,同一批共享)
None 时回退为旧格式 PUR-日期-时间-流水
返回:
- 有 batch_seq: PUR-20260831-1021-0001-0001(批次号-批内序号)
- 无 batch_seq: PUR-20260831-1021-0001(旧格式兼容)
"""
beijing_tz = timezone(timedelta(hours=8))
now = datetime.now(beijing_tz)
date_str = now.strftime('%Y%m%d')
time_str = now.strftime('%H%M')
if batch_seq is not None:
# 批次前缀: PUR-日期-时间-批次
batch_prefix = f"PUR-{date_str}-{time_str}-{batch_seq:04d}"
# 批内序号: 该批次前缀下的记录数 + 1
item_count = db.session.query(func.count(func.distinct(PurchaseRequest.request_no))) \
.filter(PurchaseRequest.request_no.like(f"{batch_prefix}-%")).scalar()
return f"{batch_prefix}-{(item_count + 1):04d}"
# 旧格式兼容: PUR-日期-时间-流水
prefix = f"PUR-{date_str}-{time_str}-"
existing_count = db.session.query(func.count(func.distinct(PurchaseRequest.request_no))) \
.filter(PurchaseRequest.request_no.like(f"{prefix}%")).scalar()
return f"{prefix}{(existing_count + 1):04d}"
@staticmethod
def get_next_batch_seq():
"""
返回今日下一个采购批次号(今日第几次)
统计今日已有的不同批次(单号第4段)数量,+1
"""
beijing_tz = timezone(timedelta(hours=8))
now = datetime.now(beijing_tz)
date_str = now.strftime('%Y%m%d')
time_str = now.strftime('%H%M')
prefix = f"PUR-{date_str}-{time_str}-"
existing_count = db.session.query(func.count(func.distinct(PurchaseRequest.request_no))) \
.filter(PurchaseRequest.request_no.like(f"{prefix}%")).scalar()
return f"{prefix}{(existing_count + 1):04d}"
# 查询今日所有单号,提取第4段(批次号)去重
rows = db.session.query(PurchaseRequest.request_no) \
.filter(PurchaseRequest.request_no.like(f"{prefix}%")).all()
batch_seqs = set()
for (rn,) in rows:
parts = rn.split('-')
if len(parts) >= 4:
batch_seqs.add(parts[3])
return len(batch_seqs) + 1
@staticmethod
def auto_fill_from_material(keyword: str):
@ -47,7 +89,8 @@ class PurchaseService:
data 包含: name, spec_model, quantity, purchase_date, supplier_link, remark, images,
unit_price, total_price, approver_id, base_id (可选)
"""
request_no = PurchaseService.generate_request_no()
batch_seq = data.get('batch_seq')
request_no = PurchaseService.generate_request_no(batch_seq=batch_seq)
purchase_date = data.get('purchase_date')
if isinstance(purchase_date, str):