diff --git a/inventory-backend/app/api/v1/purchase.py b/inventory-backend/app/api/v1/purchase.py
index 7f2478e..e270639 100644
--- a/inventory-backend/app/api/v1/purchase.py
+++ b/inventory-backend/app/api/v1/purchase.py
@@ -206,7 +206,7 @@ def approve_purchase_request(purchase_id):
action = data.get('action', 'approve')
reject_reason = data.get('reject_reason')
- if action not in ('approve', 'reject'):
+ if action not in ('approve', 'reject', 'close'):
return jsonify({'code': 400, 'msg': '无效的审批操作'}), 400
if action == 'reject' and not reject_reason:
@@ -219,7 +219,7 @@ def approve_purchase_request(purchase_id):
reject_reason=reject_reason
)
- msg = '审批通过' if action == 'approve' else '已驳回'
+ msg = '审批通过' if action == 'approve' else ('已驳回' if action == 'reject' else '已完结')
# ★ Fail-Closed: 审批响应剥离价格字段
resp = purchase.to_dict()
for k in ('unit_price', 'total_price', 'tax_rate'):
diff --git a/inventory-backend/app/models/purchase.py b/inventory-backend/app/models/purchase.py
index 2f43f0d..a1ff2b4 100644
--- a/inventory-backend/app/models/purchase.py
+++ b/inventory-backend/app/models/purchase.py
@@ -91,7 +91,7 @@ class PurchaseRequest(db.Model):
'total_price': float(self.total_price) if self.total_price else 0,
'tax_rate': float(self.tax_rate) if self.tax_rate else 0,
'status': self.status,
- 'status_text': ['待审批', '已通过', '已驳回', '已完成'][self.status] if self.status in [0, 1, 2, 3] else '未知',
+ 'status_text': ['待审批', '已通过', '已驳回', '已完成', '已完结'][self.status] if self.status in [0, 1, 2, 3, 4] else '未知',
'requester_id': self.requester_id,
'requester_name': self._get_user_name(self.requester_id),
'requester_email': self._get_user_email(self.requester_id),
diff --git a/inventory-backend/app/services/purchase_service.py b/inventory-backend/app/services/purchase_service.py
index 46fe694..36dfa2e 100644
--- a/inventory-backend/app/services/purchase_service.py
+++ b/inventory-backend/app/services/purchase_service.py
@@ -145,12 +145,22 @@ class PurchaseService:
if not purchase:
raise ValueError("采购申请不存在")
- if purchase.status != 0:
- raise ValueError("当前状态不允许审批")
-
beijing_tz = timezone(timedelta(hours=8))
now = datetime.now(beijing_tz)
+ # ★ 完结:库管将「已通过(1)」的申请单置为「已完结(4)」(仿出库审批)
+ if action == 'close':
+ if purchase.status != 1:
+ raise ValueError("仅「已通过」的采购申请可完结")
+ purchase.status = 4
+ purchase.approver_id = user_id
+ purchase.approved_at = now
+ db.session.commit()
+ return purchase
+
+ if purchase.status != 0:
+ raise ValueError("当前状态不允许审批")
+
if action == 'approve':
purchase.status = 1
purchase.approver_id = user_id
diff --git a/inventory-web/src/views/purchase/index.vue b/inventory-web/src/views/purchase/index.vue
index 3ece0d8..e836a83 100644
--- a/inventory-web/src/views/purchase/index.vue
+++ b/inventory-web/src/views/purchase/index.vue
@@ -10,6 +10,7 @@
已通过
已驳回
已完成
+ 已完结
刷新
@@ -57,6 +58,10 @@
通过
驳回
+
+
+ 完结
+
@@ -703,10 +708,16 @@ const canApprove = computed(() => {
|| userStore.hasPermission('inbound_purchase:operation')
})
+// ★ 完结权限:超级管理员 / 库管(采购操作权限)
+const canClose = computed(() => {
+ return userStore.role === 'SUPER_ADMIN'
+ || userStore.hasPermission('inbound_purchase:operation')
+})
+
// --- 工具函数 ---
const statusTagType = (status: number) => {
const map: Record = {
- 0: 'warning', 1: 'success', 2: 'danger', 3: 'info'
+ 0: 'warning', 1: 'success', 2: 'danger', 3: 'info', 4: 'info'
}
return map[status] ?? 'info'
}
@@ -1170,6 +1181,26 @@ const handleApprove = async (row: any) => {
}
}
+// ★ 完结已通过的采购申请(库管)
+const handleCloseRequest = async (row: any) => {
+ try {
+ await ElMessageBox.confirm(
+ `确定强制完结采购申请【${row.request_no}】吗?\n\n` +
+ `此操作将使该单从「已通过」列表中移除,且不可恢复。`,
+ '⚠️ 强制完结确认',
+ { confirmButtonText: '确认完结', cancelButtonText: '取消', type: 'warning' }
+ )
+ } catch { return }
+
+ try {
+ await approvePurchase(row.id, { action: 'close' })
+ ElMessage.success(`采购申请 ${row.request_no} 已完结`)
+ await fetchData()
+ } catch (err: any) {
+ ElMessage.error(err?.msg || err?.message || '完结失败')
+ }
+}
+
const openRejectDialog = (row: any) => {
currentRejectRow.value = row
rejectReason.value = ''