From 6bdc8a82f20c286ca3c7df92a5bbf1037a61ec2b Mon Sep 17 00:00:00 2001 From: yueli Date: Wed, 9 Sep 2026 10:47:29 +0800 Subject: [PATCH] =?UTF-8?q?feat(borrow,outbound):=20=E7=94=B3=E8=AF=B7?= =?UTF-8?q?=E9=A2=84=E6=A3=80=E6=8C=89=E9=9C=80=E6=98=BE=E7=A4=BA=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E4=BA=BA=E2=80=94=E2=80=94=E9=BB=98=E8=AE=A4=E4=B8=8D?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=EF=BC=8C=E5=91=BD=E4=B8=AD=E9=9C=80=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E6=89=8D=E5=87=BA=E7=8E=B0=E5=B9=B6=E5=BF=85=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端新增 /borrow/request/check-approval 与 /outbound/request/check-approval 预检接口 - 申请弹窗默认隐藏审批人;提交前预检:命中需审批→显示审批人并红字列物料、未选拦截;未命中→隐藏并直接提交(approver=null) - 申请 items 补传 base_id 供后端 ID 精确判定 --- inventory-backend/app/api/v1/outbound.py | 22 ++++++++ inventory-backend/app/api/v1/transactions.py | 19 +++++++ inventory-web/src/api/outbound.ts | 9 ++++ inventory-web/src/api/transaction.ts | 9 ++++ .../src/views/borrow/apply/index.vue | 51 +++++++++++++++---- .../src/views/outbound/Selection.vue | 50 ++++++++++++++---- 6 files changed, 138 insertions(+), 22 deletions(-) diff --git a/inventory-backend/app/api/v1/outbound.py b/inventory-backend/app/api/v1/outbound.py index c8b89e4..f82f47c 100644 --- a/inventory-backend/app/api/v1/outbound.py +++ b/inventory-backend/app/api/v1/outbound.py @@ -429,6 +429,28 @@ def create_outbound_request(): return jsonify({'code': 500, 'msg': f'服务器内部错误: {str(e)}'}), 500 +# -------------------------------------------------------- +# 4.1 出库申请预检(判断所选物料是否需审批,驱动前端是否显示审批人) +# POST /api/v1/outbound/request/check-approval +# -------------------------------------------------------- +@outbound_bp.route('/request/check-approval', methods=['POST']) +@jwt_required() +@permission_required('outbound_selection') +def check_outbound_approval(): + try: + data = request.get_json() or {} + items = data.get('items', []) or [] + from app.services.approval_control import resolve_approval_control + need_approval, flagged = resolve_approval_control(items) + return jsonify({ + "code": 200, "msg": "success", + "data": {"need_approval": need_approval, "materials": flagged} + }), 200 + except Exception as e: + traceback.print_exc() + return jsonify({"code": 500, "msg": f"预检失败: {str(e)}"}), 500 + + # -------------------------------------------------------- # 5. 审批出库申请 # PATCH /api/v1/outbound/request//approve diff --git a/inventory-backend/app/api/v1/transactions.py b/inventory-backend/app/api/v1/transactions.py index 85b5ab8..d41df29 100644 --- a/inventory-backend/app/api/v1/transactions.py +++ b/inventory-backend/app/api/v1/transactions.py @@ -311,6 +311,25 @@ def close_borrow_request(request_id): return jsonify({'code': 500, 'msg': f'服务器内部错误: {str(e)}'}), 500 +# --- 借库申请预检(判断所选物料是否需审批,驱动前端是否显示审批人) --- +@trans_bp.route('/borrow/request/check-approval', methods=['POST']) +@jwt_required() +@permission_required('op_borrow_apply') +def check_borrow_approval(): + try: + data = request.get_json() or {} + items = data.get('items', []) or [] + from app.services.approval_control import resolve_approval_control + need_approval, flagged = resolve_approval_control(items) + return jsonify({ + "code": 200, "msg": "success", + "data": {"need_approval": need_approval, "materials": flagged} + }), 200 + except Exception as e: + import traceback; traceback.print_exc() + return jsonify({"code": 500, "msg": f"预检失败: {str(e)}"}), 500 + + # --- 获取借库审批单列表 --- @trans_bp.route('/borrow/request', methods=['GET']) @jwt_required() diff --git a/inventory-web/src/api/outbound.ts b/inventory-web/src/api/outbound.ts index d70d86c..153af15 100644 --- a/inventory-web/src/api/outbound.ts +++ b/inventory-web/src/api/outbound.ts @@ -99,6 +99,15 @@ export function submitOutboundRequest(data: { }) } +// 出库申请预检:判断所选物料是否需审批 +export function checkOutboundApproval(data: { items: Array<{ name?: string; spec_model?: string }> }) { + return request({ + url: '/v1/outbound/request/check-approval', + method: 'post', + data + }) +} + /** * 获取出库审批申请单列表 * @param params 支持 status, page, limit diff --git a/inventory-web/src/api/transaction.ts b/inventory-web/src/api/transaction.ts index 746e6f6..2b8420d 100644 --- a/inventory-web/src/api/transaction.ts +++ b/inventory-web/src/api/transaction.ts @@ -149,6 +149,15 @@ export function submitBorrowRequest(data: { }) } +// 借库申请预检:判断所选物料是否需审批 +export function checkBorrowApproval(data: { items: Array<{ name?: string; spec_model?: string }> }) { + return request({ + url: '/v1/transactions/borrow/request/check-approval', + method: 'post', + data + }) +} + /** * 获取借库审批申请单列表 * @param params 支持 status, page, limit diff --git a/inventory-web/src/views/borrow/apply/index.vue b/inventory-web/src/views/borrow/apply/index.vue index fa6934c..ca90866 100644 --- a/inventory-web/src/views/borrow/apply/index.vue +++ b/inventory-web/src/views/borrow/apply/index.vue @@ -394,7 +394,10 @@ - + +
+ 以下物料需审批出库/借库:{{ approvalNeedText }},请选择审批人 +
(null) const approvers = ref([]) const requestSubmitting = ref(false) +// 是否需审批(由提交前预检驱动,需审批才显示审批人输入框) +const approvalNeedVisible = ref(false) +const approvalNeedText = ref('') const stockList = ref([]) const stockTotal = ref(0) @@ -1052,6 +1058,8 @@ const openRequestDialog = () => { requestApproverId.value = null requestExpectedReturn.value = '' requestIndefinite.value = false + approvalNeedVisible.value = false // 打开时默认隐藏审批人,命中需审批时由预检显示 + approvalNeedText.value = '' loadApprovers() requestDialogVisible.value = true } @@ -1096,23 +1104,44 @@ const buildRemarkWithShortage = (baseRemark: string): string => { const confirmSubmitRequest = async () => { requestSubmitting.value = true try { + const items = validSelectedItems.value.map((item: any) => ({ + base_id: item.base_id ?? null, + name: item.name || '', + spec_model: item.standard || '', + warehouse_location: item.warehouse_location || '', + quantity: item.export_quantity || 0, + expected_return_time: requestIndefinite.value ? null : requestExpectedReturn.value, + is_indefinite: requestIndefinite.value + })) + + // ★ 提交前预检:判断所选物料是否需审批 → 只有需审批才显示并必选审批人 + let needApproval = false + let flagged: any[] = [] + try { + const chk: any = await checkBorrowApproval({ items }) + needApproval = !!chk?.data?.need_approval + flagged = chk?.data?.materials || [] + } catch (e) { needApproval = false; flagged = [] } + approvalNeedVisible.value = needApproval + approvalNeedText.value = flagged.map((m: any) => `${m.name}(${m.spec_model || '-'})`).join(';') + + if (needApproval && !requestApproverId.value) { + ElMessage.warning(`以下物料需审批出库/借库:${approvalNeedText.value}。请先选择审批人`) + return + } + const payload = { - items: validSelectedItems.value.map((item: any) => ({ - name: item.name || '', - spec_model: item.standard || '', - warehouse_location: item.warehouse_location || '', - quantity: item.export_quantity || 0, - expected_return_time: requestIndefinite.value ? null : requestExpectedReturn.value, - is_indefinite: requestIndefinite.value - })), + items, remark: buildRemarkWithShortage(requestRemark.value.trim()), - approver_id: requestApproverId.value + approver_id: needApproval ? requestApproverId.value : null } await submitBorrowRequest(payload) requestDialogVisible.value = false selectedItems.value = [] + approvalNeedVisible.value = false + approvalNeedText.value = '' ElMessage.success('借库申请已提交(含需审批物料时将进入审批,否则直接交库管执行)') } catch (err: any) { ElMessage.error(err?.message || err?.msg || '提交申请失败,请重试') diff --git a/inventory-web/src/views/outbound/Selection.vue b/inventory-web/src/views/outbound/Selection.vue index 637da22..edb08d2 100644 --- a/inventory-web/src/views/outbound/Selection.vue +++ b/inventory-web/src/views/outbound/Selection.vue @@ -409,7 +409,10 @@
- + +
+ 以下物料需审批出库/借库:{{ approvalNeedText }},请选择审批人 +
(null) const approvers = ref([]) const requestSubmitting = ref(false) +// 是否需审批(由提交前预检驱动,需审批才显示审批人输入框) +const approvalNeedVisible = ref(false) +const approvalNeedText = ref('') const stockList = ref([]) // 服务端分页数据 const stockTotal = ref(0) @@ -1079,6 +1085,8 @@ const openRequestDialog = () => { } requestRemark.value = '' requestApproverId.value = null + approvalNeedVisible.value = false // 打开时默认隐藏审批人,命中需审批时由预检显示 + approvalNeedText.value = '' loadApprovers() requestDialogVisible.value = true } @@ -1100,7 +1108,6 @@ const confirmSubmitRequest = async () => { ElMessage.warning('请填写申请原因') return } - // 审批人改为可选:默认不审批时无需选择;所选物料命中“需审批”但未选审批人时,后端会提示补选 if (!requestOutboundType.value) { ElMessage.warning('请选择出库类型') return @@ -1108,17 +1115,36 @@ const confirmSubmitRequest = async () => { requestSubmitting.value = true try { + const items = validSelectedItems.value.map(item => ({ + material_type: item.typeLabel || item.type || '', + base_id: item.base_id ?? null, + name: item.name || '', + spec_model: item.standard || '', + warehouse_location: item.warehouse_location || '', + quantity: item.export_quantity || 0 + })) + + // ★ 提交前预检:判断所选物料是否需审批 → 只有需审批才显示并必选审批人 + let needApproval = false + let flagged: any[] = [] + try { + const chk: any = await checkOutboundApproval({ items }) + needApproval = !!chk?.data?.need_approval + flagged = chk?.data?.materials || [] + } catch (e) { needApproval = false; flagged = [] } + approvalNeedVisible.value = needApproval + approvalNeedText.value = flagged.map((m: any) => `${m.name}(${m.spec_model || '-'})`).join(';') + + if (needApproval && !requestApproverId.value) { + ElMessage.warning(`以下物料需审批出库/借库:${approvalNeedText.value}。请先选择审批人`) + return + } + const payload: any = { - items: validSelectedItems.value.map(item => ({ - material_type: item.typeLabel || item.type || '', - name: item.name || '', - spec_model: item.standard || '', - warehouse_location: item.warehouse_location || '', - quantity: item.export_quantity || 0 - })), + items, remark: buildRemarkWithShortage(trimmed), outbound_type: requestOutboundType.value, - approver_id: requestApproverId.value + approver_id: needApproval ? requestApproverId.value : null } await submitOutboundRequest(payload) @@ -1126,6 +1152,8 @@ const confirmSubmitRequest = async () => { // 成功:关闭弹窗、清空列表、提示 requestDialogVisible.value = false selectedItems.value = [] + approvalNeedVisible.value = false + approvalNeedText.value = '' ElMessage.success('出库申请已提交(含需审批物料时将进入审批,否则直接交库管执行)') } catch (err: any) { ElMessage.error(err?.message || err?.msg || '提交申请失败,请重试')