where('email', $email)->first(); if ($existing) { throw ValidationException::withMessages(['email' => ['A user with this email already exists.']]); } $previous = UserInvitation::query() ->where('organization_id', $organization->getKey()) ->where('email', $email) ->first(); if (! $previous || ! $previous->isUsable()) { $this->seatQuota->assertAvailable($organization->getKey()); } $token = Str::random(64); $invitation = UserInvitation::query()->updateOrCreate( ['organization_id' => $organization->getKey(), 'email' => $email], [ 'role' => $role, 'profile' => $profile, 'token_hash' => hash('sha256', $token), 'expires_at' => now()->addHours(72), 'invited_by' => $actor->getKey(), 'accepted_by' => null, 'accepted_at' => null, 'revoked_at' => null, ], ); Notification::route('mail', $email)->notify(new UserInvited($organization->name, $token)); return $invitation; } public function accept(string $token, string $name, string $password): User { return DB::transaction(function () use ($token, $name, $password) { $invitation = UserInvitation::query() ->where('token_hash', hash('sha256', $token)) ->lockForUpdate() ->first(); if (! $invitation || ! $invitation->isUsable()) { throw ValidationException::withMessages(['token' => ['The invitation is invalid or expired.']]); } $profile = $invitation->profile ?? []; $user = User::query()->create([ 'organization_id' => $invitation->organization_id, 'name' => trim(implode(' ', array_filter([$profile['firstName'] ?? null, $profile['lastName'] ?? null]))) ?: $name, 'first_name' => $profile['firstName'] ?? null, 'last_name' => $profile['lastName'] ?? null, 'department' => $profile['department'] ?? null, 'job_level' => $profile['jobLevel'] ?? null, 'direct_manager_id' => $profile['directManagerId'] ?? null, 'email' => $invitation->email, 'email_verified_at' => now(), 'password' => Hash::make($password), 'role' => $invitation->role, 'status' => AccountStatus::Active, 'locale' => 'fa', 'timezone' => 'Asia/Tehran', ]); if (($profile['teamIds'] ?? []) !== []) { $user->teams()->sync($profile['teamIds']); } $invitation->update(['accepted_by' => $user->getKey(), 'accepted_at' => now()]); return $user; }); } }