improve import / export flow and clean up relationships
This commit is contained in:
hackerESQ
2024-08-28 22:06:47 -05:00
parent e684c1f4a3
commit 69c43dc41f
15 changed files with 104 additions and 74 deletions
+3 -3
View File
@@ -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();
}
/**
+1 -1
View File
@@ -26,7 +26,7 @@ class PortfoliosSheet implements FromCollection, WithHeadings, WithTitle
*/
public function collection()
{
return auth()->user()->portfolios;
return Portfolio::myPortfolios()->get();
}
/**
+5 -11
View File
@@ -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();
}
/**
+6 -9
View File
@@ -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']));
}
}
+6 -5
View File
@@ -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();
}
);
+3 -1
View File
@@ -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
}
}
+14 -12
View File
@@ -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'],
]);
}
}
+1 -1
View File
@@ -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']])
+23 -15
View File
@@ -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;
}
}
+9
View File
@@ -38,6 +38,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()
{
return $this->belongsTo(Portfolio::class);
+3 -2
View File
@@ -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) {
+7
View File
@@ -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]);
+9 -1
View File
@@ -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']);
+1
View File
@@ -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\'
+13 -13
View File
@@ -7,7 +7,7 @@
>
@livewire('manage-transaction-form', [
'portfolio' => $portfolio,
'symbol' => $market_data->symbol,
'symbol' => $holding->market_data->symbol,
])
</x-ib-modal>
@@ -16,7 +16,7 @@
<x-slot:title>
<a href="{{ route('portfolio.show', ['portfolio' => $portfolio->id]) }}" title="{{ __('Portfolio') }}">
{{ $portfolio->title }}
</a> » <span title="{{ __('Holding') }}">{{ $market_data->symbol }}</span>
</a> » <span title="{{ __('Holding') }}">{{ $holding->market_data->symbol }}</span>
</x-slot:title>
<x-ib-flex-spacer />
@@ -35,16 +35,16 @@
<x-ib-card class="md:col-span-5">
<x-slot:title class="pb-2">
{{ $market_data->symbol }}
<span class="text-sm"> {{ $market_data->name }} </span>
{{ $holding->market_data->symbol }}
<span class="text-sm"> {{ $holding->market_data->name }} </span>
</x-slot:title>
<div class="font-bold text-2xl py-1 flex items-center">
{{ Number::currency($market_data->market_value ?? 0) }}
{{ Number::currency($holding->market_data->market_value ?? 0) }}
<x-gain-loss-arrow-badge
:cost-basis="$holding->average_cost_basis"
:market-value="$market_data->market_value"
:market-value="$holding->market_data->market_value"
/>
</div>
@@ -75,7 +75,7 @@
<p class="pt-2 text-sm">
{{ __('Market Data Age') }}:
{{ \Carbon\Carbon::parse($market_data->updated_at)->diffForHumans() }}
{{ \Carbon\Carbon::parse($holding->market_data->updated_at)->diffForHumans() }}
</p>
</x-ib-card>
@@ -84,26 +84,26 @@
<p>
<span class="font-bold">{{ __('Forward PE') }}: </span>
{{ $market_data->forward_pe }}
{{ $holding->market_data->forward_pe }}
</p>
<p>
<span class="font-bold">{{ __('Trailing PE') }}: </span>
{{ $market_data->trailing_pe }}
{{ $holding->market_data->trailing_pe }}
</p>
<p>
<span class="font-bold">{{ __('Market Cap') }}: </span>
${{ Number::forHumans($market_data->market_cap ?? 0) }}
${{ Number::forHumans($holding->market_data->market_cap ?? 0) }}
</p>
<p>
<span class="font-bold">{{ __('52 week') }}: </span>
<x-fifty-two-week-range
:low="$market_data->fifty_two_week_low"
:high="$market_data->fifty_two_week_high"
:current="$market_data->market_value"
:low="$holding->market_data->fifty_two_week_low"
:high="$holding->market_data->fifty_two_week_high"
:current="$holding->market_data->market_value"
/>
</p>