diff --git a/lib/mv/membership/member.ex b/lib/mv/membership/member.ex new file mode 100644 index 0000000..b6fe9a8 --- /dev/null +++ b/lib/mv/membership/member.ex @@ -0,0 +1,108 @@ +defmodule Mv.Membership.Member do + use Ecto.Schema + import Ecto.Changeset + import EctoCommons.EmailValidator + + @primary_key {:id, :binary_id, autogenerate: true} + @foreign_key_type :binary_id + schema "members" do + field :first_name, :string + field :last_name, :string + field :email, :string + field :phone_number, :string + field :postal_code, :string + field :birth_date, :date + field :paid, :boolean, default: false + field :join_date, :date + field :exit_date, :date + field :notes, :string + field :city, :string + field :street, :string + field :house_number, :string + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(member, attrs) do + member + |> cast(attrs, [:first_name, :last_name, :email, :birth_date, :paid, :phone_number, :join_date, :exit_date, :notes, :city, :street, :house_number, :postal_code]) + |> validate_required([:first_name, :last_name, :email]) + |> validate_length(:first_name, min: 1) + |> validate_length(:last_name, min: 1) + |> validate_length(:email, min: 5, max: 254) + |> validate_email(:email, checks: [:html_input, :pow]) + |> validate_birth_date() + |> validate_join_date() + |> validate_exit_date() + |> validate_phone_number() + |> validate_postal_code() + end + + def create_changeset(member, attrs, _metadata) do + changeset(member, attrs) + end + + def update_changeset(member, attrs, _metadata) do + changeset(member, attrs) + end + + defp validate_birth_date(changeset) do + case get_field(changeset, :birth_date) do + nil -> changeset + birth_date -> + if Date.compare(birth_date, Date.utc_today()) == :gt do + add_error(changeset, :birth_date, "cannot be in the future") + else + changeset + end + end + end + + defp validate_join_date(changeset) do + case get_field(changeset, :join_date) do + nil -> changeset + join_date -> + if Date.compare(join_date, Date.utc_today()) == :gt do + add_error(changeset, :join_date, "cannot be in the future") + else + changeset + end + end + end + + defp validate_exit_date(changeset) do + join_date = get_field(changeset, :join_date) + exit_date = get_field(changeset, :exit_date) + + if join_date && exit_date && Date.compare(exit_date, join_date) != :gt do + add_error(changeset, :exit_date, "cannot be before join date") + else + changeset + end + end + + defp validate_phone_number(changeset) do + case get_field(changeset, :phone_number) do + nil -> changeset + phone_number -> + if Regex.match?(~r/^\+?[0-9\- ]{6,20}$/, phone_number) do + changeset + else + add_error(changeset, :phone_number, "is not a valid phone number") + end + end + end + + defp validate_postal_code(changeset) do + case get_field(changeset, :postal_code) do + nil -> changeset + postal_code -> + if Regex.match?(~r/^\d{5}$/, postal_code) do + changeset + else + add_error(changeset, :postal_code, "must consist of 5 digits") + end + end + end +end diff --git a/priv/repo/migrations/20250703144002_create_members.exs b/priv/repo/migrations/20250703144002_create_members.exs new file mode 100644 index 0000000..29df339 --- /dev/null +++ b/priv/repo/migrations/20250703144002_create_members.exs @@ -0,0 +1,26 @@ +defmodule Mv.Repo.Migrations.CreateMembers do + use Ecto.Migration + + def change do + create table(:members, primary_key: false) do + add :id, :uuid, primary_key: true, default: fragment("gen_random_uuid()") + add :first_name, :string, null: false + add :last_name, :string, null: false + add :email, :string, null: false + add :birth_date, :date + add :paid, :boolean, default: false + add :phone_number, :string + add :join_date, :date + add :exit_date, :date + add :notes, :string + add :city, :string + add :street, :string + add :house_number, :string + add :postal_code, :string + + timestamps(type: :utc_datetime) + end + + create unique_index(:members, [:email]) + end +end