From 664127ee30701e253181335623b24eb55e46ee42 Mon Sep 17 00:00:00 2001 From: yueli Date: Mon, 31 Aug 2026 13:46:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(purchase):=20=E9=87=87=E8=B4=AD=E6=89=B9?= =?UTF-8?q?=E6=AC=A1=E5=8F=B7=E6=94=AF=E6=8C=81=EF=BC=8C=E5=90=8C=E4=B8=80?= =?UTF-8?q?=E6=89=B9=E6=8F=90=E4=BA=A4=E7=9A=84=E8=AE=B0=E5=BD=95=E5=8F=AF?= =?UTF-8?q?=E5=88=86=E7=BB=84=E6=9F=A5=E7=9C=8B/=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 背景: 循环提交把一批物品生成多条独立申请,无法识别'哪些是同一批采购' 后端: - purchase_request 模型新增 batch_no 字段(同一批提交共享) - create 接口接收 batch_no,无则自动生成 BATCH-yyyyMMdd-HHmm-随机 - 新增 GET /purchase/batch/ 按批次查询所有记录 - 批量审批接口支持按 batch_no 一键审批整批 前端: - 提交时生成 batch_no 共享给所有行 - 列表新增批次号列(可点击) - 新增'整批查看'弹窗:显示同批次所有记录 + 一键批量通过本批 - 操作列新增'整批'按钮 数据库需执行: ALTER TABLE purchase_request ADD COLUMN batch_no VARCHAR(100); --- inventory-backend/app/api/v1/purchase.py | 50 +++++++- inventory-backend/app/models/purchase.py | 3 + .../app/services/purchase_service.py | 16 +++ inventory-web/src/api/purchase.ts | 11 +- inventory-web/src/views/purchase/index.vue | 109 +++++++++++++++++- 5 files changed, 183 insertions(+), 6 deletions(-) diff --git a/inventory-backend/app/api/v1/purchase.py b/inventory-backend/app/api/v1/purchase.py index a8d04cb..7fcc250 100644 --- a/inventory-backend/app/api/v1/purchase.py +++ b/inventory-backend/app/api/v1/purchase.py @@ -157,6 +157,38 @@ def get_purchase_detail(purchase_id): return jsonify({'code': 500, 'msg': str(e)}), 500 +# -------------------------------------------------------- +# 3.5 按批次查询采购申请(同一批提交的多条记录) +# GET /api/v1/purchase/batch/ +# -------------------------------------------------------- +@purchase_bp.route('/batch/', methods=['GET']) +@jwt_required() +def get_purchase_by_batch(batch_no): + """按批次号查询同一批提交的所有采购申请记录""" + try: + from app.models.purchase import PurchaseRequest + records = PurchaseRequest.query.filter( + PurchaseRequest.batch_no == batch_no + ).order_by(PurchaseRequest.id.asc()).all() + + user_id = get_current_user_id() + has_perm = _user_has_purchase_perm() + + items = [] + for r in records: + d = r.to_dict() + # 非本人且无权限时,价格字段剥离 + if d['requester_id'] != user_id and not has_perm: + for k in ('unit_price', 'total_price', 'tax_rate'): + d.pop(k, None) + items.append(d) + + return jsonify({'code': 200, 'msg': '获取成功', 'data': {'batch_no': batch_no, 'items': items}}), 200 + except Exception as e: + traceback.print_exc() + return jsonify({'code': 500, 'msg': str(e)}), 500 + + # -------------------------------------------------------- # 4. 审批采购申请 # PATCH /api/v1/purchase//approve @@ -214,7 +246,9 @@ def batch_approve_purchase(): 请求体: { - "ids": [1, 2, 3], + "ids": [1, 2, 3], // 按 ID 列表审批 + 或 + "batch_no": "BATCH-xxx", // 按批次号审批整批(同一批提交的记录) "action": "approve" | "reject", "reject_reason": "驳回原因(reject 时必填)" } @@ -223,11 +257,23 @@ 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') action = data.get('action', 'approve') reject_reason = data.get('reject_reason') + # 若传 batch_no,则查出该批次的所有待审批记录 ID + if batch_no: + from app.models.purchase import PurchaseRequest + records = PurchaseRequest.query.filter( + PurchaseRequest.batch_no == batch_no, + PurchaseRequest.status == 0 + ).all() + ids = [r.id for r in records] + if not ids: + return jsonify({'code': 400, 'msg': '该批次没有待审批的记录'}), 400 + if not ids or not isinstance(ids, list): - return jsonify({'code': 400, 'msg': 'ids 不能为空'}), 400 + return jsonify({'code': 400, 'msg': 'ids 或 batch_no 不能为空'}), 400 if action not in ('approve', 'reject'): return jsonify({'code': 400, 'msg': '无效的审批操作'}), 400 if action == 'reject' and not reject_reason: diff --git a/inventory-backend/app/models/purchase.py b/inventory-backend/app/models/purchase.py index 2f43f0d..0236fa1 100644 --- a/inventory-backend/app/models/purchase.py +++ b/inventory-backend/app/models/purchase.py @@ -11,6 +11,8 @@ 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='采购名称') @@ -79,6 +81,7 @@ 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 '', diff --git a/inventory-backend/app/services/purchase_service.py b/inventory-backend/app/services/purchase_service.py index adf9eaf..700fa6b 100644 --- a/inventory-backend/app/services/purchase_service.py +++ b/inventory-backend/app/services/purchase_service.py @@ -20,6 +20,18 @@ 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): """ @@ -67,8 +79,12 @@ 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', ''), diff --git a/inventory-web/src/api/purchase.ts b/inventory-web/src/api/purchase.ts index 85f3b4b..8bb259d 100644 --- a/inventory-web/src/api/purchase.ts +++ b/inventory-web/src/api/purchase.ts @@ -89,7 +89,8 @@ export function approvePurchase(id: number, data: { // 批量审批采购申请(循环提交产生的多条记录可一次审批) export function batchApprovePurchase(data: { - ids: number[] + ids?: number[] + batch_no?: string action: 'approve' | 'reject' reject_reason?: string }) { @@ -100,6 +101,14 @@ export function batchApprovePurchase(data: { }) } +// 按批次号查询同批次的采购申请记录 +export function getPurchaseByBatch(batchNo: string) { + return request({ + url: `/purchase/batch/${encodeURIComponent(batchNo)}`, + method: 'get' + }) +} + // 获取可选审批人列表 export function getPurchaseApprovers() { return request({ diff --git a/inventory-web/src/views/purchase/index.vue b/inventory-web/src/views/purchase/index.vue index 506a6b3..1ce5eed 100644 --- a/inventory-web/src/views/purchase/index.vue +++ b/inventory-web/src/views/purchase/index.vue @@ -52,14 +52,24 @@ + + + + - +