isAfter($today)) { return; } $totalDays = (int) $startDate->diffInDays($today); if ($totalDays === 0) { $account->balances()->updateOrCreate( ['balance_date' => $today->toDateString()], ['balance' => $currentBalance], ); return; } $rangeStart = $from ?? $startDate; $rangeEnd = $to ?? $today; $dates = $this->buildDateList($startDate, $today, $rangeStart, $rangeEnd); if (empty($dates)) { return; } $now = now(); $rows = []; foreach ($dates as $date) { $elapsedDays = $startDate->diffInDays($date); $balance = (int) round( $originalAmount + ($currentBalance - $originalAmount) * ($elapsedDays / $totalDays) ); $rows[] = [ 'id' => (string) Str::uuid(), 'account_id' => $account->id, 'balance_date' => $date->toDateString(), 'balance' => $balance, 'created_at' => $now, 'updated_at' => $now, ]; if (count($rows) >= self::UPSERT_CHUNK_SIZE) { $this->upsertBalances($rows); $rows = []; } } $this->upsertBalances($rows); } /** * Upsert a batch of balance rows, keyed by (account_id, balance_date). * * @param list> $rows */ private function upsertBalances(array $rows): void { if ($rows === []) { return; } AccountBalance::upsert($rows, ['account_id', 'balance_date'], ['balance', 'updated_at']); } /** * Build the list of dates for balance generation: * start date, 1st of each intermediate month, and today. * * Only dates within $rangeStart..$rangeEnd are included. * * @return Carbon[] */ private function buildDateList(Carbon $startDate, Carbon $today, Carbon $rangeStart, Carbon $rangeEnd): array { $dates = []; if ($startDate->gte($rangeStart) && $startDate->lte($rangeEnd)) { $dates[] = $startDate->copy(); } $firstOfNextMonth = $startDate->copy()->addMonth()->startOfMonth(); while ($firstOfNextMonth->lte($today)) { if ($firstOfNextMonth->gte($rangeStart) && $firstOfNextMonth->lte($rangeEnd)) { if (! $firstOfNextMonth->isSameDay($today)) { $dates[] = $firstOfNextMonth->copy(); } } $firstOfNextMonth->addMonth(); } if (! $startDate->isSameDay($today) && $today->gte($rangeStart) && $today->lte($rangeEnd)) { $dates[] = $today->copy(); } return $dates; } }