Automated Action 439330125e Fix code linting issues
- Fix unused imports in API endpoints
- Add proper __all__ exports in model and schema modules
- Add proper TYPE_CHECKING imports in models to prevent circular imports
- Fix import order in migrations
- Fix long lines in migration scripts
- All ruff checks passing
2025-06-05 16:58:14 +00:00

250 lines
8.5 KiB
Python

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
}