mirror of
https://github.com/plausible/analytics.git
synced 2024-08-16 13:20:59 +03:00
Always recommend a suitable plan on the choose-plan page (#4222)
* pull last_bill_date from paddle sandbox in mix task * move cycle usage checks to Quota module * move quota.ex to a subfolder * split up Quota module * set choose-plan pageview slider according to usage * silence credo
This commit is contained in:
parent
86d7031336
commit
dd1d74ccb7
@ -48,6 +48,7 @@ defmodule Mix.Tasks.PullSandboxSubscription do
|
||||
update_url: res["update_url"],
|
||||
user_id: user.id,
|
||||
status: res["state"],
|
||||
last_bill_date: res["last_payment"]["date"],
|
||||
next_bill_date: res["next_payment"]["date"],
|
||||
next_bill_amount: res["next_payment"]["amount"] |> to_string(),
|
||||
currency_code: res["next_payment"]["currency"]
|
||||
|
@ -132,7 +132,7 @@ defmodule Plausible.Billing.Feature do
|
||||
def check_availability(%Plausible.Auth.User{} = user) do
|
||||
cond do
|
||||
free?() -> :ok
|
||||
__MODULE__ in Quota.allowed_features_for(user) -> :ok
|
||||
__MODULE__ in Quota.Limits.allowed_features_for(user) -> :ok
|
||||
true -> {:error, :upgrade_required}
|
||||
end
|
||||
end
|
||||
|
@ -235,7 +235,7 @@ defmodule Plausible.Billing.Plans do
|
||||
[]
|
||||
end
|
||||
|
||||
if Enum.any?(Quota.features_usage(user), &(&1 not in growth_features)) do
|
||||
if Enum.any?(Quota.Usage.features_usage(user), &(&1 not in growth_features)) do
|
||||
:business
|
||||
else
|
||||
:growth
|
||||
|
120
lib/plausible/billing/qouta/limits.ex
Normal file
120
lib/plausible/billing/qouta/limits.ex
Normal file
@ -0,0 +1,120 @@
|
||||
defmodule Plausible.Billing.Quota.Limits do
|
||||
@moduledoc false
|
||||
|
||||
use Plausible
|
||||
alias Plausible.Users
|
||||
alias Plausible.Auth.User
|
||||
alias Plausible.Billing.{Plan, Plans, Subscription, EnterprisePlan, Feature}
|
||||
alias Plausible.Billing.Feature.{Goals, Props, StatsAPI}
|
||||
|
||||
@type over_limits_error() :: {:over_plan_limits, [limit()]}
|
||||
@typep limit() :: :site_limit | :pageview_limit | :team_member_limit
|
||||
@pageview_allowance_margin 0.1
|
||||
|
||||
on_ee do
|
||||
@limit_sites_since ~D[2021-05-05]
|
||||
@site_limit_for_trials 10
|
||||
@team_member_limit_for_trials 3
|
||||
|
||||
@spec site_limit(User.t()) :: non_neg_integer() | :unlimited
|
||||
def site_limit(user) do
|
||||
if Timex.before?(user.inserted_at, @limit_sites_since) do
|
||||
:unlimited
|
||||
else
|
||||
get_site_limit_from_plan(user)
|
||||
end
|
||||
end
|
||||
|
||||
defp get_site_limit_from_plan(user) do
|
||||
user = Users.with_subscription(user)
|
||||
|
||||
case Plans.get_subscription_plan(user.subscription) do
|
||||
%{site_limit: site_limit} -> site_limit
|
||||
:free_10k -> 50
|
||||
nil -> @site_limit_for_trials
|
||||
end
|
||||
end
|
||||
|
||||
@spec team_member_limit(User.t()) :: non_neg_integer()
|
||||
def team_member_limit(user) do
|
||||
user = Users.with_subscription(user)
|
||||
|
||||
case Plans.get_subscription_plan(user.subscription) do
|
||||
%{team_member_limit: limit} -> limit
|
||||
:free_10k -> :unlimited
|
||||
nil -> @team_member_limit_for_trials
|
||||
end
|
||||
end
|
||||
else
|
||||
def site_limit(_) do
|
||||
:unlimited
|
||||
end
|
||||
|
||||
def team_member_limit(_) do
|
||||
:unlimited
|
||||
end
|
||||
end
|
||||
|
||||
@monthly_pageview_limit_for_free_10k 10_000
|
||||
@monthly_pageview_limit_for_trials :unlimited
|
||||
|
||||
@spec monthly_pageview_limit(User.t() | Subscription.t()) ::
|
||||
non_neg_integer() | :unlimited
|
||||
def monthly_pageview_limit(%User{} = user) do
|
||||
user = Users.with_subscription(user)
|
||||
monthly_pageview_limit(user.subscription)
|
||||
end
|
||||
|
||||
def monthly_pageview_limit(subscription) do
|
||||
case Plans.get_subscription_plan(subscription) do
|
||||
%EnterprisePlan{monthly_pageview_limit: limit} ->
|
||||
limit
|
||||
|
||||
%Plan{monthly_pageview_limit: limit} ->
|
||||
limit
|
||||
|
||||
:free_10k ->
|
||||
@monthly_pageview_limit_for_free_10k
|
||||
|
||||
_any ->
|
||||
if subscription do
|
||||
Sentry.capture_message("Unknown monthly pageview limit for plan",
|
||||
extra: %{paddle_plan_id: subscription.paddle_plan_id}
|
||||
)
|
||||
end
|
||||
|
||||
@monthly_pageview_limit_for_trials
|
||||
end
|
||||
end
|
||||
|
||||
def pageview_limit_with_margin(limit, margin \\ nil) do
|
||||
margin = if margin, do: margin, else: @pageview_allowance_margin
|
||||
ceil(limit * (1 + margin))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a list of features the user can use. Trial users have the
|
||||
ability to use all features during their trial.
|
||||
"""
|
||||
def allowed_features_for(user) do
|
||||
user = Users.with_subscription(user)
|
||||
|
||||
case Plans.get_subscription_plan(user.subscription) do
|
||||
%EnterprisePlan{features: features} ->
|
||||
features
|
||||
|
||||
%Plan{features: features} ->
|
||||
features
|
||||
|
||||
:free_10k ->
|
||||
[Goals, Props, StatsAPI]
|
||||
|
||||
nil ->
|
||||
if Users.on_trial?(user) do
|
||||
Feature.list()
|
||||
else
|
||||
[Goals]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
123
lib/plausible/billing/qouta/quota.ex
Normal file
123
lib/plausible/billing/qouta/quota.ex
Normal file
@ -0,0 +1,123 @@
|
||||
defmodule Plausible.Billing.Quota do
|
||||
@moduledoc """
|
||||
This module provides functions to work with plans usage and limits.
|
||||
"""
|
||||
|
||||
use Plausible
|
||||
alias Plausible.Users
|
||||
alias Plausible.Auth.User
|
||||
alias Plausible.Billing.{Plan, Plans, EnterprisePlan}
|
||||
alias Plausible.Billing.Quota.{Usage, Limits}
|
||||
|
||||
@doc """
|
||||
Enterprise plans are always allowed to add more sites (even when
|
||||
over limit) to avoid service disruption. Their usage is checked
|
||||
in a background job instead (see `check_usage.ex`).
|
||||
"""
|
||||
def ensure_can_add_new_site(user) do
|
||||
user = Users.with_subscription(user)
|
||||
|
||||
case Plans.get_subscription_plan(user.subscription) do
|
||||
%EnterprisePlan{} ->
|
||||
:ok
|
||||
|
||||
_ ->
|
||||
usage = Usage.site_usage(user)
|
||||
limit = Limits.site_limit(user)
|
||||
|
||||
if below_limit?(usage, limit), do: :ok, else: {:error, {:over_limit, limit}}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Ensures that the given user (or the usage map) is within the limits
|
||||
of the given plan.
|
||||
|
||||
An `opts` argument can be passed with `ignore_pageview_limit: true`
|
||||
which bypasses the pageview limit check and returns `:ok` as long as
|
||||
the other limits are not exceeded.
|
||||
"""
|
||||
@spec ensure_within_plan_limits(User.t() | map(), struct() | atom() | nil, Keyword.t()) ::
|
||||
:ok | {:error, Limits.over_limits_error()}
|
||||
def ensure_within_plan_limits(user_or_usage, plan, opts \\ [])
|
||||
|
||||
def ensure_within_plan_limits(%User{} = user, %plan_mod{} = plan, opts)
|
||||
when plan_mod in [Plan, EnterprisePlan] do
|
||||
ensure_within_plan_limits(Usage.usage(user), plan, opts)
|
||||
end
|
||||
|
||||
def ensure_within_plan_limits(usage, %plan_mod{} = plan, opts)
|
||||
when plan_mod in [Plan, EnterprisePlan] do
|
||||
case exceeded_limits(usage, plan, opts) do
|
||||
[] -> :ok
|
||||
exceeded_limits -> {:error, {:over_plan_limits, exceeded_limits}}
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_within_plan_limits(_, _, _), do: :ok
|
||||
|
||||
defp exceeded_limits(usage, plan, opts) do
|
||||
for {limit, exceeded?} <- [
|
||||
{:team_member_limit, not within_limit?(usage.team_members, plan.team_member_limit)},
|
||||
{:site_limit, not within_limit?(usage.sites, plan.site_limit)},
|
||||
{:monthly_pageview_limit,
|
||||
exceeds_monthly_pageview_limit?(usage.monthly_pageviews, plan, opts)}
|
||||
],
|
||||
exceeded? do
|
||||
limit
|
||||
end
|
||||
end
|
||||
|
||||
defp exceeds_monthly_pageview_limit?(usage, plan, opts) do
|
||||
if Keyword.get(opts, :ignore_pageview_limit) do
|
||||
false
|
||||
else
|
||||
case usage do
|
||||
%{last_30_days: %{total: total}} ->
|
||||
margin = Keyword.get(opts, :pageview_allowance_margin)
|
||||
limit = Limits.pageview_limit_with_margin(plan.monthly_pageview_limit, margin)
|
||||
!within_limit?(total, limit)
|
||||
|
||||
cycles_usage ->
|
||||
exceeds_last_two_usage_cycles?(cycles_usage, plan.monthly_pageview_limit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@spec exceeds_last_two_usage_cycles?(Usage.cycles_usage(), non_neg_integer()) :: boolean()
|
||||
def exceeds_last_two_usage_cycles?(cycles_usage, allowed_volume) do
|
||||
exceeded = exceeded_cycles(cycles_usage, allowed_volume)
|
||||
:penultimate_cycle in exceeded && :last_cycle in exceeded
|
||||
end
|
||||
|
||||
@spec exceeded_cycles(Usage.cycles_usage(), non_neg_integer()) :: list()
|
||||
def exceeded_cycles(cycles_usage, allowed_volume) do
|
||||
limit = Limits.pageview_limit_with_margin(allowed_volume)
|
||||
|
||||
Enum.reduce(cycles_usage, [], fn {cycle, %{total: total}}, exceeded_cycles ->
|
||||
if below_limit?(total, limit) do
|
||||
exceeded_cycles
|
||||
else
|
||||
exceeded_cycles ++ [cycle]
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
@spec below_limit?(non_neg_integer(), non_neg_integer() | :unlimited) :: boolean()
|
||||
@doc """
|
||||
Returns whether the usage is below the limit or not.
|
||||
Returns false if usage is equal to the limit.
|
||||
"""
|
||||
def below_limit?(usage, limit) do
|
||||
if limit == :unlimited, do: true, else: usage < limit
|
||||
end
|
||||
|
||||
@spec within_limit?(non_neg_integer(), non_neg_integer() | :unlimited) :: boolean()
|
||||
@doc """
|
||||
Returns whether the usage is within the limit or not.
|
||||
Returns true if usage is equal to the limit.
|
||||
"""
|
||||
def within_limit?(usage, limit) do
|
||||
if limit == :unlimited, do: true, else: usage <= limit
|
||||
end
|
||||
end
|
@ -1,35 +1,27 @@
|
||||
defmodule Plausible.Billing.Quota do
|
||||
@moduledoc """
|
||||
This module provides functions to work with plans usage and limits.
|
||||
"""
|
||||
defmodule Plausible.Billing.Quota.Usage do
|
||||
@moduledoc false
|
||||
|
||||
use Plausible
|
||||
import Ecto.Query
|
||||
alias Plausible.Users
|
||||
alias Plausible.Auth.User
|
||||
alias Plausible.Site
|
||||
alias Plausible.Billing.{Plan, Plans, Subscription, Subscriptions, EnterprisePlan, Feature}
|
||||
alias Plausible.Billing.Feature.{Goals, RevenueGoals, Funnels, Props, StatsAPI}
|
||||
alias Plausible.Billing.{Subscriptions}
|
||||
alias Plausible.Billing.Feature.{RevenueGoals, Funnels, Props, StatsAPI}
|
||||
|
||||
@type limit() :: :site_limit | :pageview_limit | :team_member_limit
|
||||
@type cycles_usage() :: %{cycle() => usage_cycle()}
|
||||
|
||||
@type over_limits_error() :: {:over_plan_limits, [limit()]}
|
||||
@typep cycle :: :current_cycle | :last_cycle | :penultimate_cycle
|
||||
@typep last_30_days_usage() :: %{:last_30_days => usage_cycle()}
|
||||
@typep monthly_pageview_usage() :: cycles_usage() | last_30_days_usage()
|
||||
|
||||
@type monthly_pageview_usage() :: %{period() => usage_cycle()}
|
||||
|
||||
@type period :: :last_30_days | :current_cycle | :last_cycle | :penultimate_cycle
|
||||
|
||||
@type usage_cycle :: %{
|
||||
@typep usage_cycle :: %{
|
||||
date_range: Date.Range.t(),
|
||||
pageviews: non_neg_integer(),
|
||||
custom_events: non_neg_integer(),
|
||||
total: non_neg_integer()
|
||||
}
|
||||
|
||||
@pageview_allowance_margin 0.1
|
||||
|
||||
def pageview_allowance_margin(), do: @pageview_allowance_margin
|
||||
|
||||
def usage(user, opts \\ []) do
|
||||
basic_usage = %{
|
||||
monthly_pageviews: monthly_pageview_usage(user),
|
||||
@ -45,50 +37,6 @@ defmodule Plausible.Billing.Quota do
|
||||
end
|
||||
end
|
||||
|
||||
on_ee do
|
||||
@limit_sites_since ~D[2021-05-05]
|
||||
@site_limit_for_trials 10
|
||||
@team_member_limit_for_trials 3
|
||||
|
||||
@spec site_limit(User.t()) :: non_neg_integer() | :unlimited
|
||||
def site_limit(user) do
|
||||
if Timex.before?(user.inserted_at, @limit_sites_since) do
|
||||
:unlimited
|
||||
else
|
||||
get_site_limit_from_plan(user)
|
||||
end
|
||||
end
|
||||
|
||||
defp get_site_limit_from_plan(user) do
|
||||
user = Users.with_subscription(user)
|
||||
|
||||
case Plans.get_subscription_plan(user.subscription) do
|
||||
%{site_limit: site_limit} -> site_limit
|
||||
:free_10k -> 50
|
||||
nil -> @site_limit_for_trials
|
||||
end
|
||||
end
|
||||
|
||||
@spec team_member_limit(User.t()) :: non_neg_integer()
|
||||
def team_member_limit(user) do
|
||||
user = Users.with_subscription(user)
|
||||
|
||||
case Plans.get_subscription_plan(user.subscription) do
|
||||
%{team_member_limit: limit} -> limit
|
||||
:free_10k -> :unlimited
|
||||
nil -> @team_member_limit_for_trials
|
||||
end
|
||||
end
|
||||
else
|
||||
def site_limit(_) do
|
||||
:unlimited
|
||||
end
|
||||
|
||||
def team_member_limit(_) do
|
||||
:unlimited
|
||||
end
|
||||
end
|
||||
|
||||
@spec site_usage(User.t()) :: non_neg_integer()
|
||||
@doc """
|
||||
Returns the number of sites the given user owns.
|
||||
@ -97,58 +45,6 @@ defmodule Plausible.Billing.Quota do
|
||||
Plausible.Sites.owned_sites_count(user)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Enterprise plans are always allowed to add more sites (even when
|
||||
over limit) to avoid service disruption. Their usage is checked
|
||||
in a background job instead (see `check_usage.ex`).
|
||||
"""
|
||||
def ensure_can_add_new_site(user) do
|
||||
user = Users.with_subscription(user)
|
||||
|
||||
case Plans.get_subscription_plan(user.subscription) do
|
||||
%EnterprisePlan{} ->
|
||||
:ok
|
||||
|
||||
_ ->
|
||||
usage = site_usage(user)
|
||||
limit = site_limit(user)
|
||||
|
||||
if below_limit?(usage, limit), do: :ok, else: {:error, {:over_limit, limit}}
|
||||
end
|
||||
end
|
||||
|
||||
@monthly_pageview_limit_for_free_10k 10_000
|
||||
@monthly_pageview_limit_for_trials :unlimited
|
||||
|
||||
@spec monthly_pageview_limit(User.t() | Subscription.t()) ::
|
||||
non_neg_integer() | :unlimited
|
||||
def monthly_pageview_limit(%User{} = user) do
|
||||
user = Users.with_subscription(user)
|
||||
monthly_pageview_limit(user.subscription)
|
||||
end
|
||||
|
||||
def monthly_pageview_limit(subscription) do
|
||||
case Plans.get_subscription_plan(subscription) do
|
||||
%EnterprisePlan{monthly_pageview_limit: limit} ->
|
||||
limit
|
||||
|
||||
%Plan{monthly_pageview_limit: limit} ->
|
||||
limit
|
||||
|
||||
:free_10k ->
|
||||
@monthly_pageview_limit_for_free_10k
|
||||
|
||||
_any ->
|
||||
if subscription do
|
||||
Sentry.capture_message("Unknown monthly pageview limit for plan",
|
||||
extra: %{paddle_plan_id: subscription.paddle_plan_id}
|
||||
)
|
||||
end
|
||||
|
||||
@monthly_pageview_limit_for_trials
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Queries the ClickHouse database for the monthly pageview usage. If the given user's
|
||||
subscription is `active`, `past_due`, or a `deleted` (but not yet expired), a map
|
||||
@ -195,8 +91,7 @@ defmodule Plausible.Billing.Quota do
|
||||
end
|
||||
end
|
||||
|
||||
@spec usage_cycle(User.t(), period(), list() | nil, Date.t()) :: usage_cycle()
|
||||
|
||||
@spec usage_cycle(User.t(), :last_30_days | cycle(), list() | nil, Date.t()) :: usage_cycle()
|
||||
def usage_cycle(user, cycle, owned_site_ids \\ nil, today \\ Timex.today())
|
||||
|
||||
def usage_cycle(user, cycle, nil, today) do
|
||||
@ -398,114 +293,9 @@ defmodule Plausible.Billing.Quota do
|
||||
for {f_mod, used?} <- used_features, used?, f_mod.enabled?(site), do: f_mod
|
||||
end
|
||||
|
||||
@doc """
|
||||
Ensures that the given user (or the usage map) is within the limits
|
||||
of the given plan.
|
||||
|
||||
An `opts` argument can be passed with `ignore_pageview_limit: true`
|
||||
which bypasses the pageview limit check and returns `:ok` as long as
|
||||
the other limits are not exceeded.
|
||||
"""
|
||||
@spec ensure_within_plan_limits(User.t() | map(), struct() | atom() | nil, Keyword.t()) ::
|
||||
:ok | {:error, over_limits_error()}
|
||||
def ensure_within_plan_limits(user_or_usage, plan, opts \\ [])
|
||||
|
||||
def ensure_within_plan_limits(%User{} = user, %plan_mod{} = plan, opts)
|
||||
when plan_mod in [Plan, EnterprisePlan] do
|
||||
ensure_within_plan_limits(usage(user), plan, opts)
|
||||
end
|
||||
|
||||
def ensure_within_plan_limits(usage, %plan_mod{} = plan, opts)
|
||||
when plan_mod in [Plan, EnterprisePlan] do
|
||||
case exceeded_limits(usage, plan, opts) do
|
||||
[] -> :ok
|
||||
exceeded_limits -> {:error, {:over_plan_limits, exceeded_limits}}
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_within_plan_limits(_, _, _), do: :ok
|
||||
|
||||
defp exceeded_limits(usage, plan, opts) do
|
||||
for {limit, exceeded?} <- [
|
||||
{:team_member_limit, not within_limit?(usage.team_members, plan.team_member_limit)},
|
||||
{:site_limit, not within_limit?(usage.sites, plan.site_limit)},
|
||||
{:monthly_pageview_limit,
|
||||
exceeds_monthly_pageview_limit?(usage.monthly_pageviews, plan, opts)}
|
||||
],
|
||||
exceeded? do
|
||||
limit
|
||||
end
|
||||
end
|
||||
|
||||
defp exceeds_monthly_pageview_limit?(usage, plan, opts) do
|
||||
if Keyword.get(opts, :ignore_pageview_limit) do
|
||||
false
|
||||
else
|
||||
case usage do
|
||||
%{last_30_days: %{total: total}} ->
|
||||
!within_limit?(total, pageview_limit_with_margin(plan, opts))
|
||||
|
||||
billing_cycles_usage ->
|
||||
Plausible.Workers.CheckUsage.exceeds_last_two_usage_cycles?(
|
||||
billing_cycles_usage,
|
||||
plan.monthly_pageview_limit
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp pageview_limit_with_margin(%{monthly_pageview_limit: limit}, opts) do
|
||||
margin = Keyword.get(opts, :pageview_allowance_margin, @pageview_allowance_margin)
|
||||
ceil(limit * (1 + margin))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a list of features the user can use. Trial users have the
|
||||
ability to use all features during their trial.
|
||||
"""
|
||||
def allowed_features_for(user) do
|
||||
user = Users.with_subscription(user)
|
||||
|
||||
case Plans.get_subscription_plan(user.subscription) do
|
||||
%EnterprisePlan{features: features} ->
|
||||
features
|
||||
|
||||
%Plan{features: features} ->
|
||||
features
|
||||
|
||||
:free_10k ->
|
||||
[Goals, Props, StatsAPI]
|
||||
|
||||
nil ->
|
||||
if Users.on_trial?(user) do
|
||||
Feature.list()
|
||||
else
|
||||
[Goals]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp owned_sites_query(user) do
|
||||
from sm in Site.Membership,
|
||||
where: sm.role == :owner and sm.user_id == ^user.id,
|
||||
select: %{site_id: sm.site_id}
|
||||
end
|
||||
|
||||
@spec below_limit?(non_neg_integer(), non_neg_integer() | :unlimited) :: boolean()
|
||||
@doc """
|
||||
Returns whether the usage is below the limit or not.
|
||||
Returns false if usage is equal to the limit.
|
||||
"""
|
||||
def below_limit?(usage, limit) do
|
||||
if limit == :unlimited, do: true, else: usage < limit
|
||||
end
|
||||
|
||||
@spec within_limit?(non_neg_integer(), non_neg_integer() | :unlimited) :: boolean()
|
||||
@doc """
|
||||
Returns whether the usage is within the limit or not.
|
||||
Returns true if usage is equal to the limit.
|
||||
"""
|
||||
def within_limit?(usage, limit) do
|
||||
if limit == :unlimited, do: true, else: usage <= limit
|
||||
end
|
||||
end
|
@ -68,7 +68,7 @@ defmodule Plausible.Billing.SiteLocker do
|
||||
|
||||
@spec send_grace_period_end_email(Plausible.Auth.User.t()) :: Plausible.Mailer.result()
|
||||
def send_grace_period_end_email(user) do
|
||||
usage = Plausible.Billing.Quota.monthly_pageview_usage(user)
|
||||
usage = Plausible.Billing.Quota.Usage.monthly_pageview_usage(user)
|
||||
suggested_plan = Plausible.Billing.Plans.suggest(user, usage.last_cycle.total)
|
||||
|
||||
PlausibleWeb.Email.dashboard_locked(user, usage, suggested_plan)
|
||||
|
@ -26,7 +26,7 @@ defmodule Plausible.Site.Memberships.AcceptInvitation do
|
||||
@spec transfer_ownership(Site.t(), Auth.User.t()) ::
|
||||
{:ok, Site.Membership.t()}
|
||||
| {:error,
|
||||
Billing.Quota.over_limits_error()
|
||||
Billing.Quota.Limits.over_limits_error()
|
||||
| Ecto.Changeset.t()
|
||||
| :transfer_to_self
|
||||
| :no_plan}
|
||||
@ -54,7 +54,7 @@ defmodule Plausible.Site.Memberships.AcceptInvitation do
|
||||
{:ok, Site.Membership.t()}
|
||||
| {:error,
|
||||
:invitation_not_found
|
||||
| Billing.Quota.over_limits_error()
|
||||
| Billing.Quota.Limits.over_limits_error()
|
||||
| Ecto.Changeset.t()
|
||||
| :no_plan}
|
||||
def accept_invitation(invitation_id, user) do
|
||||
|
@ -41,7 +41,7 @@ defmodule Plausible.Site.Memberships.CreateInvitation do
|
||||
{:ok, [Membership.t()]}
|
||||
| {:error,
|
||||
invite_error()
|
||||
| Quota.over_limits_error()}
|
||||
| Quota.Limits.over_limits_error()}
|
||||
def bulk_transfer_ownership_direct(sites, new_owner) do
|
||||
Plausible.Repo.transaction(fn ->
|
||||
for site <- sites do
|
||||
@ -134,8 +134,8 @@ defmodule Plausible.Site.Memberships.CreateInvitation do
|
||||
|
||||
defp check_team_member_limit(site, _role, invitee_email) do
|
||||
site = Plausible.Repo.preload(site, :owner)
|
||||
limit = Quota.team_member_limit(site.owner)
|
||||
usage = Quota.team_member_usage(site.owner, exclude_emails: invitee_email)
|
||||
limit = Quota.Limits.team_member_limit(site.owner)
|
||||
usage = Quota.Usage.team_member_usage(site.owner, exclude_emails: invitee_email)
|
||||
|
||||
if Quota.below_limit?(usage, limit),
|
||||
do: :ok,
|
||||
|
@ -66,7 +66,7 @@ defmodule Plausible.Site.Memberships.Invitations do
|
||||
|
||||
on_ee do
|
||||
@spec ensure_can_take_ownership(Site.t(), Auth.User.t()) ::
|
||||
:ok | {:error, Quota.over_limits_error() | :no_plan}
|
||||
:ok | {:error, Quota.Limits.over_limits_error() | :no_plan}
|
||||
def ensure_can_take_ownership(site, new_owner) do
|
||||
site = Repo.preload(site, :owner)
|
||||
new_owner = Plausible.Users.with_subscription(new_owner)
|
||||
@ -78,7 +78,7 @@ defmodule Plausible.Site.Memberships.Invitations do
|
||||
usage_after_transfer = %{
|
||||
monthly_pageviews: monthly_pageview_usage_after_transfer(site, new_owner),
|
||||
team_members: team_member_usage_after_transfer(site, new_owner),
|
||||
sites: Quota.site_usage(new_owner) + 1
|
||||
sites: Quota.Usage.site_usage(new_owner) + 1
|
||||
}
|
||||
|
||||
Quota.ensure_within_plan_limits(usage_after_transfer, plan)
|
||||
@ -88,8 +88,8 @@ defmodule Plausible.Site.Memberships.Invitations do
|
||||
end
|
||||
|
||||
defp team_member_usage_after_transfer(site, new_owner) do
|
||||
current_usage = Quota.team_member_usage(new_owner)
|
||||
site_usage = Quota.team_member_usage(site.owner, site: site)
|
||||
current_usage = Quota.Usage.team_member_usage(new_owner)
|
||||
site_usage = Quota.Usage.team_member_usage(site.owner, site: site)
|
||||
|
||||
extra_usage =
|
||||
if Plausible.Sites.is_member?(new_owner.id, site), do: 0, else: 1
|
||||
@ -99,7 +99,7 @@ defmodule Plausible.Site.Memberships.Invitations do
|
||||
|
||||
defp monthly_pageview_usage_after_transfer(site, new_owner) do
|
||||
site_ids = Plausible.Sites.owned_site_ids(new_owner) ++ [site.id]
|
||||
Quota.monthly_pageview_usage(new_owner, site_ids)
|
||||
Quota.Usage.monthly_pageview_usage(new_owner, site_ids)
|
||||
end
|
||||
else
|
||||
@spec ensure_can_take_ownership(Site.t(), Auth.User.t()) :: :ok
|
||||
@ -117,7 +117,7 @@ defmodule Plausible.Site.Memberships.Invitations do
|
||||
def check_feature_access(site, new_owner, false = _selfhost?) do
|
||||
missing_features =
|
||||
site
|
||||
|> Quota.features_usage()
|
||||
|> Quota.Usage.features_usage()
|
||||
|> Enum.filter(&(&1.check_availability(new_owner) != :ok))
|
||||
|
||||
if missing_features == [] do
|
||||
|
@ -9,12 +9,12 @@ defmodule PlausibleWeb.AdminController do
|
||||
|> String.to_integer()
|
||||
|> Plausible.Users.with_subscription()
|
||||
|
||||
usage = Quota.usage(user, with_features: true)
|
||||
usage = Quota.Usage.usage(user, with_features: true)
|
||||
|
||||
limits = %{
|
||||
monthly_pageviews: Quota.monthly_pageview_limit(user),
|
||||
sites: Quota.site_limit(user),
|
||||
team_members: Quota.team_member_limit(user)
|
||||
monthly_pageviews: Quota.Limits.monthly_pageview_limit(user),
|
||||
sites: Quota.Limits.site_limit(user),
|
||||
team_members: Quota.Limits.team_member_limit(user)
|
||||
}
|
||||
|
||||
html_response = usage_and_limits_html(user, usage, limits)
|
||||
|
@ -633,12 +633,12 @@ defmodule PlausibleWeb.AuthController do
|
||||
subscription: user.subscription,
|
||||
invoices: Plausible.Billing.paddle_api().get_invoices(user.subscription),
|
||||
theme: user.theme || "system",
|
||||
team_member_limit: Quota.team_member_limit(user),
|
||||
team_member_usage: Quota.team_member_usage(user),
|
||||
site_limit: Quota.site_limit(user),
|
||||
site_usage: Quota.site_usage(user),
|
||||
pageview_limit: Quota.monthly_pageview_limit(user),
|
||||
pageview_usage: Quota.monthly_pageview_usage(user),
|
||||
team_member_limit: Quota.Limits.team_member_limit(user),
|
||||
team_member_usage: Quota.Usage.team_member_usage(user),
|
||||
site_limit: Quota.Limits.site_limit(user),
|
||||
site_usage: Quota.Usage.site_usage(user),
|
||||
pageview_limit: Quota.Limits.monthly_pageview_limit(user),
|
||||
pageview_usage: Quota.Usage.monthly_pageview_usage(user),
|
||||
totp_enabled?: Auth.TOTP.enabled?(user)
|
||||
)
|
||||
end
|
||||
|
@ -29,8 +29,8 @@ defmodule PlausibleWeb.Site.MembershipController do
|
||||
|> Sites.get_for_user!(conn.assigns.site.domain)
|
||||
|> Plausible.Repo.preload(:owner)
|
||||
|
||||
limit = Plausible.Billing.Quota.team_member_limit(site.owner)
|
||||
usage = Plausible.Billing.Quota.team_member_usage(site.owner)
|
||||
limit = Plausible.Billing.Quota.Limits.team_member_limit(site.owner)
|
||||
usage = Plausible.Billing.Quota.Usage.team_member_usage(site.owner)
|
||||
below_limit? = Plausible.Billing.Quota.below_limit?(usage, limit)
|
||||
|
||||
render(
|
||||
|
@ -18,8 +18,8 @@ defmodule PlausibleWeb.SiteController do
|
||||
|
||||
render(conn, "new.html",
|
||||
changeset: Plausible.Site.changeset(%Plausible.Site{}),
|
||||
first_site?: Quota.site_usage(current_user) == 0,
|
||||
site_limit: Quota.site_limit(current_user),
|
||||
first_site?: Quota.Usage.site_usage(current_user) == 0,
|
||||
site_limit: Quota.Limits.site_limit(current_user),
|
||||
site_limit_exceeded?: Quota.ensure_can_add_new_site(current_user) != :ok,
|
||||
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
||||
)
|
||||
@ -27,7 +27,7 @@ defmodule PlausibleWeb.SiteController do
|
||||
|
||||
def create_site(conn, %{"site" => site_params}) do
|
||||
user = conn.assigns[:current_user]
|
||||
first_site? = Quota.site_usage(user) == 0
|
||||
first_site? = Quota.Usage.site_usage(user) == 0
|
||||
|
||||
case Sites.create(user, site_params) do
|
||||
{:ok, %{site: site}} ->
|
||||
@ -53,7 +53,7 @@ defmodule PlausibleWeb.SiteController do
|
||||
render(conn, "new.html",
|
||||
changeset: changeset,
|
||||
first_site?: first_site?,
|
||||
site_limit: Quota.site_limit(user),
|
||||
site_limit: Quota.Limits.site_limit(user),
|
||||
site_limit_exceeded?: false,
|
||||
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
||||
alias PlausibleWeb.Components.Billing.{PlanBox, PlanBenefits, Notice, PageviewSlider}
|
||||
alias Plausible.Site
|
||||
alias Plausible.Users
|
||||
alias Plausible.Billing.{Plans, Plan, Quota}
|
||||
alias Plausible.Billing.{Plans, Quota}
|
||||
|
||||
@contact_link "https://plausible.io/contact"
|
||||
@billing_faq_link "https://plausible.io/docs/billing"
|
||||
@ -22,13 +22,7 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
||||
Users.with_subscription(user_id)
|
||||
end)
|
||||
|> assign_new(:usage, fn %{user: user} ->
|
||||
Quota.usage(user, with_features: true)
|
||||
end)
|
||||
|> assign_new(:last_30_days_usage, fn %{user: user, usage: usage} ->
|
||||
case usage do
|
||||
%{last_30_days: usage_cycle} -> usage_cycle.total
|
||||
_ -> Quota.usage_cycle(user, :last_30_days).total
|
||||
end
|
||||
Quota.Usage.usage(user, with_features: true)
|
||||
end)
|
||||
|> assign_new(:owned_plan, fn %{user: %{subscription: subscription}} ->
|
||||
Plans.get_regular_plan(subscription, only_non_expired: true)
|
||||
@ -63,11 +57,10 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
||||
get_available_volumes(available_plans)
|
||||
end)
|
||||
|> assign_new(:selected_volume, fn %{
|
||||
owned_plan: owned_plan,
|
||||
last_30_days_usage: last_30_days_usage,
|
||||
usage: usage,
|
||||
available_volumes: available_volumes
|
||||
} ->
|
||||
default_selected_volume(owned_plan, last_30_days_usage, available_volumes)
|
||||
default_selected_volume(usage.monthly_pageviews, available_volumes)
|
||||
end)
|
||||
|> assign_new(:selected_interval, fn %{current_interval: current_interval} ->
|
||||
current_interval || :monthly
|
||||
@ -149,8 +142,7 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
||||
<PlanBox.enterprise benefits={@enterprise_benefits} />
|
||||
</div>
|
||||
<p class="mx-auto mt-8 max-w-2xl text-center text-lg leading-8 text-gray-600 dark:text-gray-400">
|
||||
You have used <b><%= PlausibleWeb.AuthView.delimit_integer(@last_30_days_usage) %></b>
|
||||
billable pageviews in the last 30 days
|
||||
<.render_usage pageview_usage={@usage.monthly_pageviews} />
|
||||
</p>
|
||||
<.pageview_limit_notice :if={!@owned_plan} />
|
||||
<.help_links />
|
||||
@ -160,6 +152,22 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
||||
"""
|
||||
end
|
||||
|
||||
defp render_usage(assigns) do
|
||||
case assigns.pageview_usage do
|
||||
%{last_30_days: _} ->
|
||||
~H"""
|
||||
You have used
|
||||
<b><%= PlausibleWeb.AuthView.delimit_integer(@pageview_usage.last_30_days.total) %></b> billable pageviews in the last 30 days
|
||||
"""
|
||||
|
||||
%{last_cycle: _} ->
|
||||
~H"""
|
||||
You have used
|
||||
<b><%= PlausibleWeb.AuthView.delimit_integer(@pageview_usage.last_cycle.total) %></b> billable pageviews in the last billing cycle
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("set_interval", %{"interval" => interval}, socket) do
|
||||
new_interval =
|
||||
case interval do
|
||||
@ -189,10 +197,14 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
||||
)}
|
||||
end
|
||||
|
||||
defp default_selected_volume(%Plan{monthly_pageview_limit: limit}, _, _), do: limit
|
||||
defp default_selected_volume(pageview_usage, available_volumes) do
|
||||
total =
|
||||
case pageview_usage do
|
||||
%{last_30_days: usage} -> usage.total
|
||||
%{last_cycle: usage} -> usage.total
|
||||
end
|
||||
|
||||
defp default_selected_volume(_, last_30_days_usage, available_volumes) do
|
||||
Enum.find(available_volumes, &(last_30_days_usage < &1)) || :enterprise
|
||||
Enum.find(available_volumes, &(total < &1)) || :enterprise
|
||||
end
|
||||
|
||||
defp current_user_subscription_interval(subscription) do
|
||||
|
@ -10,7 +10,7 @@ defmodule PlausibleWeb.AuthView do
|
||||
|
||||
def subscription_quota(subscription, options) do
|
||||
subscription
|
||||
|> Plausible.Billing.Quota.monthly_pageview_limit()
|
||||
|> Plausible.Billing.Quota.Limits.monthly_pageview_limit()
|
||||
|> PlausibleWeb.StatsView.large_number_format()
|
||||
|> then(fn quota ->
|
||||
if Keyword.get(options, :format) == :long,
|
||||
|
@ -33,7 +33,7 @@ defmodule Plausible.Workers.CheckUsage do
|
||||
end
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(_job, quota_mod \\ Quota, today \\ Timex.today()) do
|
||||
def perform(_job, usage_mod \\ Quota.Usage, today \\ Timex.today()) do
|
||||
yesterday = today |> Timex.shift(days: -1)
|
||||
|
||||
active_subscribers =
|
||||
@ -56,13 +56,13 @@ defmodule Plausible.Workers.CheckUsage do
|
||||
for subscriber <- active_subscribers do
|
||||
case {subscriber.grace_period, subscriber.enterprise_plan} do
|
||||
{nil, nil} ->
|
||||
check_regular_subscriber(subscriber, quota_mod)
|
||||
check_regular_subscriber(subscriber, usage_mod)
|
||||
|
||||
{nil, _} ->
|
||||
check_enterprise_subscriber(subscriber, quota_mod)
|
||||
check_enterprise_subscriber(subscriber, usage_mod)
|
||||
|
||||
{_, nil} ->
|
||||
maybe_remove_grace_period(subscriber, quota_mod)
|
||||
maybe_remove_grace_period(subscriber, usage_mod)
|
||||
|
||||
_ ->
|
||||
:skip
|
||||
@ -72,29 +72,9 @@ defmodule Plausible.Workers.CheckUsage do
|
||||
:ok
|
||||
end
|
||||
|
||||
@spec exceeds_last_two_usage_cycles?(Quota.monthly_pageview_usage(), non_neg_integer()) ::
|
||||
boolean()
|
||||
|
||||
def exceeds_last_two_usage_cycles?(usage, limit) when is_integer(limit) do
|
||||
limit = ceil(limit * (1 + Quota.pageview_allowance_margin()))
|
||||
|
||||
Enum.all?([usage.last_cycle, usage.penultimate_cycle], fn usage ->
|
||||
not Quota.below_limit?(usage.total, limit)
|
||||
end)
|
||||
end
|
||||
|
||||
@spec last_usage_cycle_below_limit?(Quota.monthly_pageview_usage(), non_neg_integer()) ::
|
||||
boolean()
|
||||
|
||||
def last_usage_cycle_below_limit?(usage, limit) when is_integer(limit) do
|
||||
limit = ceil(limit * (1 + Quota.pageview_allowance_margin()))
|
||||
|
||||
Quota.below_limit?(usage.last_cycle.total, limit)
|
||||
end
|
||||
|
||||
defp check_site_usage_for_enterprise(subscriber) do
|
||||
limit = subscriber.enterprise_plan.site_limit
|
||||
usage = Quota.site_usage(subscriber)
|
||||
usage = Quota.Usage.site_usage(subscriber)
|
||||
|
||||
if Quota.below_limit?(usage, limit) do
|
||||
{:below_limit, {usage, limit}}
|
||||
@ -103,8 +83,8 @@ defmodule Plausible.Workers.CheckUsage do
|
||||
end
|
||||
end
|
||||
|
||||
def maybe_remove_grace_period(subscriber, quota_mod) do
|
||||
case check_pageview_usage_last_cycle(subscriber, quota_mod) do
|
||||
def maybe_remove_grace_period(subscriber, usage_mod) do
|
||||
case check_pageview_usage_last_cycle(subscriber, usage_mod) do
|
||||
{:below_limit, _} ->
|
||||
subscriber
|
||||
|> Plausible.Auth.GracePeriod.remove_changeset()
|
||||
@ -115,8 +95,8 @@ defmodule Plausible.Workers.CheckUsage do
|
||||
end
|
||||
end
|
||||
|
||||
defp check_regular_subscriber(subscriber, quota_mod) do
|
||||
case check_pageview_usage_two_cycles(subscriber, quota_mod) do
|
||||
defp check_regular_subscriber(subscriber, usage_mod) do
|
||||
case check_pageview_usage_two_cycles(subscriber, usage_mod) do
|
||||
{:over_limit, pageview_usage} ->
|
||||
suggested_plan =
|
||||
Plausible.Billing.Plans.suggest(subscriber, pageview_usage.last_cycle.total)
|
||||
@ -133,8 +113,8 @@ defmodule Plausible.Workers.CheckUsage do
|
||||
end
|
||||
end
|
||||
|
||||
def check_enterprise_subscriber(subscriber, quota_mod) do
|
||||
pageview_usage = check_pageview_usage_two_cycles(subscriber, quota_mod)
|
||||
def check_enterprise_subscriber(subscriber, usage_mod) do
|
||||
pageview_usage = check_pageview_usage_two_cycles(subscriber, usage_mod)
|
||||
site_usage = check_site_usage_for_enterprise(subscriber)
|
||||
|
||||
case {pageview_usage, site_usage} do
|
||||
@ -156,25 +136,25 @@ defmodule Plausible.Workers.CheckUsage do
|
||||
end
|
||||
end
|
||||
|
||||
defp check_pageview_usage_two_cycles(subscriber, quota_mod) do
|
||||
usage = quota_mod.monthly_pageview_usage(subscriber)
|
||||
limit = Quota.monthly_pageview_limit(subscriber)
|
||||
defp check_pageview_usage_two_cycles(subscriber, usage_mod) do
|
||||
usage = usage_mod.monthly_pageview_usage(subscriber)
|
||||
limit = Quota.Limits.monthly_pageview_limit(subscriber)
|
||||
|
||||
if exceeds_last_two_usage_cycles?(usage, limit) do
|
||||
if Quota.exceeds_last_two_usage_cycles?(usage, limit) do
|
||||
{:over_limit, usage}
|
||||
else
|
||||
{:below_limit, usage}
|
||||
end
|
||||
end
|
||||
|
||||
defp check_pageview_usage_last_cycle(subscriber, quota_mod) do
|
||||
usage = quota_mod.monthly_pageview_usage(subscriber)
|
||||
limit = Quota.monthly_pageview_limit(subscriber)
|
||||
defp check_pageview_usage_last_cycle(subscriber, usage_mod) do
|
||||
usage = usage_mod.monthly_pageview_usage(subscriber)
|
||||
limit = Quota.Limits.monthly_pageview_limit(subscriber)
|
||||
|
||||
if last_usage_cycle_below_limit?(usage, limit) do
|
||||
{:below_limit, usage}
|
||||
else
|
||||
if :last_cycle in Quota.exceeded_cycles(usage, limit) do
|
||||
{:over_limit, usage}
|
||||
else
|
||||
{:below_limit, usage}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -55,14 +55,14 @@ defmodule Plausible.Workers.SendTrialNotifications do
|
||||
end
|
||||
|
||||
defp send_tomorrow_reminder(user) do
|
||||
usage = Plausible.Billing.Quota.usage_cycle(user, :last_30_days)
|
||||
usage = Plausible.Billing.Quota.Usage.usage_cycle(user, :last_30_days)
|
||||
|
||||
PlausibleWeb.Email.trial_upgrade_email(user, "tomorrow", usage)
|
||||
|> Plausible.Mailer.send()
|
||||
end
|
||||
|
||||
defp send_today_reminder(user) do
|
||||
usage = Plausible.Billing.Quota.usage_cycle(user, :last_30_days)
|
||||
usage = Plausible.Billing.Quota.Usage.usage_cycle(user, :last_30_days)
|
||||
|
||||
PlausibleWeb.Email.trial_upgrade_email(user, "today", usage)
|
||||
|> Plausible.Mailer.send()
|
||||
|
@ -24,14 +24,14 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
user_on_v2 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v2_plan_id))
|
||||
user_on_v3 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v3_plan_id))
|
||||
|
||||
assert 50 == Quota.site_limit(user_on_v1)
|
||||
assert 50 == Quota.site_limit(user_on_v2)
|
||||
assert 50 == Quota.site_limit(user_on_v3)
|
||||
assert 50 == Quota.Limits.site_limit(user_on_v1)
|
||||
assert 50 == Quota.Limits.site_limit(user_on_v2)
|
||||
assert 50 == Quota.Limits.site_limit(user_on_v3)
|
||||
end
|
||||
|
||||
test "returns 50 when user is on free_10k plan" do
|
||||
user = insert(:user, subscription: build(:subscription, paddle_plan_id: "free_10k"))
|
||||
assert 50 == Quota.site_limit(user)
|
||||
assert 50 == Quota.Limits.site_limit(user)
|
||||
end
|
||||
|
||||
test "returns the configured site limit for enterprise plan" do
|
||||
@ -40,7 +40,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
enterprise_plan = insert(:enterprise_plan, user_id: user.id, site_limit: 500)
|
||||
insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id)
|
||||
|
||||
assert enterprise_plan.site_limit == Quota.site_limit(user)
|
||||
assert enterprise_plan.site_limit == Quota.Limits.site_limit(user)
|
||||
end
|
||||
|
||||
test "returns 10 when user in on trial" do
|
||||
@ -49,7 +49,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
trial_expiry_date: Timex.shift(Timex.now(), days: 7)
|
||||
)
|
||||
|
||||
assert 10 == Quota.site_limit(user)
|
||||
assert 10 == Quota.Limits.site_limit(user)
|
||||
end
|
||||
|
||||
test "returns the subscription limit for enterprise users who have not paid yet" do
|
||||
@ -59,7 +59,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
subscription: build(:subscription, paddle_plan_id: @v1_plan_id)
|
||||
)
|
||||
|
||||
assert 50 == Quota.site_limit(user)
|
||||
assert 50 == Quota.Limits.site_limit(user)
|
||||
end
|
||||
|
||||
test "returns 10 for enterprise users who have not upgraded yet and are on trial" do
|
||||
@ -69,7 +69,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
subscription: nil
|
||||
)
|
||||
|
||||
assert 10 == Quota.site_limit(user)
|
||||
assert 10 == Quota.Limits.site_limit(user)
|
||||
end
|
||||
end
|
||||
|
||||
@ -79,7 +79,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
insert(:site, memberships: [build(:site_membership, user: user, role: :admin)])
|
||||
insert(:site, memberships: [build(:site_membership, user: user, role: :viewer)])
|
||||
|
||||
assert Quota.site_usage(user) == 3
|
||||
assert Quota.Usage.site_usage(user) == 3
|
||||
end
|
||||
|
||||
describe "below_limit?/2" do
|
||||
@ -209,19 +209,19 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
test "is based on the plan if user is on a legacy plan" do
|
||||
user = insert(:user, subscription: build(:subscription, paddle_plan_id: @legacy_plan_id))
|
||||
|
||||
assert Quota.monthly_pageview_limit(user.subscription) == 1_000_000
|
||||
assert Quota.Limits.monthly_pageview_limit(user.subscription) == 1_000_000
|
||||
end
|
||||
|
||||
test "is based on the plan if user is on a standard plan" do
|
||||
user = insert(:user, subscription: build(:subscription, paddle_plan_id: @v1_plan_id))
|
||||
|
||||
assert Quota.monthly_pageview_limit(user.subscription) == 10_000
|
||||
assert Quota.Limits.monthly_pageview_limit(user.subscription) == 10_000
|
||||
end
|
||||
|
||||
test "free_10k has 10k monthly_pageview_limit" do
|
||||
user = insert(:user, subscription: build(:subscription, paddle_plan_id: "free_10k"))
|
||||
|
||||
assert Quota.monthly_pageview_limit(user.subscription) == 10_000
|
||||
assert Quota.Limits.monthly_pageview_limit(user.subscription) == 10_000
|
||||
end
|
||||
|
||||
test "is based on the enterprise plan if user is on an enterprise plan" do
|
||||
@ -233,14 +233,14 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
subscription =
|
||||
insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id)
|
||||
|
||||
assert Quota.monthly_pageview_limit(subscription) == 100_000
|
||||
assert Quota.Limits.monthly_pageview_limit(subscription) == 100_000
|
||||
end
|
||||
|
||||
test "does not limit pageviews when user has a pending enterprise plan" do
|
||||
user = insert(:user)
|
||||
subscription = insert(:subscription, user_id: user.id, paddle_plan_id: "pending-enterprise")
|
||||
|
||||
assert Quota.monthly_pageview_limit(subscription) == :unlimited
|
||||
assert Quota.Limits.monthly_pageview_limit(subscription) == :unlimited
|
||||
end
|
||||
end
|
||||
|
||||
@ -282,7 +282,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
]
|
||||
)
|
||||
|
||||
assert Quota.team_member_usage(me) == 3
|
||||
assert Quota.Usage.team_member_usage(me) == 3
|
||||
end
|
||||
|
||||
test "counts the same email address as one team member" do
|
||||
@ -310,7 +310,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
|
||||
insert(:invitation, site: site_i_own_3, inviter: me, email: "joe@plausible.test")
|
||||
|
||||
assert Quota.team_member_usage(me) == 2
|
||||
assert Quota.Usage.team_member_usage(me) == 2
|
||||
end
|
||||
|
||||
test "counts pending invitations as team members" do
|
||||
@ -332,7 +332,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
insert(:invitation, site: site_i_own, inviter: member)
|
||||
insert(:invitation, site: site_i_have_access, inviter: me)
|
||||
|
||||
assert Quota.team_member_usage(me) == 3
|
||||
assert Quota.Usage.team_member_usage(me) == 3
|
||||
end
|
||||
|
||||
test "does not count ownership transfer as a team member" do
|
||||
@ -341,12 +341,12 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
|
||||
insert(:invitation, site: site_i_own, inviter: me, role: :owner)
|
||||
|
||||
assert Quota.team_member_usage(me) == 0
|
||||
assert Quota.Usage.team_member_usage(me) == 0
|
||||
end
|
||||
|
||||
test "returns zero when user does not have any site" do
|
||||
me = insert(:user)
|
||||
assert Quota.team_member_usage(me) == 0
|
||||
assert Quota.Usage.team_member_usage(me) == 0
|
||||
end
|
||||
|
||||
test "does not count email report recipients as team members" do
|
||||
@ -358,7 +358,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
recipients: ["adam@plausible.test", "vini@plausible.test"]
|
||||
)
|
||||
|
||||
assert Quota.team_member_usage(me) == 0
|
||||
assert Quota.Usage.team_member_usage(me) == 0
|
||||
end
|
||||
|
||||
test "excludes specific emails from limit calculation" do
|
||||
@ -377,11 +377,13 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
insert(:invitation, site: site_i_own, inviter: member)
|
||||
invitation = insert(:invitation, site: site_i_own, inviter: me, email: "foo@example.com")
|
||||
|
||||
assert Quota.team_member_usage(me) == 4
|
||||
assert Quota.team_member_usage(me, exclude_emails: "arbitrary@example.com") == 4
|
||||
assert Quota.team_member_usage(me, exclude_emails: member.email) == 3
|
||||
assert Quota.team_member_usage(me, exclude_emails: invitation.email) == 3
|
||||
assert Quota.team_member_usage(me, exclude_emails: [member.email, invitation.email]) == 2
|
||||
assert Quota.Usage.team_member_usage(me) == 4
|
||||
assert Quota.Usage.team_member_usage(me, exclude_emails: "arbitrary@example.com") == 4
|
||||
assert Quota.Usage.team_member_usage(me, exclude_emails: member.email) == 3
|
||||
assert Quota.Usage.team_member_usage(me, exclude_emails: invitation.email) == 3
|
||||
|
||||
assert Quota.Usage.team_member_usage(me, exclude_emails: [member.email, invitation.email]) ==
|
||||
2
|
||||
end
|
||||
end
|
||||
|
||||
@ -392,14 +394,14 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
user_on_v2 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v2_plan_id))
|
||||
user_on_v3 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v3_plan_id))
|
||||
|
||||
assert :unlimited == Quota.team_member_limit(user_on_v1)
|
||||
assert :unlimited == Quota.team_member_limit(user_on_v2)
|
||||
assert :unlimited == Quota.team_member_limit(user_on_v3)
|
||||
assert :unlimited == Quota.Limits.team_member_limit(user_on_v1)
|
||||
assert :unlimited == Quota.Limits.team_member_limit(user_on_v2)
|
||||
assert :unlimited == Quota.Limits.team_member_limit(user_on_v3)
|
||||
end
|
||||
|
||||
test "returns unlimited when user is on free_10k plan" do
|
||||
user = insert(:user, subscription: build(:subscription, paddle_plan_id: "free_10k"))
|
||||
assert :unlimited == Quota.team_member_limit(user)
|
||||
assert :unlimited == Quota.Limits.team_member_limit(user)
|
||||
end
|
||||
|
||||
test "returns 5 when user in on trial" do
|
||||
@ -408,7 +410,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
trial_expiry_date: Timex.shift(Timex.now(), days: 7)
|
||||
)
|
||||
|
||||
assert 3 == Quota.team_member_limit(user)
|
||||
assert 3 == Quota.Limits.team_member_limit(user)
|
||||
end
|
||||
|
||||
test "returns the enterprise plan limit" do
|
||||
@ -419,7 +421,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
subscription: build(:subscription, paddle_plan_id: "123321")
|
||||
)
|
||||
|
||||
assert 27 == Quota.team_member_limit(user)
|
||||
assert 27 == Quota.Limits.team_member_limit(user)
|
||||
end
|
||||
|
||||
test "reads from json file when the user is on a v4 plan" do
|
||||
@ -427,22 +429,22 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
|
||||
user_on_business = insert(:user, subscription: build(:business_subscription))
|
||||
|
||||
assert 3 == Quota.team_member_limit(user_on_growth)
|
||||
assert 10 == Quota.team_member_limit(user_on_business)
|
||||
assert 3 == Quota.Limits.team_member_limit(user_on_growth)
|
||||
assert 10 == Quota.Limits.team_member_limit(user_on_business)
|
||||
end
|
||||
|
||||
test "returns unlimited when user is on a v3 business plan" do
|
||||
user =
|
||||
insert(:user, subscription: build(:subscription, paddle_plan_id: @v3_business_plan_id))
|
||||
|
||||
assert :unlimited == Quota.team_member_limit(user)
|
||||
assert :unlimited == Quota.Limits.team_member_limit(user)
|
||||
end
|
||||
end
|
||||
|
||||
describe "features_usage/1" do
|
||||
test "returns an empty list for a user/site who does not use any feature" do
|
||||
assert [] == Quota.features_usage(insert(:user))
|
||||
assert [] == Quota.features_usage(insert(:site))
|
||||
assert [] == Quota.Usage.features_usage(insert(:user))
|
||||
assert [] == Quota.Usage.features_usage(insert(:site))
|
||||
end
|
||||
|
||||
test "returns [Props] when user/site uses custom props" do
|
||||
@ -454,8 +456,8 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
memberships: [build(:site_membership, user: user, role: :owner)]
|
||||
)
|
||||
|
||||
assert [Props] == Quota.features_usage(site)
|
||||
assert [Props] == Quota.features_usage(user)
|
||||
assert [Props] == Quota.Usage.features_usage(site)
|
||||
assert [Props] == Quota.Usage.features_usage(user)
|
||||
end
|
||||
|
||||
on_ee do
|
||||
@ -467,8 +469,8 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
steps = Enum.map(goals, &%{"goal_id" => &1.id})
|
||||
Plausible.Funnels.create(site, "dummy", steps)
|
||||
|
||||
assert [Funnels] == Quota.features_usage(site)
|
||||
assert [Funnels] == Quota.features_usage(user)
|
||||
assert [Funnels] == Quota.Usage.features_usage(site)
|
||||
assert [Funnels] == Quota.Usage.features_usage(user)
|
||||
end
|
||||
|
||||
test "returns [RevenueGoals] when user/site uses revenue goals" do
|
||||
@ -476,8 +478,8 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
site = insert(:site, memberships: [build(:site_membership, user: user, role: :owner)])
|
||||
insert(:goal, currency: :USD, site: site, event_name: "Purchase")
|
||||
|
||||
assert [RevenueGoals] == Quota.features_usage(site)
|
||||
assert [RevenueGoals] == Quota.features_usage(user)
|
||||
assert [RevenueGoals] == Quota.Usage.features_usage(site)
|
||||
assert [RevenueGoals] == Quota.Usage.features_usage(user)
|
||||
end
|
||||
end
|
||||
|
||||
@ -485,7 +487,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
user = insert(:user)
|
||||
insert(:api_key, user: user)
|
||||
|
||||
assert [StatsAPI] == Quota.features_usage(user)
|
||||
assert [StatsAPI] == Quota.Usage.features_usage(user)
|
||||
end
|
||||
|
||||
on_ee do
|
||||
@ -504,8 +506,8 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
steps = Enum.map(goals, &%{"goal_id" => &1.id})
|
||||
Plausible.Funnels.create(site, "dummy", steps)
|
||||
|
||||
assert [Props, Funnels, RevenueGoals] == Quota.features_usage(site)
|
||||
assert [Props, Funnels, RevenueGoals] == Quota.features_usage(user)
|
||||
assert [Props, Funnels, RevenueGoals] == Quota.Usage.features_usage(site)
|
||||
assert [Props, Funnels, RevenueGoals] == Quota.Usage.features_usage(user)
|
||||
end
|
||||
end
|
||||
|
||||
@ -517,7 +519,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
memberships: [build(:site_membership, user: user, role: :admin)]
|
||||
)
|
||||
|
||||
assert [] == Quota.features_usage(user)
|
||||
assert [] == Quota.Usage.features_usage(user)
|
||||
end
|
||||
end
|
||||
|
||||
@ -525,7 +527,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
on_ee do
|
||||
test "users with expired trials have no access to subscription features" do
|
||||
user = insert(:user, trial_expiry_date: ~D[2023-01-01])
|
||||
assert [Goals] == Quota.allowed_features_for(user)
|
||||
assert [Goals] == Quota.Limits.allowed_features_for(user)
|
||||
end
|
||||
end
|
||||
|
||||
@ -534,14 +536,14 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
user_on_v2 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v2_plan_id))
|
||||
user_on_v3 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v3_plan_id))
|
||||
|
||||
assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user_on_v1)
|
||||
assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user_on_v2)
|
||||
assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user_on_v3)
|
||||
assert [Goals, Props, StatsAPI] == Quota.Limits.allowed_features_for(user_on_v1)
|
||||
assert [Goals, Props, StatsAPI] == Quota.Limits.allowed_features_for(user_on_v2)
|
||||
assert [Goals, Props, StatsAPI] == Quota.Limits.allowed_features_for(user_on_v3)
|
||||
end
|
||||
|
||||
test "returns [Goals, Props, StatsAPI] when user is on free_10k plan" do
|
||||
user = insert(:user, subscription: build(:subscription, paddle_plan_id: "free_10k"))
|
||||
assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user)
|
||||
assert [Goals, Props, StatsAPI] == Quota.Limits.allowed_features_for(user)
|
||||
end
|
||||
|
||||
on_ee do
|
||||
@ -560,14 +562,14 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id)
|
||||
|
||||
assert [Plausible.Billing.Feature.StatsAPI, Plausible.Billing.Feature.Funnels] ==
|
||||
Quota.allowed_features_for(user)
|
||||
Quota.Limits.allowed_features_for(user)
|
||||
end
|
||||
end
|
||||
|
||||
test "returns all features when user in on trial" do
|
||||
user = insert(:user, trial_expiry_date: Timex.shift(Timex.now(), days: 7))
|
||||
|
||||
assert Plausible.Billing.Feature.list() == Quota.allowed_features_for(user)
|
||||
assert Plausible.Billing.Feature.list() == Quota.Limits.allowed_features_for(user)
|
||||
end
|
||||
|
||||
test "returns previous plan limits for enterprise users who have not paid yet" do
|
||||
@ -577,7 +579,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
subscription: build(:subscription, paddle_plan_id: @v1_plan_id)
|
||||
)
|
||||
|
||||
assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user)
|
||||
assert [Goals, Props, StatsAPI] == Quota.Limits.allowed_features_for(user)
|
||||
end
|
||||
|
||||
test "returns all features for enterprise users who have not upgraded yet and are on trial" do
|
||||
@ -587,7 +589,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
subscription: nil
|
||||
)
|
||||
|
||||
assert Plausible.Billing.Feature.list() == Quota.allowed_features_for(user)
|
||||
assert Plausible.Billing.Feature.list() == Quota.Limits.allowed_features_for(user)
|
||||
end
|
||||
|
||||
test "returns old plan features for enterprise customers who are due to change a plan" do
|
||||
@ -602,7 +604,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
)
|
||||
|
||||
insert(:enterprise_plan, user_id: user.id, paddle_plan_id: "new-paddle-plan-id")
|
||||
assert [Plausible.Billing.Feature.StatsAPI] == Quota.allowed_features_for(user)
|
||||
assert [Plausible.Billing.Feature.StatsAPI] == Quota.Limits.allowed_features_for(user)
|
||||
end
|
||||
end
|
||||
|
||||
@ -619,7 +621,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
pageviews: 0,
|
||||
date_range: date_range
|
||||
}
|
||||
} = Quota.monthly_pageview_usage(user)
|
||||
} = Quota.Usage.monthly_pageview_usage(user)
|
||||
|
||||
assert date_range.last == Date.utc_today()
|
||||
assert Date.compare(date_range.first, date_range.last) == :lt
|
||||
@ -650,7 +652,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
pageviews: 3,
|
||||
date_range: %{}
|
||||
}
|
||||
} = Quota.monthly_pageview_usage(user)
|
||||
} = Quota.Usage.monthly_pageview_usage(user)
|
||||
end
|
||||
|
||||
test "returns usage for user with subscription and a site" do
|
||||
@ -693,7 +695,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
pageviews: 0,
|
||||
date_range: %{}
|
||||
}
|
||||
} = Quota.monthly_pageview_usage(user)
|
||||
} = Quota.Usage.monthly_pageview_usage(user)
|
||||
end
|
||||
|
||||
test "returns usage for only a subset of site IDs" do
|
||||
@ -740,7 +742,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
pageviews: 0,
|
||||
date_range: %{}
|
||||
}
|
||||
} = Quota.monthly_pageview_usage(user, [site1.id, site3.id])
|
||||
} = Quota.Usage.monthly_pageview_usage(user, [site1.id, site3.id])
|
||||
end
|
||||
end
|
||||
|
||||
@ -777,13 +779,13 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
insert(:subscription, user_id: user.id, last_bill_date: last_bill_date)
|
||||
|
||||
assert %{date_range: penultimate_cycle, pageviews: 2, custom_events: 3, total: 5} =
|
||||
Quota.usage_cycle(user, :penultimate_cycle, nil, today)
|
||||
Quota.Usage.usage_cycle(user, :penultimate_cycle, nil, today)
|
||||
|
||||
assert %{date_range: last_cycle, pageviews: 3, custom_events: 2, total: 5} =
|
||||
Quota.usage_cycle(user, :last_cycle, nil, today)
|
||||
Quota.Usage.usage_cycle(user, :last_cycle, nil, today)
|
||||
|
||||
assert %{date_range: current_cycle, pageviews: 0, custom_events: 3, total: 3} =
|
||||
Quota.usage_cycle(user, :current_cycle, nil, today)
|
||||
Quota.Usage.usage_cycle(user, :current_cycle, nil, today)
|
||||
|
||||
assert penultimate_cycle == Date.range(~D[2023-04-03], ~D[2023-05-02])
|
||||
assert last_cycle == Date.range(~D[2023-05-03], ~D[2023-06-02])
|
||||
@ -794,7 +796,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
today = ~D[2023-06-01]
|
||||
|
||||
assert %{date_range: last_30_days, pageviews: 4, custom_events: 1, total: 5} =
|
||||
Quota.usage_cycle(user, :last_30_days, nil, today)
|
||||
Quota.Usage.usage_cycle(user, :last_30_days, nil, today)
|
||||
|
||||
assert last_30_days == Date.range(~D[2023-05-02], ~D[2023-06-01])
|
||||
end
|
||||
@ -817,7 +819,7 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
insert(:subscription, user_id: user.id, last_bill_date: last_bill_date)
|
||||
|
||||
assert %{date_range: last_cycle, pageviews: 3, custom_events: 2, total: 5} =
|
||||
Quota.usage_cycle(user, :last_cycle, nil, today)
|
||||
Quota.Usage.usage_cycle(user, :last_cycle, nil, today)
|
||||
|
||||
assert last_cycle == Date.range(~D[2023-05-03], ~D[2023-06-02])
|
||||
end
|
||||
@ -829,13 +831,13 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
user = insert(:user, subscription: build(:subscription, last_bill_date: last_bill_date))
|
||||
|
||||
assert %{date_range: penultimate_cycle} =
|
||||
Quota.usage_cycle(user, :penultimate_cycle, nil, today)
|
||||
Quota.Usage.usage_cycle(user, :penultimate_cycle, nil, today)
|
||||
|
||||
assert %{date_range: last_cycle} =
|
||||
Quota.usage_cycle(user, :last_cycle, nil, today)
|
||||
Quota.Usage.usage_cycle(user, :last_cycle, nil, today)
|
||||
|
||||
assert %{date_range: current_cycle} =
|
||||
Quota.usage_cycle(user, :current_cycle, nil, today)
|
||||
Quota.Usage.usage_cycle(user, :current_cycle, nil, today)
|
||||
|
||||
assert penultimate_cycle == Date.range(~D[2020-12-01], ~D[2020-12-31])
|
||||
assert last_cycle == Date.range(~D[2021-01-01], ~D[2021-01-31])
|
||||
@ -849,13 +851,13 @@ defmodule Plausible.Billing.QuotaTest do
|
||||
user = insert(:user, subscription: build(:subscription, last_bill_date: last_bill_date))
|
||||
|
||||
assert %{date_range: penultimate_cycle, total: 0} =
|
||||
Quota.usage_cycle(user, :penultimate_cycle, nil, today)
|
||||
Quota.Usage.usage_cycle(user, :penultimate_cycle, nil, today)
|
||||
|
||||
assert %{date_range: last_cycle, total: 0} =
|
||||
Quota.usage_cycle(user, :last_cycle, nil, today)
|
||||
Quota.Usage.usage_cycle(user, :last_cycle, nil, today)
|
||||
|
||||
assert %{date_range: current_cycle, total: 0} =
|
||||
Quota.usage_cycle(user, :current_cycle, nil, today)
|
||||
Quota.Usage.usage_cycle(user, :current_cycle, nil, today)
|
||||
|
||||
assert penultimate_cycle == Date.range(~D[2020-11-01], ~D[2020-11-30])
|
||||
assert last_cycle == Date.range(~D[2020-12-01], ~D[2020-12-31])
|
||||
|
@ -364,7 +364,7 @@ defmodule PlausibleWeb.SiteControllerTest do
|
||||
})
|
||||
|
||||
assert redirected_to(conn) == "/example.com/snippet?site_created=true"
|
||||
assert Plausible.Billing.Quota.site_usage(user) == 3
|
||||
assert Plausible.Billing.Quota.Usage.site_usage(user) == 3
|
||||
end
|
||||
|
||||
for url <- ["https://Example.com/", "HTTPS://EXAMPLE.COM/", "/Example.com/", "//Example.com/"] do
|
||||
|
@ -8,6 +8,7 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
alias Plausible.{Repo, Billing.Subscription}
|
||||
|
||||
@v1_10k_yearly_plan_id "572810"
|
||||
@v4_growth_10k_yearly_plan_id "857079"
|
||||
@v4_growth_200k_yearly_plan_id "857081"
|
||||
@v4_business_5m_monthly_plan_id "857111"
|
||||
@v3_business_10k_monthly_plan_id "857481"
|
||||
@ -358,16 +359,18 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
"https://plausible.io/white-label-web-analytics"
|
||||
end
|
||||
|
||||
test "displays usage", %{conn: conn, site: site} do
|
||||
test "displays usage in the last cycle", %{conn: conn, site: site} do
|
||||
yesterday = NaiveDateTime.utc_now() |> NaiveDateTime.add(-1, :day)
|
||||
|
||||
populate_stats(site, [
|
||||
build(:pageview),
|
||||
build(:pageview)
|
||||
build(:pageview, timestamp: yesterday),
|
||||
build(:pageview, timestamp: yesterday)
|
||||
])
|
||||
|
||||
{:ok, _lv, doc} = get_liveview(conn)
|
||||
assert doc =~ "You have used"
|
||||
assert doc =~ "<b>2</b>"
|
||||
assert doc =~ "billable pageviews in the last 30 days"
|
||||
assert doc =~ "billable pageviews in the last billing cycle"
|
||||
end
|
||||
|
||||
test "gets default selected interval from current subscription plan", %{conn: conn} do
|
||||
@ -375,9 +378,9 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
assert class_of_element(doc, @yearly_interval_button) =~ @interval_button_active_class
|
||||
end
|
||||
|
||||
test "gets default pageview limit from current subscription plan", %{conn: conn} do
|
||||
test "sets pageview slider according to last cycle usage", %{conn: conn} do
|
||||
{:ok, _lv, doc} = get_liveview(conn)
|
||||
assert text_of_element(doc, @slider_value) == "200k"
|
||||
assert text_of_element(doc, @slider_value) == "10k"
|
||||
end
|
||||
|
||||
test "pageview slider changes selected volume", %{conn: conn} do
|
||||
@ -401,7 +404,9 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
end
|
||||
|
||||
test "checkout button text and click-disabling CSS classes are dynamic", %{conn: conn} do
|
||||
{:ok, lv, doc} = get_liveview(conn)
|
||||
{:ok, lv, _doc} = get_liveview(conn)
|
||||
|
||||
doc = set_slider(lv, "200k")
|
||||
|
||||
assert text_of_element(doc, @growth_checkout_button) == "Currently on this plan"
|
||||
assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none bg-gray-400"
|
||||
@ -431,7 +436,7 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
growth_checkout_button = find(doc, @growth_checkout_button)
|
||||
|
||||
assert text_of_attr(growth_checkout_button, "onclick") =~
|
||||
"if (true) {window.location = '#{Routes.billing_path(conn, :change_plan_preview, @v4_growth_200k_yearly_plan_id)}'}"
|
||||
"if (true) {window.location = '#{Routes.billing_path(conn, :change_plan_preview, @v4_growth_10k_yearly_plan_id)}'}"
|
||||
|
||||
set_slider(lv, "5M")
|
||||
doc = element(lv, @monthly_interval_button) |> render_click()
|
||||
@ -446,9 +451,9 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
describe "for a user with a v4 business subscription plan" do
|
||||
setup [:create_user, :create_site, :log_in, :subscribe_v4_business]
|
||||
|
||||
test "gets default pageview limit from current subscription plan", %{conn: conn} do
|
||||
test "sets pageview slider according to last cycle usage", %{conn: conn} do
|
||||
{:ok, _lv, doc} = get_liveview(conn)
|
||||
assert text_of_element(doc, @slider_value) == "5M"
|
||||
assert text_of_element(doc, @slider_value) == "10k"
|
||||
end
|
||||
|
||||
test "makes it clear that the user is currently on a business tier", %{conn: conn} do
|
||||
@ -462,11 +467,12 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
end
|
||||
|
||||
test "checkout button text and click-disabling CSS classes are dynamic", %{conn: conn} do
|
||||
{:ok, lv, doc} = get_liveview(conn)
|
||||
{:ok, lv, _doc} = get_liveview(conn)
|
||||
|
||||
doc = set_slider(lv, "5M")
|
||||
|
||||
assert text_of_element(doc, @business_checkout_button) == "Currently on this plan"
|
||||
assert class_of_element(doc, @business_checkout_button) =~ "pointer-events-none bg-gray-400"
|
||||
assert text_of_element(doc, @growth_checkout_button) == "Downgrade to Growth"
|
||||
|
||||
doc = element(lv, @yearly_interval_button) |> render_click()
|
||||
|
||||
@ -634,7 +640,10 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
|
||||
test "checkout buttons are disabled + notice about billing details (unless plan owned already)",
|
||||
%{conn: conn} do
|
||||
{:ok, lv, doc} = get_liveview(conn)
|
||||
{:ok, lv, _doc} = get_liveview(conn)
|
||||
|
||||
doc = set_slider(lv, "200k")
|
||||
|
||||
assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none bg-gray-400"
|
||||
assert text_of_element(doc, @growth_checkout_button) =~ "Currently on this plan"
|
||||
refute element_exists?(doc, "#{@growth_checkout_button} + p")
|
||||
@ -664,7 +673,10 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
||||
|
||||
test "checkout buttons are disabled + notice about billing details when plan not owned already",
|
||||
%{conn: conn} do
|
||||
{:ok, lv, doc} = get_liveview(conn)
|
||||
{:ok, lv, _doc} = get_liveview(conn)
|
||||
|
||||
doc = set_slider(lv, "200k")
|
||||
|
||||
assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none bg-gray-400"
|
||||
assert text_of_element(doc, @growth_checkout_button) =~ "Currently on this plan"
|
||||
refute element_exists?(doc, "#{@growth_checkout_button} + p")
|
||||
|
@ -31,8 +31,8 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
test "does not send an email if account has been over the limit for one billing month", %{
|
||||
user: user
|
||||
} do
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 9_000},
|
||||
@ -46,7 +46,7 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
last_bill_date: Timex.shift(Timex.today(), days: -1)
|
||||
)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub)
|
||||
CheckUsage.perform(nil, usage_stub)
|
||||
|
||||
assert_no_emails_delivered()
|
||||
assert Repo.reload(user).grace_period == nil
|
||||
@ -55,8 +55,8 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
test "does not send an email if account is over the limit by less than 10%", %{
|
||||
user: user
|
||||
} do
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 10_999},
|
||||
@ -70,7 +70,7 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
last_bill_date: Timex.shift(Timex.today(), days: -1)
|
||||
)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub)
|
||||
CheckUsage.perform(nil, usage_stub)
|
||||
|
||||
assert_no_emails_delivered()
|
||||
assert Repo.reload(user).grace_period == nil
|
||||
@ -79,8 +79,8 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
test "sends an email when an account is over their limit for two consecutive billing months", %{
|
||||
user: user
|
||||
} do
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 11_000},
|
||||
@ -94,7 +94,7 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
last_bill_date: Timex.shift(Timex.today(), days: -1)
|
||||
)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub)
|
||||
CheckUsage.perform(nil, usage_stub)
|
||||
|
||||
assert_email_delivered_with(
|
||||
to: [user],
|
||||
@ -107,8 +107,8 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
test "sends an email suggesting enterprise plan when usage is greater than 10M ", %{
|
||||
user: user
|
||||
} do
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 11_000_000},
|
||||
@ -122,7 +122,7 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
last_bill_date: Timex.shift(Timex.today(), days: -1)
|
||||
)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub)
|
||||
CheckUsage.perform(nil, usage_stub)
|
||||
|
||||
assert_delivered_email_matches(%{html_body: html_body})
|
||||
|
||||
@ -136,8 +136,8 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
|> Plausible.Auth.GracePeriod.start_changeset()
|
||||
|> Repo.update!()
|
||||
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 11_000},
|
||||
@ -151,7 +151,7 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
last_bill_date: Timex.shift(Timex.today(), days: -1)
|
||||
)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub)
|
||||
CheckUsage.perform(nil, usage_stub)
|
||||
|
||||
assert_no_emails_delivered()
|
||||
assert Repo.reload(user).grace_period.id == existing_grace_period.id
|
||||
@ -160,8 +160,8 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
test "recommends a plan to upgrade to", %{
|
||||
user: user
|
||||
} do
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 11_000},
|
||||
@ -175,7 +175,7 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
last_bill_date: Timex.shift(Timex.today(), days: -1)
|
||||
)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub)
|
||||
CheckUsage.perform(nil, usage_stub)
|
||||
|
||||
assert_delivered_email_matches(%{
|
||||
html_body: html_body
|
||||
@ -185,8 +185,8 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
end
|
||||
|
||||
test "clears grace period when plan is applicable again", %{user: user} do
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 11_000},
|
||||
@ -200,11 +200,11 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
last_bill_date: Timex.shift(Timex.today(), days: -1)
|
||||
)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub)
|
||||
CheckUsage.perform(nil, usage_stub)
|
||||
assert user |> Repo.reload() |> Plausible.Auth.GracePeriod.active?()
|
||||
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 11_000},
|
||||
@ -212,7 +212,7 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
}
|
||||
end)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub)
|
||||
CheckUsage.perform(nil, usage_stub)
|
||||
refute user |> Repo.reload() |> Plausible.Auth.GracePeriod.active?()
|
||||
end
|
||||
|
||||
@ -223,8 +223,8 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
|> Plausible.Auth.GracePeriod.start_manual_lock_changeset()
|
||||
|> Repo.update!()
|
||||
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 1_100_000},
|
||||
@ -240,7 +240,7 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
last_bill_date: Timex.shift(Timex.today(), days: -1)
|
||||
)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub)
|
||||
CheckUsage.perform(nil, usage_stub)
|
||||
|
||||
assert_no_emails_delivered()
|
||||
assert Repo.reload(user).grace_period.id == existing_grace_period.id
|
||||
@ -250,8 +250,8 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
%{
|
||||
user: user
|
||||
} do
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 1_100_000},
|
||||
@ -267,7 +267,7 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
last_bill_date: Timex.shift(Timex.today(), days: -1)
|
||||
)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub)
|
||||
CheckUsage.perform(nil, usage_stub)
|
||||
|
||||
assert_email_delivered_with(
|
||||
to: [{nil, "enterprise@plausible.io"}],
|
||||
@ -279,8 +279,8 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
%{
|
||||
user: user
|
||||
} do
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 1},
|
||||
@ -300,7 +300,7 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
last_bill_date: Timex.shift(Timex.today(), days: -1)
|
||||
)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub)
|
||||
CheckUsage.perform(nil, usage_stub)
|
||||
|
||||
assert_email_delivered_with(
|
||||
to: [{nil, "enterprise@plausible.io"}],
|
||||
@ -309,8 +309,8 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
end
|
||||
|
||||
test "starts grace period when plan is outgrown", %{user: user} do
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 1_100_000},
|
||||
@ -326,7 +326,7 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
last_bill_date: Timex.shift(Timex.today(), days: -1)
|
||||
)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub)
|
||||
CheckUsage.perform(nil, usage_stub)
|
||||
assert user |> Repo.reload() |> Plausible.Auth.GracePeriod.active?()
|
||||
end
|
||||
end
|
||||
@ -335,8 +335,8 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
test "checks usage one day after the last_bill_date", %{
|
||||
user: user
|
||||
} do
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 11_000},
|
||||
@ -350,7 +350,7 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
last_bill_date: Timex.shift(Timex.today(), days: -1)
|
||||
)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub)
|
||||
CheckUsage.perform(nil, usage_stub)
|
||||
|
||||
assert_email_delivered_with(
|
||||
to: [user],
|
||||
@ -361,8 +361,8 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
test "does not check exactly one month after last_bill_date", %{
|
||||
user: user
|
||||
} do
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 11_000},
|
||||
@ -376,7 +376,7 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
last_bill_date: ~D[2021-03-28]
|
||||
)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub, ~D[2021-03-28])
|
||||
CheckUsage.perform(nil, usage_stub, ~D[2021-03-28])
|
||||
|
||||
assert_no_emails_delivered()
|
||||
end
|
||||
@ -385,8 +385,8 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
%{
|
||||
user: user
|
||||
} do
|
||||
quota_stub =
|
||||
Plausible.Billing.Quota
|
||||
usage_stub =
|
||||
Plausible.Billing.Quota.Usage
|
||||
|> stub(:monthly_pageview_usage, fn _user ->
|
||||
%{
|
||||
penultimate_cycle: %{date_range: @date_range, total: 11_000},
|
||||
@ -400,7 +400,7 @@ defmodule Plausible.Workers.CheckUsageTest do
|
||||
last_bill_date: ~D[2021-06-29]
|
||||
)
|
||||
|
||||
CheckUsage.perform(nil, quota_stub, ~D[2021-08-30])
|
||||
CheckUsage.perform(nil, usage_stub, ~D[2021-08-30])
|
||||
|
||||
assert_email_delivered_with(
|
||||
to: [user],
|
||||
|
Loading…
Reference in New Issue
Block a user