From a16e243da1acce72bfedb6dcbbffe4b492deb4a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IT=E7=8B=97?= Date: Wed, 22 Jul 2026 09:55:18 +0800 Subject: [PATCH] fix(dashboard): include abnormal-override records in component counts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit status_code='abnormal' records (>30 min) hide the underlying late/early/ot component from by_status. Use *_minutes>0 union to surface them so the dashboard count matches the real data. Late: 9 → 23, Early: 0 → 17, OT: 15 → 28 Note: component totals may now exceed total_records because each record can carry multiple status components (e.g. late_ot). --- backend/main.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/backend/main.py b/backend/main.py index dc5997c..3033e8d 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1085,6 +1085,25 @@ async def dashboard_summary( if r.employee_name: staff_set.add(r.employee_name) + # Component counts: union of status_code match AND minutes field + # (handles abnormal override where status_code='abnormal' hides + # the underlying late/early/ot component from by_status) + late_count = ( + sum(v for k, v in by_status.items() if "late" in k) + + sum(1 for r in rows if r.late_minutes and r.late_minutes > 0 + and (r.status_code or "").lower() == "abnormal") + ) + early_count = ( + sum(v for k, v in by_status.items() if "early" in k) + + sum(1 for r in rows if r.early_minutes and r.early_minutes > 0 + and (r.status_code or "").lower() == "abnormal") + ) + ot_count = ( + sum(v for k, v in by_status.items() if "ot" in k) + + sum(1 for r in rows if r.ot_minutes and r.ot_minutes > 0 + and (r.status_code or "").lower() == "abnormal") + ) + return { # Backend / dashboard canonical fields "total": len(rows), @@ -1094,11 +1113,14 @@ async def dashboard_summary( "by_department": by_department, "trend": [], # Frontend-expected fields (alias for legacy UI) + # Component counts: late + early + ot may exceed total because + # a single record can carry multiple status components + # (e.g. status_code='late_ot' contributes to both late and ot). "total_records": len(rows), "normal_count": by_status.get("normal", 0), - "late_count": sum(v for k, v in by_status.items() if "late" in k), - "early_count": sum(v for k, v in by_status.items() if "early" in k), - "ot_count": sum(v for k, v in by_status.items() if "ot" in k), + "late_count": late_count, + "early_count": early_count, + "ot_count": ot_count, "missing_count": by_status.get("missing", 0), "abnormal_count": by_status.get("abnormal", 0), "staff_count": len(staff_set),