From c0076a125cebb4d2843a691f5c802f74419a4b06 Mon Sep 17 00:00:00 2001 From: IT Dog Date: Sun, 19 Jul 2026 23:45:34 +0800 Subject: [PATCH] Fix: exclude accident source + filter email-format names from employees list Root cause: Accident.employee_name contains submitter EMAIL addresses (e.g. info@scmedical.hk), not the injured staff name. UNION of all three sources polluted the attendance staff dropdown with emails. Fix: - Removed Accident from /api/employees/list UNION (source=all now means attendance + leave only) - Added EMAIL_RE filter to skip any name containing @ (defensive) - Updated docstring + response includes excluded field explaining why Verified: 12 clean staff names returned (down from 16 with 4 emails mixed in) --- backend/main.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/backend/main.py b/backend/main.py index ed11b55..37c462a 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1294,32 +1294,35 @@ async def update_attendance_record( # ============ Employees List (combined source) ============ @app.get("/api/employees/list") async def list_employees( - source: Optional[str] = "all", # "all" / "attendance" / "leave" / "accident" + source: Optional[str] = "all", # "all" / "attendance" / "leave" db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): - """List distinct employee names from attendance + leave + accident records. + """List distinct employee names (real staff only). + + Sources: attendance_records + leave_records (NOT accident - that table stores + submitter emails, not staff names). + + Filter: names containing "@" (email format) are excluded automatically. Used by frontend dropdowns to show ALL staff, even those who haven't taken leave yet. - - source: - - all (default): UNION of attendance + leave + accident - - attendance: only attendance_records.employee_name - - leave: only leave_records.employee_name - - accident: only Accident.employee_name """ + import re + EMAIL_RE = re.compile(r"@") names = set() if source in ("all", "attendance"): for n in db.query(AttendanceRecord.employee_name).distinct().all(): - if n[0]: names.add(n[0]) + if n[0] and not EMAIL_RE.search(n[0]): + names.add(n[0]) if source in ("all", "leave"): for n in db.query(LeaveRecord.employee_name).distinct().all(): - if n[0]: names.add(n[0]) - if source in ("all", "accident"): - for n in db.query(Accident.employee_name).distinct().all(): - if n[0]: names.add(n[0]) + if n[0] and not EMAIL_RE.search(n[0]): + names.add(n[0]) + # NOTE: Accident.employee_name contains submitter EMAIL (e.g. info@scmedical.hk), + # NOT the injured staff name. We exclude accident from employee list to avoid + # polluting staff dropdown with email addresses. sorted_names = sorted(names, key=lambda x: x.lower()) - return {"employees": sorted_names, "count": len(sorted_names), "source": source} + return {"employees": sorted_names, "count": len(sorted_names), "source": source, "excluded": "accident (email column)"} # ============ Holidays CRUD ============# ============ Holidays CRUD ============