Files
maskanx_crm_backend/app/api/activities.py
T

144 lines
4.3 KiB
Python
Raw Normal View History

2026-08-01 10:28:41 +05:30
from datetime import UTC, datetime
from fastapi import APIRouter, Query, Response, status
from sqlalchemy import func, select
from app.core.security import Admin, CurrentUser, Database, Writer
from app.models import Activity, Contact, Lead, Organization, User
from app.schemas import ActivityCreate, ActivityOut, ActivityUpdate, Page
from app.services import (
activity_load_options,
activity_to_out,
add_audit,
apply_updates,
model_or_404,
verify_optional_reference,
)
router = APIRouter(tags=["Activities"])
def _validate_references(
db: Database,
tenant_id: str,
values: dict[str, object],
) -> None:
verify_optional_reference(db, User, values.get("owner_id"), tenant_id)
verify_optional_reference(db, Contact, values.get("contact_id"), tenant_id)
verify_optional_reference(db, Organization, values.get("organization_id"), tenant_id)
verify_optional_reference(db, Lead, values.get("lead_id"), tenant_id)
@router.get("/activities", response_model=Page[ActivityOut])
def list_activities(
user: CurrentUser,
db: Database,
is_done: bool | None = None,
lead_id: str | None = None,
contact_id: str | None = None,
organization_id: str | None = None,
page: int = Query(default=1, ge=1),
page_size: int = Query(default=25, ge=1, le=100),
) -> Page[ActivityOut]:
filters = [Activity.tenant_id == user.tenant_id]
if is_done is not None:
filters.append(Activity.is_done == is_done)
if lead_id:
filters.append(Activity.lead_id == lead_id)
if contact_id:
filters.append(Activity.contact_id == contact_id)
if organization_id:
filters.append(Activity.organization_id == organization_id)
total = db.scalar(select(func.count(Activity.id)).where(*filters)) or 0
records = db.scalars(
select(Activity)
.where(*filters)
.options(*activity_load_options())
.order_by(Activity.is_done, Activity.due_at, Activity.created_at.desc())
.offset((page - 1) * page_size)
.limit(page_size),
).all()
return Page(
items=[activity_to_out(item) for item in records],
total=total,
page=page,
page_size=page_size,
)
@router.post(
"/activities",
response_model=ActivityOut,
status_code=status.HTTP_201_CREATED,
)
def create_activity(
payload: ActivityCreate,
user: Writer,
db: Database,
) -> ActivityOut:
values = payload.model_dump()
_validate_references(db, user.tenant_id, values)
if values["is_done"]:
values["completed_at"] = datetime.now(UTC)
activity = Activity(tenant_id=user.tenant_id, **values)
db.add(activity)
db.flush()
add_audit(
db,
actor=user,
tenant_id=user.tenant_id,
action="activity.created",
entity_type="activity",
entity_id=activity.id,
)
db.commit()
activity = db.scalar(
select(Activity)
.where(Activity.id == activity.id)
.options(*activity_load_options()),
)
return activity_to_out(activity)
@router.patch("/activities/{activity_id}", response_model=ActivityOut)
def update_activity(
activity_id: str,
payload: ActivityUpdate,
user: Writer,
db: Database,
) -> ActivityOut:
activity = model_or_404(db, Activity, activity_id, user.tenant_id)
values = payload.model_dump(exclude_unset=True)
_validate_references(db, user.tenant_id, values)
if "is_done" in values:
values["completed_at"] = datetime.now(UTC) if values["is_done"] else None
apply_updates(activity, values)
add_audit(
db,
actor=user,
tenant_id=user.tenant_id,
action="activity.updated",
entity_type="activity",
entity_id=activity.id,
payload={"fields": sorted(values)},
)
db.commit()
activity = db.scalar(
select(Activity)
.where(Activity.id == activity.id)
.options(*activity_load_options()),
)
return activity_to_out(activity)
@router.delete("/activities/{activity_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_activity(
activity_id: str,
user: Admin,
db: Database,
) -> Response:
activity = model_or_404(db, Activity, activity_id, user.tenant_id)
db.delete(activity)
db.commit()
return Response(status_code=status.HTTP_204_NO_CONTENT)