feat: add on_leave status to reporting progress + week_leaves stat

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-12 13:09:05 +08:00
parent b6105295bb
commit 56adc625c0
+31 -3
View File
@@ -1,6 +1,6 @@
from datetime import date, timedelta from datetime import date, timedelta
from uuid import UUID from uuid import UUID
from sqlalchemy import select, func from sqlalchemy import select, func, and_
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import aliased from sqlalchemy.orm import aliased
from app.models.customer import Customer 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.mini_business import MiniBusiness
from app.models.key_visit import KeyVisit from app.models.key_visit import KeyVisit
from app.models.daily_note import DailyNote from app.models.daily_note import DailyNote
from app.models.leave import Leave
from app.utils.timezone import today_cst 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)) select(func.count(KeyVisit.id))
)).scalar() or 0 )).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 { return {
"week_visits": visits_count, "week_visits": visits_count,
"work_plans": plans_count, "work_plans": plans_count,
"mini_business": mini_biz_count, "mini_business": mini_biz_count,
"key_visits": key_visit_count, "key_visits": key_visit_count,
"week_leaves": leaves_count,
"week_start": str(monday), "week_start": str(monday),
"week_end": str(sunday), "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()} 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 = [] progress = []
for m in reporters: for m in reporters:
count = visit_map.get(str(m.id), 0) count = visit_map.get(str(m.id), 0)
# Calculate expected working days (Mon-Fri) # Calculate expected working days (Mon-Fri)
days_passed = min((date.today() - monday).days + 1, 5) days_passed = min((date.today() - monday).days + 1, 5)
expected = days_passed # At least 1 visit per working day expected = days_passed # At least 1 visit per working day
mid = str(m.id)
leave = on_leave_today.get(mid)
progress.append({ progress.append({
"manager_id": str(m.id), "manager_id": mid,
"manager_name": m.name, "manager_name": m.name,
"department": m.department, "department": m.department,
"visit_count": count, "visit_count": count,
"expected": expected, "expected": expected,
"completed": count >= expected, "completed": count >= expected,
"has_reported_today": False, # Will be set below "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 # Check today's reporting — visits OR daily notes
today = today_cst()
today_visits = await db.execute( today_visits = await db.execute(
select(Visit.manager_id).where(Visit.visit_date == today) select(Visit.manager_id).where(Visit.visit_date == today)
) )