where('user_id', $request->user()->id); })->findOrFail($holdingId); return response()->json($holding->transactions()->orderBy('traded_at', 'desc')->get()); } public function store(Request $request, string $holdingId) { $holding = InvestmentHolding::whereHas('account', function ($q) use ($request) { $q->where('user_id', $request->user()->id); })->findOrFail($holdingId); $validated = $request->validate([ 'action' => 'required|in:buy,sell', 'shares' => 'nullable|numeric|min:0', 'price' => 'nullable|numeric|min:0', 'fee' => 'nullable|numeric|min:0', 'tax' => 'nullable|numeric|min:0', 'traded_at' => 'required|date', 'note' => 'nullable|string', 'amount' => 'nullable|numeric|min:0', // 實際投入金額(基金用) 'nav' => 'nullable|numeric|min:0', // 淨值(基金用) ]); $transaction = $holding->transactions()->create($validated); // 更新持倉的 shares 和 avg_cost $this->recalculateHolding($holding); return response()->json($transaction, 201); } public function destroy(Request $request, string $holdingId, string $id) { $holding = InvestmentHolding::whereHas('account', function ($q) use ($request) { $q->where('user_id', $request->user()->id); })->findOrFail($holdingId); $transaction = $holding->transactions()->findOrFail($id); $transaction->delete(); $this->recalculateHolding($holding); return response()->json(null, 204); } // 每次買賣後重新計算平均成本和持有股數 private function recalculateHolding(InvestmentHolding $holding): void { $transactions = $holding->transactions()->orderBy('traded_at')->get(); $totalUnits = 0; $totalCost = 0; $presentUnits = 0; foreach ($transactions as $tx) { if ($tx->action === 'buy') { if ($holding->type === 'fund') { $units = $tx->nav > 0 ? $tx->amount / $tx->nav : 0; $totalCost += $tx->amount + $tx->fee; $presentUnits += $units; $totalUnits += $units; } else { $totalCost += $tx->shares * $tx->price + $tx->fee; $presentUnits += $tx->shares; $totalUnits += $tx->shares; } } else { if ($holding->type === 'fund') { $units = $tx->nav > 0 ? $tx->amount / $tx->nav : 0; $totalUnits -= $units; } else { $totalUnits -= $tx->shares; } } } $holding->update([ 'shares' => max(0, $totalUnits), // 總持有股數 'avg_cost' => $presentUnits > 0 ? $totalCost / $presentUnits : 0, ]); } }