From 56adc625c04905b68471cbaa99105475915700cd Mon Sep 17 00:00:00 2001 From: v6ole Date: Sun, 12 Jul 2026 13:09:05 +0800 Subject: [PATCH] feat: add on_leave status to reporting progress + week_leaves stat Co-Authored-By: Claude --- backend/app/services/dashboard.py | 34 ++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/backend/app/services/dashboard.py b/backend/app/services/dashboard.py index e57b80c..e072e5a 100644 --- a/backend/app/services/dashboard.py +++ b/backend/app/services/dashboard.py @@ -1,6 +1,6 @@ from datetime import date, timedelta from uuid import UUID -from sqlalchemy import select, func +from sqlalchemy import select, func, and_ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import aliased from app.models.customer import Customer @@ -10,6 +10,7 @@ from app.models.work_plan import WorkPlan from app.models.mini_business import MiniBusiness from app.models.key_visit import KeyVisit from app.models.daily_note import DailyNote +from app.models.leave import Leave from app.utils.timezone import today_cst @@ -41,11 +42,19 @@ async def get_dashboard_stats(db: AsyncSession, reference_date: date | None = No select(func.count(KeyVisit.id)) )).scalar() or 0 + # Week leave count — distinct managers on leave this week + leaves_count = (await db.execute( + select(func.count(func.distinct(Leave.manager_id))).where( + and_(Leave.start_date <= sunday, Leave.end_date >= monday) + ) + )).scalar() or 0 + return { "week_visits": visits_count, "work_plans": plans_count, "mini_business": mini_biz_count, "key_visits": key_visit_count, + "week_leaves": leaves_count, "week_start": str(monday), "week_end": str(sunday), } @@ -69,24 +78,43 @@ async def get_reporting_progress(db: AsyncSession, reference_date: date | None = ) visit_map = {str(uid): cnt for uid, cnt in visits_result.all()} + # Query leave records for today — build a set of managers on leave + today = today_cst() + leaves_result = await db.execute( + select(Leave).where(and_( + Leave.start_date <= today, + Leave.end_date >= today, + )) + ) + on_leave_today = {str(r.manager_id): r for r in leaves_result.scalars().all()} + progress = [] for m in reporters: count = visit_map.get(str(m.id), 0) # Calculate expected working days (Mon-Fri) days_passed = min((date.today() - monday).days + 1, 5) expected = days_passed # At least 1 visit per working day + + mid = str(m.id) + leave = on_leave_today.get(mid) + progress.append({ - "manager_id": str(m.id), + "manager_id": mid, "manager_name": m.name, "department": m.department, "visit_count": count, "expected": expected, "completed": count >= expected, "has_reported_today": False, # Will be set below + "on_leave": leave is not None, + "leave_info": { + "leave_type": leave.leave_type, + "start_date": str(leave.start_date), + "end_date": str(leave.end_date), + } if leave else None, }) # Check today's reporting — visits OR daily notes - today = today_cst() today_visits = await db.execute( select(Visit.manager_id).where(Visit.visit_date == today) )