updates
This commit is contained in:
@@ -346,10 +346,11 @@ async def get_housekeeping_tasks(
|
||||
date: Optional[str] = Query(None),
|
||||
page: int = Query(1, ge=1),
|
||||
limit: int = Query(20, ge=1, le=100),
|
||||
include_cleaning_rooms: bool = Query(True, description='Include rooms in cleaning status even without tasks'),
|
||||
current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get housekeeping tasks with filtering"""
|
||||
"""Get housekeeping tasks with filtering. Also includes rooms in cleaning status."""
|
||||
try:
|
||||
# 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()
|
||||
@@ -359,8 +360,14 @@ async def get_housekeeping_tasks(
|
||||
query = db.query(HousekeepingTask)
|
||||
|
||||
# Filter by assigned_to for housekeeping and staff users (not admin)
|
||||
# But also include unassigned tasks so they can pick them up
|
||||
if is_housekeeping_or_staff:
|
||||
query = query.filter(HousekeepingTask.assigned_to == current_user.id)
|
||||
query = query.filter(
|
||||
or_(
|
||||
HousekeepingTask.assigned_to == current_user.id,
|
||||
HousekeepingTask.assigned_to.is_(None)
|
||||
)
|
||||
)
|
||||
|
||||
if room_id:
|
||||
query = query.filter(HousekeepingTask.room_id == room_id)
|
||||
@@ -379,7 +386,11 @@ async def get_housekeeping_tasks(
|
||||
tasks = query.offset(offset).limit(limit).all()
|
||||
|
||||
result = []
|
||||
task_room_ids = set()
|
||||
|
||||
# Process existing tasks
|
||||
for task in tasks:
|
||||
task_room_ids.add(task.room_id)
|
||||
result.append({
|
||||
'id': task.id,
|
||||
'room_id': task.room_id,
|
||||
@@ -396,9 +407,84 @@ async def get_housekeeping_tasks(
|
||||
'notes': task.notes,
|
||||
'quality_score': task.quality_score,
|
||||
'estimated_duration_minutes': task.estimated_duration_minutes,
|
||||
'actual_duration_minutes': task.actual_duration_minutes
|
||||
'actual_duration_minutes': task.actual_duration_minutes,
|
||||
'room_status': task.room.status.value if task.room else None
|
||||
})
|
||||
|
||||
# Include rooms in cleaning status that don't have tasks (or have unassigned tasks for housekeeping users)
|
||||
if include_cleaning_rooms:
|
||||
rooms_query = db.query(Room).filter(Room.status == RoomStatus.cleaning)
|
||||
|
||||
if room_id:
|
||||
rooms_query = rooms_query.filter(Room.id == room_id)
|
||||
|
||||
# For housekeeping/staff users, also include rooms with unassigned tasks
|
||||
if is_housekeeping_or_staff:
|
||||
# Get room IDs with unassigned tasks
|
||||
unassigned_task_rooms = db.query(HousekeepingTask.room_id).filter(
|
||||
and_(
|
||||
HousekeepingTask.assigned_to.is_(None),
|
||||
HousekeepingTask.status.in_([HousekeepingStatus.pending, HousekeepingStatus.in_progress])
|
||||
)
|
||||
).distinct().all()
|
||||
unassigned_room_ids = [r[0] for r in unassigned_task_rooms]
|
||||
|
||||
# Include rooms in cleaning status OR rooms with unassigned tasks
|
||||
if unassigned_room_ids:
|
||||
rooms_query = db.query(Room).filter(
|
||||
or_(
|
||||
Room.status == RoomStatus.cleaning,
|
||||
Room.id.in_(unassigned_room_ids)
|
||||
)
|
||||
)
|
||||
if room_id:
|
||||
rooms_query = rooms_query.filter(Room.id == room_id)
|
||||
|
||||
cleaning_rooms = rooms_query.all()
|
||||
|
||||
# Add rooms in cleaning status that don't have tasks in current page results
|
||||
for room in cleaning_rooms:
|
||||
if room.id not in task_room_ids:
|
||||
# Check if there are any pending tasks for this room
|
||||
pending_tasks = db.query(HousekeepingTask).filter(
|
||||
and_(
|
||||
HousekeepingTask.room_id == room.id,
|
||||
HousekeepingTask.status.in_([HousekeepingStatus.pending, HousekeepingStatus.in_progress])
|
||||
)
|
||||
).all()
|
||||
|
||||
# For housekeeping/staff, only show if there are unassigned tasks or if room is in cleaning
|
||||
if is_housekeeping_or_staff:
|
||||
has_unassigned = any(t.assigned_to is None for t in pending_tasks)
|
||||
if not has_unassigned and room.status != RoomStatus.cleaning:
|
||||
continue
|
||||
|
||||
# Create a virtual task entry for rooms in cleaning status
|
||||
result.append({
|
||||
'id': None, # No task ID since this is a room status entry
|
||||
'room_id': room.id,
|
||||
'room_number': room.room_number,
|
||||
'booking_id': None,
|
||||
'task_type': 'vacant', # Default task type
|
||||
'status': 'pending',
|
||||
'scheduled_time': datetime.utcnow().isoformat(),
|
||||
'started_at': None,
|
||||
'completed_at': None,
|
||||
'assigned_to': None,
|
||||
'assigned_staff_name': None,
|
||||
'checklist_items': [],
|
||||
'notes': 'Room is in cleaning mode',
|
||||
'quality_score': None,
|
||||
'estimated_duration_minutes': None,
|
||||
'actual_duration_minutes': None,
|
||||
'room_status': room.status.value,
|
||||
'is_room_status_only': True # Flag to indicate this is from room status, not a task
|
||||
})
|
||||
|
||||
# Update total count to include cleaning rooms
|
||||
if include_cleaning_rooms:
|
||||
total = len(result)
|
||||
|
||||
return {
|
||||
'status': 'success',
|
||||
'data': {
|
||||
@@ -418,11 +504,16 @@ async def get_housekeeping_tasks(
|
||||
@router.post('/housekeeping')
|
||||
async def create_housekeeping_task(
|
||||
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)
|
||||
):
|
||||
"""Create a new housekeeping task"""
|
||||
try:
|
||||
# Check user role - housekeeping users can only assign tasks to themselves
|
||||
role = db.query(Role).filter(Role.id == current_user.role_id).first()
|
||||
is_admin = role and role.name == 'admin'
|
||||
is_housekeeping = role and role.name == 'housekeeping'
|
||||
|
||||
room = db.query(Room).filter(Room.id == task_data.get('room_id')).first()
|
||||
if not room:
|
||||
raise HTTPException(status_code=404, detail='Room not found')
|
||||
@@ -430,6 +521,14 @@ async def create_housekeeping_task(
|
||||
scheduled_time = datetime.fromisoformat(task_data['scheduled_time'].replace('Z', '+00:00'))
|
||||
assigned_to = task_data.get('assigned_to')
|
||||
|
||||
# Housekeeping users can only assign tasks to themselves
|
||||
if is_housekeeping and assigned_to and assigned_to != current_user.id:
|
||||
raise HTTPException(status_code=403, detail='Housekeeping users can only assign tasks to themselves')
|
||||
|
||||
# If housekeeping user doesn't specify assigned_to, assign to themselves
|
||||
if is_housekeeping and not assigned_to:
|
||||
assigned_to = current_user.id
|
||||
|
||||
task = HousekeepingTask(
|
||||
room_id=task_data['room_id'],
|
||||
booking_id=task_data.get('booking_id'),
|
||||
@@ -450,8 +549,7 @@ async def create_housekeeping_task(
|
||||
# Send notification to assigned staff member if task is assigned
|
||||
if assigned_to:
|
||||
try:
|
||||
from ..routes.chat_routes import manager
|
||||
assigned_staff = db.query(User).filter(User.id == assigned_to).first()
|
||||
from ...notifications.routes.notification_routes import notification_manager
|
||||
task_data_notification = {
|
||||
'id': task.id,
|
||||
'room_id': task.room_id,
|
||||
@@ -467,13 +565,9 @@ async def create_housekeeping_task(
|
||||
'data': task_data_notification
|
||||
}
|
||||
# Send notification to the specific staff member
|
||||
if assigned_to in manager.staff_connections:
|
||||
try:
|
||||
await manager.staff_connections[assigned_to].send_json(notification_data)
|
||||
except Exception as e:
|
||||
logger.error(f'Error sending housekeeping task notification to staff {assigned_to}: {str(e)}', exc_info=True, extra={'staff_id': assigned_to})
|
||||
await notification_manager.send_to_user(assigned_to, notification_data)
|
||||
except Exception as e:
|
||||
logger.error(f'Error setting up housekeeping task notification: {str(e)}', exc_info=True)
|
||||
logger.error(f'Error sending housekeeping task notification: {str(e)}', exc_info=True)
|
||||
|
||||
return {
|
||||
'status': 'success',
|
||||
@@ -504,21 +598,34 @@ async def update_housekeeping_task(
|
||||
is_housekeeping_or_staff = role and role.name in ('housekeeping', 'staff')
|
||||
|
||||
if is_housekeeping_or_staff:
|
||||
# Housekeeping and staff can only update tasks assigned to them
|
||||
if task.assigned_to != current_user.id:
|
||||
# Housekeeping and staff can start unassigned tasks (assign to themselves)
|
||||
if task.assigned_to is None:
|
||||
# Allow housekeeping users to assign unassigned tasks to themselves
|
||||
if 'status' in task_data and task_data['status'] == 'in_progress':
|
||||
task.assigned_to = current_user.id
|
||||
task_data['assigned_to'] = current_user.id
|
||||
elif task.assigned_to != current_user.id:
|
||||
# If task is assigned, only the assigned user can update it
|
||||
raise HTTPException(status_code=403, detail='You can only update tasks assigned to you')
|
||||
# Housekeeping and staff cannot change assignment
|
||||
if 'assigned_to' in task_data and task_data.get('assigned_to') != task.assigned_to:
|
||||
# Housekeeping and staff cannot change assignment of already assigned tasks
|
||||
if 'assigned_to' in task_data and task.assigned_to is not None 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 is_admin:
|
||||
new_assigned_to = task_data.get('assigned_to')
|
||||
if new_assigned_to != old_assigned_to:
|
||||
task.assigned_to = new_assigned_to
|
||||
assigned_to_changed = True
|
||||
# Handle assignment - admin can assign, housekeeping can self-assign unassigned tasks
|
||||
if 'assigned_to' in task_data:
|
||||
if is_admin:
|
||||
new_assigned_to = task_data.get('assigned_to')
|
||||
if new_assigned_to != old_assigned_to:
|
||||
task.assigned_to = new_assigned_to
|
||||
assigned_to_changed = True
|
||||
elif is_housekeeping_or_staff and task.assigned_to is None:
|
||||
# Housekeeping can assign unassigned tasks to themselves when starting
|
||||
if task_data.get('assigned_to') == current_user.id:
|
||||
task.assigned_to = current_user.id
|
||||
assigned_to_changed = True
|
||||
|
||||
if 'status' in task_data:
|
||||
new_status = HousekeepingStatus(task_data['status'])
|
||||
@@ -534,6 +641,9 @@ async def update_housekeeping_task(
|
||||
|
||||
if new_status == HousekeepingStatus.in_progress and not task.started_at:
|
||||
task.started_at = datetime.utcnow()
|
||||
# If task was unassigned, assign it to the current user
|
||||
if task.assigned_to is None and is_housekeeping_or_staff:
|
||||
task.assigned_to = current_user.id
|
||||
elif new_status == HousekeepingStatus.completed and not task.completed_at:
|
||||
task.completed_at = datetime.utcnow()
|
||||
if task.started_at:
|
||||
@@ -568,8 +678,23 @@ async def update_housekeeping_task(
|
||||
# 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
|
||||
# No pending tasks and no maintenance - room is ready for check-in
|
||||
# Check if there are any upcoming bookings for this room
|
||||
from ...bookings.models.booking import Booking, BookingStatus
|
||||
upcoming_booking = db.query(Booking).filter(
|
||||
and_(
|
||||
Booking.room_id == room.id,
|
||||
Booking.status == BookingStatus.confirmed,
|
||||
Booking.check_in_date <= datetime.utcnow() + timedelta(days=1)
|
||||
)
|
||||
).first()
|
||||
|
||||
if upcoming_booking:
|
||||
# Room has upcoming booking, keep as available (ready for check-in)
|
||||
room.status = RoomStatus.available
|
||||
else:
|
||||
# No upcoming bookings, room is available
|
||||
room.status = RoomStatus.available
|
||||
|
||||
if 'checklist_items' in task_data:
|
||||
task.checklist_items = task_data['checklist_items']
|
||||
@@ -591,7 +716,7 @@ async def update_housekeeping_task(
|
||||
# Send notification if assignment changed
|
||||
if assigned_to_changed and task.assigned_to:
|
||||
try:
|
||||
from ..routes.chat_routes import manager
|
||||
from ...notifications.routes.notification_routes import notification_manager
|
||||
room = db.query(Room).filter(Room.id == task.room_id).first()
|
||||
task_data_notification = {
|
||||
'id': task.id,
|
||||
@@ -608,13 +733,9 @@ async def update_housekeeping_task(
|
||||
'data': task_data_notification
|
||||
}
|
||||
# Send notification to the newly assigned staff member
|
||||
if task.assigned_to in manager.staff_connections:
|
||||
try:
|
||||
await manager.staff_connections[task.assigned_to].send_json(notification_data)
|
||||
except Exception as e:
|
||||
logger.error(f'Error sending housekeeping task notification to staff {task.assigned_to}: {str(e)}', exc_info=True, extra={'staff_id': task.assigned_to})
|
||||
await notification_manager.send_to_user(task.assigned_to, notification_data)
|
||||
except Exception as e:
|
||||
logger.error(f'Error setting up housekeeping task notification: {str(e)}', exc_info=True)
|
||||
logger.error(f'Error sending housekeeping task notification: {str(e)}', exc_info=True)
|
||||
|
||||
return {
|
||||
'status': 'success',
|
||||
|
||||
Reference in New Issue
Block a user