diff --git a/app/Exports/Sheets/TransactionsSheet.php b/app/Exports/Sheets/TransactionsSheet.php index afd1f6d..9abd820 100644 --- a/app/Exports/Sheets/TransactionsSheet.php +++ b/app/Exports/Sheets/TransactionsSheet.php @@ -24,6 +24,7 @@ class TransactionsSheet implements FromCollection, WithHeadings, WithTitle 'Cost Basis', 'Sale Price', 'Split', + 'Reinvested Dividend', 'Date', 'Created', 'Updated' diff --git a/app/Models/Dividend.php b/app/Models/Dividend.php index cf40078..1a0b477 100644 --- a/app/Models/Dividend.php +++ b/app/Models/Dividend.php @@ -6,6 +6,7 @@ use App\Models\Holding; use App\Models\MarketData; use App\Models\Transaction; use Illuminate\Support\Str; +use Illuminate\Support\Carbon; use Illuminate\Database\Eloquent\Model; use App\Interfaces\MarketData\MarketDataInterface; use Illuminate\Database\Eloquent\Concerns\HasUuids; @@ -86,10 +87,15 @@ class Dividend extends Model (new self)->insert($dividend_data->toArray()); // sync to holdings - self::syncHoldings($dividend_data); + self::syncHoldings($symbol); + + // get market data + $market_data = MarketData::firstOrNew(['symbol' => $symbol]); + + // re-invest dividends + self::reinvestDividends($dividend_data, $market_data); // sync last dividend amount to market data table - $market_data = MarketData::firstOrNew(['symbol' => $symbol]); $market_data->last_dividend_amount = $dividend_data->sortByDesc('date')->first()['dividend_amount']; $market_data->save(); } @@ -97,10 +103,8 @@ class Dividend extends Model return $dividend_data; } - public static function syncHoldings($dividend_data): void + public static function syncHoldings(string $symbol): void { - $symbol = $dividend_data->last()['symbol']; - // group by holdings $dividends = self::select(['holdings.portfolio_id', 'dividends.date', 'dividends.symbol', 'dividends.dividend_amount']) ->selectRaw(' @@ -115,7 +119,7 @@ class Dividend extends Model ') ->join('transactions', 'transactions.symbol', '=', 'dividends.symbol') ->join('holdings', 'transactions.portfolio_id', '=', 'holdings.portfolio_id') - ->where('dividends.symbol', $dividend_data->last()['symbol']) + ->where('dividends.symbol', $symbol) ->groupBy('holdings.portfolio_id', 'dividends.date', 'dividends.symbol', 'dividends.dividend_amount', 'total_received') ->havingRaw('total_received > 0') ->get(); @@ -130,4 +134,29 @@ class Dividend extends Model ]); }); } + + public static function reinvestDividends(iterable $dividend_data, MarketData $market_data): void + { + // re-invest dividends + Holding::where([ + 'symbol' => $market_data->symbol, + 'reinvest_dividends' => true, + ]) + ->get() + ->each(function($holding) use ($dividend_data, $market_data) { + + foreach($dividend_data as $dividend) { + + Transaction::create([ + 'date' => $dividend['date'], + 'portfolio_id' => $holding->portfolio_id, + 'symbol' => $holding->symbol, + 'transaction_type' => "BUY", + 'reinvested_dividend' => true, + 'cost_basis' => $market_data->market_value, + 'quantity' => ($dividend['dividend_amount'] * $holding->qtyOwned(Carbon::parse($dividend['date']))) / $market_data->market_value, + ]); + } + }); + } } diff --git a/app/Models/Holding.php b/app/Models/Holding.php index 9e22242..c9ed89e 100644 --- a/app/Models/Holding.php +++ b/app/Models/Holding.php @@ -26,11 +26,13 @@ class Holding extends Model 'realized_gain_dollars', 'dividends_earned', 'splits_synced_at', + 'reinvest_dividends' ]; protected $casts = [ 'splits_synced_at' => 'datetime', - 'first_transaction_date' => 'datetime' + 'first_transaction_date' => 'datetime', + 'reinvest_dividends' => 'boolean' ]; protected $attributes = [ @@ -209,6 +211,19 @@ class Holding extends Model $this->save(); } + public function qtyOwned(\Illuminate\Support\Carbon $date = null) + { + if ($date == null) $date = now(); + + $transactions = $this->transactions->where('date', '<=', $date); + + $purchases = $transactions->where('transaction_type', 'BUY')->sum('quantity'); + + $sales = $transactions->where('transaction_type', 'SELL')->sum('quantity'); + + return $purchases - $sales; + } + public function dailyPerformance( \Illuminate\Support\Carbon $start_date = null, \Illuminate\Support\Carbon $end_date = null, diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index d03918f..7faa735 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -23,6 +23,7 @@ class Transaction extends Model 'cost_basis', 'sale_price', 'split', + 'reinvested_dividend' ]; protected $hidden = []; @@ -30,6 +31,7 @@ class Transaction extends Model protected $casts = [ 'date' => 'datetime', 'split' => 'boolean', + 'reinvested_dividend' => 'boolean' ]; protected static function boot() diff --git a/database/migrations/2024_10_18_000001_add_reinvestment_columns.php b/database/migrations/2024_10_18_000001_add_reinvestment_columns.php new file mode 100644 index 0000000..28defcc --- /dev/null +++ b/database/migrations/2024_10_18_000001_add_reinvestment_columns.php @@ -0,0 +1,36 @@ +boolean('reinvest_dividends')->nullable()->after('quantity'); + }); + + Schema::table('transactions', function (Blueprint $table) { + $table->boolean('reinvested_dividend')->nullable()->after('split'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('holdings', function (Blueprint $table) { + $table->dropColumn('reinvest_dividends'); + }); + + Schema::table('transactions', function (Blueprint $table) { + $table->dropColumn('reinvested_dividend'); + }); + } +}; diff --git a/lang/en.json b/lang/en.json index 93ee19a..a42e1a6 100644 --- a/lang/en.json +++ b/lang/en.json @@ -11,11 +11,13 @@ "Log in": "Log in", "Register": "Register", "Create": "Create", + "Update": "Update", "Cancel": "Cancel", "Save": "Save", "Close": "Close", "or": "or", "and": "and", + "Yes": "Yes", "Nothing to show here yet": "Nothing to show here yet", "Hang on! You're doing that too much.": "Hang on! You're doing that too much.", @@ -116,6 +118,11 @@ "Total Market Value": "Total Market Value", "Realized Gain/Loss": "Realized Gain/Loss", "Dividends Earned": "Dividends Earned", + "Dividends": "Dividends", + "Dividend options": "Dividend options", + "Dividend options saved": "Dividend options saved", + "Reinvest dividends": "Reinvest dividends", + "Automatically generate buy transactions for any dividends earned.": "Automatically generate buy transactions for any dividends earned.", "Split": "Split", "Splits": "Splits", "No splits for :symbol yet": "No splits for :symbol yet", diff --git a/lang/es.json b/lang/es.json index 989a22c..375bbeb 100644 --- a/lang/es.json +++ b/lang/es.json @@ -11,11 +11,13 @@ "Log in": "Iniciar sesión", "Register": "Registrarse", "Create": "Crear", + "Update": "Actualizar", "Cancel": "Cancelar", "Save": "Guardar", "Close": "Cerrar", "or": "o", "and": "y", + "Yes": "Sí", "Nothing to show here yet": "No hay nada que mostrar aquí todavía", "Hang on! You're doing that too much.": "¡Por favor espere un momento!", @@ -116,6 +118,11 @@ "Total Market Value": "Valor Total de Mercado", "Realized Gain/Loss": "Ganancia/Pérdida Realizada", "Dividends Earned": "Dividendos Ganados", + "Dividends": "Dividendos", + "Dividend options": "Opciones de dividendos", + "Dividend options saved": "Opciones de dividendos guardadas", + "Reinvest dividends": "Reinvertir dividendos", + "Automatically generate buy transactions for any dividends earned.": "Genere automáticamente transacciones de compra para cualquier dividendo obtenido.", "Split": "Division", "Splits": "Divisiones", "No splits for :symbol yet": "No hay divisiones para :symbol", diff --git a/resources/views/components/ib-card.blade.php b/resources/views/components/ib-card.blade.php index 989c4c2..05c7131 100644 --- a/resources/views/components/ib-card.blade.php +++ b/resources/views/components/ib-card.blade.php @@ -4,7 +4,7 @@ {{ $attributes->merge(['class' => 'bg-slate-100 dark:bg-base-200 rounded-lg']) }} > -