Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions backend/src/storage/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub async fn list_assets(pool: &SqlitePool) -> Result<Vec<AssetRecord>, StorageE
asset_prices.currency_code,
asset_prices.as_of,
(
SELECT SUM(CASE transaction_type WHEN 'BUY' THEN quantity ELSE -quantity END)
SELECT SUM(CASE transaction_type WHEN 'SELL' THEN -quantity ELSE quantity END)
FROM asset_transactions
WHERE asset_id = assets.id
) as total_quantity,
Expand Down Expand Up @@ -110,7 +110,7 @@ pub async fn get_asset(pool: &SqlitePool, asset_id: AssetId) -> Result<AssetReco
asset_prices.currency_code,
asset_prices.as_of,
(
SELECT SUM(CASE transaction_type WHEN 'BUY' THEN quantity ELSE -quantity END)
SELECT SUM(CASE transaction_type WHEN 'SELL' THEN -quantity ELSE quantity END)
FROM asset_transactions
WHERE asset_id = assets.id
) as total_quantity,
Expand Down
61 changes: 61 additions & 0 deletions backend/src/storage/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,67 @@ async fn creates_asset_transactions_and_derives_positions() {
);
}

#[tokio::test]
async fn opening_transaction_counts_toward_asset_total_quantity() {
let pool = test_pool().await;

let account_id = create_account(
&pool,
CreateAccountInput {
name: account_name("Imported Broker"),
account_type: AccountType::Broker,
base_currency: Currency::Eur,
},
)
.await
.expect("account insert should succeed");

let amd_id = create_asset(
&pool,
CreateAssetInput {
symbol: asset_symbol("AMD"),
name: asset_name("Advanced Micro Devices"),
asset_type: AssetType::Stock,
quote_symbol: None,
isin: None,
},
)
.await
.expect("asset insert should succeed");

create_asset_transaction(
&pool,
CreateAssetTransactionInput {
account_id,
asset_id: amd_id,
transaction_type: AssetTransactionType::Opening,
trade_date: trade_date("2026-08-28"),
quantity: asset_quantity("100"),
unit_price: asset_unit_price("108.92551"),
currency_code: Currency::Usd,
notes: Some("imported position".to_string()),
},
)
.await
.expect("opening insert should succeed");

let assets = list_assets(&pool).await.expect("asset list should succeed");

let amd = assets
.iter()
.find(|asset| asset.symbol.as_str() == "AMD")
.expect("AMD asset should be listed");

assert_eq!(
amd.total_quantity
.as_ref()
.map(|q| q.as_decimal())
.expect("opening position should produce a total quantity"),
rust_decimal::Decimal::new(100, 0),
"OPENING quantity must count positively toward total_quantity"
);
}

#[tokio::test]
async fn derives_positions_for_maximum_supported_transaction_quantities() {
let pool = test_pool().await;
Expand Down
Loading