mirror of
https://github.com/we-promise/sure.git
synced 2026-04-07 14:31:25 +00:00
Add "Reset account" followed by sample data preload (#163)
* Add reset with sample data option on profile settings * No need for "member" user in preload * Cleanup/shorten copy
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
class UsersController < ApplicationController
|
||||
before_action :set_user
|
||||
before_action :ensure_admin, only: :reset
|
||||
before_action :ensure_admin, only: %i[reset reset_with_sample_data]
|
||||
|
||||
def update
|
||||
@user = Current.user
|
||||
@@ -40,6 +40,11 @@ class UsersController < ApplicationController
|
||||
redirect_to settings_profile_path, notice: t(".success")
|
||||
end
|
||||
|
||||
def reset_with_sample_data
|
||||
FamilyResetJob.perform_later(Current.family, load_sample_data_for_email: @user.email)
|
||||
redirect_to settings_profile_path, notice: t(".success")
|
||||
end
|
||||
|
||||
def destroy
|
||||
if @user.deactivate
|
||||
Current.session.destroy
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class FamilyResetJob < ApplicationJob
|
||||
queue_as :low_priority
|
||||
|
||||
def perform(family)
|
||||
def perform(family, load_sample_data_for_email: nil)
|
||||
# Delete all family data except users
|
||||
ActiveRecord::Base.transaction do
|
||||
# Delete accounts and related data
|
||||
@@ -12,7 +12,11 @@ class FamilyResetJob < ApplicationJob
|
||||
family.plaid_items.destroy_all
|
||||
family.imports.destroy_all
|
||||
family.budgets.destroy_all
|
||||
end
|
||||
|
||||
if load_sample_data_for_email.present?
|
||||
Demo::Generator.new.generate_new_user_data_for!(family.reload, email: load_sample_data_for_email)
|
||||
else
|
||||
family.sync_later
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require "securerandom"
|
||||
|
||||
class Demo::Generator
|
||||
# @param seed [Integer, String, nil] Seed value used to initialise the internal PRNG. If nil, the ENV variable DEMO_DATA_SEED will
|
||||
# be honoured and default to a random seed when not present.
|
||||
@@ -59,6 +61,25 @@ class Demo::Generator
|
||||
end
|
||||
end
|
||||
|
||||
def generate_new_user_data_for!(family, email:)
|
||||
with_timing(__method__, max_seconds: 1000) do
|
||||
family = family.reload
|
||||
admin_user = ensure_admin_user!(family, email)
|
||||
|
||||
puts "📊 Creating sample financial data for #{family.name}..."
|
||||
ActiveRecord::Base.transaction do
|
||||
create_realistic_categories!(family)
|
||||
create_realistic_accounts!(family)
|
||||
create_realistic_transactions!(family)
|
||||
generate_budget_auto_fill!(family)
|
||||
end
|
||||
|
||||
family.sync_later
|
||||
|
||||
puts "✅ Sample data loaded successfully!"
|
||||
end
|
||||
end
|
||||
|
||||
# Generate comprehensive realistic demo data with multi-currency
|
||||
def generate_default_data!(skip_clear: false, email: "user@example.com")
|
||||
if skip_clear
|
||||
@@ -118,6 +139,17 @@ class Demo::Generator
|
||||
Demo::DataCleaner.new.destroy_everything!
|
||||
end
|
||||
|
||||
def ensure_admin_user!(family, email)
|
||||
user = family.users.find_by(email: email)
|
||||
return user if user&.admin? || user&.super_admin?
|
||||
|
||||
raise ActiveRecord::RecordNotFound, "No admin user with email #{email} found in family ##{family.id}"
|
||||
end
|
||||
|
||||
def partner_email_for(email)
|
||||
"partner_#{email}"
|
||||
end
|
||||
|
||||
def create_family_and_users!(family_name, email, onboarded:, subscribed:)
|
||||
family = Family.create!(
|
||||
name: family_name,
|
||||
|
||||
@@ -137,9 +137,30 @@
|
||||
href: reset_user_path(@user),
|
||||
method: :delete,
|
||||
confirm: CustomConfirm.new(
|
||||
title: "Reset account?",
|
||||
body: "This will delete all data associated with your account. Your user profile will remain active.",
|
||||
btn_text: "Reset account",
|
||||
title: t(".confirm_reset.title"),
|
||||
body: t(".confirm_reset.body"),
|
||||
btn_text: t(".reset_account"),
|
||||
destructive: true,
|
||||
high_severity: true
|
||||
)
|
||||
) %>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
||||
<div class="w-full md:w-2/3">
|
||||
<h3 class="font-medium text-primary"><%= t(".reset_account_with_sample_data") %></h3>
|
||||
<p class="text-secondary text-sm"><%= t(".reset_account_with_sample_data_warning") %></p>
|
||||
</div>
|
||||
|
||||
<%= render DS::Button.new(
|
||||
text: t(".reset_account_with_sample_data"),
|
||||
variant: "destructive",
|
||||
href: reset_with_sample_data_user_path(@user),
|
||||
method: :delete,
|
||||
confirm: CustomConfirm.new(
|
||||
title: t(".confirm_reset_with_sample_data.title"),
|
||||
body: t(".confirm_reset_with_sample_data.body"),
|
||||
btn_text: t(".reset_account_with_sample_data"),
|
||||
destructive: true,
|
||||
high_severity: true
|
||||
)
|
||||
|
||||
@@ -51,6 +51,9 @@ en:
|
||||
confirm_reset:
|
||||
body: Are you sure you want to reset your account? This will delete all your accounts, categories, merchants, tags, and other data. This action cannot be undone.
|
||||
title: Reset account?
|
||||
confirm_reset_with_sample_data:
|
||||
body: Are you sure you want to reset your account and load sample data? This will delete your existing data and replace it with demo data so you can explore Sure safely.
|
||||
title: Reset account and load sample data?
|
||||
confirm_remove_invitation:
|
||||
body: Are you sure you want to remove the invitation for %{email}?
|
||||
title: Remove Invitation
|
||||
@@ -63,6 +66,8 @@ en:
|
||||
your data and cannot be undone.
|
||||
reset_account: Reset account
|
||||
reset_account_warning: Resetting your account will delete all your accounts, categories, merchants, tags, and other data, but keep your user account intact.
|
||||
reset_account_with_sample_data: Reset and preload
|
||||
reset_account_with_sample_data_warning: Delete all your existing data and then load fresh sample data so you can explore with a pre-filled environment.
|
||||
email: Email
|
||||
first_name: First Name
|
||||
household_form_input_placeholder: Enter household name
|
||||
|
||||
@@ -35,6 +35,9 @@ nb:
|
||||
confirm_reset:
|
||||
body: Er du sikker på at du vil tilbakestille kontoen din? Dette vil slette alle kontoene dine, kategorier, forhandlere, tagger og andre data. Denne handlingen kan ikke angres.
|
||||
title: Tilbakestill konto?
|
||||
confirm_reset_with_sample_data:
|
||||
body: Are you sure you want to reset your account and load sample data? This will delete your existing data and replace it with demo data so you can explore Sure safely.
|
||||
title: Reset account and load sample data?
|
||||
confirm_remove_invitation:
|
||||
body: Er du sikker på at du vil fjerne invitasjonen for %{email}?
|
||||
title: Fjern invitasjon
|
||||
@@ -47,6 +50,8 @@ nb:
|
||||
dataene dine og kan ikke angres.
|
||||
reset_account: Tilbakestill konto
|
||||
reset_account_warning: Tilbakestilling av kontoen din vil slette alle kontoene dine, kategorier, forhandlere, tagger og andre data, men beholde brukerkontoen din intakt.
|
||||
reset_account_with_sample_data: Reset account and load sample data
|
||||
reset_account_with_sample_data_warning: Resetting your account will delete all your existing data and then load fresh sample data so you can explore Sure with a pre-filled environment.
|
||||
email: E-post
|
||||
first_name: Fornavn
|
||||
household_form_input_placeholder: Angi husholdningsnavn
|
||||
|
||||
@@ -35,6 +35,9 @@ tr:
|
||||
confirm_reset:
|
||||
body: Hesabınızı sıfırlamak istediğinizden emin misiniz? Bu işlem tüm hesaplarınızı, kategorilerinizi, satıcılarınızı, etiketlerinizi ve diğer verilerinizi silecektir. Bu işlem geri alınamaz.
|
||||
title: Hesap sıfırlansın mı?
|
||||
confirm_reset_with_sample_data:
|
||||
body: Are you sure you want to reset your account and load sample data? This will delete your existing data and replace it with demo data so you can explore Sure safely.
|
||||
title: Reset account and load sample data?
|
||||
confirm_remove_invitation:
|
||||
body: "%{email} için daveti kaldırmak istediğinizden emin misiniz?"
|
||||
title: "Daveti Kaldır"
|
||||
@@ -46,6 +49,8 @@ tr:
|
||||
delete_account_warning: Hesabınızı silmek tüm verilerinizi kalıcı olarak kaldırır ve geri alınamaz.
|
||||
reset_account: Hesabı sıfırla
|
||||
reset_account_warning: Hesabınızı sıfırlamak tüm hesaplarınızı, kategorilerinizi, satıcılarınızı, etiketlerinizi ve diğer verilerinizi silecek, ancak kullanıcı hesabınızı koruyacaktır.
|
||||
reset_account_with_sample_data: Reset account and load sample data
|
||||
reset_account_with_sample_data_warning: Resetting your account will delete all your existing data and then load fresh sample data so you can explore Sure with a pre-filled environment.
|
||||
email: E-posta
|
||||
first_name: Ad
|
||||
household_form_input_placeholder: Hane adı girin
|
||||
|
||||
@@ -11,3 +11,5 @@ en:
|
||||
reset:
|
||||
success: Your account has been reset. Data will be deleted in the background in some time.
|
||||
unauthorized: You are not authorized to perform this action
|
||||
reset_with_sample_data:
|
||||
success: Your account has been reset and sample data is being prepared. You’ll see demo data shortly.
|
||||
|
||||
@@ -7,6 +7,9 @@ nb:
|
||||
email_change_failed: Kunne ikke endre e-postadresse.
|
||||
email_change_initiated: Vennligst sjekk din nye e-postadresse for bekreftelsesinstruksjoner.
|
||||
success: Profilen din har blitt oppdatert.
|
||||
reset:
|
||||
success: Kontoen din har blitt tilbakestilt. Data vil bli slettet i bakgrunnen om en stund.
|
||||
unauthorized: Du er ikke autorisert til å utføre denne handlingen.
|
||||
reset:
|
||||
success: Kontoen din har blitt tilbakestilt. Data vil bli slettet i bakgrunnen om en stund.
|
||||
unauthorized: Du er ikke autorisert til å utføre denne handlingen.
|
||||
reset_with_sample_data:
|
||||
success: Kontoen din har blitt tilbakestilt og demodata forberedes. Du vil snart se eksempeldata.
|
||||
|
||||
|
||||
@@ -9,4 +9,7 @@ tr:
|
||||
success: Profiliniz güncellendi.
|
||||
reset:
|
||||
success: Hesabınız sıfırlandı. Verileriniz arka planda bir süre sonra silinecek.
|
||||
unauthorized: Bu işlemi gerçekleştirmeye yetkiniz yok.
|
||||
unauthorized: Bu işlemi gerçekleştirmeye yetkiniz yok.
|
||||
reset_with_sample_data:
|
||||
success: Hesabınız sıfırlandı ve örnek veriler hazırlanıyor. Kısa süre içinde demo verileri göreceksiniz.
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ Rails.application.routes.draw do
|
||||
|
||||
resources :users, only: %i[update destroy] do
|
||||
delete :reset, on: :member
|
||||
delete :reset_with_sample_data, on: :member
|
||||
patch :rule_prompt_settings, on: :member
|
||||
end
|
||||
|
||||
|
||||
@@ -40,7 +40,9 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
|
||||
budget = budgets(:one)
|
||||
plaid_item = plaid_items(:one)
|
||||
|
||||
Provider::Plaid.any_instance.expects(:remove_item).with(plaid_item.access_token).once
|
||||
provider = mock
|
||||
provider.expects(:remove_item).with(plaid_item.access_token).once
|
||||
PlaidItem.any_instance.stubs(:plaid_provider).returns(provider)
|
||||
|
||||
perform_enqueued_jobs(only: FamilyResetJob) do
|
||||
delete reset_user_url(@user)
|
||||
@@ -58,6 +60,36 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_not PlaidItem.exists?(plaid_item.id)
|
||||
end
|
||||
|
||||
test "admin can reset family data and load sample data" do
|
||||
account = accounts(:investment)
|
||||
category = categories(:income)
|
||||
tag = tags(:one)
|
||||
merchant = merchants(:netflix)
|
||||
import = imports(:transaction)
|
||||
budget = budgets(:one)
|
||||
plaid_item = plaid_items(:one)
|
||||
|
||||
provider = mock
|
||||
provider.expects(:remove_item).with(plaid_item.access_token).once
|
||||
PlaidItem.any_instance.stubs(:plaid_provider).returns(provider)
|
||||
Demo::Generator.any_instance.expects(:generate_new_user_data_for!).with(@user.family, email: @user.email)
|
||||
|
||||
perform_enqueued_jobs(only: FamilyResetJob) do
|
||||
delete reset_with_sample_data_user_url(@user)
|
||||
end
|
||||
|
||||
assert_redirected_to settings_profile_url
|
||||
assert_equal I18n.t("users.reset_with_sample_data.success"), flash[:notice]
|
||||
|
||||
assert_not Account.exists?(account.id)
|
||||
assert_not Category.exists?(category.id)
|
||||
assert_not Tag.exists?(tag.id)
|
||||
assert_not Merchant.exists?(merchant.id)
|
||||
assert_not Import.exists?(import.id)
|
||||
assert_not Budget.exists?(budget.id)
|
||||
assert_not PlaidItem.exists?(plaid_item.id)
|
||||
end
|
||||
|
||||
test "non-admin cannot reset family data" do
|
||||
sign_in @member = users(:family_member)
|
||||
|
||||
@@ -68,6 +100,16 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_no_enqueued_jobs only: FamilyResetJob
|
||||
end
|
||||
|
||||
test "non-admin cannot reset family data with sample data" do
|
||||
sign_in @member = users(:family_member)
|
||||
|
||||
delete reset_with_sample_data_user_url(@member)
|
||||
|
||||
assert_redirected_to settings_profile_url
|
||||
assert_equal I18n.t("users.reset.unauthorized"), flash[:alert]
|
||||
assert_no_enqueued_jobs only: FamilyResetJob
|
||||
end
|
||||
|
||||
test "member can deactivate their account" do
|
||||
sign_in @member = users(:family_member)
|
||||
delete user_url(@member)
|
||||
|
||||
Reference in New Issue
Block a user