fix(sync): match EHR users by AD account regardless of status

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Marsway 2026-07-15 23:11:38 +08:00
parent b16764b633
commit 6df400f31d
2 changed files with 63 additions and 17 deletions

View File

@ -418,16 +418,11 @@ def _root_org_name(org: dict[str, Any], org_by_oid: dict[str, dict[str, Any]]) -
return last_name
def _is_current_employee(item: dict[str, Any], current_status_values: set[str]) -> bool:
def _is_syncable_employee_record(item: dict[str, Any]) -> bool:
rec = item.get("recordInfo") or {}
emp = item.get("employeeInfo") or {}
if not isinstance(rec, dict) or not isinstance(emp, dict):
return False
if str(rec.get("lastWorkDate") or "").strip():
return False
status = _field_translate_or_value(item, "EmployeeStatus")
if current_status_values and status and status not in current_status_values:
return False
for key in ("isDeleted", "IsDeleted", "deleted", "disabled", "Disabled"):
raw = emp.get(key, rec.get(key))
if _to_bool_or_none(raw) is True:
@ -500,14 +495,6 @@ class SyncEhrToAdUserJob(BaseJob):
default_company = str(params.get("default_company") or "").strip()
target_sam_accounts = _parse_target_sam_accounts(params)
current_status_values_param = params.get("current_status_values")
if isinstance(current_status_values_param, list):
current_status_values = {str(x).strip() for x in current_status_values_param if str(x).strip()}
elif str(current_status_values_param or "").strip():
current_status_values = {x.strip() for x in str(current_status_values_param).split(",") if x.strip()}
else:
current_status_values = set()
location_mappings = params.get("location_mappings")
location_mappings = location_mappings if isinstance(location_mappings, dict) else None
@ -554,7 +541,7 @@ class SyncEhrToAdUserJob(BaseJob):
users_by_sam: dict[str, dict[str, Any]] = {}
user_id_to_sam: dict[int, str] = {}
for item in emp_rows:
if not isinstance(item, dict) or not _is_current_employee(item, current_status_values):
if not isinstance(item, dict) or not _is_syncable_employee_record(item):
continue
sam = _field_value(item, domain_account_key)
if not sam:
@ -571,7 +558,7 @@ class SyncEhrToAdUserJob(BaseJob):
break
logger.info(
"EHR 当前用户准备完成employee_rows=%s current_with_ad_account=%s org_rows=%s target_sam_accounts=%s",
"EHR AD 用户准备完成employee_rows=%s users_with_ad_account=%s org_rows=%s target_sam_accounts=%s",
len(emp_rows),
len(users_by_sam),
len(org_rows),
@ -760,7 +747,7 @@ class SyncEhrToAdUserJob(BaseJob):
"filtered_by_target_sam": bool(target_sam_accounts),
"target_sam_accounts": len(target_sam_accounts),
"ehr_employee_rows": len(emp_rows),
"ehr_current_users_with_ad_account": len(users_by_sam),
"ehr_users_with_ad_account": len(users_by_sam),
"processed": processed,
"updated": updated,
"skipped_unchanged": skipped_unchanged,

View File

@ -0,0 +1,59 @@
import sys
import types
import importlib.util
from pathlib import Path
ROOT = Path(__file__).resolve().parents[3]
sys.path.insert(0, str(ROOT))
base_module = types.ModuleType("app.jobs.base")
class BaseJob:
pass
base_module.BaseJob = BaseJob
sys.modules["app.jobs.base"] = base_module
api_module = types.ModuleType("extensions.sync_ehr_to_ad.api")
api_module.ActiveDirectoryClient = object
api_module.SyncEhrToAdApi = object
sys.modules["extensions.sync_ehr_to_ad.api"] = api_module
spec = importlib.util.spec_from_file_location("sync_ehr_to_ad_job", ROOT / "extensions/sync_ehr_to_ad/job.py")
job_module = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(job_module)
_is_syncable_employee_record = job_module._is_syncable_employee_record
def _ehr_item(*, employee_status="离职", last_work_date="", emp_flags=None, rec_flags=None):
emp_flags = emp_flags or {}
rec_flags = rec_flags or {}
return {
"employeeInfo": {
"translateProperties": {"EmployeeStatus": employee_status},
"customProperties": {"extADAccountName_606508_511687157": "zhangsan"},
**emp_flags,
},
"recordInfo": {
"lastWorkDate": last_work_date,
**rec_flags,
},
}
def test_syncable_employee_record_includes_any_employee_status():
assert _is_syncable_employee_record(_ehr_item(employee_status="离职")) is True
def test_syncable_employee_record_includes_records_with_last_work_date():
assert _is_syncable_employee_record(_ehr_item(last_work_date="2026-07-01")) is True
def test_syncable_employee_record_excludes_deleted_or_disabled_records():
assert _is_syncable_employee_record(_ehr_item(emp_flags={"isDeleted": True})) is False
assert _is_syncable_employee_record(_ehr_item(rec_flags={"disabled": True})) is False