@@ -46,6 +46,23 @@ defmodule Atlas.Exchange do
4646 |> Repo . all ( )
4747 end
4848
49+ @ doc """
50+ Returns the list of pending shift exchange requests.
51+
52+ ## Examples
53+
54+ iex> list_pending_shift_exchange_requests()
55+ [%ShiftExchangeRequest{}, ...]
56+
57+ """
58+ def list_pending_shift_exchange_requests ( opts \\ [ ] ) do
59+ ShiftExchangeRequest
60+ |> apply_filters ( opts )
61+ |> where ( [ r ] , r . status == :pending )
62+ |> order_by ( [ r ] , asc: r . inserted_at )
63+ |> Repo . all ( )
64+ end
65+
4966 @ doc """
5067 Gets a single shift_exchange_request.
5168
@@ -239,6 +256,32 @@ defmodule Atlas.Exchange do
239256 end )
240257 end
241258
259+ def maybe_auto_approve_pending_requests ( opts \\ [ ] ) do
260+ pending_requests = list_pending_shift_exchange_requests ( opts )
261+
262+ Enum . each ( pending_requests , fn req ->
263+ case Repo . transaction ( maybe_auto_approve_request ( req ) ) do
264+ { :ok , _changes } ->
265+ # Reload student and shift for notification email
266+ user = University . get_student! ( req . student_id , preloads: [ :user ] ) . user
267+ shift_to = Shifts . get_shift! ( req . shift_to , preloads: [ :course ] )
268+
269+ UserNotifier . deliver_shift_exchange_request_fulfilled (
270+ user ,
271+ shift_to . course . name ,
272+ Shifts.Shift . short_name ( shift_to )
273+ )
274+
275+ { :error , :shift_has_space , :no_space , _ } ->
276+ # Couldn't auto approve → do nothing
277+ :ok
278+
279+ { :error , _step , _reason , _changes } ->
280+ :ok
281+ end
282+ end )
283+ end
284+
242285 ## Graph-related utility functions
243286
244287 defp build_graph ( requests ) do
@@ -378,4 +421,50 @@ defmodule Atlas.Exchange do
378421 { :error , :transaction_failed }
379422 end
380423 end
424+
425+ defp maybe_auto_approve_request ( % ShiftExchangeRequest { } = req ) do
426+ Multi . new ( )
427+ |> Multi . run ( :shift_has_space , fn _repo , _changes ->
428+ enrolled_count =
429+ Repo . one (
430+ from ( se in ShiftEnrollment ,
431+ where: se . shift_id == ^ req . shift_to and se . status in [ :active , :inactive ] ,
432+ select: count ( se . student_id , :distinct )
433+ )
434+ )
435+
436+ shift = Shifts . get_shift! ( req . shift_to )
437+ from_shift = Shifts . get_shift! ( req . shift_from )
438+ from_shift_occupation = University . get_shift_enrollment_count ( req . shift_from )
439+
440+ cond do
441+ from_shift_occupation - 1 <= round ( from_shift . capacity * 0.8 ) ->
442+ { :error , :shift_from_underoccupied }
443+
444+ enrolled_count < shift . capacity ->
445+ { :ok , :has_space }
446+
447+ true ->
448+ { :error , :no_space }
449+ end
450+ end )
451+ |> Multi . update (
452+ :approve_request ,
453+ Ecto.Changeset . change ( req , status: :approved )
454+ )
455+ |> Multi . delete_all (
456+ :delete_from_enrollment ,
457+ from ( se in ShiftEnrollment ,
458+ where: se . student_id == ^ req . student_id and se . shift_id == ^ req . shift_from
459+ )
460+ )
461+ |> Multi . insert (
462+ :insert_to_enrollment ,
463+ ShiftEnrollment . changeset ( % ShiftEnrollment { } , % {
464+ student_id: req . student_id ,
465+ shift_id: req . shift_to ,
466+ status: :active
467+ } )
468+ )
469+ end
381470end
0 commit comments