spazio-solazzo/lib/spazio_solazzo/booking_system/space.ex
2026-02-02 01:25:24 +01:00

125 lines
3.6 KiB
Elixir

defmodule SpazioSolazzo.BookingSystem.Space do
@moduledoc """
Represents a physical or virtual space that contains bookable assets.
"""
use Ash.Resource,
otp_app: :spazio_solazzo,
domain: SpazioSolazzo.BookingSystem,
data_layer: AshPostgres.DataLayer,
authorizers: [Ash.Policy.Authorizer]
postgres do
table "spaces"
repo SpazioSolazzo.Repo
end
actions do
defaults [:read]
create :create do
accept [:name, :description, :slug, :public_capacity, :real_capacity]
validate fn changeset, _ctx ->
real_capacity = Ash.Changeset.get_attribute(changeset, :real_capacity)
public_capacity = Ash.Changeset.get_attribute(changeset, :public_capacity)
cond do
real_capacity && real_capacity <= 0 ->
{:error, field: :real_capacity, message: "must be greater than 0"}
public_capacity && public_capacity <= 0 ->
{:error, field: :public_capacity, message: "must be greater than 0"}
real_capacity && public_capacity && public_capacity > real_capacity ->
{:error,
field: :public_capacity, message: "must be less than or equal to real_capacity"}
true ->
:ok
end
end
end
action :check_availability, :atom do
argument :space_id, :uuid, allow_nil?: false
argument :date, :date, allow_nil?: false
argument :start_time, :time, allow_nil?: false
argument :end_time, :time, allow_nil?: false
run fn input, _context ->
require Ash.Query
space_id = input.arguments.space_id
date_arg = input.arguments.date
start_time_arg = input.arguments.start_time
end_time_arg = input.arguments.end_time
start_datetime = DateTime.new!(date_arg, start_time_arg, "Etc/UTC")
end_datetime = DateTime.new!(date_arg, end_time_arg, "Etc/UTC")
# Load the space
case Ash.get(__MODULE__, space_id) do
{:ok, space} ->
# Get accepted bookings for this space that overlap with the requested time slot
query =
SpazioSolazzo.BookingSystem.Booking
|> Ash.Query.filter(
expr(
space_id == ^space_id and state == :accepted and start_datetime < ^end_datetime and
end_datetime > ^start_datetime
)
)
case Ash.read(query) do
{:ok, overlapping_bookings} ->
current_count = length(overlapping_bookings)
availability =
cond do
current_count >= space.real_capacity -> :over_real_capacity
current_count >= space.public_capacity -> :over_public_capacity
true -> :available
end
{:ok, availability}
error ->
error
end
error ->
error
end
end
end
end
attributes do
uuid_primary_key :id
attribute :name, :string, allow_nil?: false, public?: true
attribute :description, :string, allow_nil?: false, public?: true
attribute :slug, :string, allow_nil?: false, public?: true
attribute :public_capacity, :integer, allow_nil?: false, public?: true
attribute :real_capacity, :integer, allow_nil?: false, public?: true
end
identities do
identity :unique_name, [:name]
identity :unique_slug, [:slug]
end
policies do
policy action(:check_availability) do
authorize_if always()
end
policy action_type(:read) do
authorize_if always()
end
policy action_type(:create) do
authorize_if always()
end
end
end