| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- """
- 费控制度成员联动同步工具
- 部门停用/员工解约时自动移除相关制度中的成员引用。
- 员工调部门/部门新增员工时自动创建本地额度记录。
- """
- from app.api.v1.module_system.auth.schema import AuthSchema
- from app.core.alipay import AlipayClient
- from app.core.logger import log
- from app.plugin.module_payment.expense.institution.crud import InstitutionCRUD
- async def _sync_employee_quota(
- auth: AuthSchema, enterprise_id: str, employee_id: str, department_ids: list[str], is_add: bool
- ) -> None:
- """根据员工所属部门,同步本地额度记录
- 扫描所有按部门模式的制度,如果该制度引用了员工所属部门,
- 则为员工创建(或删除)本地 pay_expense_quota 记录。
- """
- if not employee_id:
- 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, delete as sa_delete, select
- try:
- crud = InstitutionCRUD(auth)
- institutions = await crud.list(
- search={
- "enterprise_id": enterprise_id,
- "status__ne": "INSTITUTION_DELETE",
- "applicable_scope": "department",
- },
- order_by=[{"id": "desc"}],
- )
- if not institutions:
- return
- for inst in institutions:
- inst_id = inst.institution_id
- scope_owner_ids_str = getattr(inst, "scope_owner_id_list", None) or getattr(inst, "department_id", None)
- if not inst_id:
- continue
- # 判断该制度的部门是否匹配员工部门
- matched = False
- if scope_owner_ids_str:
- import json
- try:
- scope_ids = json.loads(scope_owner_ids_str) if isinstance(scope_owner_ids_str, str) else scope_owner_ids_str
- except (json.JSONDecodeError, TypeError):
- scope_ids = [str(scope_owner_ids_str)] if scope_owner_ids_str else []
- for dept_id in department_ids:
- if dept_id in scope_ids:
- matched = True
- break
- else:
- continue
- if not matched:
- continue
- tenant_id = auth.user.tenant_id if auth.user else 1
- if is_add:
- # 员工加入部门 → 创建额度记录
- 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"scope_{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)
- log.info(
- f"部门联动 - 新增员工额度: employee_id={employee_id}, "
- f"institution_id={inst_id}"
- )
- else:
- # 员工离开部门 → 删除额度记录
- del_stmt = sa_delete(QuotaModel).where(
- QuotaModel.employee_id == employee_id,
- QuotaModel.institution_id == inst_id,
- )
- await auth.db.execute(del_stmt)
- log.info(
- f"部门联动 - 删除员工额度: employee_id={employee_id}, "
- f"institution_id={inst_id}"
- )
- await auth.db.flush()
- except Exception as e:
- log.error(f"部门联动同步额度失败(不影响主体操作): {e}")
- async def sync_employee_add_to_department_institutions(
- auth: AuthSchema,
- enterprise_id: str,
- employee_id: str,
- department_ids: list[str],
- ) -> None:
- """员工加入部门时,为引用该部门的制度创建本地额度记录"""
- await _sync_employee_quota(auth, enterprise_id, employee_id, department_ids, is_add=True)
- async def sync_employee_remove_from_department_institutions(
- auth: AuthSchema,
- enterprise_id: str,
- employee_id: str,
- department_ids: list[str],
- ) -> None:
- """员工离开部门时,从引用该部门的制度中删除本地额度记录"""
- await _sync_employee_quota(auth, enterprise_id, employee_id, department_ids, is_add=False)
- async def remove_department_from_institution_scopes(
- auth: AuthSchema,
- enterprise_id: str,
- department_id: str,
- ) -> None:
- """
- 当部门被停用时,扫描所有引用该部门的制度,移除该部门
- 此方法被 department/service.py 的停用方法调用
- """
- try:
- crud = InstitutionCRUD(auth)
- institutions = await crud.list(
- search={"enterprise_id": enterprise_id, "status__ne": "INSTITUTION_DELETE"},
- order_by=[{"id": "desc"}],
- )
- if not institutions:
- return
- for inst in institutions:
- inst_id = inst.institution_id
- if not inst_id:
- continue
- from .service import InstitutionScopeService
- await InstitutionScopeService.scope_modify_service(
- auth=auth,
- institution_id=inst_id,
- data={
- "enterprise_id": enterprise_id,
- "adapter_type": "EMPLOYEE_DEPARTMENT",
- "delete_owner_id_list": [department_id],
- },
- )
- log.info(f"已从制度 {inst_id} 中移除停用部门 {department_id}")
- except Exception as e:
- log.error(f"移除部门失败(不影响主体操作): {e}")
- async def remove_employee_from_institution_scopes(
- auth: AuthSchema,
- enterprise_id: str,
- employee_id: str,
- ) -> None:
- """
- 当员工被解约时,扫描所有按员工模式引用该员工的制度,移除该员工
- 此方法被 employee/service.py 的删除方法调用
- """
- try:
- crud = InstitutionCRUD(auth)
- institutions = await crud.list(
- search={"enterprise_id": enterprise_id, "status__ne": "INSTITUTION_DELETE"},
- order_by=[{"id": "desc"}],
- )
- if not institutions:
- return
- for inst in institutions:
- inst_id = inst.institution_id
- if not inst_id:
- continue
- from .service import InstitutionScopeService
- 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.error(f"移除员工失败(不影响主体操作): {e}")
|