defmodule Mv.MembershipFees.MembershipFeeCycleTest do @moduledoc """ Tests for MembershipFeeCycle resource, focusing on status management actions. """ use Mv.DataCase, async: true alias Mv.MembershipFees.MembershipFeeCycle alias Mv.MembershipFees.MembershipFeeType alias Mv.Membership.Member # Helper to create a membership fee type defp create_fee_type(attrs) do default_attrs = %{ name: "Test Fee Type #{System.unique_integer([:positive])}", amount: Decimal.new("50.00"), interval: :yearly } attrs = Map.merge(default_attrs, attrs) MembershipFeeType |> Ash.Changeset.for_create(:create, attrs) |> Ash.create!() end # Helper to create a member defp create_member(attrs) do default_attrs = %{ first_name: "Test", last_name: "Member", email: "test.member.#{System.unique_integer([:positive])}@example.com" } attrs = Map.merge(default_attrs, attrs) Member |> Ash.Changeset.for_create(:create_member, attrs) |> Ash.create!() end # Helper to create a cycle defp create_cycle(member, fee_type, attrs) do default_attrs = %{ cycle_start: ~D[2024-01-01], amount: Decimal.new("50.00"), member_id: member.id, membership_fee_type_id: fee_type.id } attrs = Map.merge(default_attrs, attrs) MembershipFeeCycle |> Ash.Changeset.for_create(:create, attrs) |> Ash.create!() end describe "status defaults" do test "status defaults to :unpaid when creating a cycle" do fee_type = create_fee_type(%{interval: :yearly}) member = create_member(%{membership_fee_type_id: fee_type.id}) cycle = MembershipFeeCycle |> Ash.Changeset.for_create(:create, %{ cycle_start: ~D[2024-01-01], amount: Decimal.new("50.00"), member_id: member.id, membership_fee_type_id: fee_type.id }) |> Ash.create!() assert cycle.status == :unpaid end end describe "mark_as_paid" do test "sets status to :paid" do fee_type = create_fee_type(%{interval: :yearly}) member = create_member(%{membership_fee_type_id: fee_type.id}) cycle = create_cycle(member, fee_type, %{status: :unpaid}) assert {:ok, updated} = Ash.update(cycle, %{}, action: :mark_as_paid) assert updated.status == :paid end test "can set notes when marking as paid" do fee_type = create_fee_type(%{interval: :yearly}) member = create_member(%{membership_fee_type_id: fee_type.id}) cycle = create_cycle(member, fee_type, %{status: :unpaid}) assert {:ok, updated} = Ash.update(cycle, %{notes: "Payment received via bank transfer"}, action: :mark_as_paid ) assert updated.status == :paid assert updated.notes == "Payment received via bank transfer" end test "can change from suspended to paid" do fee_type = create_fee_type(%{interval: :yearly}) member = create_member(%{membership_fee_type_id: fee_type.id}) cycle = create_cycle(member, fee_type, %{status: :suspended}) assert {:ok, updated} = Ash.update(cycle, %{}, action: :mark_as_paid) assert updated.status == :paid end end describe "mark_as_suspended" do test "sets status to :suspended" do fee_type = create_fee_type(%{interval: :yearly}) member = create_member(%{membership_fee_type_id: fee_type.id}) cycle = create_cycle(member, fee_type, %{status: :unpaid}) assert {:ok, updated} = Ash.update(cycle, %{}, action: :mark_as_suspended) assert updated.status == :suspended end test "can set notes when marking as suspended" do fee_type = create_fee_type(%{interval: :yearly}) member = create_member(%{membership_fee_type_id: fee_type.id}) cycle = create_cycle(member, fee_type, %{status: :unpaid}) assert {:ok, updated} = Ash.update(cycle, %{notes: "Waived due to special circumstances"}, action: :mark_as_suspended ) assert updated.status == :suspended assert updated.notes == "Waived due to special circumstances" end test "can change from paid to suspended" do fee_type = create_fee_type(%{interval: :yearly}) member = create_member(%{membership_fee_type_id: fee_type.id}) cycle = create_cycle(member, fee_type, %{status: :paid}) assert {:ok, updated} = Ash.update(cycle, %{}, action: :mark_as_suspended) assert updated.status == :suspended end end describe "mark_as_unpaid" do test "sets status to :unpaid" do fee_type = create_fee_type(%{interval: :yearly}) member = create_member(%{membership_fee_type_id: fee_type.id}) cycle = create_cycle(member, fee_type, %{status: :paid}) assert {:ok, updated} = Ash.update(cycle, %{}, action: :mark_as_unpaid) assert updated.status == :unpaid end test "can set notes when marking as unpaid" do fee_type = create_fee_type(%{interval: :yearly}) member = create_member(%{membership_fee_type_id: fee_type.id}) cycle = create_cycle(member, fee_type, %{status: :paid}) assert {:ok, updated} = Ash.update(cycle, %{notes: "Payment was reversed"}, action: :mark_as_unpaid) assert updated.status == :unpaid assert updated.notes == "Payment was reversed" end test "can change from suspended to unpaid" do fee_type = create_fee_type(%{interval: :yearly}) member = create_member(%{membership_fee_type_id: fee_type.id}) cycle = create_cycle(member, fee_type, %{status: :suspended}) assert {:ok, updated} = Ash.update(cycle, %{}, action: :mark_as_unpaid) assert updated.status == :unpaid end end describe "status transitions" do test "all status transitions are allowed" do fee_type = create_fee_type(%{interval: :yearly}) member = create_member(%{membership_fee_type_id: fee_type.id}) # unpaid -> paid cycle1 = create_cycle(member, fee_type, %{status: :unpaid}) assert {:ok, c1} = Ash.update(cycle1, %{}, action: :mark_as_paid) assert c1.status == :paid # paid -> suspended assert {:ok, c2} = Ash.update(c1, %{}, action: :mark_as_suspended) assert c2.status == :suspended # suspended -> unpaid assert {:ok, c3} = Ash.update(c2, %{}, action: :mark_as_unpaid) assert c3.status == :unpaid # unpaid -> suspended assert {:ok, c4} = Ash.update(c3, %{}, action: :mark_as_suspended) assert c4.status == :suspended # suspended -> paid assert {:ok, c5} = Ash.update(c4, %{}, action: :mark_as_paid) assert c5.status == :paid # paid -> unpaid assert {:ok, c6} = Ash.update(c5, %{}, action: :mark_as_unpaid) assert c6.status == :unpaid end end end