fix(dashboard): include abnormal-override records in component counts

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).
This commit is contained in:
IT狗
2026-07-22 09:55:18 +08:00
parent 4a01b53541
commit a16e243da1
+25 -3
View File
@@ -1085,6 +1085,25 @@ async def dashboard_summary(
if r.employee_name: if r.employee_name:
staff_set.add(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 { return {
# Backend / dashboard canonical fields # Backend / dashboard canonical fields
"total": len(rows), "total": len(rows),
@@ -1094,11 +1113,14 @@ async def dashboard_summary(
"by_department": by_department, "by_department": by_department,
"trend": [], "trend": [],
# Frontend-expected fields (alias for legacy UI) # 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), "total_records": len(rows),
"normal_count": by_status.get("normal", 0), "normal_count": by_status.get("normal", 0),
"late_count": sum(v for k, v in by_status.items() if "late" in k), "late_count": late_count,
"early_count": sum(v for k, v in by_status.items() if "early" in k), "early_count": early_count,
"ot_count": sum(v for k, v in by_status.items() if "ot" in k), "ot_count": ot_count,
"missing_count": by_status.get("missing", 0), "missing_count": by_status.get("missing", 0),
"abnormal_count": by_status.get("abnormal", 0), "abnormal_count": by_status.get("abnormal", 0),
"staff_count": len(staff_set), "staff_count": len(staff_set),