From c1c494893fd2ab1253d1c59a61e5af1c686b4c0e Mon Sep 17 00:00:00 2001 From: DXC Date: Tue, 17 Mar 2026 11:56:04 +0800 Subject: [PATCH] feat: implement dynamic inspection requirement logic based on material master data --- inventory-backend/app/api/v1/inbound/base.py | 48 +++++++- inventory-backend/app/models/base.py | 5 + inventory-backend/app/models/inbound/buy.py | 2 + .../app/services/inbound/base_service.py | 4 +- .../app/services/inbound/buy_service.py | 47 +++++++- inventory-web/src/api/material_base.ts | 9 ++ inventory-web/src/views/material/list.vue | 109 +++++++++++++++++- inventory-web/src/views/stock/inbound/buy.vue | 63 ++++++++-- 8 files changed, 273 insertions(+), 14 deletions(-) diff --git a/inventory-backend/app/api/v1/inbound/base.py b/inventory-backend/app/api/v1/inbound/base.py index 259318a..aefad99 100644 --- a/inventory-backend/app/api/v1/inbound/base.py +++ b/inventory-backend/app/api/v1/inbound/base.py @@ -1,6 +1,6 @@ # 文件路径: app/api/v1/inbound/base.py -from flask import Blueprint, request, jsonify, send_file, g +from flask import Blueprint, request, jsonify, send_file, g, current_app from app.extensions import db from app.services.inbound.base_service import MaterialBaseService from app.utils.decorators import login_required, permission_required, audit_log @@ -410,3 +410,49 @@ def batch_set_warning(): db.session.rollback() current_app.logger.error(f"批量设置预警失败: {str(e)}") return jsonify({"code": 500, "msg": f"批量设置预警失败: {str(e)}"}), 500 + + +# ============================================================================== +# 2.6 批量设置强制质检 API (POST /api/v1/inbound/base/batch-inspection) +# ============================================================================== +@inbound_base_bp.route('/batch-inspection', methods=['POST']) +@permission_required('material_list:operation') +def batch_set_inspection(): + """ + 批量设置物料强制质检标记 + 请求体格式: { + "ids": [1, 2, 3], + "isInspectionRequired": true + } + """ + try: + data = request.get_json() + if not data: + return jsonify({"code": 400, "msg": "No data provided"}), 400 + + ids = data.get('ids', []) + is_inspection_required = bool(data.get('isInspectionRequired', False)) + + if not ids: + return jsonify({"code": 400, "msg": "请选择要设置的物料"}), 400 + + updated_count = 0 + for base_id in ids: + material = MaterialBase.query.get(base_id) + if material: + material.is_inspection_required = is_inspection_required + updated_count += 1 + + db.session.commit() + return jsonify({ + "code": 200, + "msg": f"批量设置成功,已更新 {updated_count} 条记录", + "data": { + "updated": updated_count + } + }) + + except Exception as e: + db.session.rollback() + current_app.logger.error(f"批量设置强制质检失败: {str(e)}") + return jsonify({"code": 500, "msg": f"批量设置强制质检失败: {str(e)}"}), 500 diff --git a/inventory-backend/app/models/base.py b/inventory-backend/app/models/base.py index 9e3ee5d..45b6163 100644 --- a/inventory-backend/app/models/base.py +++ b/inventory-backend/app/models/base.py @@ -31,6 +31,9 @@ class MaterialBase(db.Model): # 启用状态 is_enabled = db.Column(db.Boolean, default=True, comment='是否启用') + # 强制质检标记(采购入库时必须上传检测报告) + is_inspection_required = db.Column(db.Boolean, default=False, comment='是否强制要求质检') + # ============================================================ # 关联关系区域 # ============================================================ @@ -81,6 +84,8 @@ class MaterialBase(db.Model): 'generalImage': parse_list(self.product_image), # 【核心修改】:直接返回布尔值,不再转成 1 或 0 'isEnabled': bool(self.is_enabled), + # 强制质检标记 + 'isInspectionRequired': bool(self.is_inspection_required), } diff --git a/inventory-backend/app/models/inbound/buy.py b/inventory-backend/app/models/inbound/buy.py index 1de1ee6..866a447 100644 --- a/inventory-backend/app/models/inbound/buy.py +++ b/inventory-backend/app/models/inbound/buy.py @@ -81,6 +81,8 @@ class StockBuy(db.Model): 'category': self.base.category if self.base else '', 'unit': self.base.unit if self.base else '', 'material_type': self.base.material_type if self.base else '', + # 强制质检标记 + 'isInspectionRequired': bool(self.base.is_inspection_required) if self.base else False, 'sku': self.sku, 'inbound_date': self.in_date.strftime('%Y-%m-%d') if self.in_date else '', diff --git a/inventory-backend/app/services/inbound/base_service.py b/inventory-backend/app/services/inbound/base_service.py index 66dd570..de220e8 100644 --- a/inventory-backend/app/services/inbound/base_service.py +++ b/inventory-backend/app/services/inbound/base_service.py @@ -75,7 +75,9 @@ class MaterialBaseService: 'category': item.category, 'unit': item.unit, 'type': item.material_type, - 'status': '启用' + 'status': '启用', + # 强制质检标记 + 'isInspectionRequired': bool(item.is_inspection_required) }) return results except Exception as e: diff --git a/inventory-backend/app/services/inbound/buy_service.py b/inventory-backend/app/services/inbound/buy_service.py index 365ed14..027cdbc 100644 --- a/inventory-backend/app/services/inbound/buy_service.py +++ b/inventory-backend/app/services/inbound/buy_service.py @@ -94,6 +94,22 @@ class BuyInboundService: if not material: raise ValueError("所选物料不存在") if not material.is_enabled: raise ValueError(f"物料【{material.name}】已停用") + # ============================================================ + # 强制质检校验:如果物料标记为强制质检,则必须提供到检状态和检测报告 + # ============================================================ + if material.is_inspection_required: + inspection_status = data.get('inspection_status') + if not inspection_status: + raise ValueError(f"物料【{material.name}】为强管控物料,必须选择到检状态") + + # 检查检测报告:文件列表或外部链接至少有一个 + # 前端会将外部链接添加到 inspection_report 数组中一起提交 + inspection_report_list = data.get('inspection_report', []) + has_report_file = inspection_report_list and len(inspection_report_list) > 0 + + if not has_report_file: + raise ValueError(f"物料【{material.name}】为强管控物料,必须提供检测报告文件或外部链接") + BuyInboundService._check_unique( base_id=base_id, serial_number=data.get('serial_number'), @@ -171,7 +187,36 @@ class BuyInboundService: try: stock = StockBuy.query.get(stock_id) if not stock: raise ValueError("记录不存在") - BuyInboundService._check_unique(base_id=data.get('base_id', stock.base_id), + + # 获取物料信息用于校验 + base_id = data.get('base_id', stock.base_id) + material = MaterialBase.query.get(base_id) + + # ============================================================ + # 强制质检校验:如果物料标记为强制质检,则必须提供到检状态和检测报告 + # ============================================================ + if material and material.is_inspection_required: + inspection_status = data.get('inspection_status', stock.inspection_status) + if not inspection_status: + raise ValueError(f"物料【{material.name}】为强管控物料,必须选择到检状态") + + # 检查检测报告:文件列表至少有一个 + inspection_report_list = data.get('inspection_report') + if inspection_report_list is None: + # 如果没有传入,使用现有的 + import json as json_module + try: + existing_reports = json_module.loads(stock.inspection_report) if stock.inspection_report else [] + except: + existing_reports = [] + has_report_file = existing_reports and len(existing_reports) > 0 + else: + has_report_file = inspection_report_list and len(inspection_report_list) > 0 + + if not has_report_file: + raise ValueError(f"物料【{material.name}】为强管控物料,必须提供检测报告文件或外部链接") + + BuyInboundService._check_unique(base_id=base_id, serial_number=data.get('serial_number', stock.serial_number), batch_number=data.get('batch_number', stock.batch_number), exclude_id=stock_id) diff --git a/inventory-web/src/api/material_base.ts b/inventory-web/src/api/material_base.ts index 73165c0..c690f25 100644 --- a/inventory-web/src/api/material_base.ts +++ b/inventory-web/src/api/material_base.ts @@ -60,4 +60,13 @@ export function batchSetWarning(data: any[]) { method: 'post', data }) +} + +// 6. 批量设置强制质检 +export function batchSetInspection(data: { ids: number[], isInspectionRequired: boolean }) { + return request({ + url: '/inbound/base/batch-inspection', + method: 'post', + data + }) } \ No newline at end of file diff --git a/inventory-web/src/views/material/list.vue b/inventory-web/src/views/material/list.vue index 04b2459..2e53196 100644 --- a/inventory-web/src/views/material/list.vue +++ b/inventory-web/src/views/material/list.vue @@ -146,6 +146,17 @@ + + + 批量质检设置 + + 新增 @@ -295,6 +306,13 @@ /> + + + + + + + + + + + +
+ 开启后,这些物料在采购入库时必须上传检测报告或外部链接 +
+
+ +