221 lines
5 KiB
Elixir
221 lines
5 KiB
Elixir
defmodule Mv.Membership.Member do
|
|
use Ash.Resource,
|
|
domain: Mv.Membership,
|
|
data_layer: AshPostgres.DataLayer
|
|
|
|
postgres do
|
|
table "members"
|
|
repo Mv.Repo
|
|
end
|
|
|
|
actions do
|
|
defaults [:read]
|
|
|
|
create :create_member do
|
|
primary? true
|
|
argument :properties, {:array, :map}
|
|
|
|
accept [
|
|
:first_name,
|
|
:last_name,
|
|
:email,
|
|
:birth_date,
|
|
:paid,
|
|
:phone_number,
|
|
:join_date,
|
|
:exit_date,
|
|
:notes,
|
|
:city,
|
|
:street,
|
|
:house_number,
|
|
:postal_code
|
|
]
|
|
|
|
change manage_relationship(:properties, type: :create)
|
|
end
|
|
|
|
destroy :destroy do
|
|
primary? true
|
|
change Ash.Resource.Change.Builtins.cascade_destroy(:properties)
|
|
end
|
|
|
|
update :update_member do
|
|
primary? true
|
|
require_atomic? false
|
|
argument :properties, {:array, :map}
|
|
|
|
accept [
|
|
:first_name,
|
|
:last_name,
|
|
:email,
|
|
:birth_date,
|
|
:paid,
|
|
:phone_number,
|
|
:join_date,
|
|
:exit_date,
|
|
:notes,
|
|
:city,
|
|
:street,
|
|
:house_number,
|
|
:postal_code
|
|
]
|
|
|
|
change manage_relationship(:properties, on_match: :update, on_no_match: :create)
|
|
end
|
|
end
|
|
|
|
validations do
|
|
# Required fields are covered by allow_nil? false
|
|
|
|
# First name and last name must not be empty
|
|
validate present(:first_name)
|
|
validate present(:last_name)
|
|
validate present(:email)
|
|
|
|
# Birth date not in the future
|
|
validate fn changeset, _ ->
|
|
birth_date = Ash.Changeset.get_attribute(changeset, :birth_date)
|
|
|
|
if birth_date && Date.compare(birth_date, Date.utc_today()) == :gt do
|
|
{:error, field: :birth_date, message: "cannot be in the future"}
|
|
else
|
|
:ok
|
|
end
|
|
end
|
|
|
|
# Join date not in the future
|
|
validate fn changeset, _ ->
|
|
join_date = Ash.Changeset.get_attribute(changeset, :join_date)
|
|
|
|
if join_date && Date.compare(join_date, Date.utc_today()) == :gt do
|
|
{:error, field: :join_date, message: "cannot be in the future"}
|
|
else
|
|
:ok
|
|
end
|
|
end
|
|
|
|
# Exit date not before join date
|
|
validate fn changeset, _ ->
|
|
join_date = Ash.Changeset.get_attribute(changeset, :join_date)
|
|
exit_date = Ash.Changeset.get_attribute(changeset, :exit_date)
|
|
|
|
if join_date && exit_date && Date.compare(exit_date, join_date) == :lt do
|
|
{:error, field: :exit_date, message: "cannot be before join date"}
|
|
else
|
|
:ok
|
|
end
|
|
end
|
|
|
|
# Phone number format (only if set)
|
|
validate fn changeset, _ ->
|
|
phone = Ash.Changeset.get_attribute(changeset, :phone_number)
|
|
|
|
if phone && !Regex.match?(~r/^\+?[0-9\- ]{6,20}$/, phone) do
|
|
{:error, field: :phone_number, message: "is not a valid phone number"}
|
|
else
|
|
:ok
|
|
end
|
|
end
|
|
|
|
# Postal code format (only if set)
|
|
validate fn changeset, _ ->
|
|
postal_code = Ash.Changeset.get_attribute(changeset, :postal_code)
|
|
|
|
if postal_code && !Regex.match?(~r/^\d{5}$/, postal_code) do
|
|
{:error, field: :postal_code, message: "must consist of 5 digits"}
|
|
else
|
|
:ok
|
|
end
|
|
end
|
|
|
|
# paid must be boolean if set
|
|
validate fn changeset, _ ->
|
|
paid = Ash.Changeset.get_attribute(changeset, :paid)
|
|
|
|
if not is_nil(paid) and not is_boolean(paid) do
|
|
{:error, field: :paid, message: "must be true or false"}
|
|
else
|
|
:ok
|
|
end
|
|
end
|
|
|
|
# Email validation with EctoCommons.EmailValidator
|
|
validate fn changeset, _ ->
|
|
email = Ash.Changeset.get_attribute(changeset, :email)
|
|
|
|
changeset2 =
|
|
{%{}, %{email: :string}}
|
|
|> Ecto.Changeset.cast(%{email: email}, [:email])
|
|
|> EctoCommons.EmailValidator.validate_email(:email, checks: [:html_input, :pow])
|
|
|
|
if changeset2.valid? do
|
|
:ok
|
|
else
|
|
{:error, field: :email, message: "is not a valid email"}
|
|
end
|
|
end
|
|
end
|
|
|
|
attributes do
|
|
uuid_v7_primary_key :id
|
|
|
|
attribute :first_name, :string do
|
|
allow_nil? false
|
|
constraints min_length: 1
|
|
end
|
|
|
|
attribute :last_name, :string do
|
|
allow_nil? false
|
|
constraints min_length: 1
|
|
end
|
|
|
|
attribute :email, :string do
|
|
allow_nil? false
|
|
constraints min_length: 5, max_length: 254
|
|
end
|
|
|
|
attribute :birth_date, :date do
|
|
allow_nil? true
|
|
end
|
|
|
|
attribute :paid, :boolean do
|
|
allow_nil? true
|
|
end
|
|
|
|
attribute :phone_number, :string do
|
|
allow_nil? true
|
|
end
|
|
|
|
attribute :join_date, :date do
|
|
allow_nil? true
|
|
end
|
|
|
|
attribute :exit_date, :date do
|
|
allow_nil? true
|
|
end
|
|
|
|
attribute :notes, :string do
|
|
allow_nil? true
|
|
end
|
|
|
|
attribute :city, :string do
|
|
allow_nil? true
|
|
end
|
|
|
|
attribute :street, :string do
|
|
allow_nil? true
|
|
end
|
|
|
|
attribute :house_number, :string do
|
|
allow_nil? true
|
|
end
|
|
|
|
attribute :postal_code, :string do
|
|
allow_nil? true
|
|
end
|
|
end
|
|
|
|
relationships do
|
|
has_many :properties, Mv.Membership.Property, destination_attribute: :member_id
|
|
end
|
|
end
|