update to python fastpi
This commit is contained in:
288
Backend/src/routes/report_routes.py
Normal file
288
Backend/src/routes/report_routes.py
Normal file
@@ -0,0 +1,288 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func, and_
|
||||
from typing import Optional
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from ..config.database import get_db
|
||||
from ..middleware.auth import get_current_user, authorize_roles
|
||||
from ..models.user import User
|
||||
from ..models.booking import Booking, BookingStatus
|
||||
from ..models.payment import Payment, PaymentStatus
|
||||
from ..models.room import Room
|
||||
|
||||
router = APIRouter(prefix="/reports", tags=["reports"])
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def get_reports(
|
||||
from_date: Optional[str] = Query(None, alias="from"),
|
||||
to_date: Optional[str] = Query(None, alias="to"),
|
||||
type: Optional[str] = Query(None),
|
||||
current_user: User = Depends(authorize_roles("admin", "staff")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get comprehensive reports (Admin/Staff only)"""
|
||||
try:
|
||||
# Parse dates if provided
|
||||
start_date = None
|
||||
end_date = None
|
||||
if from_date:
|
||||
try:
|
||||
start_date = datetime.strptime(from_date, "%Y-%m-%d")
|
||||
except ValueError:
|
||||
start_date = datetime.fromisoformat(from_date.replace('Z', '+00:00'))
|
||||
if to_date:
|
||||
try:
|
||||
end_date = datetime.strptime(to_date, "%Y-%m-%d")
|
||||
# Set to end of day
|
||||
end_date = end_date.replace(hour=23, minute=59, second=59)
|
||||
except ValueError:
|
||||
end_date = datetime.fromisoformat(to_date.replace('Z', '+00:00'))
|
||||
|
||||
# Base queries
|
||||
booking_query = db.query(Booking)
|
||||
payment_query = db.query(Payment).filter(Payment.payment_status == PaymentStatus.completed)
|
||||
|
||||
# Apply date filters
|
||||
if start_date:
|
||||
booking_query = booking_query.filter(Booking.created_at >= start_date)
|
||||
payment_query = payment_query.filter(Payment.payment_date >= start_date)
|
||||
if end_date:
|
||||
booking_query = booking_query.filter(Booking.created_at <= end_date)
|
||||
payment_query = payment_query.filter(Payment.payment_date <= end_date)
|
||||
|
||||
# Total bookings
|
||||
total_bookings = booking_query.count()
|
||||
|
||||
# Total revenue
|
||||
total_revenue = payment_query.with_entities(func.sum(Payment.amount)).scalar() or 0.0
|
||||
|
||||
# Total customers (unique users with bookings)
|
||||
total_customers = db.query(func.count(func.distinct(Booking.user_id))).scalar() or 0
|
||||
if start_date or end_date:
|
||||
customer_query = db.query(func.count(func.distinct(Booking.user_id)))
|
||||
if start_date:
|
||||
customer_query = customer_query.filter(Booking.created_at >= start_date)
|
||||
if end_date:
|
||||
customer_query = customer_query.filter(Booking.created_at <= end_date)
|
||||
total_customers = customer_query.scalar() or 0
|
||||
|
||||
# Available rooms
|
||||
available_rooms = db.query(Room).filter(Room.status == "available").count()
|
||||
|
||||
# Occupied rooms (rooms with active bookings)
|
||||
occupied_rooms = db.query(func.count(func.distinct(Booking.room_id))).filter(
|
||||
Booking.status.in_([BookingStatus.confirmed, BookingStatus.checked_in])
|
||||
).scalar() or 0
|
||||
|
||||
# Revenue by date (daily breakdown)
|
||||
revenue_by_date = []
|
||||
if start_date and end_date:
|
||||
daily_revenue_query = db.query(
|
||||
func.date(Payment.payment_date).label('date'),
|
||||
func.sum(Payment.amount).label('revenue'),
|
||||
func.count(func.distinct(Payment.booking_id)).label('bookings')
|
||||
).filter(Payment.payment_status == PaymentStatus.completed)
|
||||
|
||||
if start_date:
|
||||
daily_revenue_query = daily_revenue_query.filter(Payment.payment_date >= start_date)
|
||||
if end_date:
|
||||
daily_revenue_query = daily_revenue_query.filter(Payment.payment_date <= end_date)
|
||||
|
||||
daily_revenue_query = daily_revenue_query.group_by(
|
||||
func.date(Payment.payment_date)
|
||||
).order_by(func.date(Payment.payment_date))
|
||||
|
||||
daily_data = daily_revenue_query.all()
|
||||
revenue_by_date = [
|
||||
{
|
||||
"date": str(date),
|
||||
"revenue": float(revenue or 0),
|
||||
"bookings": int(bookings or 0)
|
||||
}
|
||||
for date, revenue, bookings in daily_data
|
||||
]
|
||||
|
||||
# Bookings by status
|
||||
bookings_by_status = {}
|
||||
for status in BookingStatus:
|
||||
count = booking_query.filter(Booking.status == status).count()
|
||||
status_name = status.value if hasattr(status, 'value') else str(status)
|
||||
bookings_by_status[status_name] = count
|
||||
|
||||
# Top rooms (by revenue)
|
||||
top_rooms_query = db.query(
|
||||
Room.id,
|
||||
Room.room_number,
|
||||
func.count(Booking.id).label('bookings'),
|
||||
func.sum(Payment.amount).label('revenue')
|
||||
).join(Booking, Room.id == Booking.room_id).join(
|
||||
Payment, Booking.id == Payment.booking_id
|
||||
).filter(Payment.payment_status == PaymentStatus.completed)
|
||||
|
||||
if start_date:
|
||||
top_rooms_query = top_rooms_query.filter(Booking.created_at >= start_date)
|
||||
if end_date:
|
||||
top_rooms_query = top_rooms_query.filter(Booking.created_at <= end_date)
|
||||
|
||||
top_rooms_data = top_rooms_query.group_by(Room.id, Room.room_number).order_by(
|
||||
func.sum(Payment.amount).desc()
|
||||
).limit(10).all()
|
||||
|
||||
top_rooms = [
|
||||
{
|
||||
"room_id": room_id,
|
||||
"room_number": room_number,
|
||||
"bookings": int(bookings or 0),
|
||||
"revenue": float(revenue or 0)
|
||||
}
|
||||
for room_id, room_number, bookings, revenue in top_rooms_data
|
||||
]
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"success": True,
|
||||
"data": {
|
||||
"total_bookings": total_bookings,
|
||||
"total_revenue": float(total_revenue),
|
||||
"total_customers": int(total_customers),
|
||||
"available_rooms": available_rooms,
|
||||
"occupied_rooms": occupied_rooms,
|
||||
"revenue_by_date": revenue_by_date if revenue_by_date else None,
|
||||
"bookings_by_status": bookings_by_status,
|
||||
"top_rooms": top_rooms if top_rooms else None,
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/dashboard")
|
||||
async def get_dashboard_stats(
|
||||
current_user: User = Depends(authorize_roles("admin", "staff")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get dashboard statistics (Admin/Staff only)"""
|
||||
try:
|
||||
# Total bookings
|
||||
total_bookings = db.query(Booking).count()
|
||||
|
||||
# Active bookings
|
||||
active_bookings = db.query(Booking).filter(
|
||||
Booking.status.in_([BookingStatus.pending, BookingStatus.confirmed, BookingStatus.checked_in])
|
||||
).count()
|
||||
|
||||
# Total revenue (from completed payments)
|
||||
total_revenue = db.query(func.sum(Payment.amount)).filter(
|
||||
Payment.payment_status == PaymentStatus.completed
|
||||
).scalar() or 0.0
|
||||
|
||||
# Today's revenue
|
||||
today_start = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
today_revenue = db.query(func.sum(Payment.amount)).filter(
|
||||
and_(
|
||||
Payment.payment_status == PaymentStatus.completed,
|
||||
Payment.payment_date >= today_start
|
||||
)
|
||||
).scalar() or 0.0
|
||||
|
||||
# Total rooms
|
||||
total_rooms = db.query(Room).count()
|
||||
|
||||
# Available rooms
|
||||
available_rooms = db.query(Room).filter(Room.status == "available").count()
|
||||
|
||||
# Recent bookings (last 7 days)
|
||||
week_ago = datetime.utcnow() - timedelta(days=7)
|
||||
recent_bookings = db.query(Booking).filter(
|
||||
Booking.created_at >= week_ago
|
||||
).count()
|
||||
|
||||
# Pending payments
|
||||
pending_payments = db.query(Payment).filter(
|
||||
Payment.payment_status == PaymentStatus.pending
|
||||
).count()
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"data": {
|
||||
"total_bookings": total_bookings,
|
||||
"active_bookings": active_bookings,
|
||||
"total_revenue": float(total_revenue),
|
||||
"today_revenue": float(today_revenue),
|
||||
"total_rooms": total_rooms,
|
||||
"available_rooms": available_rooms,
|
||||
"recent_bookings": recent_bookings,
|
||||
"pending_payments": pending_payments,
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/revenue")
|
||||
async def get_revenue_report(
|
||||
start_date: Optional[str] = Query(None),
|
||||
end_date: Optional[str] = Query(None),
|
||||
current_user: User = Depends(authorize_roles("admin", "staff")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get revenue report (Admin/Staff only)"""
|
||||
try:
|
||||
query = db.query(Payment).filter(
|
||||
Payment.payment_status == PaymentStatus.completed
|
||||
)
|
||||
|
||||
if start_date:
|
||||
start = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
|
||||
query = query.filter(Payment.payment_date >= start)
|
||||
|
||||
if end_date:
|
||||
end = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
|
||||
query = query.filter(Payment.payment_date <= end)
|
||||
|
||||
# Total revenue
|
||||
total_revenue = db.query(func.sum(Payment.amount)).filter(
|
||||
Payment.payment_status == PaymentStatus.completed
|
||||
).scalar() or 0.0
|
||||
|
||||
# Revenue by payment method
|
||||
revenue_by_method = db.query(
|
||||
Payment.payment_method,
|
||||
func.sum(Payment.amount).label('total')
|
||||
).filter(
|
||||
Payment.payment_status == PaymentStatus.completed
|
||||
).group_by(Payment.payment_method).all()
|
||||
|
||||
method_breakdown = {}
|
||||
for method, total in revenue_by_method:
|
||||
method_name = method.value if hasattr(method, 'value') else str(method)
|
||||
method_breakdown[method_name] = float(total or 0)
|
||||
|
||||
# Revenue by date (daily breakdown)
|
||||
daily_revenue = db.query(
|
||||
func.date(Payment.payment_date).label('date'),
|
||||
func.sum(Payment.amount).label('total')
|
||||
).filter(
|
||||
Payment.payment_status == PaymentStatus.completed
|
||||
).group_by(func.date(Payment.payment_date)).order_by(func.date(Payment.payment_date).desc()).limit(30).all()
|
||||
|
||||
daily_breakdown = [
|
||||
{
|
||||
"date": date.isoformat() if isinstance(date, datetime) else str(date),
|
||||
"revenue": float(total or 0)
|
||||
}
|
||||
for date, total in daily_revenue
|
||||
]
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"data": {
|
||||
"total_revenue": float(total_revenue),
|
||||
"revenue_by_method": method_breakdown,
|
||||
"daily_breakdown": daily_breakdown,
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
Reference in New Issue
Block a user