feat: implement filter logic for boolean ustom fields

This commit is contained in:
Simon 2026-01-20 18:01:25 +01:00
parent b701b84260
commit da9ec06e8e
Signed by: simon
GPG key ID: 40E7A58C4AA1EDB2
2 changed files with 558 additions and 321 deletions

View file

@ -985,14 +985,18 @@ defmodule MvWeb.MemberLive.IndexTest do
end
# Helper to create a member with a boolean custom field value
defp create_member_with_boolean_value(member_attrs \\ %{}, custom_field, value) do
defp create_member_with_boolean_value(member_attrs, custom_field, value) do
{:ok, member} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "Test",
last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com"
} |> Map.merge(member_attrs))
|> Ash.Changeset.for_create(
:create_member,
%{
first_name: "Test",
last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com"
}
|> Map.merge(member_attrs)
)
|> Ash.create()
{:ok, _cfv} =
@ -1011,403 +1015,469 @@ defmodule MvWeb.MemberLive.IndexTest do
# Tests for get_boolean_custom_field_value/2
test "get_boolean_custom_field_value extracts true from Ash.Union format", %{conn: _conn} do
boolean_field = create_boolean_custom_field()
member = create_member_with_boolean_value(%{}, boolean_field, true)
boolean_field = create_boolean_custom_field()
member = create_member_with_boolean_value(%{}, boolean_field, true)
# Test the function (will fail until implemented)
result = MvWeb.MemberLive.Index.get_boolean_custom_field_value(member, boolean_field)
# Test the function (will fail until implemented)
result = MvWeb.MemberLive.Index.get_boolean_custom_field_value(member, boolean_field)
assert result == true
end
assert result == true
end
test "get_boolean_custom_field_value extracts false from Ash.Union format", %{conn: _conn} do
boolean_field = create_boolean_custom_field()
member = create_member_with_boolean_value(%{}, boolean_field, false)
boolean_field = create_boolean_custom_field()
member = create_member_with_boolean_value(%{}, boolean_field, false)
result = MvWeb.MemberLive.Index.get_boolean_custom_field_value(member, boolean_field)
result = MvWeb.MemberLive.Index.get_boolean_custom_field_value(member, boolean_field)
assert result == false
end
assert result == false
end
test "get_boolean_custom_field_value extracts true from map format with type and value keys", %{conn: _conn} do
boolean_field = create_boolean_custom_field()
{:ok, member} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "Test",
last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
test "get_boolean_custom_field_value extracts true from map format with _union_type and _union_value keys",
%{conn: _conn} do
boolean_field = create_boolean_custom_field()
# Create CustomFieldValue with map format
{:ok, _cfv} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member.id,
custom_field_id: boolean_field.id,
value: %{"type" => "boolean", "value" => true}
})
|> Ash.create()
{:ok, member} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "Test",
last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
# Reload member with custom field values
member = member |> Ash.load!(:custom_field_values)
# Create CustomFieldValue with map format (Ash expects _union_type and _union_value)
{:ok, _cfv} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member.id,
custom_field_id: boolean_field.id,
value: %{"_union_type" => "boolean", "_union_value" => true}
})
|> Ash.create()
result = MvWeb.MemberLive.Index.get_boolean_custom_field_value(member, boolean_field)
# Reload member with custom field values
member = member |> Ash.load!(:custom_field_values)
assert result == true
end
result = MvWeb.MemberLive.Index.get_boolean_custom_field_value(member, boolean_field)
test "get_boolean_custom_field_value returns nil when no CustomFieldValue exists", %{conn: _conn} do
boolean_field = create_boolean_custom_field()
{:ok, member} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "Test",
last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
assert result == true
end
# Member has no custom field value for this field
member = member |> Ash.load!(:custom_field_values)
test "get_boolean_custom_field_value returns nil when no CustomFieldValue exists", %{
conn: _conn
} do
boolean_field = create_boolean_custom_field()
result = MvWeb.MemberLive.Index.get_boolean_custom_field_value(member, boolean_field)
{:ok, member} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "Test",
last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
assert result == nil
end
# Member has no custom field value for this field
member = member |> Ash.load!(:custom_field_values)
test "get_boolean_custom_field_value returns nil when CustomFieldValue has nil value", %{conn: _conn} do
boolean_field = create_boolean_custom_field()
{:ok, member} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "Test",
last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
result = MvWeb.MemberLive.Index.get_boolean_custom_field_value(member, boolean_field)
# Create CustomFieldValue with nil value (edge case)
{:ok, _cfv} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member.id,
custom_field_id: boolean_field.id,
value: nil
})
|> Ash.create()
assert result == nil
end
member = member |> Ash.load!(:custom_field_values)
test "get_boolean_custom_field_value returns nil when CustomFieldValue has nil value", %{
conn: _conn
} do
boolean_field = create_boolean_custom_field()
result = MvWeb.MemberLive.Index.get_boolean_custom_field_value(member, boolean_field)
{:ok, member} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "Test",
last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
assert result == nil
end
# Create CustomFieldValue with nil value (edge case)
{:ok, _cfv} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member.id,
custom_field_id: boolean_field.id,
value: nil
})
|> Ash.create()
test "get_boolean_custom_field_value returns nil for non-boolean CustomFieldValue", %{conn: _conn} do
string_field = create_string_custom_field()
boolean_field = create_boolean_custom_field()
member = member |> Ash.load!(:custom_field_values)
{:ok, member} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "Test",
last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
result = MvWeb.MemberLive.Index.get_boolean_custom_field_value(member, boolean_field)
# Create string custom field value (not boolean)
{:ok, _cfv} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member.id,
custom_field_id: string_field.id,
value: %{"_union_type" => "string", "_union_value" => "test"}
})
|> Ash.create()
assert result == nil
end
member = member |> Ash.load!(:custom_field_values)
test "get_boolean_custom_field_value returns nil for non-boolean CustomFieldValue", %{
conn: _conn
} do
string_field = create_string_custom_field()
boolean_field = create_boolean_custom_field()
# Try to get boolean value from string field - should return nil
result = MvWeb.MemberLive.Index.get_boolean_custom_field_value(member, boolean_field)
{:ok, member} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "Test",
last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
assert result == nil
end
# Create string custom field value (not boolean)
{:ok, _cfv} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member.id,
custom_field_id: string_field.id,
value: %{"_union_type" => "string", "_union_value" => "test"}
})
|> Ash.create()
member = member |> Ash.load!(:custom_field_values)
# Try to get boolean value from string field - should return nil
result = MvWeb.MemberLive.Index.get_boolean_custom_field_value(member, boolean_field)
assert result == nil
end
# Tests for apply_boolean_custom_field_filters/2
test "apply_boolean_custom_field_filters filters members with true value and excludes false/without values", %{conn: _conn} do
boolean_field = create_boolean_custom_field()
test "apply_boolean_custom_field_filters filters members with true value and excludes false/without values",
%{conn: _conn} do
boolean_field = create_boolean_custom_field()
member_with_true = create_member_with_boolean_value(%{first_name: "TrueMember"}, boolean_field, true)
member_with_false = create_member_with_boolean_value(%{first_name: "FalseMember"}, boolean_field, false)
{:ok, member_without_value} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "NoValue",
last_name: "Member",
email: "novalue.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
member_with_true =
create_member_with_boolean_value(%{first_name: "TrueMember"}, boolean_field, true)
member_without_value = member_without_value |> Ash.load!(:custom_field_values)
member_with_false =
create_member_with_boolean_value(%{first_name: "FalseMember"}, boolean_field, false)
members = [member_with_true, member_with_false, member_without_value]
filters = %{to_string(boolean_field.id) => true}
{:ok, member_without_value} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "NoValue",
last_name: "Member",
email: "novalue.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
result = MvWeb.MemberLive.Index.apply_boolean_custom_field_filters(members, filters)
member_without_value = member_without_value |> Ash.load!(:custom_field_values)
assert length(result) == 1
assert List.first(result).id == member_with_true.id
refute Enum.any?(result, &(&1.id == member_with_false.id))
refute Enum.any?(result, &(&1.id == member_without_value.id))
end
members = [member_with_true, member_with_false, member_without_value]
filters = %{to_string(boolean_field.id) => true}
all_custom_fields = Mv.Membership.CustomField |> Ash.read!()
test "apply_boolean_custom_field_filters filters members with false value and excludes true/without values", %{conn: _conn} do
boolean_field = create_boolean_custom_field()
result =
MvWeb.MemberLive.Index.apply_boolean_custom_field_filters(
members,
filters,
all_custom_fields
)
member_with_true = create_member_with_boolean_value(%{first_name: "TrueMember"}, boolean_field, true)
member_with_false = create_member_with_boolean_value(%{first_name: "FalseMember"}, boolean_field, false)
{:ok, member_without_value} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "NoValue",
last_name: "Member",
email: "novalue.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
assert length(result) == 1
assert List.first(result).id == member_with_true.id
refute Enum.any?(result, &(&1.id == member_with_false.id))
refute Enum.any?(result, &(&1.id == member_without_value.id))
end
member_without_value = member_without_value |> Ash.load!(:custom_field_values)
test "apply_boolean_custom_field_filters filters members with false value and excludes true/without values",
%{conn: _conn} do
boolean_field = create_boolean_custom_field()
members = [member_with_true, member_with_false, member_without_value]
filters = %{to_string(boolean_field.id) => false}
member_with_true =
create_member_with_boolean_value(%{first_name: "TrueMember"}, boolean_field, true)
result = MvWeb.MemberLive.Index.apply_boolean_custom_field_filters(members, filters)
member_with_false =
create_member_with_boolean_value(%{first_name: "FalseMember"}, boolean_field, false)
assert length(result) == 1
assert List.first(result).id == member_with_false.id
refute Enum.any?(result, &(&1.id == member_with_true.id))
refute Enum.any?(result, &(&1.id == member_without_value.id))
end
{:ok, member_without_value} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "NoValue",
last_name: "Member",
email: "novalue.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
test "apply_boolean_custom_field_filters returns all members when filter map is empty", %{conn: _conn} do
boolean_field = create_boolean_custom_field()
member_without_value = member_without_value |> Ash.load!(:custom_field_values)
member1 = create_member_with_boolean_value(%{first_name: "Member1"}, boolean_field, true)
member2 = create_member_with_boolean_value(%{first_name: "Member2"}, boolean_field, false)
members = [member_with_true, member_with_false, member_without_value]
filters = %{to_string(boolean_field.id) => false}
all_custom_fields = Mv.Membership.CustomField |> Ash.read!()
members = [member1, member2]
filters = %{}
result =
MvWeb.MemberLive.Index.apply_boolean_custom_field_filters(
members,
filters,
all_custom_fields
)
result = MvWeb.MemberLive.Index.apply_boolean_custom_field_filters(members, filters)
assert length(result) == 1
assert List.first(result).id == member_with_false.id
refute Enum.any?(result, &(&1.id == member_with_true.id))
refute Enum.any?(result, &(&1.id == member_without_value.id))
end
assert length(result) == 2
assert Enum.all?([member1.id, member2.id], fn id ->
Enum.any?(result, &(&1.id == id))
end)
end
test "apply_boolean_custom_field_filters returns all members when filter map is empty", %{
conn: _conn
} do
boolean_field = create_boolean_custom_field()
member1 = create_member_with_boolean_value(%{first_name: "Member1"}, boolean_field, true)
member2 = create_member_with_boolean_value(%{first_name: "Member2"}, boolean_field, false)
test "apply_boolean_custom_field_filters applies multiple filters with AND logic", %{conn: _conn} do
boolean_field1 = create_boolean_custom_field(%{name: "Field1"})
boolean_field2 = create_boolean_custom_field(%{name: "Field2"})
members = [member1, member2]
filters = %{}
all_custom_fields = Mv.Membership.CustomField |> Ash.read!()
# Member with both fields = true
{:ok, member_both_true} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "BothTrue",
last_name: "Member",
email: "bothtrue.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
result =
MvWeb.MemberLive.Index.apply_boolean_custom_field_filters(
members,
filters,
all_custom_fields
)
{:ok, _cfv1} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_both_true.id,
custom_field_id: boolean_field1.id,
value: %{"_union_type" => "boolean", "_union_value" => true}
})
|> Ash.create()
assert length(result) == 2
{:ok, _cfv2} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_both_true.id,
custom_field_id: boolean_field2.id,
value: %{"_union_type" => "boolean", "_union_value" => true}
})
|> Ash.create()
assert Enum.all?([member1.id, member2.id], fn id ->
Enum.any?(result, &(&1.id == id))
end)
end
member_both_true = member_both_true |> Ash.load!(:custom_field_values)
test "apply_boolean_custom_field_filters applies multiple filters with AND logic", %{
conn: _conn
} do
boolean_field1 = create_boolean_custom_field(%{name: "Field1"})
boolean_field2 = create_boolean_custom_field(%{name: "Field2"})
# Member with field1 = true, field2 = false
{:ok, member_mixed} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "Mixed",
last_name: "Member",
email: "mixed.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
# Member with both fields = true
{:ok, member_both_true} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "BothTrue",
last_name: "Member",
email: "bothtrue.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
{:ok, _cfv3} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_mixed.id,
custom_field_id: boolean_field1.id,
value: %{"_union_type" => "boolean", "_union_value" => true}
})
|> Ash.create()
{:ok, _cfv1} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_both_true.id,
custom_field_id: boolean_field1.id,
value: %{"_union_type" => "boolean", "_union_value" => true}
})
|> Ash.create()
{:ok, _cfv4} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_mixed.id,
custom_field_id: boolean_field2.id,
value: %{"_union_type" => "boolean", "_union_value" => false}
})
|> Ash.create()
{:ok, _cfv2} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_both_true.id,
custom_field_id: boolean_field2.id,
value: %{"_union_type" => "boolean", "_union_value" => true}
})
|> Ash.create()
member_mixed = member_mixed |> Ash.load!(:custom_field_values)
member_both_true = member_both_true |> Ash.load!(:custom_field_values)
members = [member_both_true, member_mixed]
filters = %{
to_string(boolean_field1.id) => true,
to_string(boolean_field2.id) => true
}
# Member with field1 = true, field2 = false
{:ok, member_mixed} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "Mixed",
last_name: "Member",
email: "mixed.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
result = MvWeb.MemberLive.Index.apply_boolean_custom_field_filters(members, filters)
{:ok, _cfv3} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_mixed.id,
custom_field_id: boolean_field1.id,
value: %{"_union_type" => "boolean", "_union_value" => true}
})
|> Ash.create()
# Only member_both_true should match (both fields = true)
assert length(result) == 1
assert List.first(result).id == member_both_true.id
end
{:ok, _cfv4} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_mixed.id,
custom_field_id: boolean_field2.id,
value: %{"_union_type" => "boolean", "_union_value" => false}
})
|> Ash.create()
test "apply_boolean_custom_field_filters ignores filter with non-existent custom field ID", %{conn: _conn} do
boolean_field = create_boolean_custom_field()
fake_id = Ecto.UUID.generate()
member_mixed = member_mixed |> Ash.load!(:custom_field_values)
member = create_member_with_boolean_value(%{first_name: "Member"}, boolean_field, true)
members = [member_both_true, member_mixed]
members = [member]
filters = %{fake_id => true}
filters = %{
to_string(boolean_field1.id) => true,
to_string(boolean_field2.id) => true
}
result = MvWeb.MemberLive.Index.apply_boolean_custom_field_filters(members, filters)
all_custom_fields = Mv.Membership.CustomField |> Ash.read!()
# Should return all members since fake_id doesn't match any custom field
assert length(result) == 1
end
result =
MvWeb.MemberLive.Index.apply_boolean_custom_field_filters(
members,
filters,
all_custom_fields
)
# Only member_both_true should match (both fields = true)
assert length(result) == 1
assert List.first(result).id == member_both_true.id
end
test "apply_boolean_custom_field_filters ignores filter with non-existent custom field ID", %{
conn: _conn
} do
boolean_field = create_boolean_custom_field()
fake_id = Ecto.UUID.generate()
member = create_member_with_boolean_value(%{first_name: "Member"}, boolean_field, true)
members = [member]
filters = %{fake_id => true}
all_custom_fields = Mv.Membership.CustomField |> Ash.read!()
result =
MvWeb.MemberLive.Index.apply_boolean_custom_field_filters(
members,
filters,
all_custom_fields
)
# Should return all members since fake_id doesn't match any custom field
assert length(result) == 1
end
# Integration tests for boolean custom field filters in load_members
test "boolean filter integration filters members by boolean custom field value via URL parameter", %{conn: conn} do
conn = conn_with_oidc_user(conn)
boolean_field = create_boolean_custom_field()
test "boolean filter integration filters members by boolean custom field value via URL parameter",
%{conn: conn} do
conn = conn_with_oidc_user(conn)
boolean_field = create_boolean_custom_field()
member_with_true = create_member_with_boolean_value(%{first_name: "TrueMember"}, boolean_field, true)
member_with_false = create_member_with_boolean_value(%{first_name: "FalseMember"}, boolean_field, false)
{:ok, member_without_value} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "NoValue",
last_name: "Member",
email: "novalue.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
_member_with_true =
create_member_with_boolean_value(%{first_name: "TrueMember"}, boolean_field, true)
# Test true filter
{:ok, _view, html_true} =
live(conn, "/members?bf_#{boolean_field.id}=true")
_member_with_false =
create_member_with_boolean_value(%{first_name: "FalseMember"}, boolean_field, false)
assert html_true =~ "TrueMember"
refute html_true =~ "FalseMember"
refute html_true =~ "NoValue"
{:ok, _member_without_value} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "NoValue",
last_name: "Member",
email: "novalue.member.#{System.unique_integer([:positive])}@example.com"
})
|> Ash.create()
# Test false filter
{:ok, _view, html_false} =
live(conn, "/members?bf_#{boolean_field.id}=false")
# Test true filter
{:ok, _view, html_true} =
live(conn, "/members?bf_#{boolean_field.id}=true")
assert html_false =~ "FalseMember"
refute html_false =~ "TrueMember"
refute html_false =~ "NoValue"
end
assert html_true =~ "TrueMember"
refute html_true =~ "FalseMember"
refute html_true =~ "NoValue"
# Test false filter
{:ok, _view, html_false} =
live(conn, "/members?bf_#{boolean_field.id}=false")
assert html_false =~ "FalseMember"
refute html_false =~ "TrueMember"
refute html_false =~ "NoValue"
end
test "boolean filter integration works together with cycle_status_filter", %{conn: conn} do
conn = conn_with_oidc_user(conn)
boolean_field = create_boolean_custom_field()
fee_type = create_fee_type(%{interval: :yearly})
today = Date.utc_today()
last_year_start = Date.new!(today.year - 1, 1, 1)
conn = conn_with_oidc_user(conn)
boolean_field = create_boolean_custom_field()
fee_type = create_fee_type(%{interval: :yearly})
today = Date.utc_today()
last_year_start = Date.new!(today.year - 1, 1, 1)
# Member with true boolean value and paid status
{:ok, member_paid_true} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "PaidTrue",
last_name: "Member",
email: "paidtrue.member.#{System.unique_integer([:positive])}@example.com",
membership_fee_type_id: fee_type.id
})
|> Ash.create()
# Member with true boolean value and paid status
{:ok, member_paid_true} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "PaidTrue",
last_name: "Member",
email: "paidtrue.member.#{System.unique_integer([:positive])}@example.com",
membership_fee_type_id: fee_type.id
})
|> Ash.create()
{:ok, _cfv} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_paid_true.id,
custom_field_id: boolean_field.id,
value: %{"_union_type" => "boolean", "_union_value" => true}
})
|> Ash.create()
{:ok, _cfv} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_paid_true.id,
custom_field_id: boolean_field.id,
value: %{"_union_type" => "boolean", "_union_value" => true}
})
|> Ash.create()
create_cycle(member_paid_true, fee_type, %{cycle_start: last_year_start, status: :paid})
create_cycle(member_paid_true, fee_type, %{cycle_start: last_year_start, status: :paid})
# Member with true boolean value but unpaid status
{:ok, member_unpaid_true} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "UnpaidTrue",
last_name: "Member",
email: "unpaidtrue.member.#{System.unique_integer([:positive])}@example.com",
membership_fee_type_id: fee_type.id
})
|> Ash.create()
# Member with true boolean value but unpaid status
{:ok, member_unpaid_true} =
Mv.Membership.Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "UnpaidTrue",
last_name: "Member",
email: "unpaidtrue.member.#{System.unique_integer([:positive])}@example.com",
membership_fee_type_id: fee_type.id
})
|> Ash.create()
{:ok, _cfv2} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_unpaid_true.id,
custom_field_id: boolean_field.id,
value: %{"_union_type" => "boolean", "_union_value" => true}
})
|> Ash.create()
{:ok, _cfv2} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_unpaid_true.id,
custom_field_id: boolean_field.id,
value: %{"_union_type" => "boolean", "_union_value" => true}
})
|> Ash.create()
create_cycle(member_unpaid_true, fee_type, %{cycle_start: last_year_start, status: :unpaid})
create_cycle(member_unpaid_true, fee_type, %{cycle_start: last_year_start, status: :unpaid})
# Test both filters together
{:ok, view, html} =
live(conn, "/members?cycle_status_filter=paid&bf_#{boolean_field.id}=true")
# Test both filters together
{:ok, _view, html} =
live(conn, "/members?cycle_status_filter=paid&bf_#{boolean_field.id}=true")
# Only member_paid_true should match both filters
assert html =~ "PaidTrue"
refute html =~ "UnpaidTrue"
end
# Only member_paid_true should match both filters
assert html =~ "PaidTrue"
refute html =~ "UnpaidTrue"
end
test "boolean filter integration works together with search query", %{conn: conn} do
conn = conn_with_oidc_user(conn)
boolean_field = create_boolean_custom_field()
conn = conn_with_oidc_user(conn)
boolean_field = create_boolean_custom_field()
member_with_true = create_member_with_boolean_value(%{first_name: "TrueMember"}, boolean_field, true)
member_with_false = create_member_with_boolean_value(%{first_name: "FalseMember"}, boolean_field, false)
_member_with_true =
create_member_with_boolean_value(%{first_name: "TrueMember"}, boolean_field, true)
# Test search + boolean filter
{:ok, view, html} =
live(conn, "/members?query=TrueMember&bf_#{boolean_field.id}=true")
_member_with_false =
create_member_with_boolean_value(%{first_name: "FalseMember"}, boolean_field, false)
# Only member_with_true should match both search and filter
assert html =~ "TrueMember"
refute html =~ "FalseMember"
end
# Test search + boolean filter
{:ok, _view, html} =
live(conn, "/members?query=TrueMember&bf_#{boolean_field.id}=true")
# Only member_with_true should match both search and filter
assert html =~ "TrueMember"
refute html =~ "FalseMember"
end
end
end