mirror of
https://github.com/we-promise/sure.git
synced 2026-04-17 19:14:11 +00:00
Add cost basis source tracking with manual override and lock protection (#623)
* Add cost basis tracking and management to holdings - Added migration to introduce `cost_basis_source` and `cost_basis_locked` fields to `holdings`. - Implemented backfill for existing holdings to set `cost_basis_source` based on heuristics. - Introduced `Holding::CostBasisReconciler` to manage cost basis resolution logic. - Added user interface components for editing and locking cost basis in holdings. - Updated `materializer` to integrate reconciliation logic and respect locked holdings. - Extended tests for cost basis-related workflows to ensure accuracy and reliability. * Fix cost basis calculation in holdings controller - Ensure `cost_basis` is converted to decimal for accurate arithmetic. - Fix conditional check to properly validate positive `cost_basis`. * Improve cost basis validation and error handling in holdings controller - Allow zero as a valid cost basis for gifted/inherited shares. - Add error handling with user feedback for invalid cost basis values. --------- Co-authored-by: Josh Waldrep <joshua.waldrep5+github@gmail.com>
This commit is contained in:
@@ -27,4 +27,38 @@ class HoldingsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_redirected_to account_path(@holding.account)
|
||||
assert_empty @holding.account.entries.where(entryable: @holding.account.trades.where(security: @holding.security))
|
||||
end
|
||||
|
||||
test "updates cost basis with total amount divided by qty" do
|
||||
# Given: holding with 10 shares
|
||||
@holding.update!(qty: 10, cost_basis: nil, cost_basis_source: nil, cost_basis_locked: false)
|
||||
|
||||
# When: user submits total cost basis of $100 (should become $10 per share)
|
||||
patch holding_path(@holding), params: { holding: { cost_basis: "100.00" } }
|
||||
|
||||
# Redirects to account page holdings tab to refresh list
|
||||
assert_redirected_to account_path(@holding.account, tab: "holdings")
|
||||
@holding.reload
|
||||
|
||||
# Then: cost_basis should be per-share ($10), not total
|
||||
assert_equal 10.0, @holding.cost_basis.to_f
|
||||
assert_equal "manual", @holding.cost_basis_source
|
||||
assert @holding.cost_basis_locked?
|
||||
end
|
||||
|
||||
test "unlock_cost_basis removes lock" do
|
||||
# Given: locked holding
|
||||
@holding.update!(cost_basis: 50.0, cost_basis_source: "manual", cost_basis_locked: true)
|
||||
|
||||
# When: user unlocks
|
||||
post unlock_cost_basis_holding_path(@holding)
|
||||
|
||||
# Redirects to account page holdings tab to refresh list
|
||||
assert_redirected_to account_path(@holding.account, tab: "holdings")
|
||||
@holding.reload
|
||||
|
||||
# Then: lock is removed but cost_basis and source remain
|
||||
assert_not @holding.cost_basis_locked?
|
||||
assert_equal 50.0, @holding.cost_basis.to_f
|
||||
assert_equal "manual", @holding.cost_basis_source
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user