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, :destroy] 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 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 compare(:birth_date, less_than_or_equal_to: &Date.utc_today/0), where: [present(:birth_date)], message: "cannot be in the future" # Join date not in the future validate compare(:join_date, less_than_or_equal_to: &Date.utc_today/0), where: [present(:join_date)], message: "cannot be in the future" # Exit date not before join date validate compare(:exit_date, greater_than: :join_date), where: [present([:join_date, :exit_date])], message: "cannot be before join date" # Phone number format (only if set) validate match(:phone_number, ~r/^\+?[0-9\- ]{6,20}$/), where: [present(:phone_number)], message: "is not a valid phone number" # Postal code format (only if set) validate match(:postal_code, ~r/^\d{5}$/), where: [present(:postal_code)], message: "must consist of 5 digits" # 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 attribute :search_vector, AshPostgres.Tsvector, writable?: false, public?: false, select_by_default?: false end relationships do has_many :properties, Mv.Membership.Property end end