1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-10 07:55:21 +02:00

Fix: Purge stale holdings from accounts during sync

This commit is contained in:
Zach Gollwitzer 2025-03-05 11:51:13 -05:00
parent eaa1b6abe0
commit 798cd40015
3 changed files with 31 additions and 14 deletions

View file

@ -10,11 +10,11 @@ class Account::Syncer
holdings = sync_holdings
balances = sync_balances(holdings)
account.reload
update_account_info(balances, holdings) unless account.plaid_account_id.present?
update_account_info(balances, holdings) unless
convert_records_to_family_currency(balances, holdings) unless account.currency == account.family.currency
# Enrich if user opted in or if we're syncing transactions from a Plaid account on the hosted app
if account.family.data_enrichment_enabled? || (account.plaid_account_id.present? && Rails.application.config.app_mode.hosted?)
if account.family.data_enrichment_enabled? || (plaid_sync? && Rails.application.config.app_mode.hosted?)
account.enrich_data
else
Rails.logger.info("Data enrichment is disabled, skipping enrichment for account #{account.id}")
@ -41,15 +41,13 @@ class Account::Syncer
def sync_holdings
calculator = Account::HoldingCalculator.new(account)
calculated_holdings = calculator.calculate(reverse: account.plaid_account_id.present?)
calculated_holdings = calculator.calculate(reverse: plaid_sync?)
current_time = Time.now
Account.transaction do
load_holdings(calculated_holdings)
# Purge outdated holdings
account.holdings.delete_by("date < ? OR security_id NOT IN (?)", account_start_date, calculated_holdings.map(&:security_id))
purge_outdated_holdings
end
calculated_holdings
@ -57,13 +55,11 @@ class Account::Syncer
def sync_balances(holdings)
calculator = Account::BalanceCalculator.new(account, holdings: holdings)
calculated_balances = calculator.calculate(reverse: account.plaid_account_id.present?, start_date: start_date)
calculated_balances = calculator.calculate(reverse: plaid_sync?, start_date: start_date)
Account.transaction do
load_balances(calculated_balances)
# Purge outdated balances
account.balances.delete_by("date < ?", account_start_date)
purge_outdated_balances
end
calculated_balances
@ -131,4 +127,23 @@ class Account::Syncer
unique_by: %i[account_id security_id date currency]
)
end
def purge_outdated_balances
account.balances.delete_by("date < ?", account_start_date)
end
def plaid_sync?
account.plaid_account_id.present?
end
def purge_outdated_holdings
portfolio_security_ids = account.entries.account_trades.map { |entry| entry.entryable.security_id }.uniq
if portfolio_security_ids.empty? && !plaid_sync?
# If there are no securities in the portfolio and it's not a plaid sync, delete all holdings
account.holdings.delete_all
else
account.holdings.delete_by("date < ? OR security_id NOT IN (?)", account_start_date, portfolio_security_ids)
end
end
end

View file

@ -21,12 +21,12 @@
</div>
<div class="rounded-lg bg-white shadow-border-xs">
<%= render "account/holdings/cash", account: @account %>
<%= render "account/holdings/ruler" %>
<% if @account.current_holdings.any? %>
<%= render "account/holdings/cash", account: @account %>
<%= render "account/holdings/ruler" %>
<%= render partial: "account/holdings/holding", collection: @account.current_holdings, spacer_template: "ruler" %>
<% else %>
<p class="text-secondary text-sm p-4"><%= t(".no_holdings") %></p>
<% end %>
</div>
</div>

View file

@ -17,6 +17,8 @@ class Account::SyncerTest < ActiveSupport::TestCase
@account.family.update! currency: "USD"
@account.update! currency: "EUR"
@account.entries.create!(date: 1.day.ago.to_date, currency: "EUR", amount: 500, name: "Buy AAPL", entryable: Account::Trade.new(security: securities(:aapl), qty: 10, price: 50, currency: "EUR"))
ExchangeRate.create!(date: 1.day.ago.to_date, from_currency: "EUR", to_currency: "USD", rate: 1.2)
ExchangeRate.create!(date: Date.current, from_currency: "EUR", to_currency: "USD", rate: 2)