-module(core_booking). -include("records.hrl"). -export([create/3, get_by_id/1, list_by_event/1, list_by_user/1, update/2, delete/1]). -export([count_bookings/0, get_by_event_and_user/2]). -export([list_all/0]). % ← добавлено %% ───────────────────────────────────────────────────────────────── %% Значения по умолчанию для необязательных полей %% ───────────────────────────────────────────────────────────────── -define(DEFAULT_NOTES, <<>>). -define(DEFAULT_REMINDER_SENT, false). -define(DEFAULT_CONFIRMED_AT, {{1970,1,1},{0,0,0}}). %%%------------------------------------------------------------------- %%% @doc Создание бронирования. %%% @end %%%------------------------------------------------------------------- -spec create(EventId :: binary(), UserId :: binary(), Status :: atom()) -> {ok, #booking{}} | {error, term()}. create(EventId, UserId, Status) -> Id = infra_utils:generate_id(16), Now = calendar:universal_time(), Booking = #booking{ id = Id, event_id = EventId, user_id = UserId, status = Status, notes = ?DEFAULT_NOTES, reminder_sent = ?DEFAULT_REMINDER_SENT, confirmed_at = ?DEFAULT_CONFIRMED_AT, created_at = Now, updated_at = Now }, F = fun() -> mnesia:write(Booking), {ok, Booking} end, case mnesia:transaction(F) of {atomic, Result} -> Result; {aborted, Reason} -> {error, Reason} end. %%%------------------------------------------------------------------- %%% @doc Получить бронирование по идентификатору. %%% @end %%%------------------------------------------------------------------- -spec get_by_id(Id :: binary()) -> {ok, #booking{}} | {error, not_found}. get_by_id(Id) -> case mnesia:dirty_read(booking, Id) of [] -> {error, not_found}; [Booking] -> {ok, Booking} end. %%%------------------------------------------------------------------- %%% @doc Список бронирований для события. %%% @end %%%------------------------------------------------------------------- -spec list_by_event(EventId :: binary()) -> {ok, [#booking{}]}. list_by_event(EventId) -> Match = #booking{event_id = EventId, _ = '_'}, Bookings = mnesia:dirty_match_object(Match), {ok, Bookings}. %%%------------------------------------------------------------------- %%% @doc Список бронирований пользователя. %%% @end %%%------------------------------------------------------------------- -spec list_by_user(UserId :: binary()) -> {ok, [#booking{}]}. list_by_user(UserId) -> Match = #booking{user_id = UserId, _ = '_'}, Bookings = mnesia:dirty_match_object(Match), {ok, Bookings}. %%%------------------------------------------------------------------- %%% @doc Получить все бронирования (административный режим). %%% @end %%%------------------------------------------------------------------- -spec list_all() -> [#booking{}]. list_all() -> mnesia:dirty_match_object(#booking{_ = '_'}). %%%------------------------------------------------------------------- %%% @doc Обновить поля бронирования. %%% @end %%%------------------------------------------------------------------- -spec update(Id :: binary(), Updates :: [{atom(), term()}]) -> {ok, #booking{}} | {error, not_found | term()}. update(Id, Updates) -> F = fun() -> case mnesia:read(booking, Id) of [] -> {error, not_found}; [Booking] -> UpdatedBooking = apply_updates(Booking, Updates), mnesia:write(UpdatedBooking), {ok, UpdatedBooking} end end, case mnesia:transaction(F) of {atomic, Result} -> Result; {aborted, Reason} -> {error, Reason} end. %%%------------------------------------------------------------------- %%% @doc Удаление бронирования (физическое). %%% @end %%%------------------------------------------------------------------- -spec delete(Id :: binary()) -> ok | {error, not_found}. delete(Id) -> case mnesia:dirty_read(booking, Id) of [] -> {error, not_found}; [_] -> mnesia:dirty_delete(booking, Id), ok end. %%%------------------------------------------------------------------- %%% @doc Количество бронирований. %%% @end %%%------------------------------------------------------------------- -spec count_bookings() -> non_neg_integer(). count_bookings() -> mnesia:table_info(booking, size). %%%------------------------------------------------------------------- %%% @doc Получить бронирование по событию и пользователю. %%% @end %%%------------------------------------------------------------------- -spec get_by_event_and_user(EventId :: binary(), UserId :: binary()) -> {ok, #booking{}} | {error, not_found}. get_by_event_and_user(EventId, UserId) -> Match = #booking{event_id = EventId, user_id = UserId, _ = '_'}, case mnesia:dirty_match_object(Match) of [] -> {error, not_found}; [Booking] -> {ok, Booking} end. %%%=================================================================== %%% ВНУТРЕННИЕ ФУНКЦИИ %%%=================================================================== apply_updates(Booking, Updates) -> Updated = lists:foldl(fun({Field, Value}, B) -> set_field(Field, Value, B) end, Booking, Updates), Updated#booking{updated_at = calendar:universal_time()}. set_field(status, Value, B) -> B#booking{status = Value}; set_field(notes, Value, B) -> B#booking{notes = Value}; set_field(reminder_sent, Value, B) -> B#booking{reminder_sent = Value}; set_field(confirmed_at, Value, B) -> B#booking{confirmed_at = Value}; set_field(event_id, Value, B) -> B#booking{event_id = Value}; set_field(user_id, Value, B) -> B#booking{user_id = Value}; set_field(_, _, B) -> B.