mirror of
https://github.com/we-promise/sure.git
synced 2026-04-19 12:04:08 +00:00
Improve handling of cost_basis during holding materialization and display (#619)
- Refactored `persist_holdings` to separate and conditionally upsert holdings with and without cost_basis. - Updated `avg_cost` logic to treat 0 cost_basis as unknown and return nil when cost_basis cannot be determined. - Modified trend and investment calculation to exclude holdings with unknown cost_basis. - Adjusted `average_cost` formatting to handle nil values in API responses and views. - Added comprehensive tests to ensure cost_basis preservation and fallback behavior. - Localized `unknown` label for display when cost_basis is unavailable. Co-authored-by: Josh Waldrep <joshua.waldrep5+github@gmail.com>
This commit is contained in:
@@ -26,4 +26,53 @@ class Holding::MaterializerTest < ActiveSupport::TestCase
|
||||
Holding::Materializer.new(@account, strategy: :forward).materialize_holdings
|
||||
end
|
||||
end
|
||||
|
||||
test "preserves provider cost_basis when trade-derived cost_basis is nil" do
|
||||
# Simulate a provider-imported holding with cost_basis (e.g., from SimpleFIN)
|
||||
# This is the realistic scenario: linked account with provider holdings but no trades
|
||||
provider_cost_basis = BigDecimal("150.00")
|
||||
holding = Holding.create!(
|
||||
account: @account,
|
||||
security: @aapl,
|
||||
qty: 10,
|
||||
price: 200,
|
||||
amount: 2000,
|
||||
currency: "USD",
|
||||
date: Date.current,
|
||||
cost_basis: provider_cost_basis
|
||||
)
|
||||
|
||||
# Use :reverse strategy (what linked accounts use) - doesn't purge holdings
|
||||
# The AAPL holding has no trades, so computed cost_basis is nil
|
||||
# The materializer should preserve the provider cost_basis, not overwrite with nil
|
||||
Holding::Materializer.new(@account, strategy: :reverse).materialize_holdings
|
||||
|
||||
holding.reload
|
||||
assert_equal provider_cost_basis, holding.cost_basis,
|
||||
"Provider cost_basis should be preserved when no trades exist for this security"
|
||||
end
|
||||
|
||||
test "updates cost_basis when trade-derived cost_basis is available" do
|
||||
# Create a holding with provider cost_basis
|
||||
Holding.create!(
|
||||
account: @account,
|
||||
security: @aapl,
|
||||
qty: 10,
|
||||
price: 200,
|
||||
amount: 2000,
|
||||
currency: "USD",
|
||||
date: Date.current,
|
||||
cost_basis: BigDecimal("150.00") # Provider says $150
|
||||
)
|
||||
|
||||
# Create a trade that gives us a different cost basis
|
||||
create_trade(@aapl, account: @account, qty: 10, price: 180, date: Date.current)
|
||||
|
||||
# Use :reverse strategy - with trades, it should compute cost_basis from them
|
||||
Holding::Materializer.new(@account, strategy: :reverse).materialize_holdings
|
||||
|
||||
holding = @account.holdings.find_by(security: @aapl, date: Date.current)
|
||||
assert_equal BigDecimal("180.00"), holding.cost_basis,
|
||||
"Trade-derived cost_basis should override provider cost_basis when available"
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user