Избавление от undefined, всем необязательным полям присваиваются дефолтные значения #22
This commit is contained in:
+153
-156
@@ -1,191 +1,188 @@
|
||||
-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]).
|
||||
|
||||
-export([create_booking/2, confirm_booking/3, cancel_booking/2]).
|
||||
-export([get_booking/2, list_event_bookings/2, list_user_bookings/1]).
|
||||
-export([auto_confirm/1, check_timeout_confirmations/0]).
|
||||
|
||||
%% Создание бронирования (запись на событие)
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @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 Event#event.status of
|
||||
active ->
|
||||
% Проверяем календарь для политики подтверждения
|
||||
case core_calendar:get_by_id(Event#event.calendar_id) of
|
||||
{ok, Calendar} ->
|
||||
case Calendar#calendar.status of
|
||||
active ->
|
||||
% Проверяем, что есть места
|
||||
case check_capacity(EventId, Event#event.capacity) of
|
||||
{ok, _} ->
|
||||
% Проверяем, не записан ли уже пользователь
|
||||
case core_booking:get_by_event_and_user(EventId, UserId) of
|
||||
{error, not_found} ->
|
||||
ActualEventId = get_actual_event_id(Event, UserId),
|
||||
case core_booking:create(ActualEventId, UserId) of
|
||||
{ok, Booking} ->
|
||||
handle_confirmation_policy(Booking, Event, Calendar),
|
||||
logic_notification:notify_booking(UserId, Booking), % ← Уведомление
|
||||
{ok, Booking};
|
||||
Error ->
|
||||
Error
|
||||
end;
|
||||
{ok, _} ->
|
||||
{error, already_booked}
|
||||
end;
|
||||
{error, full} ->
|
||||
{error, event_full}
|
||||
end;
|
||||
_ ->
|
||||
{error, calendar_not_active}
|
||||
end;
|
||||
_ ->
|
||||
{error, calendar_not_found}
|
||||
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, event_not_active}
|
||||
{error, full} ->
|
||||
{error, full}
|
||||
end;
|
||||
Error ->
|
||||
Error
|
||||
{error, not_found} ->
|
||||
{error, not_found}
|
||||
end.
|
||||
|
||||
%% Подтверждение бронирования (владельцем календаря)
|
||||
confirm_booking(UserId, BookingId, Action) when Action =:= confirm; Action =:= decline ->
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @doc Подтверждение бронирования (двухарная версия).
|
||||
%%% @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 logic_event:get_event(UserId, Booking#booking.event_id) of
|
||||
{ok, Event} ->
|
||||
% Проверяем, что пользователь может редактировать календарь
|
||||
case logic_calendar:get_calendar(UserId, Event#event.calendar_id) of
|
||||
{ok, Calendar} ->
|
||||
case logic_calendar:can_edit(UserId, Calendar) of
|
||||
true ->
|
||||
NewStatus = case Action of
|
||||
confirm -> confirmed;
|
||||
decline -> cancelled
|
||||
end,
|
||||
case core_booking:update_status(BookingId, NewStatus) of
|
||||
{ok, Updated} ->
|
||||
logic_notification:notify_booking(Updated#booking.user_id, Updated),
|
||||
{ok, Updated};
|
||||
Error -> Error
|
||||
end;
|
||||
false ->
|
||||
{error, access_denied}
|
||||
end;
|
||||
Error -> Error
|
||||
end;
|
||||
Error -> Error
|
||||
case Booking#booking.status of
|
||||
pending ->
|
||||
Now = calendar:universal_time(),
|
||||
core_booking:update(BookingId, [{status, confirmed}, {confirmed_at, Now}]);
|
||||
_ ->
|
||||
{error, access_denied}
|
||||
end;
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
%% Отмена бронирования (участником)
|
||||
cancel_booking(UserId, BookingId) ->
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @doc Подтверждение бронирования (трёхарная версия для обработчиков).
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-spec confirm_booking(UserId :: binary(), BookingId :: binary(), confirm) ->
|
||||
{ok, #booking{}} | {error, not_found | access_denied}.
|
||||
confirm_booking(UserId, BookingId, confirm) ->
|
||||
confirm_booking(BookingId, UserId).
|
||||
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @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 ->
|
||||
core_booking:update_status(BookingId, cancelled);
|
||||
false ->
|
||||
{error, access_denied}
|
||||
true -> {ok, Booking};
|
||||
false -> {error, access_denied}
|
||||
end;
|
||||
Error ->
|
||||
Error
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
%% Получение бронирования
|
||||
get_booking(UserId, BookingId) ->
|
||||
case core_booking:get_by_id(BookingId) of
|
||||
{ok, Booking} ->
|
||||
% Проверяем доступ к событию
|
||||
case logic_event:get_event(UserId, Booking#booking.event_id) of
|
||||
{ok, _} -> {ok, Booking};
|
||||
Error -> Error
|
||||
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}.
|
||||
|
||||
%% Список бронирований события (для владельца)
|
||||
list_event_bookings(UserId, EventId) ->
|
||||
case logic_event:get_event(UserId, EventId) of
|
||||
{ok, Event} ->
|
||||
% Проверяем права на календарь
|
||||
case logic_calendar:get_calendar(UserId, Event#event.calendar_id) of
|
||||
{ok, Calendar} ->
|
||||
case logic_calendar:can_edit(UserId, Calendar) of
|
||||
true ->
|
||||
core_booking:list_by_event(EventId);
|
||||
false ->
|
||||
{error, access_denied}
|
||||
end;
|
||||
Error ->
|
||||
Error
|
||||
end;
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
%% Список бронирований пользователя
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @doc Список бронирований пользователя.
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-spec list_user_bookings(UserId :: binary()) -> {ok, [#booking{}]}.
|
||||
list_user_bookings(UserId) ->
|
||||
core_booking:list_by_user(UserId).
|
||||
|
||||
%% Автоматическое подтверждение (для политики auto)
|
||||
auto_confirm(BookingId) ->
|
||||
core_booking:update_status(BookingId, confirmed).
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @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.
|
||||
|
||||
%% Проверка истечения timeout подтверждений
|
||||
check_timeout_confirmations() ->
|
||||
% Получаем все pending бронирования для календарей с timeout
|
||||
% В реальной реализации нужно периодически вызывать эту функцию
|
||||
ok.
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @doc Административное получение бронирования.
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-spec get_booking_admin(BookingId :: binary()) ->
|
||||
{ok, #booking{}} | {error, not_found}.
|
||||
get_booking_admin(BookingId) ->
|
||||
core_booking:get_by_id(BookingId).
|
||||
|
||||
%% Внутренние функции
|
||||
check_capacity(_EventId, undefined) ->
|
||||
{ok, unlimited};
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @doc Список бронирований события (административный).
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-spec list_event_bookings(EventId :: binary()) -> {ok, [#booking{}]}.
|
||||
list_event_bookings(EventId) ->
|
||||
core_booking:list_by_event(EventId).
|
||||
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @doc Список всех бронирований (административный).
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-spec list_bookings_admin() -> {ok, [#booking{}]}.
|
||||
list_bookings_admin() ->
|
||||
{ok, core_booking:list_all()}.
|
||||
|
||||
%%%===================================================================
|
||||
%%% ВНУТРЕННИЕ ФУНКЦИИ
|
||||
%%%===================================================================
|
||||
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @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.
|
||||
|
||||
get_actual_event_id(Event, _UserId) ->
|
||||
case Event#event.event_type of
|
||||
recurring ->
|
||||
% Для повторяющихся событий нужно материализовать вхождение
|
||||
% Здесь предполагается, что start_time передаётся в запросе
|
||||
% В полной реализации нужно получать occurrence_start из параметров
|
||||
Event#event.id;
|
||||
single ->
|
||||
Event#event.id
|
||||
end.
|
||||
|
||||
handle_confirmation_policy(Booking, _Event, Calendar) ->
|
||||
io:format("Confirmation policy: ~p~n", [Calendar#calendar.confirmation]),
|
||||
case Calendar#calendar.confirmation of
|
||||
auto ->
|
||||
io:format("Auto-confirming booking ~p~n", [Booking#booking.id]),
|
||||
auto_confirm(Booking#booking.id);
|
||||
manual ->
|
||||
io:format("Manual confirmation, leaving pending~n"),
|
||||
ok;
|
||||
{timeout, Seconds} ->
|
||||
io:format("Timeout confirmation: ~p seconds~n", [Seconds]),
|
||||
spawn(fun() ->
|
||||
timer:sleep(Seconds * 1000),
|
||||
case core_booking:get_by_id(Booking#booking.id) of
|
||||
{ok, B} when B#booking.status =:= pending ->
|
||||
auto_confirm(Booking#booking.id);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end)
|
||||
end.
|
||||
Reference in New Issue
Block a user