spazio-solazzo/lib/spazio_solazzo/booking_system/booking.ex
2026-01-10 19:03:02 +01:00

192 lines
5.6 KiB
Elixir

defmodule SpazioSolazzo.BookingSystem.Booking do
@moduledoc """
Represents a customer booking with state management for reservation lifecycle.
"""
use Ash.Resource,
otp_app: :spazio_solazzo,
domain: SpazioSolazzo.BookingSystem,
data_layer: AshPostgres.DataLayer,
notifiers: [Ash.Notifier.PubSub],
authorizers: [Ash.Policy.Authorizer],
extensions: [AshStateMachine]
alias SpazioSolazzo.BookingSystem.Booking.EmailWorker
postgres do
table "bookings"
repo SpazioSolazzo.Repo
references do
reference :user, on_delete: :nilify, index?: true
end
end
state_machine do
initial_states([:reserved])
default_initial_state(:reserved)
transitions do
transition(:confirm_booking, from: :reserved, to: :completed)
transition(:cancel, from: :reserved, to: :cancelled)
end
end
actions do
defaults [:read]
read :list_active_asset_bookings_by_date do
argument :asset_id, :uuid, allow_nil?: false
argument :date, :date, allow_nil?: false
filter expr(
asset_id == ^arg(:asset_id) and date == ^arg(:date) and
state in [:reserved, :completed]
)
end
create :create do
argument :time_slot_template_id, :uuid, allow_nil?: false
argument :asset_id, :uuid, allow_nil?: false
argument :user_id, :uuid, allow_nil?: false
argument :date, :date, allow_nil?: false
argument :customer_name, :string, allow_nil?: false
argument :customer_email, :string, allow_nil?: false
argument :customer_phone, :string, allow_nil?: false
argument :customer_comment, :string, allow_nil?: true
change manage_relationship(:time_slot_template_id, :time_slot_template,
type: :append_and_remove
)
change manage_relationship(:asset_id, :asset, type: :append_and_remove)
change manage_relationship(:user_id, :user, type: :append_and_remove, authorize?: false)
change fn changeset, _ctx ->
template_id = Ash.Changeset.get_argument(changeset, :time_slot_template_id)
case Ash.get(SpazioSolazzo.BookingSystem.TimeSlotTemplate, template_id) do
{:ok, template} ->
changeset
|> Ash.Changeset.force_change_attribute(:start_time, template.start_time)
|> Ash.Changeset.force_change_attribute(:end_time, template.end_time)
|> Ash.Changeset.force_change_attribute(
:date,
Ash.Changeset.get_argument(changeset, :date)
)
|> Ash.Changeset.force_change_attribute(
:customer_name,
Ash.Changeset.get_argument(changeset, :customer_name)
)
|> Ash.Changeset.force_change_attribute(
:customer_email,
Ash.Changeset.get_argument(changeset, :customer_email)
)
|> Ash.Changeset.force_change_attribute(
:customer_phone,
Ash.Changeset.get_argument(changeset, :customer_phone)
)
|> Ash.Changeset.force_change_attribute(
:customer_comment,
Ash.Changeset.get_argument(changeset, :customer_comment)
)
{:error, _} ->
Ash.Changeset.add_error(changeset,
field: :time_slot_template_id,
message: "Template not found"
)
end
end
change after_action(fn _changeset, booking, _ctx ->
%{
booking_id: booking.id,
customer_name: booking.customer_name,
customer_email: booking.customer_email,
customer_phone: booking.customer_phone,
customer_comment: booking.customer_comment,
date: Calendar.strftime(booking.date, "%A, %B %d"),
start_time: booking.start_time,
end_time: booking.end_time
}
|> EmailWorker.new()
|> Oban.insert!()
{:ok, booking}
end)
end
update :confirm_booking do
accept []
change transition_state(:completed)
end
update :cancel do
accept []
change transition_state(:cancelled)
end
destroy :destroy do
description "Delete a booking record"
primary? true
end
end
policies do
policy action([:cancel, :confirm_booking]) do
authorize_if always()
end
policy action_type(:destroy) do
authorize_if expr(:user_id == ^actor(:id))
end
policy action_type(:read) do
authorize_if always()
end
policy action_type(:create) do
authorize_if always()
end
end
pub_sub do
module SpazioSolazzoWeb.Endpoint
prefix "booking"
publish :create, ["created"]
publish :cancel, ["cancelled"]
end
attributes do
uuid_primary_key :id
attribute :date, :date, allow_nil?: false
attribute :customer_name, :string, allow_nil?: false
attribute :customer_email, :string, allow_nil?: false
attribute :start_time, :time, allow_nil?: false
attribute :end_time, :time, allow_nil?: false
attribute :customer_phone, :string, allow_nil?: false
attribute :customer_comment, :string, allow_nil?: true
attribute :state, :atom do
allow_nil? false
default :reserved
public? true
constraints one_of: [:reserved, :completed, :cancelled]
end
create_timestamp :inserted_at
update_timestamp :updated_at
end
relationships do
belongs_to :asset, SpazioSolazzo.BookingSystem.Asset
belongs_to :time_slot_template, SpazioSolazzo.BookingSystem.TimeSlotTemplate
belongs_to :user, SpazioSolazzo.Accounts.User do
allow_nil? true
end
end
end