250 lines
10 KiB
Erlang
Executable File
250 lines
10 KiB
Erlang
Executable File
-module(logic_booking).
|
|
-include("records.hrl").
|
|
-export([create_booking/2, confirm_booking/2, confirm_booking/3,
|
|
cancel_booking/2, cancel_booking/3, get_booking/2,
|
|
list_bookings/2, list_user_bookings/1, delete_booking/2,
|
|
list_bookings_admin/0, get_booking_admin/1,
|
|
list_event_bookings/1, list_event_bookings/2]).
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Создание бронирования со статусом `pending`.
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec create_booking(UserId :: binary(), EventId :: binary()) ->
|
|
{ok, #booking{}} | {error, full | already_booked | not_found}.
|
|
create_booking(UserId, EventId) ->
|
|
case core_event:get_by_id(EventId) of
|
|
{ok, Event} ->
|
|
case check_capacity(EventId, Event#event.capacity) of
|
|
{ok, _} ->
|
|
case core_booking:get_by_event_and_user(EventId, UserId) of
|
|
{error, not_found} ->
|
|
core_booking:create(EventId, UserId, pending);
|
|
{ok, _} ->
|
|
{error, already_booked}
|
|
end;
|
|
{error, full} ->
|
|
{error, full}
|
|
end;
|
|
{error, not_found} ->
|
|
{error, not_found}
|
|
end.
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Подтверждение бронирования (двухарная версия).
|
|
%%% Только владелец календаря события или admin.
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec confirm_booking(BookingId :: binary(), UserId :: binary()) ->
|
|
{ok, #booking{}} | {error, not_found | access_denied}.
|
|
confirm_booking(BookingId, UserId) ->
|
|
case core_booking:get_by_id(BookingId) of
|
|
{ok, Booking} ->
|
|
case can_manage_event_bookings(UserId, Booking#booking.event_id) of
|
|
true ->
|
|
case Booking#booking.status of
|
|
pending ->
|
|
Now = calendar:universal_time(),
|
|
core_booking:update(BookingId, [{status, confirmed}, {confirmed_at, Now}]);
|
|
_ ->
|
|
{error, access_denied}
|
|
end;
|
|
{error, Reason} ->
|
|
{error, Reason}
|
|
end;
|
|
Error -> Error
|
|
end.
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Подтверждение или отклонение бронирования (для обработчиков).
|
|
%%% `decline` переводит pending-заявку в `cancelled`.
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec confirm_booking(UserId :: binary(), BookingId :: binary(), confirm | decline) ->
|
|
{ok, #booking{}} | {error, not_found | access_denied}.
|
|
confirm_booking(UserId, BookingId, confirm) ->
|
|
confirm_booking(BookingId, UserId);
|
|
confirm_booking(UserId, BookingId, decline) ->
|
|
case core_booking:get_by_id(BookingId) of
|
|
{ok, Booking} ->
|
|
case can_manage_event_bookings(UserId, Booking#booking.event_id) of
|
|
true ->
|
|
case Booking#booking.status of
|
|
pending ->
|
|
core_booking:update(BookingId, [{status, cancelled}]);
|
|
_ ->
|
|
{error, access_denied}
|
|
end;
|
|
{error, Reason} ->
|
|
{error, Reason}
|
|
end;
|
|
Error -> Error
|
|
end.
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Отмена бронирования (двухарная версия).
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec cancel_booking(BookingId :: binary(), UserId :: binary()) ->
|
|
{ok, #booking{}} | {error, not_found | access_denied}.
|
|
cancel_booking(BookingId, UserId) ->
|
|
case core_booking:get_by_id(BookingId) of
|
|
{ok, Booking} ->
|
|
case Booking#booking.status of
|
|
cancelled ->
|
|
{ok, Booking};
|
|
_ ->
|
|
case Booking#booking.user_id =:= UserId of
|
|
true -> core_booking:update(BookingId, [{status, cancelled}]);
|
|
false -> {error, access_denied}
|
|
end
|
|
end;
|
|
Error -> Error
|
|
end.
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Отмена бронирования (трёхарная версия для обработчиков).
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec cancel_booking(UserId :: binary(), BookingId :: binary(), cancel) ->
|
|
{ok, #booking{}} | {error, not_found | access_denied}.
|
|
cancel_booking(UserId, BookingId, cancel) ->
|
|
cancel_booking(BookingId, UserId).
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Получение бронирования по ID.
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec get_booking(BookingId :: binary(), UserId :: binary()) ->
|
|
{ok, #booking{}} | {error, not_found | access_denied}.
|
|
get_booking(BookingId, UserId) ->
|
|
case core_booking:get_by_id(BookingId) of
|
|
{ok, Booking} ->
|
|
case Booking#booking.user_id =:= UserId of
|
|
true -> {ok, Booking};
|
|
false -> {error, access_denied}
|
|
end;
|
|
Error -> Error
|
|
end.
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Список бронирований события.
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec list_bookings(EventId :: binary(), UserId :: binary()) ->
|
|
{ok, [#booking{}]}.
|
|
list_bookings(EventId, UserId) ->
|
|
{ok, Bookings} = core_booking:list_by_event(EventId),
|
|
Filtered = case admin_utils:is_admin(UserId) of
|
|
true -> Bookings;
|
|
false -> [B || B <- Bookings, B#booking.user_id =:= UserId]
|
|
end,
|
|
{ok, Filtered}.
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Список бронирований пользователя.
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec list_user_bookings(UserId :: binary()) -> {ok, [#booking{}]}.
|
|
list_user_bookings(UserId) ->
|
|
core_booking:list_by_user(UserId).
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Удаление бронирования (только владелец).
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec delete_booking(BookingId :: binary(), UserId :: binary()) ->
|
|
ok | {error, not_found | access_denied}.
|
|
delete_booking(BookingId, UserId) ->
|
|
case core_booking:get_by_id(BookingId) of
|
|
{ok, Booking} ->
|
|
case Booking#booking.user_id =:= UserId of
|
|
true -> core_booking:delete(BookingId);
|
|
false -> {error, access_denied}
|
|
end;
|
|
Error -> Error
|
|
end.
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Административное получение бронирования.
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec get_booking_admin(BookingId :: binary()) ->
|
|
{ok, #booking{}} | {error, not_found}.
|
|
get_booking_admin(BookingId) ->
|
|
core_booking:get_by_id(BookingId).
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Список бронирований события (административный / внутренний).
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec list_event_bookings(EventId :: binary()) -> {ok, [#booking{}]}.
|
|
list_event_bookings(EventId) ->
|
|
core_booking:list_by_event(EventId).
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Список бронирований события для владельца календаря (или admin).
|
|
%%% Возвращает полный список заявок без фильтрации по участнику.
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec list_event_bookings(UserId :: binary(), EventId :: binary()) ->
|
|
{ok, [#booking{}]} | {error, not_found | access_denied}.
|
|
list_event_bookings(UserId, EventId) ->
|
|
case can_manage_event_bookings(UserId, EventId) of
|
|
true ->
|
|
core_booking:list_by_event(EventId);
|
|
{error, Reason} ->
|
|
{error, Reason}
|
|
end.
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Список всех бронирований (административный).
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec list_bookings_admin() -> {ok, [#booking{}]}.
|
|
list_bookings_admin() ->
|
|
{ok, core_booking:list_all()}.
|
|
|
|
%%%===================================================================
|
|
%%% ВНУТРЕННИЕ ФУНКЦИИ
|
|
%%%===================================================================
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Владелец календаря события или admin может управлять заявками.
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec can_manage_event_bookings(UserId :: binary(), EventId :: binary()) ->
|
|
true | {error, not_found | access_denied}.
|
|
can_manage_event_bookings(UserId, EventId) ->
|
|
case core_event:get_by_id(EventId) of
|
|
{ok, Event} ->
|
|
case core_calendar:get_by_id(Event#event.calendar_id) of
|
|
{ok, Calendar} ->
|
|
case Calendar#calendar.owner_id =:= UserId
|
|
orelse admin_utils:is_admin(UserId) of
|
|
true -> true;
|
|
false -> {error, access_denied}
|
|
end;
|
|
{error, not_found} ->
|
|
{error, not_found}
|
|
end;
|
|
{error, not_found} ->
|
|
{error, not_found}
|
|
end.
|
|
|
|
%%%-------------------------------------------------------------------
|
|
%%% @doc Проверка вместимости события.
|
|
%%% `undefined` и `0` означают неограниченную вместимость.
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-spec check_capacity(EventId :: binary(), Capacity :: integer() | undefined) ->
|
|
{ok, integer() | unlimited} | {error, full}.
|
|
check_capacity(_EventId, undefined) -> {ok, unlimited};
|
|
check_capacity(_EventId, 0) -> {ok, unlimited};
|
|
check_capacity(EventId, Capacity) ->
|
|
{ok, Bookings} = core_booking:list_by_event(EventId),
|
|
ConfirmedCount = length([B || B <- Bookings, B#booking.status =:= confirmed]),
|
|
case ConfirmedCount < Capacity of
|
|
true -> {ok, Capacity - ConfirmedCount};
|
|
false -> {error, full}
|
|
end. |