import uuid from datetime import date, timedelta from decimal import Decimal from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import and_, or_, select from sqlalchemy.orm import Session, contains_eager, joinedload from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.menu_category import MenuCategory from ..models.product_version import ProductVersion from ..schemas.update_product_prices import UpdateProductPrices, UpdateProductPricesItem from ..schemas.user_token import UserToken from . import optional_query_date router = APIRouter() @router.get("", response_model=UpdateProductPrices) def get_update_product_prices( date_: Optional[date] = Depends(optional_query_date), user: UserToken = Security(get_user, scopes=["products"]), ) -> UpdateProductPrices: if date_ is None: return UpdateProductPrices( date=date.today(), items=[], ) with SessionFuture() as db: return UpdateProductPrices( date=date_, items=update_product_prices_list(None, date_, db), ) @router.get("/{id_}", response_model=UpdateProductPrices) def get_update_product_prices_id( id_: uuid.UUID, date_: Optional[date] = Depends(optional_query_date), user: UserToken = Security(get_user, scopes=["products"]), ) -> UpdateProductPrices: if date_ is None: return UpdateProductPrices( date=date.today(), menuCategoryId=id_, items=[], ) with SessionFuture() as db: return UpdateProductPrices( date=date_, menuCategoryId=id_, items=update_product_prices_list(id_, date_, db), ) def update_product_prices_list( menu_category_id: Optional[uuid.UUID], date_: date, db: Session ) -> List[UpdateProductPricesItem]: list_ = ( select(ProductVersion) .join(ProductVersion.menu_category) .where( and_( or_( ProductVersion.valid_from == None, # noqa: E711 ProductVersion.valid_from <= date_, ), or_( ProductVersion.valid_till == None, # noqa: E711 ProductVersion.valid_till >= date_, ), ) ) ) if menu_category_id is not None: list_ = list_.where(ProductVersion.menu_category_id == menu_category_id) list_.order_by( MenuCategory.sort_order, MenuCategory.name, ProductVersion.sort_order, ProductVersion.name, ProductVersion.valid_from.nullsfirst(), ).options( joinedload(ProductVersion.menu_category, innerjoin=True), contains_eager(ProductVersion.menu_category), ) return [ UpdateProductPricesItem(id=item.product_id, name=item.full_name, oldPrice=item.price, newPrice=item.price) for item in db.execute(list_).scalars().all() ] @router.post("", response_model=UpdateProductPrices) def save_update_product_prices( data: UpdateProductPrices, user: UserToken = Security(get_user, scopes=["products"]), ) -> UpdateProductPrices: with SessionFuture() as db: for item in data.items: update_product(item.id, item.new_price, data.date_, db) db.commit() return UpdateProductPrices( date=data.date_, items=update_product_prices_list(None, data.date_, db), ) @router.post("/{id_}", response_model=UpdateProductPrices) def save_update_product_prices_id( id_: uuid.UUID, data: UpdateProductPrices, user: UserToken = Security(get_user, scopes=["products"]), ) -> UpdateProductPrices: with SessionFuture() as db: for item in data.items: update_product(item.id, item.new_price, data.date_, db) db.commit() return UpdateProductPrices( date=data.date_, menuCategoryId=id_, items=update_product_prices_list(id_, data.date_, db), ) def update_product(id_: uuid.UUID, price: Decimal, date_: date, db: Session): item: ProductVersion = db.execute( select(ProductVersion) .join(ProductVersion.menu_category) .where( and_( ProductVersion.product_id == id_, or_( ProductVersion.valid_from == None, # noqa: E711 ProductVersion.valid_from <= date_, ), or_( ProductVersion.valid_till == None, # noqa: E711 ProductVersion.valid_till >= date_, ), ) ) ).scalar_one() if item.valid_till is not None: # Allow adding a product here splitting the valid from and to, but not implemented right now raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Product has been invalidated", ) if item.valid_from == date_: # Update the product as valid from the the same item.price = price db.commit() else: # Create a new version of the product from the new details item.valid_till = date_ - timedelta(days=1) product_version = ProductVersion( product_id=item.product_id, name=item.name, units=item.units, menu_category_id=item.menu_category_id, sale_category_id=item.sale_category_id, price=price, has_happy_hour=item.has_happy_hour, is_not_available=item.is_not_available, quantity=item.quantity, valid_from=date_, valid_till=None, sort_order=item.sort_order, ) db.add(product_version)