updates
This commit is contained in:
Binary file not shown.
59
Backend/seeds_data/add_housekeeping_role.py
Normal file
59
Backend/seeds_data/add_housekeeping_role.py
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to add the 'housekeeping' role to the database.
|
||||
Run this script once to create the housekeeping role if it doesn't exist.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Add the Backend directory to the path
|
||||
backend_dir = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(backend_dir))
|
||||
|
||||
from src.shared.config.database import SessionLocal
|
||||
from src.models import Role # Use the models __init__ which handles all imports
|
||||
|
||||
def add_housekeeping_role():
|
||||
"""Add the housekeeping role to the database if it doesn't exist."""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Check if housekeeping role already exists
|
||||
existing_role = db.query(Role).filter(Role.name == 'housekeeping').first()
|
||||
|
||||
if existing_role:
|
||||
print("✓ Housekeeping role already exists in the database.")
|
||||
print(f" Role ID: {existing_role.id}")
|
||||
print(f" Role Name: {existing_role.name}")
|
||||
return
|
||||
|
||||
# Create the housekeeping role
|
||||
housekeeping_role = Role(
|
||||
name='housekeeping',
|
||||
description='Housekeeping staff role with access to room cleaning tasks and status updates'
|
||||
)
|
||||
db.add(housekeeping_role)
|
||||
db.commit()
|
||||
db.refresh(housekeeping_role)
|
||||
|
||||
print("✓ Housekeeping role created successfully!")
|
||||
print(f" Role ID: {housekeeping_role.id}")
|
||||
print(f" Role Name: {housekeeping_role.name}")
|
||||
print(f" Description: {housekeeping_role.description}")
|
||||
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print(f"✗ Error creating housekeeping role: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Adding housekeeping role to the database...")
|
||||
print("=" * 60)
|
||||
add_housekeeping_role()
|
||||
print("=" * 60)
|
||||
print("Done!")
|
||||
81
Backend/seeds_data/add_housekeeping_user.py
Normal file
81
Backend/seeds_data/add_housekeeping_user.py
Normal file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to add a housekeeping user to the database.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Add the Backend directory to the path
|
||||
backend_dir = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(backend_dir))
|
||||
|
||||
from src.shared.config.database import SessionLocal
|
||||
from src.models import Role, User
|
||||
import bcrypt
|
||||
|
||||
def add_housekeeping_user():
|
||||
"""Add the housekeeping user to the database if it doesn't exist."""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Get housekeeping role
|
||||
housekeeping_role = db.query(Role).filter(Role.name == 'housekeeping').first()
|
||||
|
||||
if not housekeeping_role:
|
||||
print("✗ Housekeeping role not found! Please run add_housekeeping_role.py first.")
|
||||
sys.exit(1)
|
||||
|
||||
# Check if user already exists
|
||||
existing_user = db.query(User).filter(User.email == 'housekeeping@gnxsoft.com').first()
|
||||
|
||||
if existing_user:
|
||||
print("✓ Housekeeping user already exists in the database.")
|
||||
print(f" User ID: {existing_user.id}")
|
||||
print(f" Email: {existing_user.email}")
|
||||
print(f" Full Name: {existing_user.full_name}")
|
||||
print(f" Role: {existing_user.role.name if existing_user.role else 'N/A'}")
|
||||
return
|
||||
|
||||
# Hash password
|
||||
password = 'P4eli240453.'
|
||||
password_bytes = password.encode('utf-8')
|
||||
salt = bcrypt.gensalt()
|
||||
hashed_password = bcrypt.hashpw(password_bytes, salt).decode('utf-8')
|
||||
|
||||
# Create the housekeeping user
|
||||
housekeeping_user = User(
|
||||
email='housekeeping@gnxsoft.com',
|
||||
password=hashed_password,
|
||||
full_name='Housekeeping Staff',
|
||||
role_id=housekeeping_role.id,
|
||||
is_active=True,
|
||||
currency='EUR'
|
||||
)
|
||||
db.add(housekeeping_user)
|
||||
db.commit()
|
||||
db.refresh(housekeeping_user)
|
||||
|
||||
print("✓ Housekeeping user created successfully!")
|
||||
print(f" User ID: {housekeeping_user.id}")
|
||||
print(f" Email: {housekeeping_user.email}")
|
||||
print(f" Full Name: {housekeeping_user.full_name}")
|
||||
print(f" Role: {housekeeping_role.name}")
|
||||
print(f" Password: P4eli240453.")
|
||||
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print(f"✗ Error creating housekeeping user: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Adding housekeeping user to the database...")
|
||||
print("=" * 60)
|
||||
add_housekeeping_user()
|
||||
print("=" * 60)
|
||||
print("Done!")
|
||||
|
||||
149
Backend/seeds_data/assign_housekeeping_tasks.py
Normal file
149
Backend/seeds_data/assign_housekeeping_tasks.py
Normal file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to assign test housekeeping tasks to the housekeeping user.
|
||||
This creates sample tasks for testing the housekeeping dashboard.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# Add the Backend directory to the path
|
||||
backend_dir = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(backend_dir))
|
||||
|
||||
from src.shared.config.database import SessionLocal
|
||||
from src.models import Role, User, Room
|
||||
from src.hotel_services.models.housekeeping_task import HousekeepingTask, HousekeepingStatus, HousekeepingType
|
||||
|
||||
def assign_housekeeping_tasks():
|
||||
"""Assign test housekeeping tasks to the housekeeping user."""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Get housekeeping user
|
||||
housekeeping_user = db.query(User).join(Role).filter(
|
||||
Role.name == 'housekeeping',
|
||||
User.email == 'housekeeping@gnxsoft.com'
|
||||
).first()
|
||||
|
||||
if not housekeeping_user:
|
||||
print("✗ Housekeeping user not found! Please run add_housekeeping_user.py first.")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"✓ Found housekeeping user: {housekeeping_user.email} (ID: {housekeeping_user.id})")
|
||||
|
||||
# Get admin user for created_by
|
||||
admin_role = db.query(Role).filter(Role.name == 'admin').first()
|
||||
admin_user = db.query(User).filter(User.role_id == admin_role.id).first() if admin_role else None
|
||||
|
||||
# Get some rooms
|
||||
rooms = db.query(Room).limit(5).all()
|
||||
|
||||
if not rooms:
|
||||
print("✗ No rooms found in the database! Please seed rooms first.")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"✓ Found {len(rooms)} rooms")
|
||||
|
||||
# Default checklist items for different task types
|
||||
checklists = {
|
||||
'checkout': [
|
||||
{'item': 'Bathroom cleaned', 'completed': False, 'notes': ''},
|
||||
{'item': 'Beds made with fresh linens', 'completed': False, 'notes': ''},
|
||||
{'item': 'Trash emptied', 'completed': False, 'notes': ''},
|
||||
{'item': 'Towels replaced', 'completed': False, 'notes': ''},
|
||||
{'item': 'Amenities restocked', 'completed': False, 'notes': ''},
|
||||
{'item': 'Floor vacuumed and mopped', 'completed': False, 'notes': ''},
|
||||
{'item': 'Surfaces dusted', 'completed': False, 'notes': ''},
|
||||
{'item': 'Windows and mirrors cleaned', 'completed': False, 'notes': ''},
|
||||
],
|
||||
'stayover': [
|
||||
{'item': 'Beds made', 'completed': False, 'notes': ''},
|
||||
{'item': 'Trash emptied', 'completed': False, 'notes': ''},
|
||||
{'item': 'Towels replaced', 'completed': False, 'notes': ''},
|
||||
{'item': 'Bathroom cleaned', 'completed': False, 'notes': ''},
|
||||
],
|
||||
'vacant': [
|
||||
{'item': 'Deep clean bathroom', 'completed': False, 'notes': ''},
|
||||
{'item': 'Change linens', 'completed': False, 'notes': ''},
|
||||
{'item': 'Vacuum and mop', 'completed': False, 'notes': ''},
|
||||
{'item': 'Dust surfaces', 'completed': False, 'notes': ''},
|
||||
{'item': 'Check amenities', 'completed': False, 'notes': ''},
|
||||
],
|
||||
'inspection': [
|
||||
{'item': 'Check all amenities', 'completed': False, 'notes': ''},
|
||||
{'item': 'Test electronics', 'completed': False, 'notes': ''},
|
||||
{'item': 'Check for damages', 'completed': False, 'notes': ''},
|
||||
{'item': 'Verify cleanliness', 'completed': False, 'notes': ''},
|
||||
],
|
||||
}
|
||||
|
||||
# Create tasks for today
|
||||
today = datetime.utcnow().replace(hour=9, minute=0, second=0, microsecond=0)
|
||||
task_types = ['checkout', 'stayover', 'vacant', 'inspection']
|
||||
|
||||
created_count = 0
|
||||
skipped_count = 0
|
||||
|
||||
for i, room in enumerate(rooms):
|
||||
# Cycle through task types
|
||||
task_type = task_types[i % len(task_types)]
|
||||
|
||||
# Check if task already exists for this room today
|
||||
existing_task = db.query(HousekeepingTask).filter(
|
||||
HousekeepingTask.room_id == room.id,
|
||||
HousekeepingTask.assigned_to == housekeeping_user.id,
|
||||
HousekeepingTask.status == HousekeepingStatus.pending,
|
||||
HousekeepingTask.scheduled_time >= today.replace(hour=0, minute=0),
|
||||
HousekeepingTask.scheduled_time < today.replace(hour=23, minute=59, second=59)
|
||||
).first()
|
||||
|
||||
if existing_task:
|
||||
print(f" ⚠️ Task already exists for Room {room.room_number}, skipping...")
|
||||
skipped_count += 1
|
||||
continue
|
||||
|
||||
# Schedule tasks at different times throughout the day
|
||||
scheduled_time = today + timedelta(hours=i)
|
||||
|
||||
task = HousekeepingTask(
|
||||
room_id=room.id,
|
||||
booking_id=None,
|
||||
task_type=HousekeepingType(task_type),
|
||||
status=HousekeepingStatus.pending,
|
||||
scheduled_time=scheduled_time,
|
||||
assigned_to=housekeeping_user.id,
|
||||
created_by=admin_user.id if admin_user else housekeeping_user.id,
|
||||
checklist_items=checklists.get(task_type, []),
|
||||
notes=f'Test task for Room {room.room_number} - {task_type} cleaning',
|
||||
estimated_duration_minutes=45 if task_type == 'checkout' else 30
|
||||
)
|
||||
|
||||
db.add(task)
|
||||
created_count += 1
|
||||
print(f" ✓ Created {task_type} task for Room {room.room_number} (scheduled: {scheduled_time.strftime('%Y-%m-%d %H:%M')})")
|
||||
|
||||
db.commit()
|
||||
|
||||
print(f"\n✓ Tasks assigned successfully!")
|
||||
print(f" - Created: {created_count} task(s)")
|
||||
print(f" - Skipped: {skipped_count} task(s) (already exist)")
|
||||
print(f" - Assigned to: {housekeeping_user.email}")
|
||||
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print(f"✗ Error assigning housekeeping tasks: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Assigning test housekeeping tasks to housekeeping user...")
|
||||
print("=" * 60)
|
||||
assign_housekeeping_tasks()
|
||||
print("=" * 60)
|
||||
print("Done!")
|
||||
|
||||
@@ -26,7 +26,8 @@ def seed_roles(db: Session):
|
||||
{'name': 'admin', 'description': 'Administrator with full access'},
|
||||
{'name': 'staff', 'description': 'Staff member with limited admin access'},
|
||||
{'name': 'customer', 'description': 'Regular customer'},
|
||||
{'name': 'accountant', 'description': 'Accountant role with access to financial data, payments, and invoices'}
|
||||
{'name': 'accountant', 'description': 'Accountant role with access to financial data, payments, and invoices'},
|
||||
{'name': 'housekeeping', 'description': 'Housekeeping staff role with access to room cleaning tasks and status updates'}
|
||||
]
|
||||
|
||||
for role_data in roles_data:
|
||||
|
||||
Binary file not shown.
@@ -730,6 +730,46 @@ async def update_booking(id: int, booking_data: UpdateBookingRequest, current_us
|
||||
room.status = RoomStatus.maintenance
|
||||
else:
|
||||
room.status = RoomStatus.cleaning
|
||||
|
||||
# Auto-create housekeeping task for checkout cleaning
|
||||
from ...hotel_services.models.housekeeping_task import HousekeepingTask, HousekeepingStatus, HousekeepingType
|
||||
|
||||
# Check if a pending checkout task already exists for this room
|
||||
existing_task = db.query(HousekeepingTask).filter(
|
||||
and_(
|
||||
HousekeepingTask.room_id == room.id,
|
||||
HousekeepingTask.booking_id == booking.id,
|
||||
HousekeepingTask.task_type == HousekeepingType.checkout,
|
||||
HousekeepingTask.status.in_([HousekeepingStatus.pending, HousekeepingStatus.in_progress])
|
||||
)
|
||||
).first()
|
||||
|
||||
if not existing_task:
|
||||
# Default checklist items for checkout cleaning
|
||||
checkout_checklist = [
|
||||
{'item': 'Bathroom cleaned', 'completed': False, 'notes': ''},
|
||||
{'item': 'Beds made with fresh linens', 'completed': False, 'notes': ''},
|
||||
{'item': 'Trash emptied', 'completed': False, 'notes': ''},
|
||||
{'item': 'Towels replaced', 'completed': False, 'notes': ''},
|
||||
{'item': 'Amenities restocked', 'completed': False, 'notes': ''},
|
||||
{'item': 'Floor vacuumed and mopped', 'completed': False, 'notes': ''},
|
||||
{'item': 'Surfaces dusted', 'completed': False, 'notes': ''},
|
||||
{'item': 'Windows and mirrors cleaned', 'completed': False, 'notes': ''},
|
||||
]
|
||||
|
||||
housekeeping_task = HousekeepingTask(
|
||||
room_id=room.id,
|
||||
booking_id=booking.id,
|
||||
task_type=HousekeepingType.checkout,
|
||||
status=HousekeepingStatus.pending,
|
||||
scheduled_time=datetime.utcnow(), # Schedule immediately
|
||||
created_by=current_user.id,
|
||||
checklist_items=checkout_checklist,
|
||||
notes=f'Auto-created on checkout for booking {booking.booking_number}',
|
||||
estimated_duration_minutes=45 # Default 45 minutes for checkout cleaning
|
||||
)
|
||||
db.add(housekeeping_task)
|
||||
db.flush() # Flush to get the task ID if needed for notifications
|
||||
elif new_status == BookingStatus.cancelled:
|
||||
# Update room status when booking is cancelled
|
||||
if booking.payments:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -346,19 +346,20 @@ async def get_housekeeping_tasks(
|
||||
date: Optional[str] = Query(None),
|
||||
page: int = Query(1, ge=1),
|
||||
limit: int = Query(20, ge=1, le=100),
|
||||
current_user: User = Depends(authorize_roles('admin', 'staff')),
|
||||
current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get housekeeping tasks with filtering"""
|
||||
try:
|
||||
# Check if user is staff (not admin) - staff should only see their assigned tasks
|
||||
# Check user role - housekeeping and staff users should only see their assigned tasks
|
||||
role = db.query(Role).filter(Role.id == current_user.role_id).first()
|
||||
is_staff = role and role.name == 'staff'
|
||||
is_admin = role and role.name == 'admin'
|
||||
is_housekeeping_or_staff = role and role.name in ('housekeeping', 'staff')
|
||||
|
||||
query = db.query(HousekeepingTask)
|
||||
|
||||
# Filter by assigned_to for staff users
|
||||
if is_staff:
|
||||
# Filter by assigned_to for housekeeping and staff users (not admin)
|
||||
if is_housekeeping_or_staff:
|
||||
query = query.filter(HousekeepingTask.assigned_to == current_user.id)
|
||||
|
||||
if room_id:
|
||||
@@ -488,7 +489,7 @@ async def create_housekeeping_task(
|
||||
async def update_housekeeping_task(
|
||||
task_id: int,
|
||||
task_data: dict,
|
||||
current_user: User = Depends(authorize_roles('admin', 'staff')),
|
||||
current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Update a housekeeping task"""
|
||||
@@ -497,22 +498,23 @@ async def update_housekeeping_task(
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail='Housekeeping task not found')
|
||||
|
||||
# Check if user is staff (not admin) - staff can only update their own assigned tasks
|
||||
# Check user role - housekeeping and staff users can only update their own assigned tasks
|
||||
role = db.query(Role).filter(Role.id == current_user.role_id).first()
|
||||
is_staff = role and role.name == 'staff'
|
||||
is_admin = role and role.name == 'admin'
|
||||
is_housekeeping_or_staff = role and role.name in ('housekeeping', 'staff')
|
||||
|
||||
if is_staff:
|
||||
# Staff can only update tasks assigned to them
|
||||
if is_housekeeping_or_staff:
|
||||
# Housekeeping and staff can only update tasks assigned to them
|
||||
if task.assigned_to != current_user.id:
|
||||
raise HTTPException(status_code=403, detail='You can only update tasks assigned to you')
|
||||
# Staff cannot change assignment
|
||||
# Housekeeping and staff cannot change assignment
|
||||
if 'assigned_to' in task_data and task_data.get('assigned_to') != task.assigned_to:
|
||||
raise HTTPException(status_code=403, detail='You cannot change task assignment')
|
||||
|
||||
old_assigned_to = task.assigned_to
|
||||
assigned_to_changed = False
|
||||
|
||||
if 'assigned_to' in task_data and not is_staff:
|
||||
if 'assigned_to' in task_data and is_admin:
|
||||
new_assigned_to = task_data.get('assigned_to')
|
||||
if new_assigned_to != old_assigned_to:
|
||||
task.assigned_to = new_assigned_to
|
||||
@@ -537,6 +539,37 @@ async def update_housekeeping_task(
|
||||
if task.started_at:
|
||||
duration = (task.completed_at - task.started_at).total_seconds() / 60
|
||||
task.actual_duration_minutes = int(duration)
|
||||
|
||||
# Update room status when housekeeping task is completed
|
||||
room = db.query(Room).filter(Room.id == task.room_id).first()
|
||||
if room:
|
||||
# Check if there are other pending housekeeping tasks for this room
|
||||
pending_tasks = db.query(HousekeepingTask).filter(
|
||||
and_(
|
||||
HousekeepingTask.room_id == room.id,
|
||||
HousekeepingTask.id != task.id,
|
||||
HousekeepingTask.status.in_([HousekeepingStatus.pending, HousekeepingStatus.in_progress])
|
||||
)
|
||||
).count()
|
||||
|
||||
# Check if there's active maintenance
|
||||
from ...rooms.models.room_maintenance import RoomMaintenance, MaintenanceStatus
|
||||
active_maintenance = db.query(RoomMaintenance).filter(
|
||||
and_(
|
||||
RoomMaintenance.room_id == room.id,
|
||||
RoomMaintenance.blocks_room == True,
|
||||
RoomMaintenance.status.in_([MaintenanceStatus.scheduled, MaintenanceStatus.in_progress])
|
||||
)
|
||||
).first()
|
||||
|
||||
if active_maintenance:
|
||||
room.status = RoomStatus.maintenance
|
||||
elif pending_tasks > 0:
|
||||
# Keep room as cleaning if there are other pending tasks
|
||||
room.status = RoomStatus.cleaning
|
||||
else:
|
||||
# No pending tasks and no maintenance - room is ready
|
||||
room.status = RoomStatus.available
|
||||
|
||||
if 'checklist_items' in task_data:
|
||||
task.checklist_items = task_data['checklist_items']
|
||||
|
||||
@@ -72,6 +72,26 @@ async def get_amenities(db: Session=Depends(get_db)):
|
||||
logger.error(f'Error fetching amenities: {str(e)}', exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.get('/room-types')
|
||||
async def get_room_types(db: Session=Depends(get_db)):
|
||||
"""Get all room types for dropdowns and forms."""
|
||||
try:
|
||||
room_types = db.query(RoomType).order_by(RoomType.name).all()
|
||||
room_types_list = [
|
||||
{
|
||||
'id': rt.id,
|
||||
'name': rt.name,
|
||||
'description': rt.description,
|
||||
'base_price': float(rt.base_price) if rt.base_price else 0.0,
|
||||
'capacity': rt.capacity,
|
||||
}
|
||||
for rt in room_types
|
||||
]
|
||||
return {'status': 'success', 'data': {'room_types': room_types_list}}
|
||||
except Exception as e:
|
||||
logger.error(f'Error fetching room types: {str(e)}', exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.get('/available')
|
||||
async def search_available_rooms(request: Request, from_date: str=Query(..., alias='from'), to_date: str=Query(..., alias='to'), roomId: Optional[int]=Query(None, alias='roomId'), type: Optional[str]=Query(None), capacity: Optional[int]=Query(None), page: int=Query(1, ge=1), limit: int=Query(12, ge=1, le=100), db: Session=Depends(get_db)):
|
||||
try:
|
||||
@@ -215,19 +235,17 @@ async def get_room_by_number(room_number: str, request: Request, db: Session=Dep
|
||||
@router.post('/', dependencies=[Depends(authorize_roles('admin'))])
|
||||
async def create_room(room_data: CreateRoomRequest, request: Request, current_user: User=Depends(authorize_roles('admin', 'staff')), db: Session=Depends(get_db)):
|
||||
"""Create a new room with validated input using Pydantic schema."""
|
||||
# Start transaction
|
||||
transaction = db.begin()
|
||||
try:
|
||||
# Lock room type to prevent race conditions
|
||||
room_type = db.query(RoomType).filter(RoomType.id == room_data.room_type_id).with_for_update().first()
|
||||
if not room_type:
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=404, detail='Room type not found')
|
||||
|
||||
# Check for duplicate room number with locking
|
||||
existing = db.query(Room).filter(Room.room_number == room_data.room_number).with_for_update().first()
|
||||
if existing:
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=400, detail='Room number already exists')
|
||||
|
||||
# Use price from request or default to room type base price
|
||||
@@ -250,7 +268,7 @@ async def create_room(room_data: CreateRoomRequest, request: Request, current_us
|
||||
db.flush()
|
||||
|
||||
# Commit transaction
|
||||
transaction.commit()
|
||||
db.commit()
|
||||
db.refresh(room)
|
||||
base_url = get_base_url(request)
|
||||
room_dict = {'id': room.id, 'room_type_id': room.room_type_id, 'room_number': room.room_number, 'floor': room.floor, 'status': room.status.value if isinstance(room.status, RoomStatus) else room.status, 'price': float(room.price) if room.price is not None and room.price > 0 else None, 'featured': room.featured, 'description': room.description, 'capacity': room.capacity, 'room_size': room.room_size, 'view': room.view, 'amenities': room.amenities if room.amenities else [], 'created_at': room.created_at.isoformat() if room.created_at else None, 'updated_at': room.updated_at.isoformat() if room.updated_at else None}
|
||||
@@ -262,36 +280,31 @@ async def create_room(room_data: CreateRoomRequest, request: Request, current_us
|
||||
room_dict['room_type'] = {'id': room.room_type.id, 'name': room.room_type.name, 'description': room.room_type.description, 'base_price': float(room.room_type.base_price) if room.room_type.base_price else 0.0, 'capacity': room.room_type.capacity, 'amenities': room.room_type.amenities if room.room_type.amenities else [], 'images': []}
|
||||
return success_response(data={'room': room_dict}, message='Room created successfully')
|
||||
except HTTPException:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
raise
|
||||
except IntegrityError as e:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
logger.error(f'Database integrity error during room creation: {str(e)}')
|
||||
raise HTTPException(status_code=409, detail='Room conflict detected. Please check room number.')
|
||||
except Exception as e:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
logger.error(f'Error creating room: {str(e)}', exc_info=True)
|
||||
raise HTTPException(status_code=500, detail='An error occurred while creating the room')
|
||||
|
||||
@router.put('/{id}', dependencies=[Depends(authorize_roles('admin'))])
|
||||
async def update_room(id: int, room_data: UpdateRoomRequest, request: Request, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
|
||||
"""Update a room with validated input using Pydantic schema."""
|
||||
# Start transaction
|
||||
transaction = db.begin()
|
||||
try:
|
||||
# Lock room row to prevent race conditions
|
||||
room = db.query(Room).filter(Room.id == id).with_for_update().first()
|
||||
if not room:
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=404, detail='Room not found')
|
||||
|
||||
if room_data.room_type_id:
|
||||
room_type = db.query(RoomType).filter(RoomType.id == room_data.room_type_id).first()
|
||||
if not room_type:
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=404, detail='Room type not found')
|
||||
room.room_type_id = room_data.room_type_id
|
||||
|
||||
@@ -299,7 +312,7 @@ async def update_room(id: int, room_data: UpdateRoomRequest, request: Request, c
|
||||
# Check for duplicate room number
|
||||
existing = db.query(Room).filter(Room.room_number == room_data.room_number, Room.id != id).first()
|
||||
if existing:
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=400, detail='Room number already exists')
|
||||
room.room_number = room_data.room_number
|
||||
|
||||
@@ -323,7 +336,7 @@ async def update_room(id: int, room_data: UpdateRoomRequest, request: Request, c
|
||||
room.amenities = room_data.amenities or []
|
||||
|
||||
# Commit transaction
|
||||
transaction.commit()
|
||||
db.commit()
|
||||
db.refresh(room)
|
||||
|
||||
base_url = get_base_url(request)
|
||||
@@ -359,17 +372,14 @@ async def update_room(id: int, room_data: UpdateRoomRequest, request: Request, c
|
||||
}
|
||||
return success_response(data={'room': room_dict}, message='Room updated successfully')
|
||||
except HTTPException:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
raise
|
||||
except IntegrityError as e:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
logger.error(f'Database integrity error during room update: {str(e)}')
|
||||
raise HTTPException(status_code=409, detail='Room conflict detected. Please check room number.')
|
||||
except Exception as e:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
logger.error(f'Error updating room: {str(e)}', exc_info=True)
|
||||
raise HTTPException(status_code=500, detail='An error occurred while updating the room')
|
||||
|
||||
@@ -391,8 +401,6 @@ async def delete_room(id: int, current_user: User=Depends(authorize_roles('admin
|
||||
@router.post('/bulk-delete', dependencies=[Depends(authorize_roles('admin'))])
|
||||
async def bulk_delete_rooms(room_ids: BulkDeleteRoomsRequest, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
|
||||
"""Bulk delete rooms with validated input using Pydantic schema."""
|
||||
# Start transaction
|
||||
transaction = db.begin()
|
||||
try:
|
||||
ids = room_ids.room_ids
|
||||
|
||||
@@ -401,30 +409,27 @@ async def bulk_delete_rooms(room_ids: BulkDeleteRoomsRequest, current_user: User
|
||||
found_ids = [room.id for room in rooms]
|
||||
not_found_ids = [id for id in ids if id not in found_ids]
|
||||
if not_found_ids:
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=404, detail=f'Rooms with IDs {not_found_ids} not found')
|
||||
|
||||
# Delete rooms
|
||||
deleted_count = db.query(Room).filter(Room.id.in_(ids)).delete(synchronize_session=False)
|
||||
|
||||
# Commit transaction
|
||||
transaction.commit()
|
||||
db.commit()
|
||||
return success_response(
|
||||
data={'deleted_count': deleted_count, 'deleted_ids': ids},
|
||||
message=f'Successfully deleted {deleted_count} room(s)'
|
||||
)
|
||||
except HTTPException:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
raise
|
||||
except IntegrityError as e:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
logger.error(f'Database integrity error during bulk room deletion: {str(e)}')
|
||||
raise HTTPException(status_code=409, detail='Cannot delete rooms due to existing relationships (bookings, etc.)')
|
||||
except Exception as e:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
db.rollback()
|
||||
logger.error(f'Error bulk deleting rooms: {str(e)}', exc_info=True)
|
||||
raise HTTPException(status_code=500, detail='An error occurred while deleting rooms')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user