Files
maskanx_crm_frontend/app/api/dashboard.py
T
AFFAANhandClaude Opus 5 f6a54d6f93 Initial commit: Maskan CRM backend
Independent FastAPI backend for Maskan CRM.

Owns contacts, organizations, leads, pipelines, activities, products,
quotes, users, permissions, audit records and first-party integration
credentials, with Alembic migrations against PostgreSQL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:28:41 +05:30

112 lines
3.6 KiB
Python

from datetime import UTC, datetime
from decimal import Decimal
from fastapi import APIRouter
from sqlalchemy import func, select
from app.core.security import CurrentUser, Database
from app.models import Activity, Contact, Lead, LeadSource, Stage
from app.schemas import (
DashboardResponse,
DashboardSourceMetric,
DashboardStageMetric,
)
from app.services import activity_load_options, activity_to_out, decimal_or_zero
router = APIRouter(tags=["Dashboard"])
@router.get("/dashboard", response_model=DashboardResponse)
def get_dashboard(user: CurrentUser, db: Database) -> DashboardResponse:
tenant_filter = Lead.tenant_id == user.tenant_id
total_revenue = db.scalar(
select(func.sum(Lead.value)).where(tenant_filter, Lead.status == "won"),
)
open_pipeline_value = db.scalar(
select(func.sum(Lead.value)).where(tenant_filter, Lead.status == "open"),
)
total_leads = db.scalar(select(func.count(Lead.id)).where(tenant_filter)) or 0
open_leads = db.scalar(
select(func.count(Lead.id)).where(tenant_filter, Lead.status == "open"),
) or 0
won_leads = db.scalar(
select(func.count(Lead.id)).where(tenant_filter, Lead.status == "won"),
) or 0
lost_leads = db.scalar(
select(func.count(Lead.id)).where(tenant_filter, Lead.status == "lost"),
) or 0
total_contacts = db.scalar(
select(func.count(Contact.id)).where(Contact.tenant_id == user.tenant_id),
) or 0
overdue_activities = db.scalar(
select(func.count(Activity.id)).where(
Activity.tenant_id == user.tenant_id,
Activity.is_done.is_(False),
Activity.due_at < datetime.now(UTC),
),
) or 0
stage_rows = db.execute(
select(
Stage.id,
Stage.name,
Stage.color,
func.count(Lead.id),
func.coalesce(func.sum(Lead.value), 0),
)
.outerjoin(
Lead,
(Lead.stage_id == Stage.id) & (Lead.tenant_id == user.tenant_id),
)
.where(Stage.tenant_id == user.tenant_id)
.group_by(Stage.id, Stage.name, Stage.color, Stage.position)
.order_by(Stage.position),
).all()
source_rows = db.execute(
select(
func.coalesce(LeadSource.name, "Direct"),
func.count(Lead.id),
)
.select_from(Lead)
.outerjoin(LeadSource, Lead.source_id == LeadSource.id)
.where(Lead.tenant_id == user.tenant_id)
.group_by(LeadSource.name)
.order_by(func.count(Lead.id).desc()),
).all()
recent = db.scalars(
select(Activity)
.where(Activity.tenant_id == user.tenant_id)
.options(*activity_load_options())
.order_by(Activity.created_at.desc())
.limit(5),
).all()
return DashboardResponse(
total_revenue=decimal_or_zero(total_revenue),
open_pipeline_value=decimal_or_zero(open_pipeline_value),
total_leads=total_leads,
open_leads=open_leads,
won_leads=won_leads,
lost_leads=lost_leads,
total_contacts=total_contacts,
overdue_activities=overdue_activities,
stages=[
DashboardStageMetric(
stage_id=row[0],
stage_name=row[1],
color=row[2],
lead_count=row[3],
total_value=Decimal(row[4]),
)
for row in stage_rows
],
sources=[
DashboardSourceMetric(source=row[0], lead_count=row[1])
for row in source_rows
],
recent_activities=[activity_to_out(item) for item in recent],
)