diff --git a/lib/plausible/billing/ecto/feature.ex b/lib/plausible/billing/ecto/feature.ex
new file mode 100644
index 000000000..98ac24e8c
--- /dev/null
+++ b/lib/plausible/billing/ecto/feature.ex
@@ -0,0 +1,78 @@
+defmodule Plausible.Billing.Ecto.Feature do
+ @moduledoc """
+ Ecto type representing a feature. Features are cast and stored in the
+ database as strings and loaded as modules, for example: `"props"` is loaded
+ as `Plausible.Billing.Feature.Props`.
+ """
+
+ use Ecto.Type
+
+ def type, do: :string
+
+ def cast(feature) when is_binary(feature) do
+ found =
+ Enum.find(Plausible.Billing.Feature.list(), fn mod ->
+ Atom.to_string(mod.name()) == feature
+ end)
+
+ if found, do: {:ok, found}, else: :error
+ end
+
+ def cast(mod) when is_atom(mod) do
+ {:ok, mod}
+ end
+
+ def load(feature) when is_binary(feature) do
+ cast(feature)
+ end
+
+ def dump(mod) when is_atom(mod) do
+ {:ok, Atom.to_string(mod.name())}
+ end
+end
+
+defmodule Plausible.Billing.Ecto.FeatureList do
+ @moduledoc """
+ Ecto type representing a list of features. This is a proxy for
+ `{:array, Plausible.Billing.Ecto.Feature}` and is required for Kaffy to
+ render the HTML input correctly.
+ """
+
+ use Ecto.Type
+
+ def type, do: {:array, Plausible.Billing.Ecto.Feature}
+ def cast(list), do: Ecto.Type.cast(type(), list)
+ def load(list), do: Ecto.Type.load(type(), list)
+ def dump(list), do: Ecto.Type.dump(type(), list)
+
+ def render_form(_conn, changeset, form, field, _options) do
+ features = Ecto.Changeset.get_field(changeset, field)
+
+ checkboxes =
+ for mod <- Plausible.Billing.Feature.list() do
+ [
+ {:safe, ~s()},
+ Phoenix.HTML.Tag.tag(
+ :input,
+ name: Phoenix.HTML.Form.input_name(form, field) <> "[]",
+ id: Phoenix.HTML.Form.input_id(form, field, mod.name()),
+ type: "checkbox",
+ value: mod.name(),
+ style: "margin-right: 3px;",
+ checked: mod in features
+ ),
+ mod.display_name(),
+ {:safe, ~s( )}
+ ]
+ end
+
+ [
+ {:safe, ~s(
)}
+ ]
+ end
+end
diff --git a/lib/plausible/billing/ecto/limit.ex b/lib/plausible/billing/ecto/limit.ex
new file mode 100644
index 000000000..6848ee419
--- /dev/null
+++ b/lib/plausible/billing/ecto/limit.ex
@@ -0,0 +1,39 @@
+defmodule Plausible.Billing.Ecto.Limit do
+ @moduledoc """
+ Ecto type representing a limit, that can be either a number or unlimited.
+ Unlimited is dumped to the database as `-1` and loaded as `:unlimited` to
+ keep compatibility with the rest of the codebase.
+ """
+
+ use Ecto.Type
+
+ def type, do: :integer
+
+ def cast(-1), do: {:ok, :unlimited}
+ def cast(:unlimited), do: {:ok, :unlimited}
+ def cast(other), do: Ecto.Type.cast(:integer, other)
+
+ def load(-1), do: {:ok, :unlimited}
+ def load(other), do: Ecto.Type.load(:integer, other)
+
+ def dump(:unlimited), do: {:ok, -1}
+ def dump(other), do: Ecto.Type.dump(:integer, other)
+
+ def render_form(_conn, changeset, form, field, _options) do
+ {:ok, value} = changeset |> Ecto.Changeset.get_field(field) |> dump()
+
+ [
+ {:safe, ~s()}
+ ]
+ end
+end
diff --git a/lib/plausible/billing/enterprise_plan.ex b/lib/plausible/billing/enterprise_plan.ex
index cfae0d4ff..54fa7be42 100644
--- a/lib/plausible/billing/enterprise_plan.ex
+++ b/lib/plausible/billing/enterprise_plan.ex
@@ -8,15 +8,19 @@ defmodule Plausible.Billing.EnterprisePlan do
:billing_interval,
:monthly_pageview_limit,
:hourly_api_request_limit,
- :site_limit
+ :site_limit,
+ :features,
+ :team_member_limit
]
schema "enterprise_plans" do
field :paddle_plan_id, :string
field :billing_interval, Ecto.Enum, values: [:monthly, :yearly]
field :monthly_pageview_limit, :integer
- field :hourly_api_request_limit, :integer
field :site_limit, :integer
+ field :team_member_limit, Plausible.Billing.Ecto.Limit
+ field :features, Plausible.Billing.Ecto.FeatureList, default: []
+ field :hourly_api_request_limit, :integer
belongs_to :user, Plausible.Auth.User
diff --git a/lib/plausible/billing/enterprise_plan_admin.ex b/lib/plausible/billing/enterprise_plan_admin.ex
index 08c396732..bab737530 100644
--- a/lib/plausible/billing/enterprise_plan_admin.ex
+++ b/lib/plausible/billing/enterprise_plan_admin.ex
@@ -8,14 +8,16 @@ defmodule Plausible.Billing.EnterprisePlanAdmin do
]
end
- def form_fields(_) do
+ def form_fields(_schema) do
[
user_id: nil,
paddle_plan_id: nil,
billing_interval: %{choices: [{"Yearly", "yearly"}, {"Monthly", "monthly"}]},
monthly_pageview_limit: nil,
+ site_limit: nil,
+ team_member_limit: nil,
hourly_api_request_limit: nil,
- site_limit: nil
+ features: nil
]
end
@@ -30,10 +32,16 @@ defmodule Plausible.Billing.EnterprisePlanAdmin do
paddle_plan_id: nil,
billing_interval: nil,
monthly_pageview_limit: nil,
- hourly_api_request_limit: nil,
- site_limit: nil
+ site_limit: nil,
+ team_member_limit: nil,
+ hourly_api_request_limit: nil
]
end
defp get_user_email(plan), do: plan.user.email
+
+ def update_changeset(enterprise_plan, attrs) do
+ attrs = Map.put_new(attrs, "features", [])
+ Plausible.Billing.EnterprisePlan.changeset(enterprise_plan, attrs)
+ end
end
diff --git a/lib/plausible/billing/feature.ex b/lib/plausible/billing/feature.ex
index 4d55cb17b..3c5ed0b47 100644
--- a/lib/plausible/billing/feature.ex
+++ b/lib/plausible/billing/feature.ex
@@ -102,7 +102,6 @@ defmodule Plausible.Billing.Feature do
def check_availability(%Plausible.Auth.User{} = user) do
cond do
Keyword.get(unquote(opts), :free) -> :ok
- FunWithFlags.enabled?(:premium_features_private_preview) -> :ok
__MODULE__ in Quota.allowed_features_for(user) -> :ok
true -> {:error, :upgrade_required}
end
diff --git a/lib/plausible/billing/quota.ex b/lib/plausible/billing/quota.ex
index f9e682a1f..cc9dace54 100644
--- a/lib/plausible/billing/quota.ex
+++ b/lib/plausible/billing/quota.ex
@@ -123,8 +123,8 @@ defmodule Plausible.Billing.Quota do
user = Plausible.Users.with_subscription(user)
case Plans.get_subscription_plan(user.subscription) do
- %EnterprisePlan{} ->
- :unlimited
+ %EnterprisePlan{team_member_limit: limit} ->
+ limit
%Plan{team_member_limit: limit} ->
limit
@@ -252,7 +252,7 @@ defmodule Plausible.Billing.Quota do
user = Plausible.Users.with_subscription(user)
case Plans.get_subscription_plan(user.subscription) do
- %EnterprisePlan{} -> Feature.list()
+ %EnterprisePlan{features: features} -> features
%Plan{features: features} -> features
:free_10k -> [Goals, Props, StatsAPI]
nil -> Feature.list()
diff --git a/lib/plausible_web/components/billing.ex b/lib/plausible_web/components/billing.ex
index 073ef1f42..380e68c41 100644
--- a/lib/plausible_web/components/billing.ex
+++ b/lib/plausible_web/components/billing.ex
@@ -5,10 +5,8 @@ defmodule PlausibleWeb.Components.Billing do
import PlausibleWeb.Components.Generic
require Plausible.Billing.Subscription.Status
alias Plausible.Auth.User
- alias Plausible.Billing.Feature.{RevenueGoals, Funnels}
- alias Plausible.Billing.Feature.{Props, StatsAPI}
alias PlausibleWeb.Router.Helpers, as: Routes
- alias Plausible.Billing.{Subscription, Plans, Plan, Subscriptions}
+ alias Plausible.Billing.{Subscription, Plans, Subscriptions}
attr(:billable_user, User, required: true)
attr(:current_user, User, required: true)
@@ -19,42 +17,18 @@ defmodule PlausibleWeb.Components.Billing do
# credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
def premium_feature_notice(assigns) do
- legacy_feature_access? =
- Timex.before?(assigns.billable_user.inserted_at, Plans.business_tier_launch()) &&
- assigns.feature_mod in [StatsAPI, Props]
-
- has_access? = assigns.feature_mod.check_availability(assigns.billable_user) == :ok
-
- cond do
- legacy_feature_access? ->
- ~H""
-
- Plausible.Billing.on_trial?(assigns.billable_user) ->
- ~H""
-
- not has_access? ->
- ~H"""
- <.notice class="rounded-t-md rounded-b-none" size={@size} {@rest} title="Notice">
- <%= account_label(@current_user, @billable_user) %> does not have access to <%= assigns.feature_mod.display_name() %>. To get access to this feature,
- <.upgrade_call_to_action current_user={@current_user} billable_user={@billable_user} />.
-
- """
-
- true ->
- ~H""
- end
- end
-
- defp private_preview_end do
- private_preview_ends_at = Timex.shift(Plausible.Billing.Plans.business_tier_launch(), days: 8)
-
- days_remaining = Timex.diff(private_preview_ends_at, NaiveDateTime.utc_now(), :day)
-
- cond do
- days_remaining <= 0 -> "today"
- days_remaining == 1 -> "tomorrow"
- true -> "in #{days_remaining} days"
- end
+ ~H"""
+ <.notice
+ :if={@feature_mod.check_availability(@billable_user) !== :ok}
+ class="rounded-t-md rounded-b-none"
+ size={@size}
+ title="Notice"
+ {@rest}
+ >
+ <%= account_label(@current_user, @billable_user) %> does not have access to <%= @feature_mod.display_name() %>. To get access to this feature,
+ <.upgrade_call_to_action current_user={@current_user} billable_user={@billable_user} />.
+
+ """
end
attr(:billable_user, User, required: true)
@@ -238,8 +212,7 @@ defmodule PlausibleWeb.Components.Billing do
%{
dismissable: true,
user: %User{subscription: %Subscription{status: Subscription.Status.deleted()}}
- } =
- assigns
+ } = assigns
) do
~H"""
@@ -259,8 +232,7 @@ defmodule PlausibleWeb.Components.Billing do
%{
dismissable: false,
user: %User{subscription: %Subscription{status: Subscription.Status.deleted()}}
- } =
- assigns
+ } = assigns
) do
assigns = assign(assigns, :container_id, "local-subscription-cancelled-notice")
@@ -336,49 +308,6 @@ defmodule PlausibleWeb.Components.Billing do
def subscription_paused_notice(assigns), do: ~H""
- def private_preview_end_notice(assigns) do
- user = assigns.user |> Plausible.Users.with_subscription()
-
- features_to_lose =
- case Plans.get_subscription_plan(user.subscription) do
- nil ->
- []
-
- %Plan{kind: :business} ->
- []
-
- _free_10k_or_enterprise_or_growth ->
- used_features = Plausible.Billing.Quota.features_usage(assigns.user)
- Enum.filter([Funnels, RevenueGoals], &(&1 in used_features))
- end
-
- assigns = assign(assigns, :features_to_lose, features_to_lose)
-
- ~H"""
-
- <.notice
- class="shadow-md dark:shadow-none"
- title="Notice"
- dismissable_id={"premium_features_private_preview_end__#{@user.id}"}
- >
- Business plans are now live! The private preview of <%= PlausibleWeb.TextHelpers.pretty_join(
- Enum.map(@features_to_lose, & &1.display_name())
- ) %> ends <%= private_preview_end() %> . If you wish to continue using <%= if length(
- @features_to_lose
- ) == 1,
- do:
- "this feature",
- else:
- "these features" %>,
- <.upgrade_call_to_action current_user={@user} billable_user={@user} />.
-
-
- """
- end
-
def present_enterprise_plan(assigns) do
~H"""
diff --git a/lib/plausible_web/templates/layout/_notice.html.heex b/lib/plausible_web/templates/layout/_notice.html.heex
index 17cc800df..5390d48ea 100644
--- a/lib/plausible_web/templates/layout/_notice.html.heex
+++ b/lib/plausible_web/templates/layout/_notice.html.heex
@@ -108,7 +108,5 @@
subscription={@conn.assigns.current_user.subscription}
class="container"
/>
-
- <.private_preview_end_notice user={@conn.assigns.current_user} />
<% end %>
diff --git a/mix.exs b/mix.exs
index dfdc36c87..9e0a5ba87 100644
--- a/mix.exs
+++ b/mix.exs
@@ -84,7 +84,7 @@ defmodule Plausible.MixProject do
{:hammer, "~> 6.0"},
{:httpoison, "~> 1.4"},
{:jason, "~> 1.3"},
- {:kaffy, "~> 0.9.4"},
+ {:kaffy, "~> 0.10.2"},
{:location, git: "https://github.com/plausible/location.git"},
{:mox, "~> 1.0", only: :test},
{:nanoid, "~> 2.0.2"},
diff --git a/mix.lock b/mix.lock
index af318223e..3131b689b 100644
--- a/mix.lock
+++ b/mix.lock
@@ -68,7 +68,7 @@
"joken": {:hex, :joken, "2.6.0", "b9dd9b6d52e3e6fcb6c65e151ad38bf4bc286382b5b6f97079c47ade6b1bcc6a", [:mix], [{:jose, "~> 1.11.5", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5a95b05a71cd0b54abd35378aeb1d487a23a52c324fa7efdffc512b655b5aaa7"},
"jose": {:hex, :jose, "1.11.6", "613fda82552128aa6fb804682e3a616f4bc15565a048dabd05b1ebd5827ed965", [:mix, :rebar3], [], "hexpm", "6275cb75504f9c1e60eeacb771adfeee4905a9e182103aa59b53fed651ff9738"},
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
- "kaffy": {:hex, :kaffy, "0.9.4", "6a5446cd2c782b8e122061eab409254eb1fa412adb5824169f0528d16775dc45", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.5", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "91736c9ddc34a94ed76cb56058fdb6b206c9d777b71856c90ef4554f485f13b9"},
+ "kaffy": {:hex, :kaffy, "0.10.2", "72e807c525323bd0cbc3ac0c127b7bde61caffdc576fb6554964d3fe6a2a6100", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0.2", [hex: :phoenix_view, repo: "hexpm", optional: false]}], "hexpm", "651cad5f3bcc91510a671c13c7a273b8b8195fdf2d809208708baecbb77300bf"},
"location": {:git, "https://github.com/plausible/location.git", "31f91c4df430cfc16219000ac667cc665826b77c", []},
"locus": {:hex, :locus, "2.3.6", "c9f53fd5df872fca66a54dc0aa2f8b2d3640388e56a0c39a741be0df6d8854bf", [:rebar3], [{:tls_certificate_check, "~> 1.9", [hex: :tls_certificate_check, repo: "hexpm", optional: false]}], "hexpm", "6087aa9a69673e7011837fb4b3d7f756560adde76892c32f5f93904ee30064e2"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
diff --git a/priv/repo/migrations/20231115131025_add_limits_to_enterprise_plans.exs b/priv/repo/migrations/20231115131025_add_limits_to_enterprise_plans.exs
new file mode 100644
index 000000000..cbaae0834
--- /dev/null
+++ b/priv/repo/migrations/20231115131025_add_limits_to_enterprise_plans.exs
@@ -0,0 +1,13 @@
+defmodule Plausible.Repo.Migrations.AddLimitsToEnterprisePlans do
+ use Ecto.Migration
+
+ def change do
+ alter table(:enterprise_plans) do
+ modify :hourly_api_request_limit, :integer, null: false
+ modify :monthly_pageview_limit, :integer, null: false
+ modify :site_limit, :integer, null: false
+ add :team_member_limit, :integer, null: false, default: -1
+ add :features, {:array, :string}, null: false, default: ["props", "stats_api"]
+ end
+ end
+end
diff --git a/test/plausible/billing/enterprise_plan_test.exs b/test/plausible/billing/enterprise_plan_test.exs
new file mode 100644
index 000000000..b8b6c3bb6
--- /dev/null
+++ b/test/plausible/billing/enterprise_plan_test.exs
@@ -0,0 +1,51 @@
+defmodule Plausible.Billing.EnterprisePlanTest do
+ use Plausible.DataCase
+ alias Plausible.Billing.EnterprisePlan
+
+ test "changeset/2 loads and dumps the list of features" do
+ plan = build(:enterprise_plan, user_id: insert(:user).id)
+ attrs = %{features: ["props", "stats_api"]}
+
+ assert {:ok, enterprise_plan} =
+ plan
+ |> EnterprisePlan.changeset(attrs)
+ |> Plausible.Repo.insert()
+
+ assert %EnterprisePlan{
+ features: [Plausible.Billing.Feature.Props, Plausible.Billing.Feature.StatsAPI]
+ } = enterprise_plan
+
+ assert %EnterprisePlan{
+ features: [Plausible.Billing.Feature.Props, Plausible.Billing.Feature.StatsAPI]
+ } = Plausible.Repo.get(EnterprisePlan, enterprise_plan.id)
+ end
+
+ test "changeset/2 fails when feature does not exist" do
+ plan = build(:enterprise_plan, user_id: insert(:user).id)
+ attrs = %{features: ["ga4_import"]}
+
+ assert {:error, changeset} =
+ plan
+ |> EnterprisePlan.changeset(attrs)
+ |> Plausible.Repo.insert()
+
+ assert {"is invalid", [type: Plausible.Billing.Ecto.FeatureList, validation: :cast]} ==
+ changeset.errors[:features]
+ end
+
+ test "changeset/2 loads and dumps limits" do
+ plan = build(:enterprise_plan, user_id: insert(:user).id)
+ attrs = %{team_member_limit: :unlimited, monthly_pageview_limit: 10_000}
+
+ assert {:ok, enterprise_plan} =
+ plan
+ |> EnterprisePlan.changeset(attrs)
+ |> Plausible.Repo.insert()
+
+ assert %EnterprisePlan{team_member_limit: :unlimited, monthly_pageview_limit: 10_000} =
+ enterprise_plan
+
+ assert %EnterprisePlan{team_member_limit: :unlimited, monthly_pageview_limit: 10_000} =
+ Plausible.Repo.get(EnterprisePlan, enterprise_plan.id)
+ end
+end
diff --git a/test/plausible/billing/feature_test.exs b/test/plausible/billing/feature_test.exs
index 8466e0fa3..4ce91fca8 100644
--- a/test/plausible/billing/feature_test.exs
+++ b/test/plausible/billing/feature_test.exs
@@ -7,7 +7,8 @@ defmodule Plausible.Billing.FeatureTest do
test "#{mod}.check_availability/1 returns :ok when site owner is on a enterprise plan" do
user =
insert(:user,
- enterprise_plan: build(:enterprise_plan, paddle_plan_id: "123321"),
+ enterprise_plan:
+ build(:enterprise_plan, paddle_plan_id: "123321", features: [unquote(mod)]),
subscription: build(:subscription, paddle_plan_id: "123321")
)
@@ -49,7 +50,11 @@ defmodule Plausible.Billing.FeatureTest do
test "Plausible.Billing.Feature.StatsAPI.check_availability/2 returns :ok when user is on an enterprise plan" do
user =
insert(:user,
- enterprise_plan: build(:enterprise_plan, paddle_plan_id: "123321"),
+ enterprise_plan:
+ build(:enterprise_plan,
+ paddle_plan_id: "123321",
+ features: [Plausible.Billing.Feature.StatsAPI]
+ ),
subscription: build(:subscription, paddle_plan_id: "123321")
)
diff --git a/test/plausible/billing/quota_test.exs b/test/plausible/billing/quota_test.exs
index 7de687b0f..9a2f15d33 100644
--- a/test/plausible/billing/quota_test.exs
+++ b/test/plausible/billing/quota_test.exs
@@ -376,16 +376,6 @@ defmodule Plausible.Billing.QuotaTest do
assert :unlimited == Quota.team_member_limit(user)
end
- test "returns unlimited when user is on an enterprise plan" do
- user = insert(:user)
- enterprise_plan = insert(:enterprise_plan, user_id: user.id)
-
- _subscription =
- insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id)
-
- assert :unlimited == Quota.team_member_limit(user)
- end
-
test "returns 5 when user in on trial" do
user =
insert(:user,
@@ -406,14 +396,15 @@ defmodule Plausible.Billing.QuotaTest do
assert :unlimited == Quota.team_member_limit(user)
end
- test "is unlimited for enterprise customers" do
+ test "returns the enterprise plan limit" do
user =
insert(:user,
- enterprise_plan: build(:enterprise_plan, paddle_plan_id: "123321"),
+ enterprise_plan:
+ build(:enterprise_plan, paddle_plan_id: "123321", team_member_limit: 27),
subscription: build(:subscription, paddle_plan_id: "123321")
)
- assert :unlimited == Quota.team_member_limit(user)
+ assert 27 == Quota.team_member_limit(user)
end
test "reads from json file when the user is on a v4 plan" do
@@ -522,20 +513,22 @@ defmodule Plausible.Billing.QuotaTest do
assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user)
end
- test "returns all features when user is on an enterprise plan" do
+ test "returns the enterprise plan features" do
user = insert(:user)
enterprise_plan =
insert(:enterprise_plan,
user_id: user.id,
monthly_pageview_limit: 100_000,
- site_limit: 500
+ site_limit: 500,
+ features: [Plausible.Billing.Feature.StatsAPI, Plausible.Billing.Feature.Funnels]
)
_subscription =
insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id)
- assert Plausible.Billing.Feature.list() == Quota.allowed_features_for(user)
+ assert [Plausible.Billing.Feature.StatsAPI, Plausible.Billing.Feature.Funnels] ==
+ Quota.allowed_features_for(user)
end
test "returns all features when user in on trial" do
@@ -564,25 +557,19 @@ defmodule Plausible.Billing.QuotaTest do
assert Plausible.Billing.Feature.list() == Quota.allowed_features_for(user)
end
- test "returns all features for enterprise customers" do
+ test "returns old plan features for enterprise customers who are due to change a plan" do
user =
insert(:user,
- enterprise_plan: build(:enterprise_plan, paddle_plan_id: "123321"),
- subscription: build(:subscription, paddle_plan_id: "123321")
- )
-
- assert Plausible.Billing.Feature.list() == Quota.allowed_features_for(user)
- end
-
- test "returns all features for enterprise customers who are due to change a plan" do
- user =
- insert(:user,
- enterprise_plan: build(:enterprise_plan, paddle_plan_id: "old-paddle-plan-id"),
+ enterprise_plan:
+ build(:enterprise_plan,
+ paddle_plan_id: "old-paddle-plan-id",
+ features: [Plausible.Billing.Feature.StatsAPI]
+ ),
subscription: build(:subscription, paddle_plan_id: "old-paddle-plan-id")
)
insert(:enterprise_plan, user_id: user.id, paddle_plan_id: "new-paddle-plan-id")
- assert Plausible.Billing.Feature.list() == Quota.allowed_features_for(user)
+ assert [Plausible.Billing.Feature.StatsAPI] == Quota.allowed_features_for(user)
end
end
end
diff --git a/test/support/factory.ex b/test/support/factory.ex
index 7d91eae7f..6ee76a287 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -121,7 +121,8 @@ defmodule Plausible.Factory do
billing_interval: :monthly,
monthly_pageview_limit: 1_000_000,
hourly_api_request_limit: 3000,
- site_limit: 100
+ site_limit: 100,
+ team_member_limit: 10
}
end