from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import select from sqlalchemy.orm import Session from .. import models from ..db import get_session from ..schemas import ( PaymentLineCreate, PaymentLineRead, ProjectBudgetLineCreate, ProjectBudgetLineRead, ProjectCreate, ProjectParticipantCreate, ProjectParticipantRead, ProjectPlannedItemCreate, ProjectPlannedItemRead, ProjectRead, ProjectSummary, SettlementSummary, ) from ..services.projects import calculate_project_summary from ..services.settlement import calculate_settlement router = APIRouter(tags=["projects"]) def _get_project(session: Session, project_id: str) -> models.Project: project = session.get(models.Project, project_id) if not project: raise HTTPException(status_code=404, detail="Project not found") return project @router.post("/projects", response_model=ProjectRead) def create_project( payload: ProjectCreate, session: Session = Depends(get_session) ) -> models.Project: project = models.Project(**payload.model_dump(exclude={"participant_user_ids"})) session.add(project) session.flush() for user_id in payload.participant_user_ids: session.add(models.ProjectParticipant(project_id=project.id, user_id=user_id)) session.commit() session.refresh(project) return project @router.get("/projects", response_model=list[ProjectRead]) def list_projects( household_id: str, session: Session = Depends(get_session) ) -> list[models.Project]: return list( session.scalars( select(models.Project) .where(models.Project.household_id == household_id) .order_by(models.Project.created_at.desc()) ).all() ) @router.get("/projects/{project_id}", response_model=ProjectRead) def get_project(project_id: str, session: Session = Depends(get_session)) -> models.Project: return _get_project(session, project_id) @router.post("/projects/{project_id}/participants", response_model=ProjectParticipantRead) def add_project_participant( project_id: str, payload: ProjectParticipantCreate, session: Session = Depends(get_session), ) -> models.ProjectParticipant: _get_project(session, project_id) participant = models.ProjectParticipant(project_id=project_id, **payload.model_dump()) session.add(participant) session.commit() session.refresh(participant) return participant @router.post("/projects/{project_id}/budget-lines", response_model=ProjectBudgetLineRead) def add_project_budget_line( project_id: str, payload: ProjectBudgetLineCreate, session: Session = Depends(get_session), ) -> models.ProjectBudgetLine: _get_project(session, project_id) budget_line = models.ProjectBudgetLine(project_id=project_id, **payload.model_dump()) session.add(budget_line) session.commit() session.refresh(budget_line) return budget_line @router.post("/projects/{project_id}/planned-items", response_model=ProjectPlannedItemRead) def add_project_planned_item( project_id: str, payload: ProjectPlannedItemCreate, session: Session = Depends(get_session), ) -> models.ProjectPlannedItem: _get_project(session, project_id) planned_item = models.ProjectPlannedItem(project_id=project_id, **payload.model_dump()) session.add(planned_item) session.commit() session.refresh(planned_item) return planned_item @router.post("/projects/{project_id}/payment-lines", response_model=PaymentLineRead) def add_project_payment_line( project_id: str, payload: PaymentLineCreate, session: Session = Depends(get_session), ) -> models.PaymentLine: _get_project(session, project_id) if payload.planned_item_id and not session.get( models.ProjectPlannedItem, payload.planned_item_id ): raise HTTPException(status_code=404, detail="Planned item not found") payment_line = models.PaymentLine(**payload.model_dump()) session.add(payment_line) session.commit() session.refresh(payment_line) return payment_line @router.get("/projects/{project_id}/summary", response_model=ProjectSummary) def get_project_summary(project_id: str, session: Session = Depends(get_session)) -> ProjectSummary: try: return calculate_project_summary(session, project_id) except ValueError as exc: raise HTTPException(status_code=404, detail=str(exc)) from exc @router.get("/projects/{project_id}/settlement", response_model=SettlementSummary) def get_project_settlement( project_id: str, currency: str = "EUR", include_announced: bool = False, session: Session = Depends(get_session), ) -> SettlementSummary: project = _get_project(session, project_id) return calculate_settlement( session, household_id=project.household_id, project_id=project_id, currency=currency, include_announced=include_announced, )