From 69c43dc41f8acf57c653d900035cef1eeae7c850 Mon Sep 17 00:00:00 2001 From: hackerESQ Date: Wed, 28 Aug 2024 22:06:47 -0500 Subject: [PATCH] wip improve import / export flow and clean up relationships --- app/Exports/Sheets/DailyChangesSheet.php | 6 ++-- app/Exports/Sheets/PortfoliosSheet.php | 2 +- app/Exports/Sheets/TransactionsSheet.php | 16 +++------ app/Http/Controllers/HoldingController.php | 15 ++++---- app/Http/Controllers/PortfolioController.php | 11 +++--- app/Imports/BackupImport.php | 4 ++- app/Imports/Sheets/DailyChangesSheet.php | 26 +++++++------- app/Imports/Sheets/PortfoliosSheet.php | 2 +- app/Imports/Sheets/TransactionsSheet.php | 38 ++++++++++++-------- app/Models/DailyChange.php | 9 +++++ app/Models/Holding.php | 5 +-- app/Models/Portfolio.php | 7 ++++ app/Models/Transaction.php | 10 +++++- app/Models/User.php | 1 + resources/views/holding/show.blade.php | 26 +++++++------- 15 files changed, 104 insertions(+), 74 deletions(-) diff --git a/app/Exports/Sheets/DailyChangesSheet.php b/app/Exports/Sheets/DailyChangesSheet.php index 586cfb2..aa8a768 100644 --- a/app/Exports/Sheets/DailyChangesSheet.php +++ b/app/Exports/Sheets/DailyChangesSheet.php @@ -13,10 +13,10 @@ class DailyChangesSheet implements FromCollection, WithHeadings, WithTitle { return [ 'Date', - 'Portfolio', + 'Portfolio ID', 'Total Market Value', 'Total Cost Basis', - 'Total Gain Loss', + 'Total Gain', 'Total Dividends', 'Realized Gains', 'Annotation' @@ -28,7 +28,7 @@ class DailyChangesSheet implements FromCollection, WithHeadings, WithTitle */ public function collection() { - return auth()->user()->daily_changes; + return DailyChange::myDailyChanges()->get(); } /** diff --git a/app/Exports/Sheets/PortfoliosSheet.php b/app/Exports/Sheets/PortfoliosSheet.php index 3116e07..f352a05 100644 --- a/app/Exports/Sheets/PortfoliosSheet.php +++ b/app/Exports/Sheets/PortfoliosSheet.php @@ -26,7 +26,7 @@ class PortfoliosSheet implements FromCollection, WithHeadings, WithTitle */ public function collection() { - return auth()->user()->portfolios; + return Portfolio::myPortfolios()->get(); } /** diff --git a/app/Exports/Sheets/TransactionsSheet.php b/app/Exports/Sheets/TransactionsSheet.php index 13bfd1c..f4f1a25 100644 --- a/app/Exports/Sheets/TransactionsSheet.php +++ b/app/Exports/Sheets/TransactionsSheet.php @@ -2,10 +2,11 @@ namespace App\Exports\Sheets; +use App\Models\Portfolio; use App\Models\Transaction; -use Maatwebsite\Excel\Concerns\FromCollection; -use Maatwebsite\Excel\Concerns\WithHeadings; use Maatwebsite\Excel\Concerns\WithTitle; +use Maatwebsite\Excel\Concerns\WithHeadings; +use Maatwebsite\Excel\Concerns\FromCollection; class TransactionsSheet implements FromCollection, WithHeadings, WithTitle { @@ -22,14 +23,7 @@ class TransactionsSheet implements FromCollection, WithHeadings, WithTitle 'Split', 'Date', 'Created', - 'Updated', - 'Company Name', - 'Portfolio Title', - 'Market Value', - '52 Week Low', - '52 Week High', - 'Market Data Refresh Date', - 'Gain/Loss Dollars' + 'Updated' ]; } @@ -38,7 +32,7 @@ class TransactionsSheet implements FromCollection, WithHeadings, WithTitle */ public function collection() { - return auth()->user()->transactions; + return Transaction::myTransactions()->get(); } /** diff --git a/app/Http/Controllers/HoldingController.php b/app/Http/Controllers/HoldingController.php index ac4a96b..1f71376 100644 --- a/app/Http/Controllers/HoldingController.php +++ b/app/Http/Controllers/HoldingController.php @@ -2,7 +2,6 @@ namespace App\Http\Controllers; -use App\Models\Holding; use App\Models\Portfolio; use Illuminate\Http\Request; @@ -15,14 +14,12 @@ class HoldingController extends Controller public function show(Request $request, Portfolio $portfolio, String $symbol) { + $holding = $portfolio->holdings() + ->with(['market_data']) + ->symbol($symbol) + ->portfolio($portfolio->id) + ->first(); - $holding = Holding::query() - ->portfolio($portfolio->id) - ->symbol($symbol) - ->first(); - - $market_data = $holding->market_data; - - return view('holding.show', compact(['portfolio', 'holding', 'market_data'])); + return view('holding.show', compact(['portfolio', 'holding'])); } } diff --git a/app/Http/Controllers/PortfolioController.php b/app/Http/Controllers/PortfolioController.php index a10dc1f..3c07a56 100644 --- a/app/Http/Controllers/PortfolioController.php +++ b/app/Http/Controllers/PortfolioController.php @@ -22,16 +22,17 @@ class PortfolioController extends Controller */ public function show(Portfolio $portfolio) { + $portfolio->load(['transactions', 'holdings']); + // get portfolio metrics $metrics = cache()->tags(['metrics', 'portfolio', auth()->user()->id, $portfolio->id])->remember( 'portfolio-metrics-' . $portfolio->id, 60, function () use ($portfolio) { - return - Holding::query() - ->portfolio($portfolio->id) - ->getPortfolioMetrics() - ->first(); + return Holding::query() + ->portfolio($portfolio->id) + ->getPortfolioMetrics() + ->first(); } ); diff --git a/app/Imports/BackupImport.php b/app/Imports/BackupImport.php index 2b92723..c722a44 100644 --- a/app/Imports/BackupImport.php +++ b/app/Imports/BackupImport.php @@ -20,8 +20,10 @@ class BackupImport implements WithMultipleSheets { return [ 'Portfolios' => new PortfoliosSheet, - // 'Transactions' => new TransactionsSheet, + 'Transactions' => new TransactionsSheet, // 'Daily Changes' => new DailyChangesSheet, ]; + + // Can listen for AfterSheet to run clean up afterwards } } diff --git a/app/Imports/Sheets/DailyChangesSheet.php b/app/Imports/Sheets/DailyChangesSheet.php index 1760182..e9af765 100644 --- a/app/Imports/Sheets/DailyChangesSheet.php +++ b/app/Imports/Sheets/DailyChangesSheet.php @@ -15,23 +15,25 @@ class DailyChangesSheet implements ToCollection, WithHeadingRow, SkipsEmptyRows public function collection(Collection $dailyChanges) { - foreach ($dailyChanges->sortBy('date') as $row) { - if ($row['user'] != auth()->user()->id) { + foreach ($dailyChanges as $dailyChange) { + + if ($dailyChange['user'] != auth()->user()->id) { + throw new Exception('Can\'t do that.'); } DailyChange::updateOrCreate([ - 'date' => $row['date'], - 'user_id' => $row['user'], + 'date' => $dailyChange['date'], + 'portfolio_id' => $dailyChange['portfolio_id'], ],[ - 'user_id' => $row['user'], - 'date' => $row['date'], - 'total_market_value' => $row['total_market_value'], - 'total_cost_basis' => $row['total_cost_basis'], - 'total_gain' => $row['total_gain'], - 'total_dividends_earned' => $row['total_dividends_earned'], - 'realized_gains' => $row['realized_gains'], - 'notes' => $row['notes'], + 'portfolio_id' => $dailyChange['portfolio_id'], + 'date' => $dailyChange['date'], + 'total_market_value' => $dailyChange['total_market_value'], + 'total_cost_basis' => $dailyChange['total_cost_basis'], + 'total_gain' => $dailyChange['total_gain'], + 'total_dividends' => $dailyChange['total_dividends'], + 'realized_gains' => $dailyChange['realized_gains'], + 'annotation' => $dailyChange['annotation'], ]); } } diff --git a/app/Imports/Sheets/PortfoliosSheet.php b/app/Imports/Sheets/PortfoliosSheet.php index e71d2d5..59b25a2 100644 --- a/app/Imports/Sheets/PortfoliosSheet.php +++ b/app/Imports/Sheets/PortfoliosSheet.php @@ -14,7 +14,7 @@ class PortfoliosSheet implements ToCollection, WithHeadingRow, SkipsEmptyRows public function collection(Collection $portfolios) { - foreach ($portfolios->sortBy('date') as $portfolio) { + foreach ($portfolios as $portfolio) { auth()->user()->portfolios() ->where(['id' => $portfolio['id']]) diff --git a/app/Imports/Sheets/TransactionsSheet.php b/app/Imports/Sheets/TransactionsSheet.php index 646087c..6ad02ff 100644 --- a/app/Imports/Sheets/TransactionsSheet.php +++ b/app/Imports/Sheets/TransactionsSheet.php @@ -7,28 +7,36 @@ use Illuminate\Support\Collection; use Maatwebsite\Excel\Concerns\ToCollection; use Maatwebsite\Excel\Concerns\SkipsEmptyRows; use Maatwebsite\Excel\Concerns\WithHeadingRow; +use Maatwebsite\Excel\Concerns\WithChunkReading; -class TransactionsSheet implements ToCollection, WithHeadingRow, SkipsEmptyRows +class TransactionsSheet implements ToCollection, WithHeadingRow, SkipsEmptyRows, WithChunkReading { // use Importable; public function collection(Collection $transactions) { - foreach ($transactions->sortBy('date') as $row) { + foreach ($transactions as $transaction) { - Transaction::updateOrCreate([ - 'id' => $row['id'], - ],[ - 'id' => $row['id'], - 'symbol' => $row['symbol'], - 'portfolio_id' => $row['portfolio'], - 'transaction_type' => $row['transaction'], - 'quantity' => $row['quantity'], - 'cost_basis' => $row['cost_basis'] ?? 0, - 'sale_price' => $row['sale_price'], - 'split' => $row['split'] ?? null, - 'date' => $row['date'], - ]); + Transaction::where('id', $transaction['transaction_id']) + ->firstOr(function () use ($transaction) { + + return Transaction::make()->forceFill([ + 'id' => $transaction['transaction_id'], + 'symbol' => $transaction['symbol'], + 'portfolio_id' => $transaction['portfolio_id'], + 'transaction_type' => $transaction['transaction_type'], + 'quantity' => $transaction['quantity'], + 'cost_basis' => $transaction['cost_basis'] ?? 0, + 'sale_price' => $transaction['sale_price'], + 'split' => $transaction['split'] ?? null, + 'date' => $transaction['date'], + ])->save(); + }); } } + + public function chunkSize(): int + { + return 500; + } } diff --git a/app/Models/DailyChange.php b/app/Models/DailyChange.php index 80cf328..a97d16e 100644 --- a/app/Models/DailyChange.php +++ b/app/Models/DailyChange.php @@ -37,6 +37,15 @@ class DailyChange extends Model { return $query->where('portfolio_id', $portfolio); } + + public function scopeMyDailyChanges() + { + return $this->whereHas('portfolio', function ($query) { + $query->whereHas('users', function ($query) { + $query->where('id', auth()->id()); + }); + }); + } public function portfolio() { diff --git a/app/Models/Holding.php b/app/Models/Holding.php index 6abd830..0c776cb 100644 --- a/app/Models/Holding.php +++ b/app/Models/Holding.php @@ -56,7 +56,8 @@ class Holding extends Model public function transactions() { return $this->hasMany(Transaction::class, 'symbol', 'symbol') - ->where('transactions.portfolio_id', $this->portfolio_id); + ->where('portfolio_id', $this->portfolio_id) + ->withAggregate('portfolio', 'title'); } /** @@ -141,7 +142,7 @@ class Holding extends Model public function scopeSymbol($query, $symbol) { - return $query->where('symbol', $symbol); + return $query->where('holdings.symbol', $symbol); } public function scopeWithoutWishlists($query) { diff --git a/app/Models/Portfolio.php b/app/Models/Portfolio.php index 41847cb..0c2a145 100644 --- a/app/Models/Portfolio.php +++ b/app/Models/Portfolio.php @@ -62,6 +62,13 @@ class Portfolio extends Model return $this->hasMany(DailyChange::class); } + public function scopeMyPortfolios() + { + return $this->whereHas('users', function ($query) { + $query->where('user_id', auth()->user()->id); + }); + } + public function scopeWithoutWishlists() { return $this->where(['wishlist' => false]); diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index 5fa9dbc..1e5489e 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -90,7 +90,6 @@ class Transaction extends Model public function scopeWithMarketData($query) { $query->withAggregate('market_data', 'name') - ->withAggregate('portfolio', 'title') ->withAggregate('market_data', 'market_value') ->withAggregate('market_data', 'fifty_two_week_low') ->withAggregate('market_data', 'fifty_two_week_high') @@ -108,6 +107,15 @@ class Transaction extends Model return $query->where('symbol', $symbol); } + public function scopeMyTransactions() + { + return $this->whereHas('portfolio', function ($query) { + $query->whereHas('users', function ($query) { + $query->where('id', auth()->id()); + }); + }); + } + public function refreshMarketData() { return MarketData::getMarketData($this->attributes['symbol']); diff --git a/app/Models/User.php b/app/Models/User.php index a6097d1..6a7ae1d 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -72,6 +72,7 @@ class User extends Authenticatable { return $this->hasManyDeep(Transaction::class, ['portfolio_user', Portfolio::class]) ->withMarketData() + ->withAggregate('portfolio', 'title') ->selectRaw(' CASE WHEN transaction_type = \'SELL\' diff --git a/resources/views/holding/show.blade.php b/resources/views/holding/show.blade.php index 4fb10b0..ee28cb8 100644 --- a/resources/views/holding/show.blade.php +++ b/resources/views/holding/show.blade.php @@ -7,7 +7,7 @@ > @livewire('manage-transaction-form', [ 'portfolio' => $portfolio, - 'symbol' => $market_data->symbol, + 'symbol' => $holding->market_data->symbol, ]) @@ -16,7 +16,7 @@ {{ $portfolio->title }} - » {{ $market_data->symbol }} + » {{ $holding->market_data->symbol }} @@ -35,16 +35,16 @@ - {{ $market_data->symbol }} - {{ $market_data->name }} + {{ $holding->market_data->symbol }} + {{ $holding->market_data->name }}
- {{ Number::currency($market_data->market_value ?? 0) }} + {{ Number::currency($holding->market_data->market_value ?? 0) }}
@@ -75,7 +75,7 @@

{{ __('Market Data Age') }}: - {{ \Carbon\Carbon::parse($market_data->updated_at)->diffForHumans() }} + {{ \Carbon\Carbon::parse($holding->market_data->updated_at)->diffForHumans() }}

@@ -84,26 +84,26 @@

{{ __('Forward PE') }}: - {{ $market_data->forward_pe }} + {{ $holding->market_data->forward_pe }}

{{ __('Trailing PE') }}: - {{ $market_data->trailing_pe }} + {{ $holding->market_data->trailing_pe }}

{{ __('Market Cap') }}: - ${{ Number::forHumans($market_data->market_cap ?? 0) }} + ${{ Number::forHumans($holding->market_data->market_cap ?? 0) }}

{{ __('52 week') }}: