update to python fastpi
This commit is contained in:
334
Backend/src/routes/promotion_routes.py
Normal file
334
Backend/src/routes/promotion_routes.py
Normal file
@@ -0,0 +1,334 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import or_
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
from ..config.database import get_db
|
||||
from ..middleware.auth import get_current_user, authorize_roles
|
||||
from ..models.user import User
|
||||
from ..models.promotion import Promotion, DiscountType
|
||||
|
||||
router = APIRouter(prefix="/promotions", tags=["promotions"])
|
||||
|
||||
|
||||
@router.get("/")
|
||||
async def get_promotions(
|
||||
search: Optional[str] = Query(None),
|
||||
status_filter: Optional[str] = Query(None, alias="status"),
|
||||
type: Optional[str] = Query(None),
|
||||
page: int = Query(1, ge=1),
|
||||
limit: int = Query(10, ge=1, le=100),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get all promotions with filters"""
|
||||
try:
|
||||
query = db.query(Promotion)
|
||||
|
||||
# Filter by search (code or name)
|
||||
if search:
|
||||
query = query.filter(
|
||||
or_(
|
||||
Promotion.code.like(f"%{search}%"),
|
||||
Promotion.name.like(f"%{search}%")
|
||||
)
|
||||
)
|
||||
|
||||
# Filter by status (is_active)
|
||||
if status_filter:
|
||||
is_active = status_filter == "active"
|
||||
query = query.filter(Promotion.is_active == is_active)
|
||||
|
||||
# Filter by discount type
|
||||
if type:
|
||||
try:
|
||||
query = query.filter(Promotion.discount_type == DiscountType(type))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
total = query.count()
|
||||
offset = (page - 1) * limit
|
||||
promotions = query.order_by(Promotion.created_at.desc()).offset(offset).limit(limit).all()
|
||||
|
||||
result = []
|
||||
for promo in promotions:
|
||||
promo_dict = {
|
||||
"id": promo.id,
|
||||
"code": promo.code,
|
||||
"name": promo.name,
|
||||
"description": promo.description,
|
||||
"discount_type": promo.discount_type.value if isinstance(promo.discount_type, DiscountType) else promo.discount_type,
|
||||
"discount_value": float(promo.discount_value) if promo.discount_value else 0.0,
|
||||
"min_booking_amount": float(promo.min_booking_amount) if promo.min_booking_amount else None,
|
||||
"max_discount_amount": float(promo.max_discount_amount) if promo.max_discount_amount else None,
|
||||
"start_date": promo.start_date.isoformat() if promo.start_date else None,
|
||||
"end_date": promo.end_date.isoformat() if promo.end_date else None,
|
||||
"usage_limit": promo.usage_limit,
|
||||
"used_count": promo.used_count,
|
||||
"is_active": promo.is_active,
|
||||
"created_at": promo.created_at.isoformat() if promo.created_at else None,
|
||||
}
|
||||
result.append(promo_dict)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"data": {
|
||||
"promotions": result,
|
||||
"pagination": {
|
||||
"total": total,
|
||||
"page": page,
|
||||
"limit": limit,
|
||||
"totalPages": (total + limit - 1) // limit,
|
||||
},
|
||||
},
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/{code}")
|
||||
async def get_promotion_by_code(code: str, db: Session = Depends(get_db)):
|
||||
"""Get promotion by code"""
|
||||
try:
|
||||
promotion = db.query(Promotion).filter(Promotion.code == code).first()
|
||||
if not promotion:
|
||||
raise HTTPException(status_code=404, detail="Promotion not found")
|
||||
|
||||
promo_dict = {
|
||||
"id": promotion.id,
|
||||
"code": promotion.code,
|
||||
"name": promotion.name,
|
||||
"description": promotion.description,
|
||||
"discount_type": promotion.discount_type.value if isinstance(promotion.discount_type, DiscountType) else promotion.discount_type,
|
||||
"discount_value": float(promotion.discount_value) if promotion.discount_value else 0.0,
|
||||
"min_booking_amount": float(promotion.min_booking_amount) if promotion.min_booking_amount else None,
|
||||
"max_discount_amount": float(promotion.max_discount_amount) if promotion.max_discount_amount else None,
|
||||
"start_date": promotion.start_date.isoformat() if promotion.start_date else None,
|
||||
"end_date": promotion.end_date.isoformat() if promotion.end_date else None,
|
||||
"usage_limit": promotion.usage_limit,
|
||||
"used_count": promotion.used_count,
|
||||
"is_active": promotion.is_active,
|
||||
}
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"data": {"promotion": promo_dict}
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/validate")
|
||||
async def validate_promotion(
|
||||
validation_data: dict,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Validate and apply promotion"""
|
||||
try:
|
||||
code = validation_data.get("code")
|
||||
booking_amount = float(validation_data.get("booking_amount", 0))
|
||||
|
||||
promotion = db.query(Promotion).filter(Promotion.code == code).first()
|
||||
if not promotion:
|
||||
raise HTTPException(status_code=404, detail="Promotion code not found")
|
||||
|
||||
# Check if promotion is active
|
||||
if not promotion.is_active:
|
||||
raise HTTPException(status_code=400, detail="Promotion is not active")
|
||||
|
||||
# Check date validity
|
||||
now = datetime.utcnow()
|
||||
if promotion.start_date and now < promotion.start_date:
|
||||
raise HTTPException(status_code=400, detail="Promotion is not valid at this time")
|
||||
if promotion.end_date and now > promotion.end_date:
|
||||
raise HTTPException(status_code=400, detail="Promotion is not valid at this time")
|
||||
|
||||
# Check usage limit
|
||||
if promotion.usage_limit and promotion.used_count >= promotion.usage_limit:
|
||||
raise HTTPException(status_code=400, detail="Promotion usage limit reached")
|
||||
|
||||
# Check minimum booking amount
|
||||
if promotion.min_booking_amount and booking_amount < float(promotion.min_booking_amount):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Minimum booking amount is {promotion.min_booking_amount}"
|
||||
)
|
||||
|
||||
# Calculate discount
|
||||
discount_amount = promotion.calculate_discount(booking_amount)
|
||||
final_amount = booking_amount - discount_amount
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"data": {
|
||||
"promotion": {
|
||||
"id": promotion.id,
|
||||
"code": promotion.code,
|
||||
"name": promotion.name,
|
||||
},
|
||||
"original_amount": booking_amount,
|
||||
"discount_amount": discount_amount,
|
||||
"final_amount": final_amount,
|
||||
}
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/", dependencies=[Depends(authorize_roles("admin"))])
|
||||
async def create_promotion(
|
||||
promotion_data: dict,
|
||||
current_user: User = Depends(authorize_roles("admin")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Create new promotion (Admin only)"""
|
||||
try:
|
||||
code = promotion_data.get("code")
|
||||
|
||||
# Check if code exists
|
||||
existing = db.query(Promotion).filter(Promotion.code == code).first()
|
||||
if existing:
|
||||
raise HTTPException(status_code=400, detail="Promotion code already exists")
|
||||
|
||||
discount_type = promotion_data.get("discount_type")
|
||||
discount_value = float(promotion_data.get("discount_value", 0))
|
||||
|
||||
# Validate discount value
|
||||
if discount_type == "percentage" and discount_value > 100:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Percentage discount cannot exceed 100%"
|
||||
)
|
||||
|
||||
promotion = Promotion(
|
||||
code=code,
|
||||
name=promotion_data.get("name"),
|
||||
description=promotion_data.get("description"),
|
||||
discount_type=DiscountType(discount_type),
|
||||
discount_value=discount_value,
|
||||
min_booking_amount=float(promotion_data["min_booking_amount"]) if promotion_data.get("min_booking_amount") else None,
|
||||
max_discount_amount=float(promotion_data["max_discount_amount"]) if promotion_data.get("max_discount_amount") else None,
|
||||
start_date=datetime.fromisoformat(promotion_data["start_date"].replace('Z', '+00:00')) if promotion_data.get("start_date") else None,
|
||||
end_date=datetime.fromisoformat(promotion_data["end_date"].replace('Z', '+00:00')) if promotion_data.get("end_date") else None,
|
||||
usage_limit=promotion_data.get("usage_limit"),
|
||||
used_count=0,
|
||||
is_active=promotion_data.get("status") == "active" if promotion_data.get("status") else True,
|
||||
)
|
||||
|
||||
db.add(promotion)
|
||||
db.commit()
|
||||
db.refresh(promotion)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "Promotion created successfully",
|
||||
"data": {"promotion": promotion}
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.put("/{id}", dependencies=[Depends(authorize_roles("admin"))])
|
||||
async def update_promotion(
|
||||
id: int,
|
||||
promotion_data: dict,
|
||||
current_user: User = Depends(authorize_roles("admin")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Update promotion (Admin only)"""
|
||||
try:
|
||||
promotion = db.query(Promotion).filter(Promotion.id == id).first()
|
||||
if not promotion:
|
||||
raise HTTPException(status_code=404, detail="Promotion not found")
|
||||
|
||||
# Check if new code exists (excluding current)
|
||||
code = promotion_data.get("code")
|
||||
if code and code != promotion.code:
|
||||
existing = db.query(Promotion).filter(
|
||||
Promotion.code == code,
|
||||
Promotion.id != id
|
||||
).first()
|
||||
if existing:
|
||||
raise HTTPException(status_code=400, detail="Promotion code already exists")
|
||||
|
||||
# Validate discount value
|
||||
discount_type = promotion_data.get("discount_type", promotion.discount_type.value if isinstance(promotion.discount_type, DiscountType) else promotion.discount_type)
|
||||
discount_value = promotion_data.get("discount_value")
|
||||
if discount_value is not None:
|
||||
discount_value = float(discount_value)
|
||||
if discount_type == "percentage" and discount_value > 100:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Percentage discount cannot exceed 100%"
|
||||
)
|
||||
|
||||
# Update fields
|
||||
if "code" in promotion_data:
|
||||
promotion.code = promotion_data["code"]
|
||||
if "name" in promotion_data:
|
||||
promotion.name = promotion_data["name"]
|
||||
if "description" in promotion_data:
|
||||
promotion.description = promotion_data["description"]
|
||||
if "discount_type" in promotion_data:
|
||||
promotion.discount_type = DiscountType(promotion_data["discount_type"])
|
||||
if "discount_value" in promotion_data:
|
||||
promotion.discount_value = discount_value
|
||||
if "min_booking_amount" in promotion_data:
|
||||
promotion.min_booking_amount = float(promotion_data["min_booking_amount"]) if promotion_data["min_booking_amount"] else None
|
||||
if "max_discount_amount" in promotion_data:
|
||||
promotion.max_discount_amount = float(promotion_data["max_discount_amount"]) if promotion_data["max_discount_amount"] else None
|
||||
if "start_date" in promotion_data:
|
||||
promotion.start_date = datetime.fromisoformat(promotion_data["start_date"].replace('Z', '+00:00')) if promotion_data["start_date"] else None
|
||||
if "end_date" in promotion_data:
|
||||
promotion.end_date = datetime.fromisoformat(promotion_data["end_date"].replace('Z', '+00:00')) if promotion_data["end_date"] else None
|
||||
if "usage_limit" in promotion_data:
|
||||
promotion.usage_limit = promotion_data["usage_limit"]
|
||||
if "status" in promotion_data:
|
||||
promotion.is_active = promotion_data["status"] == "active"
|
||||
|
||||
db.commit()
|
||||
db.refresh(promotion)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "Promotion updated successfully",
|
||||
"data": {"promotion": promotion}
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/{id}", dependencies=[Depends(authorize_roles("admin"))])
|
||||
async def delete_promotion(
|
||||
id: int,
|
||||
current_user: User = Depends(authorize_roles("admin")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Delete promotion (Admin only)"""
|
||||
try:
|
||||
promotion = db.query(Promotion).filter(Promotion.id == id).first()
|
||||
if not promotion:
|
||||
raise HTTPException(status_code=404, detail="Promotion not found")
|
||||
|
||||
db.delete(promotion)
|
||||
db.commit()
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "Promotion deleted successfully"
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
Reference in New Issue
Block a user