Files
sure/test/controllers/holdings_controller_test.rb
LPW bbaf7a06cc 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>
2026-01-12 14:05:46 +01:00

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