Kaynağa Gözat

fix(expense): scope编辑差异对比+部门写入+adapter_type统一+部门回显

1. 修改制度时查询旧scope,计算add/delete差异列表
2. adapter_type统一为EMPLOYEE_DEPARTMENT(创建/修改/同步一致)
3. _create_institution_quotas兼容两种department adapter_type
4. 制度详情返回department_id(部门模式下从owner_id_list提取)+ 前端读取
alphah 2 hafta önce
ebeveyn
işleme
e37a699f4b

+ 36 - 12
backend/app/plugin/module_payment/expense/institution/controller.py

@@ -117,7 +117,7 @@ async def create_institution_controller(
     scope_data = None
     adapter_type = data.get("applicable_scope")
     if adapter_type and adapter_type not in ("NONE", "none"):
-        ADAPTER_TYPE_MAP = {"all": "EMPLOYEE_ALL", "employee": "EMPLOYEE_SELECT", "department": "DEPARTMENT_SELECT"}
+        ADAPTER_TYPE_MAP = {"all": "EMPLOYEE_ALL", "employee": "EMPLOYEE_SELECT", "department": "EMPLOYEE_DEPARTMENT"}
         mapped_adapter = ADAPTER_TYPE_MAP.get(adapter_type, adapter_type)
         scope_data = {
             "adapter_type": mapped_adapter,
@@ -295,22 +295,46 @@ async def modify_institution_controller(
     # 提取 scope 变更数据(需与基础修改分两次请求)
     applicable_scope = data.get("applicable_scope", "")
     scope_info = None
+    enterprise_id = data.get("enterprise_id", "")
     if applicable_scope and applicable_scope not in ("NONE", "none"):
-        enterprise_id = data.get("enterprise_id", "")
-        ADAPTER_MAP = {"all": "EMPLOYEE_ALL", "employee": "EMPLOYEE_SELECT", "department": "DEPARTMENT_SELECT"}
+        ADAPTER_MAP = {"all": "EMPLOYEE_ALL", "employee": "EMPLOYEE_SELECT", "department": "EMPLOYEE_DEPARTMENT"}
+        new_adapter = ADAPTER_MAP.get(applicable_scope, applicable_scope)
+
+        # 查询当前scope:计算旧→新的差异
+        old_ids = []
+        try:
+            scope_old = await InstitutionScopeService.scopepageinfo_query_service(
+                auth=auth, institution_id=institution_id, enterprise_id=enterprise_id,
+                page_num=1, page_size=500,
+            )
+            old_ids = [str(i) for i in (scope_old.get("owner_id_list") or []) if i]
+        except Exception:
+            log.warning(f"查询旧scope失败,将全量覆盖: institution_id={institution_id}")
+
+        new_ids_raw = data.get("scope_owner_id_list") or []
+        new_ids = [str(i) for i in new_ids_raw if i is not None and str(i).strip()]
+
+        # 计算差异
+        old_set, new_set = set(old_ids), set(new_ids)
+        add_ids = list(new_set - old_set)
+        delete_ids = list(old_set - new_set)
+
         scope_info = {
             "enterprise_id": enterprise_id,
-            "adapter_type": ADAPTER_MAP.get(applicable_scope, applicable_scope),
+            "adapter_type": new_adapter,
             "owner_type": "ENTERPRISE_PAY_UID",
         }
-        if applicable_scope in ("employee", "department"):
-            owner_ids = data.get("scope_owner_id_list") or []
-            valid_ids = [str(i) for i in owner_ids if i is not None and str(i).strip()]
-            if valid_ids:
-                scope_info["add_owner_id_list"] = valid_ids
-            else:
-                scope_info = None
-                log.info("适用范围为按员工/按部门但未提供员工/部门ID,跳过 scope 修改")
+        if add_ids:
+            scope_info["add_owner_id_list"] = add_ids
+        if delete_ids:
+            scope_info["delete_owner_id_list"] = delete_ids
+        log.info(
+            f"scope 差异: add={add_ids}, delete={delete_ids}, "
+            f"old_count={len(old_ids)}, new_count={len(new_ids)}"
+        )
+        if not add_ids and not delete_ids:
+            scope_info = None
+            log.info("scope 无变化,跳过")
         # 从请求中移除 scope 数据,避免与基础修改冲突
         data.pop("modify_scope_info", None)
 

+ 8 - 2
backend/app/plugin/module_payment/expense/institution/service.py

@@ -317,7 +317,7 @@ class InstitutionService:
         if adapter_type == "EMPLOYEE_SELECT":
             # 按员工选择 → 直接使用传入的员工ID
             employee_ids = [str(i) for i in add_ids if i]
-        elif adapter_type == "EMPLOYEE_DEPARTMENT":
+        elif adapter_type in ("EMPLOYEE_DEPARTMENT", "DEPARTMENT_SELECT"):
             # 按部门 → 查该部门下的所有员工
             for dept_id in add_ids:
                 dept_id_str = str(dept_id)
@@ -637,9 +637,15 @@ class InstitutionService:
         # adapter_type → applicable_scope 兜底
         adapter_type = result_dict.get("adapter_type")
         if adapter_type and not result_dict.get("applicable_scope"):
-            scope_map = {"EMPLOYEE_SELECT": "employee", "EMPLOYEE_DEPARTMENT": "department", "EMPLOYEE_ALL": "all"}
+            scope_map = {"EMPLOYEE_SELECT": "employee", "EMPLOYEE_DEPARTMENT": "department", "DEPARTMENT_SELECT": "department", "EMPLOYEE_ALL": "all"}
             result_dict["applicable_scope"] = scope_map.get(adapter_type, result_dict.get("applicable_scope", "none"))
 
+        # 部门模式下,从 owner_id_list 提取 department_id
+        if result_dict.get("applicable_scope") == "department" and not result_dict.get("department_id"):
+            owner_ids = result_dict.get("owner_id_list") or result_dict.get("scope_owner_id_list")
+            if owner_ids and isinstance(owner_ids, list) and len(owner_ids) > 0:
+                result_dict["department_id"] = str(owner_ids[0])
+
         # 补充本地规则和额度
         from app.plugin.module_payment.expense.rule.model import ExpenseRuleModel
         from app.plugin.module_payment.expense.quota.model import QuotaModel

+ 1 - 0
frontend/src/api/module_payment/institution.ts

@@ -150,6 +150,7 @@ export interface InstitutionDetail {
   amount?: number;
   employee_ids?: string[];
   scope_owner_id_list?: string[];
+  department_id?: string;
   created_time: string;
   updated_time: string;
 }

+ 1 - 0
frontend/src/views/module_payment/institution/components/InstitutionForm.vue

@@ -344,6 +344,7 @@ watch(
         formData.single_limit = data.single_limit;
         formData.effective_time_type = data.effective_time_type || "unlimited";
         formData.employee_ids = data.employee_ids || [];
+        formData.department_id = data.department_id;
       }
     }
   },