startOfMonth(); $endOfMonth = Carbon::parse($selectedMonth)->endOfMonth(); return Administration::with(['senderUser', 'receiverUser']) ->where('msnv', $user->msnv) ->whereBetween('date', [$startOfMonth, $endOfMonth]) ->orderBy('date', 'desc') ->paginate(10) ->withQueryString(); } public function getOtherActiveUsers(User $currentUser): Collection { return User::where('status', config('constants.STATUS_ACTIVE')) ->where('msnv', '!=', $currentUser->msnv) ->get(); } public function sendThankcards(User $sender, string $receiverMsnv, int $amount): void { if ($sender->flag_send == config('constants.FLAG_SEND_DISABLED')) { throw new \App\Exceptions\ThankCardException(__('messages.error.no_send_permission')); } $receiver = User::where('msnv', $receiverMsnv)->first(); if (!$receiver || $receiver->status != config('constants.STATUS_ACTIVE')) { throw new \App\Exceptions\ThankCardException(__('messages.error.receiver_invalid')); } $startOfMonth = Carbon::now()->startOfMonth(); $endOfMonth = Carbon::now()->endOfMonth(); DB::transaction(function () use ($sender, $receiverMsnv, $amount, $startOfMonth, $endOfMonth) { // Lock sender record to prevent concurrent transaction modifications $lockedSender = User::where('id', $sender->id)->lockForUpdate()->first(); if ($lockedSender->flag_send == config('constants.FLAG_SEND_DISABLED')) { throw new \App\Exceptions\ThankCardException(__('messages.error.cannot_send_card')); } if ($lockedSender->card < $amount) { throw new \App\Exceptions\ThankCardException(__('messages.error.cannot_send_card')); } // Lock & check total sent cards to the receiver this month $cardsSentToThisUserThisMonth = Administration::where('msnv', $lockedSender->msnv) ->where('receiver', $receiverMsnv) ->whereBetween('date', [$startOfMonth, $endOfMonth]) ->lockForUpdate() ->sum('sent'); if ($cardsSentToThisUserThisMonth + $amount > Administration::MAX_SEND_CARD_PER_MONTH) { throw new \App\Exceptions\ThankCardException( __('messages.error.max_send_limit_template', [ 'max' => Administration::MAX_SEND_CARD_PER_MONTH, 'sent' => $cardsSentToThisUserThisMonth ]) ); } Administration::create([ 'msnv' => $receiverMsnv, 'received' => $amount, 'sender' => $lockedSender->msnv, 'sent' => 0, 'receiver' => null, 'date' => Carbon::today(), ]); Administration::create([ 'msnv' => $lockedSender->msnv, 'received' => 0, 'sender' => null, 'sent' => $amount, 'receiver' => $receiverMsnv, 'date' => Carbon::today(), ]); $lockedSender->card -= $amount; if ($lockedSender->card <= 0) { $lockedSender->card = 0; $lockedSender->flag_send = config('constants.FLAG_SEND_DISABLED'); } $lockedSender->save(); // Synchronize variables back to the original model instance $sender->card = $lockedSender->card; $sender->flag_send = $lockedSender->flag_send; }); } public function updatePassword(User $user, string $newPassword): void { $user->pass = md5($newPassword); $user->first_login = config('constants.FIRST_LOGIN_FALSE'); $user->save(); } public function getPersonalStats(User $user, string $selectedMonth): array { $startOfMonth = Carbon::parse($selectedMonth)->startOfMonth(); $endOfMonth = Carbon::parse($selectedMonth)->endOfMonth(); $receivedCount = (int) Administration::where('msnv', $user->msnv) ->whereBetween('date', [$startOfMonth, $endOfMonth]) ->sum('received'); $sentCount = (int) Administration::where('msnv', $user->msnv) ->whereBetween('date', [$startOfMonth, $endOfMonth]) ->sum('sent'); // Calculate current rank (based on received) $rankings = User::where('status', config('constants.STATUS_ACTIVE')) ->addSelect([ 'score' => Administration::selectRaw('COALESCE(SUM(received), 0)') ->whereColumn('msnv', 'user.msnv') ->whereBetween('date', [$startOfMonth, $endOfMonth]) ]) ->get() ->filter(fn($u) => $u->score > 0) ->sortBy([ ['score', 'desc'], ['msnv', 'asc'] ]) ->values(); $currentRank = 0; $prevScore = null; $rank = 1; $denseRank = 0; foreach ($rankings as $item) { $score = (int) $item->score; if ($score !== $prevScore) { $denseRank = $rank; $prevScore = $score; } if ($item->msnv == $user->msnv) { $currentRank = $denseRank; break; } $rank++; } return [ 'receivedCount' => $receivedCount, 'sentCount' => $sentCount, 'currentRank' => $currentRank ]; } public function getRankingList(string $type, string $selectedMonth, User $currentUser): array { $startOfMonth = Carbon::parse($selectedMonth)->startOfMonth(); $endOfMonth = Carbon::parse($selectedMonth)->endOfMonth(); $column = $type === 'sent' ? 'sent' : 'received'; $allActiveUsers = User::where('status', config('constants.STATUS_ACTIVE')) ->addSelect([ 'score' => Administration::selectRaw('COALESCE(SUM(' . $column . '), 0)') ->whereColumn('msnv', 'user.msnv') ->whereBetween('date', [$startOfMonth, $endOfMonth]) ]) ->get(); $rankedUsers = $allActiveUsers->filter(fn($u) => $u->score > 0) ->sortBy([ ['score', 'desc'], ['msnv', 'asc'] ]) ->values(); $prevScore = null; $rank = 1; $denseRank = 0; $rankers = []; $currentUserRankItem = null; foreach ($rankedUsers as $index => $userItem) { $score = (int) $userItem->score; if ($score !== $prevScore) { $denseRank = $rank; $prevScore = $score; } $userItem->rank = $denseRank; $userItem->score = $score; if ($userItem->msnv == $currentUser->msnv) { $currentUserRankItem = $userItem; } // Include every ranked user (no hard limit) $rankers[] = $userItem; $rank++; } if (!$currentUserRankItem) { $currentUserModel = $allActiveUsers->first(fn($u) => $u->msnv == $currentUser->msnv); if ($currentUserModel) { $currentUserRankItem = clone $currentUserModel; $currentUserRankItem->score = 0; $currentUserRankItem->rank = 0; } } return [ 'rankers' => $rankers, 'currentUserRankItem' => $currentUserRankItem ]; } // Retrieve paginated list of received ThankCards for a user public function getReceivedPageData(User $user, string $selectedMonth): array { $start = Carbon::parse($selectedMonth)->startOfMonth(); $end = Carbon::parse($selectedMonth)->endOfMonth(); $cards = Administration::where('msnv', $user->msnv) ->whereBetween('date', [$start, $end]) ->where('received', '>', 0) ->orderBy('date', 'desc') ->get(); return ['cards' => $cards]; } // Retrieve paginated list of sent ThankCards for a user public function getSentPageData(User $user, string $selectedMonth): array { $start = Carbon::parse($selectedMonth)->startOfMonth(); $end = Carbon::parse($selectedMonth)->endOfMonth(); $cards = Administration::where('msnv', $user->msnv) ->whereBetween('date', [$start, $end]) ->where('sent', '>', 0) ->orderBy('date', 'desc') ->get(); return ['cards' => $cards]; } public function updateProfile(User $user, array $data, ?\Illuminate\Http\UploadedFile $avatarFile = null): void { if (isset($data['name'])) { $user->name = $data['name']; } if ($avatarFile) { $path = $avatarFile->store('avatars', 'public'); $user->avatar = 'storage/' . $path; } if (!empty($data['new_password'])) { $user->pass = md5($data['new_password']); $user->first_login = config('constants.FIRST_LOGIN_FALSE'); } $user->save(); } public function getSentCardsToUserThisMonth(User $sender, string $receiverMsnv): int { $startOfMonth = Carbon::now()->startOfMonth(); $endOfMonth = Carbon::now()->endOfMonth(); return (int) Administration::where('msnv', $sender->msnv) ->where('receiver', $receiverMsnv) ->whereBetween('date', [$startOfMonth, $endOfMonth]) ->sum('sent'); } }