from __future__ import annotations from decimal import Decimal from fastapi import Request from sqlalchemy import func, select from sqlalchemy.orm import Session from app.models import Product, ProductKey def get_cart_map(request: Request) -> dict[str, int]: raw = request.session.get("cart") or {} cart: dict[str, int] = {} for key, value in raw.items(): try: qty = int(value) except (TypeError, ValueError): continue if qty > 0: cart[str(key)] = qty return cart def save_cart(request: Request, cart: dict[str, int]) -> None: request.session["cart"] = {str(k): int(v) for k, v in cart.items() if int(v) > 0} def cart_count(request: Request) -> int: return sum(get_cart_map(request).values()) def available_stock(db: Session, product_id: int) -> int: return int( db.scalar( select(func.count()) .select_from(ProductKey) .where(ProductKey.product_id == product_id, ProductKey.is_sold.is_(False)) ) or 0 ) def build_cart_items(db: Session, request: Request) -> tuple[list[dict], Decimal]: cart = get_cart_map(request) items: list[dict] = [] total = Decimal("0.00") if not cart: return items, total product_ids = [int(pid) for pid in cart.keys()] products = { p.id: p for p in db.scalars(select(Product).where(Product.id.in_(product_ids))).all() } clean_cart = dict(cart) for pid_str, qty in cart.items(): product = products.get(int(pid_str)) if not product or not product.is_active: clean_cart.pop(pid_str, None) continue stock = available_stock(db, product.id) qty = min(qty, max(stock, 0)) if qty <= 0: clean_cart.pop(pid_str, None) continue clean_cart[pid_str] = qty line_total = product.price * qty total += line_total items.append( { "product": product, "quantity": qty, "stock": stock, "line_total": line_total, } ) if clean_cart != cart: save_cart(request, clean_cart) return items, total