refactor(overview): use English identifier as_of_date for point-in-time filter

This commit is contained in:
Simon 2026-07-10 16:27:16 +02:00
parent ce877463d6
commit 0acd12360f
11 changed files with 113 additions and 112 deletions

View file

@ -62,16 +62,16 @@ defmodule MvWeb.MemberLive.IndexActiveFormerTest do
assert has_element?(view, "#row-#{ctx.former.id}")
end
describe "Stichtag paragraph (§1.24)" do
describe "as-of-date paragraph (§1.24)" do
test "the active/former control carries a separate 'active on reference date' input", ctx do
{:ok, view, _html} = live(ctx.conn, ~p"/members")
pick_field(view, "active_former")
# The Stichtag input sits in its own paragraph, not inside the
# The as-of-date input sits in its own paragraph, not inside the
# Active/Former button-join.
assert has_element?(view, "[data-testid='stichtag-paragraph'] input[name='stichtag']")
refute has_element?(view, ".join input[name='stichtag']")
assert has_element?(view, "[data-testid='as-of-date-paragraph'] input[name='as_of_date']")
refute has_element?(view, ".join input[name='as_of_date']")
end
test "setting a reference date activates the point-in-time membership filter", ctx do
@ -81,22 +81,22 @@ defmodule MvWeb.MemberLive.IndexActiveFormerTest do
view
|> element("[data-testid='value-control-active-former']")
|> render_change(%{"quick" => "active", "stichtag" => "2024-06-15"})
|> render_change(%{"quick" => "active", "as_of_date" => "2024-06-15"})
_ = render(view)
path = assert_patch(view)
assert path =~ "stichtag=2024-06-15"
assert path =~ "as_of_date=2024-06-15"
end
end
describe "Eigener Zeitraum exit-date range (§1.24)" do
describe "custom exit-date range (§1.24)" do
test "the exit-date control carries a separate native From/To range paragraph", ctx do
{:ok, view, _html} = live(ctx.conn, ~p"/members")
pick_field(view, "active_former")
# Third paragraph, same native two-input range control the join-date
# builder uses — separate from the Active/Former join and the Stichtag box.
# builder uses — separate from the Active/Former join and the as-of-date box.
assert has_element?(view, "[data-testid='value-control-exit-range']")
assert has_element?(

View file

@ -1,8 +1,8 @@
defmodule MvWeb.MemberLive.IndexStichtagPropertyTest do
defmodule MvWeb.MemberLive.IndexAsOfDatePropertyTest do
@moduledoc """
§2.6 Point-in-time membership ("member on the reference date X").
For arbitrary (join_date, exit_date) pairs and reference dates X, the Stichtag
For arbitrary (join_date, exit_date) pairs and reference dates X, the as-of-date
filter keeps a member iff `join_date <= X and (exit_date is nil or exit_date > X)`.
Verified at the `OverviewQuery` layer against an in-memory oracle.
"""
@ -42,7 +42,7 @@ defmodule MvWeb.MemberLive.IndexStichtagPropertyTest do
end)
end
property "Stichtag filter keeps exactly the members active on X" do
property "as-of-date filter keeps exactly the members active on X" do
actor = Mv.Helpers.SystemActor.get_system_actor()
check all(
@ -67,7 +67,7 @@ defmodule MvWeb.MemberLive.IndexStichtagPropertyTest do
do: id
actual =
%{stichtag: x}
%{as_of_date: x}
|> OverviewQuery.build()
|> Ash.read!(actor: actor)
|> MapSet.new(& &1.id)

View file

@ -1,7 +1,7 @@
defmodule MvWeb.MemberLive.IndexFilterUrlRoundtripTest do
@moduledoc """
§2.1 (extended) the v2 filter keys survive the LiveView decodeencode round
trip. Loading `/members` with a Stichtag date, the suspended flag, and a
trip. Loading `/members` with an as-of date, the suspended flag, and a
two-sided payment count range, then triggering an unrelated re-patch (a sort
click), preserves every one of those keys in the pushed URL.
"""
@ -21,15 +21,15 @@ defmodule MvWeb.MemberLive.IndexFilterUrlRoundtripTest do
%{conn: conn_with_oidc_user(conn)}
end
test "stichtag, suspended and payment count range persist through a re-patch", %{conn: conn} do
url = "/members?stichtag=2026-03-15&suspended=1&pay_filter=has_unpaid&pay_min=2&pay_max=4"
test "as-of date, suspended and payment count range persist through a re-patch", %{conn: conn} do
url = "/members?as_of_date=2026-03-15&suspended=1&pay_filter=has_unpaid&pay_min=2&pay_max=4"
{:ok, view, _html} = live(conn, url)
# Trigger an unrelated URL re-patch by sorting a stable column.
view |> element("[data-testid='payment']") |> render_click()
path = assert_patch(view)
assert path =~ "stichtag=2026-03-15"
assert path =~ "as_of_date=2026-03-15"
assert path =~ "suspended=1"
assert path =~ "pay_filter=has_unpaid"
assert path =~ "pay_min=2"

View file

@ -1,28 +1,28 @@
defmodule MvWeb.MemberLive.IndexStatusParamsCodecTest do
@moduledoc """
§2.1 (extended) URL codec round-trip for the new v2 filter keys: the
point-in-time Stichtag date and the suspended-status flag. Encoding a state to
point-in-time as-of date and the suspended-status flag. Encoding a state to
params then decoding yields the canonical state, and the default (no filter)
encodes to the empty map so a fresh URL is canonical.
"""
use ExUnit.Case, async: true
alias MvWeb.MemberLive.Index.AsOfDate
alias MvWeb.MemberLive.Index.PaymentAging
alias MvWeb.MemberLive.Index.Stichtag
describe "Stichtag date" do
describe "as-of date" do
test "a set date round-trips" do
date = ~D[2026-03-15]
assert date |> Stichtag.to_params() |> Stichtag.parse() == date
assert date |> AsOfDate.to_params() |> AsOfDate.parse() == date
end
test "nil encodes to the empty map and decodes to nil" do
assert Stichtag.to_params(nil) == %{}
assert Stichtag.parse(%{}) == nil
assert AsOfDate.to_params(nil) == %{}
assert AsOfDate.parse(%{}) == nil
end
test "a malformed date param decodes to nil" do
assert Stichtag.parse(%{"stichtag" => "not-a-date"}) == nil
assert AsOfDate.parse(%{"as_of_date" => "not-a-date"}) == nil
end
end