@@ -26,7 +26,7 @@ def tea():
2626 floor = _get_active_floor (user )
2727 floor_users = tenant_filter (User .query ).filter_by (floor = floor ).all ()
2828
29- if request .method == 'POST' and user .role in ['admin' , 'teaManager' ]:
29+ if request .method == 'POST' and user .role in ['admin' , 'teaManager' , 'pantryHead' ]:
3030 try :
3131 task_date = datetime .strptime (request .form .get ('date' ), '%Y-%m-%d' ).date ()
3232 except Exception :
@@ -66,6 +66,14 @@ def tea():
6666 flash ('tea task added successfully' , 'success' )
6767 return redirect (url_for ('ops.tea' ))
6868
69+ # Auto-complete past pending tasks for this floor
70+ tenant_filter (TeaTask .query ).filter (
71+ TeaTask .floor == floor ,
72+ TeaTask .date < date .today (),
73+ TeaTask .status == 'pending'
74+ ).update ({TeaTask .status : 'completed' }, synchronize_session = False )
75+ db .session .commit ()
76+
6977 month_param = (request .args .get ('month' ) or '' ).strip ()
7078 today = date .today ()
7179 if month_param :
@@ -117,6 +125,7 @@ def tea():
117125 selected_month = month_param ,
118126 month_start = month_start ,
119127 current_user = user ,
128+ today = today
120129 )
121130
122131@ops_bp .route ('/tea/complete/<int:task_id>' , methods = ['POST' ])
@@ -125,7 +134,7 @@ def complete_tea_task(task_id):
125134 if not user :
126135 return ('' , 401 )
127136
128- if user .role not in ['admin' , 'teaManager' ]:
137+ if user .role not in ['admin' , 'teaManager' , 'pantryHead' ]:
129138 return ('' , 403 )
130139
131140 task = tenant_filter (TeaTask .query ).filter_by (id = task_id ).first ()
@@ -138,6 +147,163 @@ def complete_tea_task(task_id):
138147 db .session .commit ()
139148 return ('' , 204 )
140149
150+ @ops_bp .route ('/tea/bulk-assign' , methods = ['POST' ])
151+ def bulk_assign_tea ():
152+ user = _require_user ()
153+ if not user or user .role not in ['admin' , 'teaManager' , 'pantryHead' ]:
154+ abort (403 )
155+
156+ floor = _get_active_floor (user )
157+ try :
158+ start_date = datetime .strptime (request .form .get ('start_date' ), '%Y-%m-%d' ).date ()
159+ end_date = datetime .strptime (request .form .get ('end_date' ), '%Y-%m-%d' ).date ()
160+ user_ids = request .form .getlist ('user_ids' )
161+ except Exception :
162+ flash ('Invalid rotation parameters' , 'error' )
163+ return redirect (url_for ('ops.tea' ))
164+
165+ if not user_ids :
166+ flash ('Please select at least one user for rotation.' , 'error' )
167+ return redirect (url_for ('ops.tea' ))
168+
169+ if start_date > end_date :
170+ flash ('Start date cannot be after end_date.' , 'error' )
171+ return redirect (url_for ('ops.tea' ))
172+
173+ current_date = start_date
174+ user_index = 0
175+ tasks_created = 0
176+ from datetime import timedelta
177+
178+ while current_date <= end_date :
179+ # Check if task already exists for this floor and date
180+ existing = tenant_filter (TeaTask .query ).filter_by (floor = floor , date = current_date ).first ()
181+ if not existing :
182+ # Try to find the next available user in the rotation
183+ found_user = False
184+ for _ in range (len (user_ids )):
185+ target_user_id = int (user_ids [user_index % len (user_ids )])
186+
187+ # Check for approved absence request
188+ absence = tenant_filter (Request .query ).filter (
189+ Request .user_id == target_user_id ,
190+ Request .status == 'approved' ,
191+ Request .request_type == 'absence' ,
192+ Request .start_date <= current_date ,
193+ Request .end_date >= current_date
194+ ).first ()
195+
196+ if not absence :
197+ # Found an available user
198+ new_task = TeaTask (
199+ date = current_date ,
200+ assigned_to_id = target_user_id ,
201+ floor = floor ,
202+ created_by_id = user .id ,
203+ tenant_id = getattr (g , 'tenant_id' , None )
204+ )
205+ db .session .add (new_task )
206+ tasks_created += 1
207+ user_index += 1 # Move to next user for next date
208+ found_user = True
209+ break
210+ else :
211+ # User is on leave, try the next one for THIS same date
212+ user_index += 1
213+
214+ # if no user was found available for this date, the date is skipped
215+
216+ current_date += timedelta (days = 1 )
217+
218+ db .session .commit ()
219+ flash (f'Successfully generated { tasks_created } tea tasks.' , 'success' )
220+ return redirect (url_for ('ops.tea' ))
221+
222+ @ops_bp .route ('/tea/bulk-assign-preview' , methods = ['POST' ])
223+ def bulk_assign_preview ():
224+ user = _require_user ()
225+ if not user or user .role not in ['admin' , 'teaManager' , 'pantryHead' ]:
226+ return jsonify ({"error" : "unauthorized" }), 403
227+
228+ floor = _get_active_floor (user )
229+ try :
230+ data = request .get_json ()
231+ start_date = datetime .strptime (data .get ('start_date' ), '%Y-%m-%d' ).date ()
232+ end_date = datetime .strptime (data .get ('end_date' ), '%Y-%m-%d' ).date ()
233+ user_ids = [int (uid ) for uid in data .get ('user_ids' , [])]
234+ except Exception :
235+ return jsonify ({"error" : "invalid_parameters" }), 400
236+
237+ if not user_ids :
238+ return jsonify ({"error" : "no_users_selected" }), 400
239+
240+ current_date = start_date
241+ user_index = 0
242+ preview_tasks = []
243+ from datetime import timedelta
244+
245+ # Get floor user names for labeling
246+ users_map = {u .id : (u .full_name or u .username or u .email ) for u in tenant_filter (User .query ).filter_by (floor = floor ).all ()}
247+
248+ while current_date <= end_date :
249+ existing = tenant_filter (TeaTask .query ).filter_by (floor = floor , date = current_date ).first ()
250+ if not existing :
251+ found_user = False
252+ for _ in range (len (user_ids )):
253+ target_user_id = user_ids [user_index % len (user_ids )]
254+ absence = tenant_filter (Request .query ).filter (
255+ Request .user_id == target_user_id ,
256+ Request .status == 'approved' ,
257+ Request .request_type == 'absence' ,
258+ Request .start_date <= current_date ,
259+ Request .end_date >= current_date
260+ ).first ()
261+
262+ if not absence :
263+ preview_tasks .append ({
264+ "date" : current_date .strftime ('%Y-%m-%d' ),
265+ "day" : current_date .strftime ('%A' ),
266+ "user_id" : target_user_id ,
267+ "user_name" : users_map .get (target_user_id , "Unknown" )
268+ })
269+ user_index += 1
270+ found_user = True
271+ break
272+ else :
273+ user_index += 1
274+
275+ current_date += timedelta (days = 1 )
276+
277+ return jsonify ({"tasks" : preview_tasks })
278+
279+ @ops_bp .route ('/tea/fail/<int:task_id>' , methods = ['POST' ])
280+ def fail_tea_task (task_id ):
281+ user = _require_user ()
282+ if not user or user .role not in ['admin' , 'teaManager' , 'pantryHead' ]:
283+ return ('' , 403 )
284+
285+ task = tenant_filter (TeaTask .query ).filter_by (id = task_id ).first ()
286+ if not task : return ('' , 404 )
287+ if user .role != 'admin' and task .floor != user .floor : return ('' , 403 )
288+
289+ task .status = 'failed'
290+ db .session .commit ()
291+ return ('' , 204 )
292+
293+ @ops_bp .route ('/tea/pending/<int:task_id>' , methods = ['POST' ])
294+ def reset_tea_task (task_id ):
295+ user = _require_user ()
296+ if not user or user .role not in ['admin' , 'teaManager' , 'pantryHead' ]:
297+ return ('' , 403 )
298+
299+ task = tenant_filter (TeaTask .query ).filter_by (id = task_id ).first ()
300+ if not task : return ('' , 404 )
301+ if user .role != 'admin' and task .floor != user .floor : return ('' , 403 )
302+
303+ task .status = 'pending'
304+ db .session .commit ()
305+ return ('' , 204 )
306+
141307@ops_bp .route ('/requests' , methods = ['GET' , 'POST' ])
142308def requests ():
143309 user = _require_user ()
0 commit comments