From ef4be7948ac0828777b288fb1675c835134bb88f Mon Sep 17 00:00:00 2001 From: Zach Gollwitzer Date: Thu, 25 Jul 2024 12:51:50 -0400 Subject: [PATCH] Implement auto family syncs on login (#1021) --- app/controllers/application_controller.rb | 2 +- app/controllers/concerns/auto_sync.rb | 13 +++++++++++++ app/models/account/sync.rb | 1 + app/models/account/syncable.rb | 8 ++++++++ app/models/demo/generator.rb | 4 ---- app/models/family.rb | 12 +++++++++++- app/views/accounts/show.html.erb | 2 ++ .../20240725163339_add_last_synced_at_to_family.rb | 5 +++++ db/schema.rb | 5 +++-- test/models/account_test.rb | 4 ++++ test/models/family_test.rb | 12 +++++++++++- 11 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 app/controllers/concerns/auto_sync.rb create mode 100644 db/migrate/20240725163339_add_last_synced_at_to_family.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 84f8a8fe..b8d5a836 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,5 @@ class ApplicationController < ActionController::Base - include Authentication, Invitable, SelfHostable + include AutoSync, Authentication, Invitable, SelfHostable include Pagy::Backend # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has. diff --git a/app/controllers/concerns/auto_sync.rb b/app/controllers/concerns/auto_sync.rb new file mode 100644 index 00000000..122710a6 --- /dev/null +++ b/app/controllers/concerns/auto_sync.rb @@ -0,0 +1,13 @@ +module AutoSync + extend ActiveSupport::Concern + + included do + before_action :sync_family, if: -> { Current.family.present? && Current.family.needs_sync? } + end + + private + + def sync_family + Current.family.sync + end +end diff --git a/app/models/account/sync.rb b/app/models/account/sync.rb index 5db21b89..1442ec0b 100644 --- a/app/models/account/sync.rb +++ b/app/models/account/sync.rb @@ -78,5 +78,6 @@ class Account::Sync < ApplicationRecord partial: "shared/notification", locals: { type: type, message: message } ) + broadcast_refresh_to account end end diff --git a/app/models/account/syncable.rb b/app/models/account/syncable.rb index a0fe78cd..382d401f 100644 --- a/app/models/account/syncable.rb +++ b/app/models/account/syncable.rb @@ -11,6 +11,14 @@ module Account::Syncable syncs.syncing.any? end + def latest_sync_date + syncs.where.not(last_ran_at: nil).pluck(:last_ran_at).max&.to_date + end + + def needs_sync? + latest_sync_date.nil? || latest_sync_date < Date.current + end + def sync_later(start_date: nil) AccountSyncJob.perform_later(self, start_date: start_date) end diff --git a/app/models/demo/generator.rb b/app/models/demo/generator.rb index b897064b..2fb27157 100644 --- a/app/models/demo/generator.rb +++ b/app/models/demo/generator.rb @@ -34,10 +34,6 @@ class Demo::Generator create_car_and_loan! puts "accounts created" - - family.sync - - puts "balances synced" puts "Demo data loaded successfully!" end end diff --git a/app/models/family.rb b/app/models/family.rb index cdcf2e8b..2aaeaf14 100644 --- a/app/models/family.rb +++ b/app/models/family.rb @@ -105,6 +105,16 @@ class Family < ApplicationRecord end def sync(start_date: nil) - accounts.active.sync(start_date: start_date) + accounts.active.each do |account| + if account.needs_sync? + account.sync_later(start_date: start_date || account.last_sync_date) + end + end + + update! last_synced_at: Time.now + end + + def needs_sync? + last_synced_at.nil? || last_synced_at.to_date < Date.current end end diff --git a/app/views/accounts/show.html.erb b/app/views/accounts/show.html.erb index 2459a7eb..b67c5869 100644 --- a/app/views/accounts/show.html.erb +++ b/app/views/accounts/show.html.erb @@ -1,3 +1,5 @@ +<%= turbo_stream_from @account %> +
diff --git a/db/migrate/20240725163339_add_last_synced_at_to_family.rb b/db/migrate/20240725163339_add_last_synced_at_to_family.rb new file mode 100644 index 00000000..a8f4c886 --- /dev/null +++ b/db/migrate/20240725163339_add_last_synced_at_to_family.rb @@ -0,0 +1,5 @@ +class AddLastSyncedAtToFamily < ActiveRecord::Migration[7.2] + def change + add_column :families, :last_synced_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index 39a0d8b1..96301c2d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_07_17_113535) do +ActiveRecord::Schema[7.2].define(version: 2024_07_25_163339) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -118,7 +118,7 @@ ActiveRecord::Schema[7.2].define(version: 2024_07_17_113535) do t.boolean "is_active", default: true, null: false t.date "last_sync_date" t.uuid "institution_id" - t.virtual "classification", type: :string, as: "\nCASE\n WHEN ((accountable_type)::text = ANY (ARRAY[('Loan'::character varying)::text, ('CreditCard'::character varying)::text, ('OtherLiability'::character varying)::text])) THEN 'liability'::text\n ELSE 'asset'::text\nEND", stored: true + t.virtual "classification", type: :string, as: "\nCASE\n WHEN ((accountable_type)::text = ANY ((ARRAY['Loan'::character varying, 'CreditCard'::character varying, 'OtherLiability'::character varying])::text[])) THEN 'liability'::text\n ELSE 'asset'::text\nEND", stored: true t.index ["accountable_type"], name: "index_accounts_on_accountable_type" t.index ["family_id"], name: "index_accounts_on_family_id" t.index ["institution_id"], name: "index_accounts_on_institution_id" @@ -194,6 +194,7 @@ ActiveRecord::Schema[7.2].define(version: 2024_07_17_113535) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "currency", default: "USD" + t.datetime "last_synced_at" end create_table "good_job_batches", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| diff --git a/test/models/account_test.rb b/test/models/account_test.rb index ddafdc78..bc1fe8ed 100644 --- a/test/models/account_test.rb +++ b/test/models/account_test.rb @@ -25,6 +25,10 @@ class AccountTest < ActiveSupport::TestCase @account.sync start_date: start_date end + test "needs sync if account has not synced today" do + assert @account.needs_sync? + end + test "groups accounts by type" do result = @family.accounts.by_group(period: Period.all) assets = result[:assets] diff --git a/test/models/family_test.rb b/test/models/family_test.rb index a992ef99..2a2649d7 100644 --- a/test/models/family_test.rb +++ b/test/models/family_test.rb @@ -48,6 +48,14 @@ class FamilyTest < ActiveSupport::TestCase assert_equal Money.new(50000, @family.currency), @family.net_worth end + test "needs sync if last family sync was before today" do + assert @family.needs_sync? + + @family.update! last_synced_at: Time.now + + assert_not @family.needs_sync? + end + test "syncs active accounts" do account = create_account(balance: 1000, accountable: CreditCard.new, is_active: false) @@ -57,7 +65,9 @@ class FamilyTest < ActiveSupport::TestCase account.update! is_active: true - Account.any_instance.expects(:sync_later).with(start_date: nil).once + Account.any_instance.expects(:needs_sync?).once.returns(true) + Account.any_instance.expects(:last_sync_date).once.returns(2.days.ago.to_date) + Account.any_instance.expects(:sync_later).with(start_date: 2.days.ago.to_date).once @family.sync end