From 823cf9fbb9d212ffab150b1ad7e3f0944f525907 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 17 Dec 2025 08:48:39 +0100 Subject: [PATCH] Add some methods to deal with storage limits --- .../controllers/admin/accounts_controller.rb | 6 +-- saas/app/models/account/limited.rb | 29 ++++++++++++- saas/app/views/admin/accounts/edit.html.erb | 17 +++++--- ...bytes_used_to_account_overridden_limits.rb | 5 +++ saas/db/saas_schema.rb | 3 +- saas/test/models/account/limited_test.rb | 41 +++++++++++++++++++ 6 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 saas/db/migrate/20251216000000_add_bytes_used_to_account_overridden_limits.rb diff --git a/saas/app/controllers/admin/accounts_controller.rb b/saas/app/controllers/admin/accounts_controller.rb index ba6200d1b..26a2b9c82 100644 --- a/saas/app/controllers/admin/accounts_controller.rb +++ b/saas/app/controllers/admin/accounts_controller.rb @@ -12,7 +12,7 @@ class Admin::AccountsController < AdminController end def update - @account.override_limits(card_count: overridden_card_count_param) + @account.override_limits(**overridden_limits_params.to_h.symbolize_keys) redirect_to saas.edit_admin_account_path(@account.external_account_id), notice: "Account limits updated" end @@ -21,7 +21,7 @@ class Admin::AccountsController < AdminController @account = Account.find_by!(external_account_id: params[:id]) end - def overridden_card_count_param - params[:account][:overridden_card_count].to_i + def overridden_limits_params + params.expect(account: [ :card_count, :bytes_used ]) end end diff --git a/saas/app/models/account/limited.rb b/saas/app/models/account/limited.rb index f7eb3cbc1..b185815e4 100644 --- a/saas/app/models/account/limited.rb +++ b/saas/app/models/account/limited.rb @@ -6,15 +6,20 @@ module Account::Limited end NEAR_CARD_LIMIT_THRESHOLD = 100 + NEAR_STORAGE_LIMIT_THRESHOLD = 500.megabytes - def override_limits(card_count:) - (overridden_limits || build_overridden_limits).update!(card_count:) + def override_limits(card_count: nil, bytes_used: nil) + (overridden_limits || build_overridden_limits).update!(card_count:, bytes_used:) end def billed_cards_count overridden_limits&.card_count || cards_count end + def billed_bytes_used + overridden_limits&.bytes_used || bytes_used + end + def nearing_plan_cards_limit? plan.limit_cards? && remaining_cards_count < NEAR_CARD_LIMIT_THRESHOLD end @@ -23,6 +28,22 @@ module Account::Limited plan.limit_cards? && billed_cards_count > plan.card_limit end + def nearing_plan_storage_limit? + remaining_storage < NEAR_STORAGE_LIMIT_THRESHOLD + end + + def exceeding_storage_limit? + billed_bytes_used > plan.storage_limit + end + + def nearing_limits? + nearing_plan_cards_limit? || nearing_plan_storage_limit? + end + + def exceeding_limits? + exceeding_card_limit? || exceeding_storage_limit? + end + def reset_overridden_limits overridden_limits&.destroy reload_overridden_limits @@ -32,4 +53,8 @@ module Account::Limited def remaining_cards_count plan.card_limit - billed_cards_count end + + def remaining_storage + plan.storage_limit - billed_bytes_used + end end diff --git a/saas/app/views/admin/accounts/edit.html.erb b/saas/app/views/admin/accounts/edit.html.erb index 49c99773d..950ecc761 100644 --- a/saas/app/views/admin/accounts/edit.html.erb +++ b/saas/app/views/admin/accounts/edit.html.erb @@ -11,14 +11,21 @@
Actual card count:
<%= @account.cards_count %>
+
+
Actual bytes used:
+
<%= storage_to_human_size(@account.bytes_used) %>
+
<%= form_with model: @account, url: saas.admin_account_path(@account.external_account_id), class: "flex flex-column gap" do |form| %> -
- <%= form.label :overridden_card_count, "Override card count", class: "font-weight-bold" %> -
- <%= form.number_field :overridden_card_count, value: @account.overridden_limits&.card_count, class: "input full-width" %> -
+
+ <%= form.label :card_count, "Override card count", class: "font-weight-bold" %> + <%= form.number_field :card_count, value: @account.overridden_limits&.card_count, class: "input" %> +
+ +
+ <%= form.label :bytes_used, "Override bytes used", class: "font-weight-bold" %> + <%= form.number_field :bytes_used, value: @account.overridden_limits&.bytes_used, class: "input" %>
diff --git a/saas/db/migrate/20251216000000_add_bytes_used_to_account_overridden_limits.rb b/saas/db/migrate/20251216000000_add_bytes_used_to_account_overridden_limits.rb new file mode 100644 index 000000000..85adad44a --- /dev/null +++ b/saas/db/migrate/20251216000000_add_bytes_used_to_account_overridden_limits.rb @@ -0,0 +1,5 @@ +class AddBytesUsedToAccountOverriddenLimits < ActiveRecord::Migration[8.2] + def change + add_column :account_overridden_limits, :bytes_used, :bigint + end +end diff --git a/saas/db/saas_schema.rb b/saas/db/saas_schema.rb index 59c78f4cb..7e9b80b92 100644 --- a/saas/db/saas_schema.rb +++ b/saas/db/saas_schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.2].define(version: 2025_12_15_170000) do +ActiveRecord::Schema[8.2].define(version: 2025_12_16_000000) do create_table "account_billing_waivers", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.uuid "account_id", null: false t.datetime "created_at", null: false @@ -20,6 +20,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_12_15_170000) 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.bigint "bytes_used" t.integer "card_count" t.datetime "created_at", null: false t.datetime "updated_at", null: false diff --git a/saas/test/models/account/limited_test.rb b/saas/test/models/account/limited_test.rb index 335d6a37c..dafca0c31 100644 --- a/saas/test/models/account/limited_test.rb +++ b/saas/test/models/account/limited_test.rb @@ -73,4 +73,45 @@ class Account::LimitedTest < ActiveSupport::TestCase assert account.exceeding_card_limit? end + + test "detect nearing storage limit" do + # Paid plans have large storage limits + accounts(:"37s").update_column(:bytes_used, 4.gigabytes) + assert_not accounts(:"37s").nearing_plan_storage_limit? + + # Free plan not near limit + accounts(:initech).update_column(:bytes_used, 400.megabytes) + assert_not accounts(:initech).nearing_plan_storage_limit? + + # Free plan near limit + accounts(:initech).update_column(:bytes_used, 600.megabytes) + assert accounts(:initech).nearing_plan_storage_limit? + end + + test "detect exceeding storage limit" do + # Free plan under limit + accounts(:initech).update_column(:bytes_used, 900.megabytes) + assert_not accounts(:initech).exceeding_storage_limit? + + # Free plan over limit + accounts(:initech).update_column(:bytes_used, 1.1.gigabytes) + assert accounts(:initech).exceeding_storage_limit? + end + + test "override bytes_used limits" do + account = accounts(:initech) + account.update_column(:bytes_used, 1.1.gigabytes) + + assert account.exceeding_storage_limit? + assert_equal 1.1.gigabytes, account.billed_bytes_used + + account.override_limits bytes_used: 500.megabytes + assert_not account.exceeding_storage_limit? + assert_equal 500.megabytes, account.billed_bytes_used + assert_equal 1.1.gigabytes, account.bytes_used # original unchanged + + account.reset_overridden_limits + assert account.exceeding_storage_limit? + assert_equal 1.1.gigabytes, account.billed_bytes_used + end end