from datetime import date, timedelta from uuid import UUID from sqlalchemy import select, func from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import aliased from app.models.customer import Customer from app.models.user import User from app.models.visit import Visit 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.utils.timezone import today_cst def get_week_range(reference_date: date | None = None): today = reference_date or date.today() monday = today - timedelta(days=today.weekday()) sunday = monday + timedelta(days=6) return monday, sunday async def get_dashboard_stats(db: AsyncSession, reference_date: date | None = None) -> dict: """Get dashboard statistics for a given week (defaults to current).""" monday, sunday = get_week_range(reference_date) today = date.today() visits_count = (await db.execute( select(func.count(Visit.id)).where(Visit.visit_date >= monday, Visit.visit_date <= sunday) )).scalar() or 0 plans_count = (await db.execute( select(func.count(WorkPlan.id)) )).scalar() or 0 mini_biz_count = (await db.execute( select(func.count(MiniBusiness.id)) )).scalar() or 0 key_visit_count = (await db.execute( select(func.count(KeyVisit.id)) )).scalar() or 0 return { "week_visits": visits_count, "work_plans": plans_count, "mini_business": mini_biz_count, "key_visits": key_visit_count, "week_start": str(monday), "week_end": str(sunday), } async def get_reporting_progress(db: AsyncSession, reference_date: date | None = None, user_id: str = "", role: str = "") -> list[dict]: """Get per-manager reporting progress. Managers only see themselves.""" monday, sunday = get_week_range(reference_date) # Get all users who need to report (any role, filtered by require_report) managers_result = await db.execute(select(User).where(User.require_report == True)) all_reporters = managers_result.scalars().all() # Filter: managers only see themselves reporters = all_reporters if role in ("director", "leader") else [m for m in all_reporters if str(m.id) == user_id] # Get visit counts per manager this week visits_result = await db.execute( select(Visit.manager_id, func.count(Visit.id)) .where(Visit.visit_date >= monday, Visit.visit_date <= sunday) .group_by(Visit.manager_id) ) visit_map = {str(uid): cnt for uid, cnt in visits_result.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 progress.append({ "manager_id": str(m.id), "manager_name": m.name, "department": m.department, "visit_count": count, "expected": expected, "completed": count >= expected, "has_reported_today": False, # Will be set below }) # 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) ) today_notes = await db.execute( select(DailyNote.manager_id).where(DailyNote.note_date == today) ) reported_today = {str(uid) for uid, in today_visits.all()} | {str(uid) for uid, in today_notes.all()} for p in progress: p["has_reported_today"] = p["manager_id"] in reported_today return progress async def get_weekly_report( db: AsyncSession, user_id: UUID, role: str, filter_manager_id: UUID | None = None, filter_customer_id: UUID | None = None, reference_date: date | None = None, ) -> dict: """Get full weekly report data organized by module.""" monday, sunday = get_week_range(reference_date) # Base filters respecting role visibility customer_map = {} user_map = {} customers_result = await db.execute(select(Customer.id, Customer.name)) customer_map = {c.id: c.name for c in customers_result.all()} users_result = await db.execute(select(User.id, User.name)) user_map = {u.id: u.name for u in users_result.all()} def build_manager_filter(existing_filter=None): """If role is manager, only see own data. Otherwise optionally filter by manager_id.""" if role == "manager": return str(user_id) return str(filter_manager_id) if filter_manager_id else None # ── Visits ── visit_query = select(Visit).where(Visit.visit_date >= monday, Visit.visit_date <= sunday) if role == "manager": visit_query = visit_query.where(Visit.manager_id == user_id) elif filter_manager_id: visit_query = visit_query.where(Visit.manager_id == filter_manager_id) if filter_customer_id: visit_query = visit_query.where(Visit.customer_id == filter_customer_id) visit_query = visit_query.order_by(Visit.visit_date.desc()) visits_result = await db.execute(visit_query) visits = visits_result.scalars().all() visits_data = [] for v in visits: # Resolve companion names: system users → names, external → direct companion_names_resolved = [user_map.get(c, str(c)) for c in (v.companions or [])] companion_names_resolved.extend(v.companion_names or []) visits_data.append({ "id": str(v.id), "customer_id": str(v.customer_id), "customer_name": customer_map.get(v.customer_id, ""), "visit_date": str(v.visit_date), "visit_method": v.visit_method, "time_range": v.time_range, "visitor_name": v.visitor_name or "", "visitor_phone": v.visitor_phone or "", "communication_content": v.communication_content, "customer_demand": v.customer_demand, "companions": [str(c) for c in (v.companions or [])], "companion_names": v.companion_names or [], "companion_names_resolved": companion_names_resolved, "photos": v.photos or [], "manager_id": str(v.manager_id), "manager_name": user_map.get(v.manager_id, ""), "edit_log": v.edit_log or [], "created_at": str(v.created_at), }) # ── Work Plans ── wp_query = select(WorkPlan) if role == "manager": wp_query = wp_query.where(WorkPlan.manager_id == user_id) elif filter_manager_id: wp_query = wp_query.where(WorkPlan.manager_id == filter_manager_id) if filter_customer_id: wp_query = wp_query.where(WorkPlan.customer_id == filter_customer_id) wp_result = await db.execute(wp_query) work_plans_data = [] for w in wp_result.scalars(): work_plans_data.append({ "id": str(w.id), "customer_id": str(w.customer_id), "customer_name": customer_map.get(w.customer_id, ""), "plan_content": w.plan_content, "plan_date": str(w.plan_date), "manager_id": str(w.manager_id), "manager_name": user_map.get(w.manager_id, ""), "status": w.status, }) # ── Mini Business ── mb_query = select(MiniBusiness) if role == "manager": mb_query = mb_query.where(MiniBusiness.manager_id == user_id) elif filter_manager_id: mb_query = mb_query.where(MiniBusiness.manager_id == filter_manager_id) if filter_customer_id: mb_query = mb_query.where(MiniBusiness.customer_id == filter_customer_id) mb_result = await db.execute(mb_query) mini_biz_data = [] for m in mb_result.scalars(): mini_biz_data.append({ "id": str(m.id), "customer_id": str(m.customer_id), "customer_name": customer_map.get(m.customer_id, ""), "product_type": m.product_type, "amount": m.amount, "follow_up_detail": m.follow_up_detail, "status": m.status, "manager_id": str(m.manager_id), "manager_name": user_map.get(m.manager_id, ""), "expected_revenue_date": m.expected_revenue_date, }) # ── Key Visits ── kv_query = select(KeyVisit) if role == "manager": kv_query = kv_query.where(KeyVisit.manager_id == user_id) elif filter_manager_id: kv_query = kv_query.where(KeyVisit.manager_id == filter_manager_id) if filter_customer_id: kv_query = kv_query.where(KeyVisit.customer_id == filter_customer_id) kv_result = await db.execute(kv_query) key_visits_data = [] for k in kv_result.scalars(): key_visits_data.append({ "id": str(k.id), "customer_id": str(k.customer_id), "customer_name": customer_map.get(k.customer_id, ""), "urgency_level": k.urgency_level, "description": k.description, "progress_status": k.progress_status, "planned_date": k.planned_date, "planned_visitor": k.planned_visitor, "visit_target": k.visit_target, "manager_id": str(k.manager_id), "manager_name": user_map.get(k.manager_id, ""), }) # ── Daily Notes ── dn_query = select(DailyNote).where(DailyNote.note_date >= monday, DailyNote.note_date <= sunday) if role == "manager": dn_query = dn_query.where(DailyNote.manager_id == user_id) elif filter_manager_id: dn_query = dn_query.where(DailyNote.manager_id == filter_manager_id) dn_query = dn_query.order_by(DailyNote.note_date.desc()) dn_result = await db.execute(dn_query) daily_notes_data = [] for d in dn_result.scalars(): daily_notes_data.append({ "id": str(d.id), "note_date": str(d.note_date), "category": d.category, "content": d.content, "time_range": d.time_range, "manager_id": str(d.manager_id), "manager_name": user_map.get(d.manager_id, ""), }) return { "week_start": str(monday), "week_end": str(sunday), "visits": visits_data, "daily_notes": daily_notes_data, "work_plans": work_plans_data, "mini_business": mini_biz_data, "key_visits": key_visits_data, }