diff --git a/src/actions/portfolio.ts b/src/actions/portfolio.ts index 7cc9741..027b8a7 100644 --- a/src/actions/portfolio.ts +++ b/src/actions/portfolio.ts @@ -73,12 +73,13 @@ function calculateCnyValueFromPrice( } export async function getPortfolioPositions(): Promise { - const allTransactions = await db + const allTransactions = await db .select({ txType: transactions.txType, quantity: transactions.quantity, price: transactions.price, exchangeRate: transactions.exchangeRate, + txCurrency: transactions.txCurrency, assetId: transactions.assetId, assetSymbol: assets.symbol, assetType: assets.type, @@ -89,6 +90,14 @@ export async function getPortfolioPositions(): Promise { .leftJoin(assets, eq(assets.id, transactions.assetId)) .orderBy(desc(transactions.executedAt)); + const rates = await db.select({ + fromCurrency: exchangeRates.fromCurrency, + toCurrency: exchangeRates.toCurrency, + rate: exchangeRates.rate, + }).from(exchangeRates); + + const rateMap = buildRateMap(rates); + const holdings = new Map { holding.quantity = holding.quantity.plus(new Big(tx.quantity)); const costPerUnit = new Big(tx.quantity).times(new Big(tx.price)); holding.totalCostNative = holding.totalCostNative.plus(costPerUnit); - const costCny = costPerUnit.times(new Big(tx.exchangeRate || '1')); + let appliedRate = tx.exchangeRate; + if ((!appliedRate || appliedRate === '1' || appliedRate === '1.00000000') && tx.txCurrency !== 'CNY') { + const fallbackRate = getRate(rateMap, tx.txCurrency, 'CNY'); + if (fallbackRate) { + appliedRate = fallbackRate; + } + } + const costCny = costPerUnit.times(new Big(appliedRate || '1')); holding.totalCostCny = holding.totalCostCny.plus(costCny); } else if (tx.txType === 'SELL') { holding.quantity = holding.quantity.minus(new Big(tx.quantity)); const sellCostPerUnit = new Big(tx.quantity).times(new Big(tx.price)); holding.totalCostNative = holding.totalCostNative.minus(sellCostPerUnit); - const sellCostCny = sellCostPerUnit.times(new Big(tx.exchangeRate || '1')); + let appliedRate = tx.exchangeRate; + if ((!appliedRate || appliedRate === '1' || appliedRate === '1.00000000') && tx.txCurrency !== 'CNY') { + const fallbackRate = getRate(rateMap, tx.txCurrency, 'CNY'); + if (fallbackRate) { + appliedRate = fallbackRate; + } + } + const sellCostCny = sellCostPerUnit.times(new Big(appliedRate || '1')); holding.totalCostCny = holding.totalCostCny.minus(sellCostCny); } else if (tx.txType === 'AIRDROP') { holding.quantity = holding.quantity.plus(new Big(tx.quantity)); @@ -142,14 +165,6 @@ export async function getPortfolioPositions(): Promise { } } - const rates = await db.select({ - fromCurrency: exchangeRates.fromCurrency, - toCurrency: exchangeRates.toCurrency, - rate: exchangeRates.rate, - }).from(exchangeRates); - - const rateMap = buildRateMap(rates); - const result: Position[] = []; let totalCnyValue = new Big('0'); let totalPnlCny = new Big('0'); diff --git a/src/actions/transaction.ts b/src/actions/transaction.ts index 24ac31f..6d49a44 100644 --- a/src/actions/transaction.ts +++ b/src/actions/transaction.ts @@ -1,9 +1,9 @@ 'use server'; import { db } from '@/db'; -import { transactions, transactionTypeEnum } from '@/db/schema'; +import { transactions, transactionTypeEnum, exchangeRates } from '@/db/schema'; import { z } from 'zod'; -import { eq, desc } from 'drizzle-orm'; +import { eq, desc, and } from 'drizzle-orm'; const createTransactionSchema = z.object({ assetId: z.string().uuid(), @@ -23,7 +23,21 @@ export async function createTransaction(params: z.infer