Просмотр исходного кода

fix(expense): 员工签约纳入全员制度+离职清理所有模式额度

- 新增 sync_employee_to_all_institution:激活时创建全员制度的额度记录
- remove_employee_from_institution_scopes 区分三种scope
  - employee:调支付宝scope.modify移除
  - department/all:Alipay自动处理,仅清理本地额度
  - 无论什么模式都清理本地pay_expense_quota
alphah 2 недель назад
Родитель
Сommit
4c0640ed7b

+ 73 - 11
backend/app/plugin/module_payment/expense/institution/scope_sync.py

@@ -156,16 +156,62 @@ async def remove_department_from_institution_scopes(
         log.error(f"移除部门失败(不影响主体操作): {e}")
 
 
+async def sync_employee_to_all_institution(
+    auth: AuthSchema, enterprise_id: str, employee_id: str
+) -> None:
+    """员工激活时,为全体员工(applicable_scope=all)的制度创建本地额度记录"""
+    if not employee_id or not enterprise_id:
+        return
+    try:
+        crud = InstitutionCRUD(auth)
+        institutions = await crud.list(
+            search={"enterprise_id": enterprise_id, "status__ne": "INSTITUTION_DELETE", "applicable_scope": "all"},
+            order_by=[{"id": "desc"}],
+        )
+        if not institutions:
+            return
+        from app.plugin.module_payment.expense.quota.model import QuotaModel
+        from app.plugin.module_payment.expense.quota.enums import QuotaStatusEnum
+        from sqlalchemy import insert, select
+        tenant_id = auth.user.tenant_id if auth.user else 1
+        for inst in institutions:
+            inst_id = inst.institution_id
+            if not inst_id:
+                continue
+            check = select(QuotaModel).where(QuotaModel.employee_id == employee_id, QuotaModel.institution_id == inst_id)
+            existing = await auth.db.execute(check)
+            if existing.scalar_one_or_none():
+                continue
+            stmt = insert(QuotaModel).values(
+                employee_id=employee_id, institution_id=inst_id,
+                out_biz_no=f"all_{inst_id}_{employee_id}",
+                total_amount=0, available_amount=0,
+                status=QuotaStatusEnum.QUOTA_PENDING.value,
+                enterprise_id=enterprise_id, tenant_id=tenant_id,
+            )
+            await auth.db.execute(stmt)
+        await auth.db.flush()
+    except Exception as e:
+        log.error(f"全员制度联动失败: {e}")
+
+
 async def remove_employee_from_institution_scopes(
     auth: AuthSchema,
     enterprise_id: str,
     employee_id: str,
 ) -> None:
     """
-    当员工被解约时,扫描所有按员工模式引用该员工的制度,移除该员工
+    当员工被解约时,从所有费控制度中移除该员工
+
+    处理三种 scope 模式:
+    - employee(指定员工):调支付宝 scope.modify 移除
+    - department/all(按部门/全员):Alipay 自动处理,只需清理本地额度
 
     此方法被 employee/service.py 的删除方法调用
     """
+    from app.plugin.module_payment.expense.quota.model import QuotaModel
+    from sqlalchemy import delete as sa_delete
+
     try:
         crud = InstitutionCRUD(auth)
         institutions = await crud.list(
@@ -177,20 +223,36 @@ async def remove_employee_from_institution_scopes(
 
         for inst in institutions:
             inst_id = inst.institution_id
+            scope = getattr(inst, "applicable_scope", "")
             if not inst_id:
                 continue
 
-            from .service import InstitutionScopeService
+            # 员工模式 → 调支付宝移除
+            if scope == "employee":
+                from .service import InstitutionScopeService
+                try:
+                    await InstitutionScopeService.scope_modify_service(
+                        auth=auth, institution_id=inst_id,
+                        data={
+                            "enterprise_id": enterprise_id,
+                            "adapter_type": "EMPLOYEE_SELECT",
+                            "delete_owner_id_list": [employee_id],
+                        },
+                    )
+                    log.info(f"已从制度 {inst_id} 中移除解约员工 {employee_id}")
+                except Exception as e:
+                    log.warning(f"支付宝移除员工失败(继续清理本地): {e}")
 
-            await InstitutionScopeService.scope_modify_service(
-                auth=auth,
-                institution_id=inst_id,
-                data={
-                    "enterprise_id": enterprise_id,
-                    "adapter_type": "EMPLOYEE_SELECT",
-                    "delete_owner_id_list": [employee_id],
-                },
+            # department/all → Alipay 自动处理,无需调 scope.modify
+
+            # 清理本地 pay_expense_quota
+            del_stmt = sa_delete(QuotaModel).where(
+                QuotaModel.employee_id == employee_id,
+                QuotaModel.institution_id == inst_id,
             )
-            log.info(f"已从制度 {inst_id} 中移除解约员工 {employee_id}")
+            await auth.db.execute(del_stmt)
+            log.info(f"已清理本地额度: employee_id={employee_id}, institution_id={inst_id}")
+
+        await auth.db.flush()
     except Exception as e:
         log.error(f"移除员工失败(不影响主体操作): {e}")

+ 8 - 1
backend/app/plugin/module_payment/notification/handlers/employee_handler.py

@@ -91,7 +91,14 @@ class EmployeeHandler(BaseHandler[dict]):
         """处理员工激活"""
         log.info(f"员工激活: employee_id={data.employee_id}")
         await self.update_employee(data, auth)
-        # 联动:员工激活时,同步到部门制度额度
+        # 联动1:全体员工级别的制度
+        if data.enterprise_id:
+            from app.plugin.module_payment.expense.institution.scope_sync import sync_employee_to_all_institution
+            await sync_employee_to_all_institution(
+                auth=auth, enterprise_id=data.enterprise_id,
+                employee_id=data.employee_id,
+            )
+        # 联动2:部门级别的制度
         dept_ids = await self._get_department_ids(data)
         if dept_ids:
             from app.plugin.module_payment.expense.institution.scope_sync import sync_employee_add_to_department_institutions