Add record to track overridden limits
Before, we were relying on just changing the cards_count in account, but this could create problems where the system to calculate the next card number fails due to the unique constraint.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
class Admin::AccountsController < AdminController
|
||||
include Admin::AccountScoped
|
||||
|
||||
layout "public"
|
||||
|
||||
before_action :set_account, only: %i[ edit update ]
|
||||
@@ -10,8 +12,8 @@ class Admin::AccountsController < AdminController
|
||||
end
|
||||
|
||||
def update
|
||||
@account.update!(account_params)
|
||||
redirect_to saas.edit_admin_account_path(@account.external_account_id), notice: "Account updated"
|
||||
@account.override_limits(card_count: overridden_card_count_param)
|
||||
redirect_to saas.edit_admin_account_path(@account.external_account_id), notice: "Account limits updated"
|
||||
end
|
||||
|
||||
private
|
||||
@@ -19,7 +21,7 @@ class Admin::AccountsController < AdminController
|
||||
@account = Account.find_by!(external_account_id: params[:id])
|
||||
end
|
||||
|
||||
def account_params
|
||||
params.expect(account: [ :cards_count ])
|
||||
def overridden_card_count_param
|
||||
params[:account][:overridden_card_count].to_i
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
class Admin::OverriddenLimitsController < AdminController
|
||||
include Admin::AccountScoped
|
||||
|
||||
def destroy
|
||||
@account.reset_overridden_limits
|
||||
redirect_to saas.edit_admin_account_path(@account.external_account_id), notice: "Limits reset"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,12 @@
|
||||
module Admin::AccountScoped
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
before_action :set_account
|
||||
end
|
||||
|
||||
private
|
||||
def set_account
|
||||
@account = Account.find_by!(external_account_id: params[:account_id] || params[:id])
|
||||
end
|
||||
end
|
||||
@@ -5,8 +5,6 @@ module Account::Billing
|
||||
has_one :subscription, class_name: "Account::Subscription", dependent: :destroy
|
||||
end
|
||||
|
||||
NEAR_CARD_LIMIT_THRESHOLD = 100
|
||||
|
||||
def plan
|
||||
active_subscription&.plan || Plan.free
|
||||
end
|
||||
@@ -18,12 +16,4 @@ module Account::Billing
|
||||
def subscribed?
|
||||
subscription.present?
|
||||
end
|
||||
|
||||
def nearing_plan_cards_limit?
|
||||
plan.limit_cards? && (plan.card_limit - cards_count) < NEAR_CARD_LIMIT_THRESHOLD
|
||||
end
|
||||
|
||||
def exceeding_card_limit?
|
||||
cards_count > plan.card_limit
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
module Account::Limited
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_one :overridden_limits, class_name: "Account::OverriddenLimits", dependent: :destroy
|
||||
end
|
||||
|
||||
NEAR_CARD_LIMIT_THRESHOLD = 100
|
||||
|
||||
def override_limits(card_count:)
|
||||
if overridden_limits
|
||||
overridden_limits.update(card_count: card_count)
|
||||
else
|
||||
create_overridden_limits(card_count: card_count)
|
||||
end
|
||||
end
|
||||
|
||||
def billed_cards_count
|
||||
overridden_limits&.card_count || cards_count
|
||||
end
|
||||
|
||||
def nearing_plan_cards_limit?
|
||||
plan.limit_cards? && (plan.card_limit - billed_cards_count) < NEAR_CARD_LIMIT_THRESHOLD
|
||||
end
|
||||
|
||||
def exceeding_card_limit?
|
||||
plan.limit_cards? && billed_cards_count > plan.card_limit
|
||||
end
|
||||
|
||||
def reset_overridden_limits
|
||||
overridden_limits&.destroy
|
||||
reload_overridden_limits
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,4 @@
|
||||
# To ease testing of limits
|
||||
class Account::OverriddenLimits < SaasRecord
|
||||
belongs_to :account
|
||||
end
|
||||
@@ -6,7 +6,7 @@
|
||||
<% if Current.account.exceeding_card_limit? %>
|
||||
<h3 class="margin-block-start margin-block-end-half txt-large font-weight-black">You’ve used up your <u><%= Plan.free.card_limit %></u> free cards</h3>
|
||||
<% else %>
|
||||
<h3 class="margin-block-start margin-block-end-half txt-large font-weight-black">You’ve used <u><%= Current.account.cards_count %></u> free cards out of <%= Plan.free.card_limit %></h3>
|
||||
<h3 class="margin-block-start margin-block-end-half txt-large font-weight-black">You’ve used <u><%= Current.account.billed_cards_count %></u> free cards out of <%= Plan.free.card_limit %></h3>
|
||||
<% end %>
|
||||
|
||||
<p class="margin-none-block-start">If you’d like to keep using Fizzy past <%= Plan.free.card_limit %> cards, it’s only <strong>$<%= Plan.paid.price %>/month</strong> for <strong>unlimited cards</strong> + <strong>unlimited users</strong>. You’ll also get <strong><%= number_to_human_size(Plan.paid.formatted_storage_limit) %></strong> of storage. </p>
|
||||
|
||||
@@ -1,14 +1,33 @@
|
||||
<h1>Edit Account <%= @account.external_account_id %></h1>
|
||||
<div class="panel shadow center" style="--panel-size: 50ch;">
|
||||
<div class="flex flex-column gap txt-medium">
|
||||
<h1 class="txt-x-large font-weight-black margin-block-none">Edit Account <%= @account.external_account_id %></h1>
|
||||
|
||||
<p><strong>Name:</strong> <%= @account.name %></p>
|
||||
<dl class="flex flex-column gap-half margin-block-none">
|
||||
<div class="flex gap-half">
|
||||
<dt class="font-weight-bold">Name:</dt>
|
||||
<dd class="margin-inline-start-none"><%= @account.name %></dd>
|
||||
</div>
|
||||
<div class="flex gap-half">
|
||||
<dt class="font-weight-bold">Actual card count:</dt>
|
||||
<dd class="margin-inline-start-none"><%= @account.cards_count %></dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<%= form_with model: @account, url: saas.admin_account_path(@account.external_account_id) do |form| %>
|
||||
<p>
|
||||
<%= form.label :cards_count %>
|
||||
<%= form.number_field :cards_count %>
|
||||
</p>
|
||||
<%= form_with model: @account, url: saas.admin_account_path(@account.external_account_id), class: "flex flex-column gap" do |form| %>
|
||||
<div class="flex flex-column gap-half">
|
||||
<%= form.label :overridden_card_count, "Override card count", class: "font-weight-bold" %>
|
||||
<div class="flex align-center gap input input--actor">
|
||||
<%= form.number_field :overridden_card_count, value: @account.overridden_limits&.card_count, class: "input full-width" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= form.submit "Update" %>
|
||||
<% end %>
|
||||
<button type="submit" class="btn btn--reversed">Save</button>
|
||||
<% end %>
|
||||
|
||||
<p><%= link_to "Back", saas.admin_accounts_path %></p>
|
||||
<% if @account.overridden_limits %>
|
||||
<%= button_to "Reset limits", saas.admin_account_overridden_limits_path(@account.external_account_id), method: :delete, class: "btn btn--negative" %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to "Back to accounts", saas.admin_accounts_path, class: "btn btn--plain txt-link" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<% if Current.account.nearing_plan_cards_limit? %>
|
||||
<div class="settings_subscription__warning full-width pad-inline center margin-block-half">
|
||||
<% if Current.user.admin? %>
|
||||
You’ve used <%= Current.account.cards_count %> out of <%= Plan.free.card_limit %> free cards. <strong><%= link_to "Upgrade to unlimited", account_settings_path(anchor: "subscription") %></strong>.
|
||||
You’ve used <%= Current.account.billed_cards_count %> out of <%= Plan.free.card_limit %> free cards. <strong><%= link_to "Upgrade to unlimited", account_settings_path(anchor: "subscription") %></strong>.
|
||||
<% else %>
|
||||
This account has used <%= Current.account.cards_count %> out of <%= Plan.free.card_limit %> free cards. <strong>Upgrade soon</strong>
|
||||
This account has used <%= Current.account.billed_cards_count %> out of <%= Plan.free.card_limit %> free cards. <strong>Upgrade soon</strong>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -5,6 +5,8 @@ Fizzy::Saas::Engine.routes.draw do
|
||||
mount Audits1984::Engine, at: "/console"
|
||||
get "stats", to: "stats#show"
|
||||
resource :account_search, only: :create
|
||||
resources :accounts
|
||||
resources :accounts do
|
||||
resource :overridden_limits, only: :destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
class CreateAccountOverriddenLimits < ActiveRecord::Migration[8.2]
|
||||
def change
|
||||
create_table :account_overridden_limits, id: :uuid do |t|
|
||||
t.references :account, null: false, type: :uuid, index: { unique: true }
|
||||
t.integer :card_count
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -10,7 +10,15 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.2].define(version: 2025_12_03_144630) do
|
||||
ActiveRecord::Schema[8.2].define(version: 2025_12_15_140000) do
|
||||
create_table "account_overridden_limits", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.uuid "account_id", null: false
|
||||
t.integer "card_count"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["account_id"], name: "index_account_overridden_limits_on_account_id", unique: true
|
||||
end
|
||||
|
||||
create_table "account_subscriptions", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.uuid "account_id", null: false
|
||||
t.datetime "cancel_at"
|
||||
|
||||
@@ -127,8 +127,8 @@ module Fizzy
|
||||
end
|
||||
|
||||
config.to_prepare do
|
||||
::Account.include(Account::Billing)
|
||||
::Signup.prepend(Fizzy::Saas::Signup)
|
||||
::Account.include Account::Billing, Account::Limited
|
||||
::Signup.prepend Fizzy::Saas::Signup
|
||||
CardsController.include(Card::LimitedCreation)
|
||||
Cards::PublishesController.include(Card::LimitedPublishing)
|
||||
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
require "rake/testtask"
|
||||
|
||||
namespace :test do
|
||||
# task :prepare_saas => :environment do
|
||||
# require "rails/test_help"
|
||||
#
|
||||
# $LOAD_PATH.unshift Fizzy::Saas::Engine.root.join("test").to_s
|
||||
# require Fizzy::Saas::Engine.root.join("test/test_helper")
|
||||
# end
|
||||
|
||||
desc "Run tests for fizzy-saas gem"
|
||||
Rake::TestTask.new(saas: :environment) do |t|
|
||||
t.libs << "test"
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
require "test_helper"
|
||||
|
||||
class Admin::AccountsControllerTest < ActionDispatch::IntegrationTest
|
||||
def saas
|
||||
Fizzy::Saas::Engine.routes.url_helpers
|
||||
end
|
||||
|
||||
test "staff can access index" do
|
||||
sign_in_as :david
|
||||
|
||||
@@ -34,15 +30,15 @@ class Admin::AccountsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "staff can update cards_count" do
|
||||
test "staff can override card count" do
|
||||
sign_in_as :david
|
||||
|
||||
untenanted do
|
||||
patch saas.admin_account_path(accounts(:"37s").external_account_id), params: { account: { cards_count: 500 } }
|
||||
patch saas.admin_account_path(accounts(:"37s").external_account_id), params: { account: { overridden_card_count: 500 } }
|
||||
assert_redirected_to saas.edit_admin_account_path(accounts(:"37s").external_account_id)
|
||||
end
|
||||
|
||||
assert_equal 500, accounts(:"37s").reload.cards_count
|
||||
assert_equal 500, accounts(:"37s").reload.billed_cards_count
|
||||
end
|
||||
|
||||
test "non-staff cannot access accounts" do
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
require "test_helper"
|
||||
|
||||
class Admin::OverriddenLimitsControllerTest < ActionDispatch::IntegrationTest
|
||||
test "staff can reset overridden limits" do
|
||||
sign_in_as :david
|
||||
account = accounts(:"37s")
|
||||
|
||||
# First set an override
|
||||
account.override_limits(card_count: 500)
|
||||
assert_equal 500, account.reload.billed_cards_count
|
||||
|
||||
# Then reset it
|
||||
untenanted do
|
||||
delete saas.admin_account_overridden_limits_path(account.external_account_id)
|
||||
assert_redirected_to saas.edit_admin_account_path(account.external_account_id)
|
||||
end
|
||||
|
||||
# Verify override was removed
|
||||
assert_nil account.reload.overridden_limits
|
||||
assert_equal account.cards_count, account.billed_cards_count
|
||||
end
|
||||
end
|
||||
@@ -1,10 +1,6 @@
|
||||
require "test_helper"
|
||||
|
||||
class Admin::StatsControllerTest < ActionDispatch::IntegrationTest
|
||||
def saas
|
||||
Fizzy::Saas::Engine.routes.url_helpers
|
||||
end
|
||||
|
||||
test "staff can access stats" do
|
||||
sign_in_as :david
|
||||
|
||||
|
||||
@@ -15,35 +15,4 @@ class Account::BillingTest < ActiveSupport::TestCase
|
||||
account.subscription.update!(status: "active")
|
||||
assert_equal account.subscription, account.active_subscription
|
||||
end
|
||||
|
||||
test "detect nearing card limit" do
|
||||
# Paid plans are never limited
|
||||
accounts(:"37s").update_column(:cards_count, 1_000_000)
|
||||
assert_not accounts(:"37s").nearing_plan_cards_limit?
|
||||
|
||||
# Free plan not near limit
|
||||
accounts(:initech).update_column(:cards_count, 899)
|
||||
assert_not accounts(:initech).nearing_plan_cards_limit?
|
||||
|
||||
# Free plan near limit
|
||||
accounts(:initech).update_column(:cards_count, 900)
|
||||
assert_not accounts(:initech).nearing_plan_cards_limit?
|
||||
|
||||
accounts(:initech).update_column(:cards_count, 901)
|
||||
assert accounts(:initech).nearing_plan_cards_limit?
|
||||
end
|
||||
|
||||
test "detect exceeding card limit" do
|
||||
# Paid plans are never limited
|
||||
accounts(:"37s").update_column(:cards_count, 1_000_000)
|
||||
assert_not accounts(:"37s").exceeding_card_limit?
|
||||
|
||||
# Free plan under limit
|
||||
accounts(:initech).update_column(:cards_count, 999)
|
||||
assert_not accounts(:initech).exceeding_card_limit?
|
||||
|
||||
# Free plan over limit
|
||||
accounts(:initech).update_column(:cards_count, 1001)
|
||||
assert accounts(:initech).exceeding_card_limit?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
require "test_helper"
|
||||
|
||||
class Account::LimitedTest < ActiveSupport::TestCase
|
||||
test "detect nearing card limit" do
|
||||
# Paid plans are never limited
|
||||
accounts(:"37s").update_column(:cards_count, 1_000_000)
|
||||
assert_not accounts(:"37s").nearing_plan_cards_limit?
|
||||
|
||||
# Free plan not near limit
|
||||
accounts(:initech).update_column(:cards_count, 899)
|
||||
assert_not accounts(:initech).nearing_plan_cards_limit?
|
||||
|
||||
# Free plan near limit
|
||||
accounts(:initech).update_column(:cards_count, 900)
|
||||
assert_not accounts(:initech).nearing_plan_cards_limit?
|
||||
|
||||
accounts(:initech).update_column(:cards_count, 901)
|
||||
assert accounts(:initech).nearing_plan_cards_limit?
|
||||
end
|
||||
|
||||
test "detect exceeding card limit" do
|
||||
# Paid plans are never limited
|
||||
accounts(:"37s").update_column(:cards_count, 1_000_000)
|
||||
assert_not accounts(:"37s").exceeding_card_limit?
|
||||
|
||||
# Free plan under limit
|
||||
accounts(:initech).update_column(:cards_count, 999)
|
||||
assert_not accounts(:initech).exceeding_card_limit?
|
||||
|
||||
# Free plan over limit
|
||||
accounts(:initech).update_column(:cards_count, 1001)
|
||||
assert accounts(:initech).exceeding_card_limit?
|
||||
end
|
||||
|
||||
test "override limits" do
|
||||
account = accounts(:initech)
|
||||
account.update_column(:cards_count, 1001)
|
||||
|
||||
assert account.exceeding_card_limit?
|
||||
assert_equal 1001, account.billed_cards_count
|
||||
|
||||
account.override_limits card_count: 500
|
||||
assert_not account.exceeding_card_limit?
|
||||
assert_equal 500, account.billed_cards_count
|
||||
assert_equal 1001, account.cards_count # original unchanged
|
||||
|
||||
account.reset_overridden_limits
|
||||
assert account.exceeding_card_limit?
|
||||
assert_equal 1001, account.billed_cards_count
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user