Check site limit for enterprise customers

This commit is contained in:
Uku Taht 2021-10-22 11:26:07 +02:00
parent 792e534edd
commit e66e9bd1b7
10 changed files with 176 additions and 61 deletions

View File

@ -7,7 +7,8 @@ defmodule Plausible.Billing.EnterprisePlan do
:paddle_plan_id,
:billing_interval,
:monthly_pageview_limit,
:hourly_api_request_limit
:hourly_api_request_limit,
:site_limit
]
schema "enterprise_plans" do
@ -15,6 +16,7 @@ defmodule Plausible.Billing.EnterprisePlan do
field :billing_interval, Ecto.Enum, values: [:monthly, :yearly]
field :monthly_pageview_limit, :integer
field :hourly_api_request_limit, :integer
field :site_limit, :integer
belongs_to :user, Plausible.Auth.User

View File

@ -14,7 +14,8 @@ defmodule Plausible.Billing.EnterprisePlanAdmin do
paddle_plan_id: nil,
billing_interval: %{choices: [{"Yearly", "yearly"}, {"Monthly", "monthly"}]},
monthly_pageview_limit: nil,
hourly_api_request_limit: nil
hourly_api_request_limit: nil,
site_limit: nil
]
end
@ -29,7 +30,8 @@ defmodule Plausible.Billing.EnterprisePlanAdmin do
paddle_plan_id: nil,
billing_interval: nil,
monthly_pageview_limit: nil,
hourly_api_request_limit: nil
hourly_api_request_limit: nil,
site_limit: nil
]
end

View File

@ -124,6 +124,17 @@ defmodule Plausible.Sites do
)
end
def count_owned_by(user) do
Repo.one(
from s in Plausible.Site,
join: sm in Plausible.Site.Membership,
on: sm.site_id == s.id,
where: sm.role == :owner,
where: sm.user_id == ^user.id,
select: count(sm)
)
end
def owner_for(site) do
Repo.one(
from u in Plausible.Auth.User,

View File

@ -128,7 +128,7 @@ defmodule PlausibleWeb.Email do
})
end
def enterprise_over_limit_email(user, usage, last_cycle) do
def enterprise_over_limit_email(user, usage, last_cycle, site_usage, site_allowance) do
base_email()
|> to("enterprise@plausible.io")
|> tag("enterprise-over-limit")
@ -136,7 +136,9 @@ defmodule PlausibleWeb.Email do
|> render("enterprise_over_limit.html", %{
user: user,
usage: usage,
last_cycle: last_cycle
last_cycle: last_cycle,
site_usage: site_usage,
site_allowance: site_allowance
})
end

View File

@ -41,7 +41,7 @@ defmodule PlausibleWeb.Router do
plug PlausibleWeb.Firewall
end
if Application.get_env(:plausible, :environment) == "dev" do
if Mix.env() == :dev do
forward "/sent-emails", Bamboo.SentEmailViewerPlug
end

View File

@ -1,8 +1,9 @@
Automated notice about an account that has gone over their enteprise plan limit.
Automated notice about an enterprise account that has gone over their limits. <br /><br />
Customer email: <% @user.email %>
Last billing cycle: <%= date_format(@last_cycle.first) %> to <%= date_format(@last_cycle.last) %>
Usage: <%= PlausibleWeb.StatsView.large_number_format(@usage) %> billable pageviews
Customer email: <%= @user.email %><br />
Last billing cycle: <%= date_format(@last_cycle.first) %> to <%= date_format(@last_cycle.last) %><br >
Pageview Usage: <%= PlausibleWeb.StatsView.large_number_format(@usage) %> billable pageviews<br />
Site usage: <%= @site_usage %> / <%= @site_allowance %> allowed sites<br />
--<br />
<%= plausible_url() %><br />

View File

@ -50,38 +50,80 @@ defmodule Plausible.Workers.CheckUsage do
)
for subscriber <- active_subscribers do
allowance = Plausible.Billing.Plans.allowance(subscriber.subscription)
{last_last_month, last_month} = billing_mod.last_two_billing_months_usage(subscriber)
is_over_limit = last_last_month > allowance && last_month > allowance
if subscriber.enterprise_plan do
check_enterprise_subscriber(subscriber, billing_mod)
else
check_regular_subscriber(subscriber, billing_mod)
end
end
cond do
is_over_limit && subscriber.enterprise_plan ->
{_, last_cycle} = billing_mod.last_two_billing_cycles(subscriber)
:ok
end
def check_enterprise_subscriber(subscriber, billing_mod) do
pageview_limit = check_pageview_limit(subscriber, billing_mod)
site_limit = check_site_limit(subscriber)
case {pageview_limit, site_limit} do
{{:within_limit, _}, {:within_limit, _}} ->
nil
{{_, {last_cycle, last_cycle_usage}}, {_, {site_usage, site_allowance}}} ->
template =
PlausibleWeb.Email.enterprise_over_limit_email(subscriber, last_month, last_cycle)
PlausibleWeb.Email.enterprise_over_limit_email(
subscriber,
last_cycle_usage,
last_cycle,
site_usage,
site_allowance
)
Plausible.Mailer.send_email_safe(template)
end
end
is_over_limit ->
{_, last_cycle} = billing_mod.last_two_billing_cycles(subscriber)
suggested_plan = Plausible.Billing.Plans.suggested_plan(subscriber, last_month)
defp check_regular_subscriber(subscriber, billing_mod) do
case check_pageview_limit(subscriber, billing_mod) do
{:over_limit, {last_cycle, last_cycle_usage}} ->
suggested_plan = Plausible.Billing.Plans.suggested_plan(subscriber, last_cycle)
template =
PlausibleWeb.Email.over_limit_email(
subscriber,
last_month,
last_cycle_usage,
last_cycle,
suggested_plan
)
Plausible.Mailer.send_email_safe(template)
true ->
_ ->
nil
end
end
:ok
defp check_pageview_limit(subscriber, billing_mod) do
allowance = Plausible.Billing.Plans.allowance(subscriber.subscription)
{_, last_cycle} = billing_mod.last_two_billing_cycles(subscriber)
{last_last_cycle_usage, last_cycle_usage} =
billing_mod.last_two_billing_months_usage(subscriber)
if last_last_cycle_usage > allowance && last_cycle_usage > allowance do
{:over_limit, {last_cycle, last_cycle_usage, allowance}}
else
{:within_limit, {last_cycle, last_cycle_usage}}
end
end
defp check_site_limit(subscriber) do
allowance = subscriber.enterprise_plan.site_limit
total_sites = Plausible.Sites.count_owned_by(subscriber)
if total_sites >= allowance do
{:over_limit, {total_sites, allowance}}
else
{:within_limit, {total_sites, allowance}}
end
end
end

View File

@ -0,0 +1,18 @@
defmodule Plausible.Repo.Migrations.AddSiteLimitToEnterprisePlans do
use Ecto.Migration
use Plausible.Repo
def change do
alter table(:enterprise_plans) do
add :site_limit, :integer
end
flush()
Repo.update_all("enterprise_plans", set: [site_limit: 50])
alter table(:enterprise_plans) do
modify :site_limit, :integer, null: false
end
end
end

View File

@ -121,7 +121,8 @@ defmodule Plausible.Factory do
paddle_plan_id: sequence(:paddle_plan_id, &"plan-#{&1}"),
billing_interval: :monthly,
monthly_pageview_limit: 1_000_000,
hourly_api_request_limit: 3000
hourly_api_request_limit: 3000,
site_limit: 100
}
end

View File

@ -31,6 +31,9 @@ defmodule Plausible.Workers.CheckUsageTest do
} do
billing_stub =
Plausible.Billing
|> stub(:last_two_billing_cycles, fn _user ->
{Date.range(Timex.today(), Timex.today()), Date.range(Timex.today(), Timex.today())}
end)
|> stub(:last_two_billing_months_usage, fn _user -> {9_000, 11_000} end)
insert(:subscription,
@ -68,7 +71,8 @@ defmodule Plausible.Workers.CheckUsageTest do
)
end
test "checks usage for enterprise customer, sends usage information to enterprise@plausible.io",
describe "enterprise customers" do
test "checks billable pageview usage for enterprise customer, sends usage information to enterprise@plausible.io",
%{
user: user
} do
@ -95,6 +99,38 @@ defmodule Plausible.Workers.CheckUsageTest do
)
end
test "checks site limit for enterprise customer, sends usage information to enterprise@plausible.io",
%{
user: user
} do
billing_stub =
Plausible.Billing
|> stub(:last_two_billing_months_usage, fn _user -> {1, 1} end)
|> stub(:last_two_billing_cycles, fn _user ->
{Date.range(Timex.today(), Timex.today()), Date.range(Timex.today(), Timex.today())}
end)
enterprise_plan = insert(:enterprise_plan, user: user, site_limit: 2)
insert(:site, members: [user])
insert(:site, members: [user])
insert(:site, members: [user])
insert(:subscription,
user: user,
paddle_plan_id: enterprise_plan.paddle_plan_id,
last_bill_date: Timex.shift(Timex.today(), days: -1)
)
CheckUsage.perform(nil, billing_stub)
assert_email_delivered_with(
to: [{nil, "enterprise@plausible.io"}],
subject: "#{user.email} has outgrown their enterprise plan"
)
end
end
describe "timing" do
test "checks usage one day after the last_bill_date", %{
user: user