From 0f22e2c33eb8a2d34dac20a264019f36b2b3f0e1 Mon Sep 17 00:00:00 2001 From: hackerESQ Date: Thu, 15 Aug 2024 21:35:43 -0500 Subject: [PATCH] wip --- app/Http/Controllers/PortfolioController.php | 2 +- .../Controllers/TransactionController.php | 19 +++ app/Models/DailyChange.php | 5 + app/Models/Portfolio.php | 23 +-- app/Models/Transaction.php | 45 ++++-- .../components/confirmation-modal.blade.php | 2 +- .../views/components/ib-flex-spacer.blade.php | 1 + resources/views/components/ib-modal.blade.php | 43 +++++ .../components/partials/side-bar.blade.php | 12 +- resources/views/dashboard.blade.php | 8 +- resources/views/layouts/main-layout.blade.php | 3 - .../livewire/manage-portfolio-form.blade.php | 7 +- .../manage-transaction-form.blade.php | 150 ++++++++++++++++++ ... => portfolio-performance-chart.blade.php} | 0 .../livewire/transactions-list.blade.php | 66 ++++++++ resources/views/portfolio/create.blade.php | 2 +- resources/views/portfolio/show.blade.php | 47 ++++-- resources/views/transaction/index.blade.php | 8 + routes/web.php | 4 +- 19 files changed, 386 insertions(+), 61 deletions(-) create mode 100644 app/Http/Controllers/TransactionController.php create mode 100644 resources/views/components/ib-flex-spacer.blade.php create mode 100644 resources/views/components/ib-modal.blade.php create mode 100644 resources/views/livewire/manage-transaction-form.blade.php rename resources/views/livewire/{portfolio-performance-cards.blade.php => portfolio-performance-chart.blade.php} (100%) create mode 100644 resources/views/livewire/transactions-list.blade.php create mode 100644 resources/views/transaction/index.blade.php diff --git a/app/Http/Controllers/PortfolioController.php b/app/Http/Controllers/PortfolioController.php index 90e3777..682db86 100644 --- a/app/Http/Controllers/PortfolioController.php +++ b/app/Http/Controllers/PortfolioController.php @@ -27,6 +27,6 @@ class PortfolioController extends Controller $portfolio->realizedGainLoss = rand(-200, 3999); $portfolio->dividendsEarned = rand(-200, 3999); - return view('portfolio.show', compact('portfolio')); + return view('portfolio.show', compact(['portfolio'])); } } diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php new file mode 100644 index 0000000..73bf50c --- /dev/null +++ b/app/Http/Controllers/TransactionController.php @@ -0,0 +1,19 @@ +where('user_id', auth()->user()->id); } + public function portfolio() + { + return $this->belongsTo(Portfolio::class); + } + } diff --git a/app/Models/Portfolio.php b/app/Models/Portfolio.php index 0b07db7..cff312d 100644 --- a/app/Models/Portfolio.php +++ b/app/Models/Portfolio.php @@ -56,7 +56,7 @@ class Portfolio extends Model * * @var array */ - protected $with = ['users']; + protected $with = ['users', 'transactions']; /** * The attributes that should be appended. @@ -70,15 +70,20 @@ class Portfolio extends Model return $this->belongsToMany(User::class)->withPivot('owner'); } - // public function holdings() - // { - // return $this->hasMany(Holding::class, 'portfolio_id', 'id'); - // } + public function holdings() + { + return $this->hasMany(Holding::class, 'portfolio_id', 'id'); + } - // public function transactions() - // { - // return $this->hasMany(Transaction::class); - // } + public function transactions() + { + return $this->hasMany(Transaction::class); + } + + public function daily_change() + { + return $this->hasMany(DailyChange::class); + } public function scopeMyPortfolios() { diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index a611f04..f40d4ed 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -4,11 +4,13 @@ namespace App\Models; use App\Models\MarketData; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Factories\HasFactory; class Transaction extends Model { use HasFactory; + use HasUuids; /** * The attributes that are mass assignable. @@ -53,33 +55,44 @@ class Transaction extends Model { parent::boot(); - static::saved(function ($model) { + static::saving(function ($transaction) { - static::syncHolding($model); + // if sale, move cost basis to sale price + if ($transaction->transaction_type == 'SELL') { + + $transaction->sale_price = $transaction->cost_basis; + $transaction->cost_basis = $transaction->holding->average_cost_basis ?? $transaction->cost_basis; + } }); - static::deleted(function ($model) { - static::syncHolding($model); + static::saved(function ($transaction) { + + // static::syncHolding($transaction); + }); + + static::deleted(function ($transaction) { + + // static::syncHolding($transaction); }); } - public static function syncHolding($model) { + public static function syncHolding($transaction) { // get the holding for a symbol and portfolio (or create one) $holding = Holding::firstOrNew([ - 'portfolio_id' => $model->portfolio_id, - 'symbol' => $model->symbol + 'portfolio_id' => $transaction->portfolio_id, + 'symbol' => $transaction->symbol ], [ - 'portfolio_id' => $model->portfolio_id, - 'symbol' => $model->symbol, - 'quantity' => $model->quantity, - 'average_cost_basis' => $model->cost_basis, - 'total_cost_basis' => $model->quantity * $model->cost_basis, + 'portfolio_id' => $transaction->portfolio_id, + 'symbol' => $transaction->symbol, + 'quantity' => $transaction->quantity, + 'average_cost_basis' => $transaction->cost_basis, + 'total_cost_basis' => $transaction->quantity * $transaction->cost_basis, ]); // pull existing transaction data $query = self::where([ - 'portfolio_id' => $model->portfolio_id, - 'symbol' => $model->symbol, + 'portfolio_id' => $transaction->portfolio_id, + 'symbol' => $transaction->symbol, ])->selectRaw('SUM(CASE WHEN transaction_type = "BUY" THEN quantity ELSE 0 END) AS `qty_purchases`') ->selectRaw('SUM(CASE WHEN transaction_type = "SELL" THEN quantity ELSE 0 END) AS `qty_sales`') ->selectRaw('SUM(CASE WHEN transaction_type = "BUY" THEN (quantity * cost_basis) ELSE 0 END) AS `cost_basis`') @@ -102,10 +115,10 @@ class Transaction extends Model $holding->save(); // load market data while we're here - $model->refreshMarketData(); + $transaction->refreshMarketData(); // sync dividends to holding - $model->syncDividendsToHolding(); + $transaction->syncDividendsToHolding(); } public function setSymbolAttribute($value) diff --git a/resources/views/components/confirmation-modal.blade.php b/resources/views/components/confirmation-modal.blade.php index 95f7fc6..e466d2e 100644 --- a/resources/views/components/confirmation-modal.blade.php +++ b/resources/views/components/confirmation-modal.blade.php @@ -1,7 +1,7 @@ @props(['id' => null, 'maxWidth' => null]) -
+
diff --git a/resources/views/components/ib-flex-spacer.blade.php b/resources/views/components/ib-flex-spacer.blade.php new file mode 100644 index 0000000..19137d3 --- /dev/null +++ b/resources/views/components/ib-flex-spacer.blade.php @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/resources/views/components/ib-modal.blade.php b/resources/views/components/ib-modal.blade.php new file mode 100644 index 0000000..c656090 --- /dev/null +++ b/resources/views/components/ib-modal.blade.php @@ -0,0 +1,43 @@ +@props([ + 'key' => 'modal', + 'showClose' => true, + 'closeOnEscape' => true, + 'title' => null, + 'subtitle' => null +]) + +
+ +
\ No newline at end of file diff --git a/resources/views/components/partials/side-bar.blade.php b/resources/views/components/partials/side-bar.blade.php index 5f090f7..63cab39 100644 --- a/resources/views/components/partials/side-bar.blade.php +++ b/resources/views/components/partials/side-bar.blade.php @@ -1,23 +1,23 @@ - + @foreach (auth()->user()->portfolios as $portfolio) - + {{ $portfolio->title }} @if($portfolio->wishlist) - + @endif @endforeach - + - - + + {{-- --}} diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 06b3287..1b10fc2 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -1,6 +1,6 @@ - @livewire('portfolio-performance-cards', [ + @livewire('portfolio-performance-chart', [ 'name' => 'dashboard' ]) @@ -47,7 +47,7 @@ {{ $portfolio->title }} @if($portfolio->wishlist) - + @endif @@ -69,7 +69,7 @@ @endif - @if (!$user->portfolios->isEmpty()) + {{-- @if (!$user->portfolios->isEmpty()) @php @@ -81,7 +81,7 @@ @endforeach - @endif + @endif --}} @if (!$user->portfolios->isEmpty()) diff --git a/resources/views/layouts/main-layout.blade.php b/resources/views/layouts/main-layout.blade.php index ee33a4e..bf3daab 100644 --- a/resources/views/layouts/main-layout.blade.php +++ b/resources/views/layouts/main-layout.blade.php @@ -7,14 +7,11 @@ {{ config('app.name', 'Laravel') }} - - @vite(['resources/css/app.css', 'resources/js/app.js']) - @livewireStyles diff --git a/resources/views/livewire/manage-portfolio-form.blade.php b/resources/views/livewire/manage-portfolio-form.blade.php index c16b6ca..73aa67c 100644 --- a/resources/views/livewire/manage-portfolio-form.blade.php +++ b/resources/views/livewire/manage-portfolio-form.blade.php @@ -73,7 +73,7 @@ new class extends Component { $this->portfolio->delete(); - $this->success(__('Portfolio deleted'), redirectTo: "/dashboard"); + $this->success(__('Portfolio deleted'), redirectTo: route('dashboard')); } }; ?> @@ -92,10 +92,11 @@ new class extends Component { @if ($portfolio) @endif diff --git a/resources/views/livewire/manage-transaction-form.blade.php b/resources/views/livewire/manage-transaction-form.blade.php new file mode 100644 index 0000000..690e58f --- /dev/null +++ b/resources/views/livewire/manage-transaction-form.blade.php @@ -0,0 +1,150 @@ +transaction_type) && $this->transaction_type == 'SELL') { + return __('Sale Price'); + } + + return __('Cost Basis'); + } + + // methods + public function mount() + { + if (isset($this->transaction)) { + + $this->symbol = $this->transaction->symbol; + $this->transaction_type = $this->transaction->transaction_type; + $this->date = $this->transaction->date->format('Y-m-d'); + $this->quantity = $this->transaction->quantity; + $this->cost_basis = $this->transaction_type == 'SELL' + ? $this->transaction->sale_price + : $this->transaction->cost_basis; + + } else { + $this->transaction_type = 'BUY'; + $this->date = now()->format('Y-m-d'); + } + } + + public function update() + { + + $this->transaction->update($this->validate()); + // $this->transaction->owner_id = auth()->user()->id; + $this->transaction->save(); + + $this->success(__('Transaction updated'), redirectTo: route('portfolio.show', ['portfolio' => $this->portfolio->id])); + } + + public function save() + { + + $transaction = $this->portfolio->transactions()->create($this->validate()); + $transaction->save(); + + $this->success(__('Transaction created'), redirectTo: route('portfolio.show', ['portfolio' => $this->portfolio->id])); + } + + public function delete() + { + + $this->transaction->delete(); + + $this->success(__('Transaction deleted'), redirectTo: route('portfolio.show', ['portfolio' => $this->portfolio->id])); + } +}; ?> + +
+ + + + + + + + + + + + + + @if ($transaction) + + @endif + + + + + + + + {{ __('Delete Transaction') }} + + + + {{ __('Are you sure you want to delete this transaction?') }} + + + + + {{ __('Cancel') }} + + + + {{ __('Delete Transaction') }} + + + +
\ No newline at end of file diff --git a/resources/views/livewire/portfolio-performance-cards.blade.php b/resources/views/livewire/portfolio-performance-chart.blade.php similarity index 100% rename from resources/views/livewire/portfolio-performance-cards.blade.php rename to resources/views/livewire/portfolio-performance-chart.blade.php diff --git a/resources/views/livewire/transactions-list.blade.php b/resources/views/livewire/transactions-list.blade.php new file mode 100644 index 0000000..d87dca9 --- /dev/null +++ b/resources/views/livewire/transactions-list.blade.php @@ -0,0 +1,66 @@ +editingTransaction = Transaction::findOrFail($transactionId); + $this->dispatch('toggle-manage-transaction'); + } + +}; ?> + +
+ + @foreach($transactions as $transaction) +
+ + + + {{ $transaction->date->format('M j, Y') }} + {{ $transaction->symbol }} + ({{ $transaction->quantity }} + @ {{ Number::currency($transaction->cost_basis) }}) + + + + +
+ @endforeach + + + @livewire('manage-transaction-form', [ + 'portfolio' => $portfolio, + 'transaction' => $editingTransaction, + ], key($editingTransaction->id ?? 'new')) + + +
\ No newline at end of file diff --git a/resources/views/portfolio/create.blade.php b/resources/views/portfolio/create.blade.php index 7d41a0f..87f74c3 100644 --- a/resources/views/portfolio/create.blade.php +++ b/resources/views/portfolio/create.blade.php @@ -1,7 +1,7 @@
- + @livewire('manage-portfolio-form')
diff --git a/resources/views/portfolio/show.blade.php b/resources/views/portfolio/show.blade.php index 2dc3d6a..934fd11 100644 --- a/resources/views/portfolio/show.blade.php +++ b/resources/views/portfolio/show.blade.php @@ -1,11 +1,20 @@
+ + @livewire('manage-transaction-form', [ + 'portfolio' => $portfolio, + ]) + + + - @livewire('manage-portfolio-form', [ 'portfolio' => $portfolio, 'hideCancel' => true @@ -16,7 +25,7 @@ @if($portfolio->wishlist) - + @endif + + + +
+ +
- @livewire('portfolio-performance-cards', [ + @livewire('portfolio-performance-chart', [ 'name' => 'portfolio-'.$portfolio->id, 'portfolio' => $portfolio ]) @@ -64,7 +83,7 @@
- + @php $users = App\Models\User::take(3)->get(); @@ -76,7 +95,7 @@ - + @php $users = App\Models\User::take(3)->get(); @@ -88,7 +107,7 @@ - + {{-- @php $users = App\Models\User::take(3)->get(); @@ -98,21 +117,17 @@ @endforeach - + --}} - - - @php - $users = App\Models\User::take(3)->get(); - @endphp + - @foreach($users as $user) - - @endforeach + @livewire('transactions-list', [ + 'transactions' => $portfolio->transactions, + 'portfolio' => $portfolio + ])
-
\ No newline at end of file diff --git a/resources/views/transaction/index.blade.php b/resources/views/transaction/index.blade.php new file mode 100644 index 0000000..999b12b --- /dev/null +++ b/resources/views/transaction/index.blade.php @@ -0,0 +1,8 @@ + +
+ + + + {{-- @livewire('manage-transaction-form', ['portfolio' => $portfolio]) --}} +
+
diff --git a/routes/web.php b/routes/web.php index 56fdc11..8373108 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,6 +3,7 @@ use Illuminate\Support\Facades\Route; use App\Http\Controllers\DashboardController; use App\Http\Controllers\PortfolioController; +use App\Http\Controllers\TransactionController; Route::get('/', function () { return view('welcome'); @@ -13,7 +14,8 @@ Route::middleware(['auth:sanctum', config('jetstream.auth_session'), 'verified'] Route::get('/dashboard', [DashboardController::class, 'show'])->name('dashboard'); Route::view('/import-export', 'import-export')->name('import-export'); - Route::redirect('/portfolio', '/portfolio/create', 301); Route::get('/portfolio/create', [PortfolioController::class, 'create'])->name('portfolio.create'); Route::get('/portfolio/{portfolio}', [PortfolioController::class, 'show'])->name('portfolio.show'); + + Route::get('/transactions', [TransactionController::class, 'index'])->name('transaction.index'); });