from datetime import datetime, timedelta from typing import Any from fastapi import APIRouter, Depends from sqlalchemy import func from sqlalchemy.orm import Session from app import crud, models from app.api import deps from app.models.inventory_transaction import TransactionType router = APIRouter() @router.get("/inventory-summary") def get_inventory_summary( db: Session = Depends(deps.get_db), current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Get summary of inventory status. """ # Get all products belonging to the user products = crud.product.get_multi_by_owner(db, owner_id=current_user.id) # Calculate summary statistics total_products = len(products) total_inventory_value = sum(p.quantity * p.cost for p in products) total_retail_value = sum(p.quantity * p.price for p in products) potential_profit = total_retail_value - total_inventory_value low_stock_count = len([p for p in products if 0 < p.quantity <= p.reorder_level]) out_of_stock_count = len([p for p in products if p.quantity == 0]) in_stock_count = total_products - low_stock_count - out_of_stock_count return { "total_products": total_products, "total_inventory_value": round(total_inventory_value, 2), "total_retail_value": round(total_retail_value, 2), "potential_profit": round(potential_profit, 2), "inventory_status": { "in_stock": in_stock_count, "low_stock": low_stock_count, "out_of_stock": out_of_stock_count } } @router.get("/transaction-history") def get_transaction_history( db: Session = Depends(deps.get_db), days: int = 30, current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Get transaction history for the specified number of days. """ # Get all products belonging to the user products = crud.product.get_multi_by_owner(db, owner_id=current_user.id) product_ids = [p.id for p in products] # Get date range end_date = datetime.utcnow() start_date = end_date - timedelta(days=days) # Get transactions for those products within the date range transactions = ( db.query( models.InventoryTransaction.transaction_type, func.count().label("count"), func.sum(models.InventoryTransaction.quantity).label("total_quantity"), ) .filter( models.InventoryTransaction.product_id.in_(product_ids), models.InventoryTransaction.transaction_date >= start_date, models.InventoryTransaction.transaction_date <= end_date, ) .group_by(models.InventoryTransaction.transaction_type) .all() ) # Format results result = {} for t_type in TransactionType: result[t_type.value] = {"count": 0, "total_quantity": 0} for t_type, count, total_quantity in transactions: result[t_type.value] = { "count": count, "total_quantity": total_quantity or 0 } return { "period_days": days, "start_date": start_date.isoformat(), "end_date": end_date.isoformat(), "transactions_by_type": result } @router.get("/product-performance") def get_product_performance( db: Session = Depends(deps.get_db), days: int = 30, limit: int = 10, current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Get top-selling products for the specified number of days. """ # Get all products belonging to the user products = crud.product.get_multi_by_owner(db, owner_id=current_user.id) product_dict = {p.id: p for p in products} # Get date range end_date = datetime.utcnow() start_date = end_date - timedelta(days=days) # Get sales transactions grouped by product sales = ( db.query( models.InventoryTransaction.product_id, func.sum(models.InventoryTransaction.quantity).label("quantity_sold"), ) .filter( models.InventoryTransaction.product_id.in_(product_dict.keys()), models.InventoryTransaction.transaction_type == TransactionType.SALE, models.InventoryTransaction.transaction_date >= start_date, models.InventoryTransaction.transaction_date <= end_date, ) .group_by(models.InventoryTransaction.product_id) .order_by(func.sum(models.InventoryTransaction.quantity).desc()) .limit(limit) .all() ) # Format results top_selling = [] for product_id, quantity_sold in sales: product = product_dict.get(product_id) if product: top_selling.append({ "id": product.id, "name": product.name, "sku": product.sku, "quantity_sold": quantity_sold, "revenue": round(quantity_sold * product.price, 2), "profit": round(quantity_sold * (product.price - product.cost), 2), }) return { "period_days": days, "start_date": start_date.isoformat(), "end_date": end_date.isoformat(), "top_selling_products": top_selling } @router.get("/category-performance") def get_category_performance( db: Session = Depends(deps.get_db), days: int = 30, current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Get sales performance by category for the specified number of days. """ # Get all products belonging to the user products = crud.product.get_multi_by_owner(db, owner_id=current_user.id) # Collect category IDs category_ids = set() for p in products: if p.category_id: category_ids.add(p.category_id) # Get categories categories = crud.category.get_multi_by_ids(db, ids=list(category_ids)) category_dict = {c.id: c for c in categories} # Get date range end_date = datetime.utcnow() start_date = end_date - timedelta(days=days) # Get sales transactions for these products sales = ( db.query( models.Product.category_id, func.sum(models.InventoryTransaction.quantity).label("quantity_sold"), ) .join( models.InventoryTransaction, models.InventoryTransaction.product_id == models.Product.id ) .filter( models.Product.owner_id == current_user.id, models.InventoryTransaction.transaction_type == TransactionType.SALE, models.InventoryTransaction.transaction_date >= start_date, models.InventoryTransaction.transaction_date <= end_date, ) .group_by(models.Product.category_id) .all() ) # Format results category_performance = [] for category_id, quantity_sold in sales: # Skip None category if not category_id: continue category = category_dict.get(category_id) if category: # Calculate revenue and profit for this category revenue = 0 profit = 0 for p in products: if p.category_id == category_id: # Get sales for this specific product product_sales = ( db.query(func.sum(models.InventoryTransaction.quantity)) .filter( models.InventoryTransaction.product_id == p.id, models.InventoryTransaction.transaction_type == TransactionType.SALE, models.InventoryTransaction.transaction_date >= start_date, models.InventoryTransaction.transaction_date <= end_date, ) .scalar() or 0 ) revenue += product_sales * p.price profit += product_sales * (p.price - p.cost) category_performance.append({ "id": category.id, "name": category.name, "quantity_sold": quantity_sold, "revenue": round(revenue, 2), "profit": round(profit, 2), }) # Sort by revenue category_performance.sort(key=lambda x: x["revenue"], reverse=True) return { "period_days": days, "start_date": start_date.isoformat(), "end_date": end_date.isoformat(), "category_performance": category_performance }