from typing import Optional from fastapi import APIRouter, HTTPException, Query, status from sqlalchemy.exc import IntegrityError from sqlmodel import Session, select from app.db import SessionDep from app.dependencies import CurrentUser, get_round_in_organization, get_wine_offering_in_organization from app.models.purchase_round import PurchaseRound from app.models.route import Route from app.models.wine_category import WineCategory from app.models.wine_offering import WineOffering, WineOfferingCreate, WineOfferingPublic, WineOfferingUpdate router = APIRouter(prefix="/wine-offerings", tags=["wine-offerings"]) def _get_category_or_404(session: Session, category_id: int) -> WineCategory: category = session.get(WineCategory, category_id) if category is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Wine category not found") return category @router.post("", response_model=WineOfferingPublic, status_code=status.HTTP_201_CREATED) def create_wine_offering(payload: WineOfferingCreate, session: SessionDep, current_user: CurrentUser) -> WineOffering: get_round_in_organization(session, payload.purchase_round_id, current_user.organization_id) _get_category_or_404(session, payload.category_id) wine_offering = WineOffering.model_validate(payload) session.add(wine_offering) session.commit() session.refresh(wine_offering) return wine_offering @router.get("", response_model=list[WineOfferingPublic]) def list_wine_offerings( session: SessionDep, current_user: CurrentUser, purchase_round_id: Optional[int] = None, limit: int = Query(default=100, le=500, gt=0), offset: int = Query(default=0, ge=0), ) -> list[WineOffering]: statement = ( select(WineOffering) .join(PurchaseRound) .join(Route) .where(Route.organization_id == current_user.organization_id) ) if purchase_round_id is not None: statement = statement.where(WineOffering.purchase_round_id == purchase_round_id) statement = statement.order_by(WineOffering.id).offset(offset).limit(limit) return list(session.exec(statement).all()) @router.get("/{wine_offering_id}", response_model=WineOfferingPublic) def get_wine_offering(wine_offering_id: int, session: SessionDep, current_user: CurrentUser) -> WineOffering: return get_wine_offering_in_organization(session, wine_offering_id, current_user.organization_id) @router.patch("/{wine_offering_id}", response_model=WineOfferingPublic) def update_wine_offering( wine_offering_id: int, payload: WineOfferingUpdate, session: SessionDep, current_user: CurrentUser ) -> WineOffering: wine_offering = get_wine_offering_in_organization(session, wine_offering_id, current_user.organization_id) update_data = payload.model_dump(exclude_unset=True) if "category_id" in update_data: _get_category_or_404(session, update_data["category_id"]) for field, value in update_data.items(): setattr(wine_offering, field, value) session.add(wine_offering) session.commit() session.refresh(wine_offering) return wine_offering @router.delete("/{wine_offering_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_wine_offering(wine_offering_id: int, session: SessionDep, current_user: CurrentUser) -> None: wine_offering = get_wine_offering_in_organization(session, wine_offering_id, current_user.organization_id) session.delete(wine_offering) try: session.commit() except IntegrityError as exc: session.rollback() raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="This wine offering has order lines and cannot be deleted", ) from exc