fix(sync): match EHR users by AD account regardless of status
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
b16764b633
commit
6df400f31d
|
|
@ -418,16 +418,11 @@ def _root_org_name(org: dict[str, Any], org_by_oid: dict[str, dict[str, Any]]) -
|
||||||
return last_name
|
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 {}
|
rec = item.get("recordInfo") or {}
|
||||||
emp = item.get("employeeInfo") or {}
|
emp = item.get("employeeInfo") or {}
|
||||||
if not isinstance(rec, dict) or not isinstance(emp, dict):
|
if not isinstance(rec, dict) or not isinstance(emp, dict):
|
||||||
return False
|
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"):
|
for key in ("isDeleted", "IsDeleted", "deleted", "disabled", "Disabled"):
|
||||||
raw = emp.get(key, rec.get(key))
|
raw = emp.get(key, rec.get(key))
|
||||||
if _to_bool_or_none(raw) is True:
|
if _to_bool_or_none(raw) is True:
|
||||||
|
|
@ -500,14 +495,6 @@ class SyncEhrToAdUserJob(BaseJob):
|
||||||
default_company = str(params.get("default_company") or "").strip()
|
default_company = str(params.get("default_company") or "").strip()
|
||||||
target_sam_accounts = _parse_target_sam_accounts(params)
|
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 = params.get("location_mappings")
|
||||||
location_mappings = location_mappings if isinstance(location_mappings, dict) else None
|
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]] = {}
|
users_by_sam: dict[str, dict[str, Any]] = {}
|
||||||
user_id_to_sam: dict[int, str] = {}
|
user_id_to_sam: dict[int, str] = {}
|
||||||
for item in emp_rows:
|
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
|
continue
|
||||||
sam = _field_value(item, domain_account_key)
|
sam = _field_value(item, domain_account_key)
|
||||||
if not sam:
|
if not sam:
|
||||||
|
|
@ -571,7 +558,7 @@ class SyncEhrToAdUserJob(BaseJob):
|
||||||
break
|
break
|
||||||
|
|
||||||
logger.info(
|
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(emp_rows),
|
||||||
len(users_by_sam),
|
len(users_by_sam),
|
||||||
len(org_rows),
|
len(org_rows),
|
||||||
|
|
@ -760,7 +747,7 @@ class SyncEhrToAdUserJob(BaseJob):
|
||||||
"filtered_by_target_sam": bool(target_sam_accounts),
|
"filtered_by_target_sam": bool(target_sam_accounts),
|
||||||
"target_sam_accounts": len(target_sam_accounts),
|
"target_sam_accounts": len(target_sam_accounts),
|
||||||
"ehr_employee_rows": len(emp_rows),
|
"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,
|
"processed": processed,
|
||||||
"updated": updated,
|
"updated": updated,
|
||||||
"skipped_unchanged": skipped_unchanged,
|
"skipped_unchanged": skipped_unchanged,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
Loading…
Reference in New Issue