mirror of
https://github.com/we-promise/sure.git
synced 2026-04-07 22:34:47 +00:00
* 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>
65 lines
2.1 KiB
Ruby
65 lines
2.1 KiB
Ruby
require "test_helper"
|
|
|
|
class HoldingsControllerTest < ActionDispatch::IntegrationTest
|
|
setup do
|
|
sign_in users(:family_admin)
|
|
@account = accounts(:investment)
|
|
@holding = @account.holdings.first
|
|
end
|
|
|
|
test "gets holdings" do
|
|
get holdings_url(account_id: @account.id)
|
|
assert_response :success
|
|
end
|
|
|
|
test "gets holding" do
|
|
get holding_path(@holding)
|
|
|
|
assert_response :success
|
|
end
|
|
|
|
test "destroys holding and associated entries" do
|
|
assert_difference -> { Holding.count } => -1,
|
|
-> { Entry.count } => -1 do
|
|
delete holding_path(@holding)
|
|
end
|
|
|
|
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
|