Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
62.61% covered (warning)
62.61%
288 / 460
38.10% covered (danger)
38.10%
8 / 21
CRAP
0.00% covered (danger)
0.00%
0 / 1
AdminUsersService
62.61% covered (warning)
62.61%
288 / 460
38.10% covered (danger)
38.10%
8 / 21
1149.05
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUsersPaginated
76.07% covered (warning)
76.07%
89 / 117
0.00% covered (danger)
0.00%
0 / 1
35.27
 deleteUsers
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
6.05
 unassignUsers
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
42
 deleteInvitation
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
5.20
 delete
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
1
 unassign
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
42
 deleteUser
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 unassignUser
25.00% covered (danger)
25.00%
2 / 8
0.00% covered (danger)
0.00%
0 / 1
3.69
 deactivateUsers
40.00% covered (danger)
40.00%
2 / 5
0.00% covered (danger)
0.00%
0 / 1
7.46
 deactivateUser
87.50% covered (warning)
87.50%
21 / 24
0.00% covered (danger)
0.00%
0 / 1
8.12
 deactivateUserBulk
81.82% covered (warning)
81.82%
9 / 11
0.00% covered (danger)
0.00%
0 / 1
7.29
 deactivateInvitedUser
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 deleteInvitedUser
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 moveUsersToGroup
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
4
 moveUserToGroup
25.00% covered (danger)
25.00%
4 / 16
0.00% covered (danger)
0.00%
0 / 1
10.75
 moveInvitationUserToGroup
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 acceptMoveToInvitation
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
90
 acceptUserInvitation
62.20% covered (warning)
62.20%
51 / 82
0.00% covered (danger)
0.00%
0 / 1
66.39
 rejectUserInvitation
53.33% covered (warning)
53.33%
24 / 45
0.00% covered (danger)
0.00%
0 / 1
37.87
 createUserForInvitationLogin
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Http\Services\Admin\Users;
4
5use App\Actions\AccountCenter\Reporting\AccountCenterReporting;
6use App\Actions\Users\CancelUserPlanAction;
7use App\Actions\Users\UpdateUserPlanAction;
8use App\Events\InstancyUserUpdate;
9use App\Events\User\Registered;
10use App\Exceptions\ExpectedException;
11use App\Http\Models\Admin\AdminUserInvitation;
12use App\Http\Models\Admin\Company;
13use App\Http\Models\Admin\CompanyGroup;
14use App\Http\Models\Admin\CompanyLicenses;
15use App\Http\Models\Auth\LinkedSocialAccount;
16use App\Http\Models\Auth\LoginHistory;
17use App\Http\Models\Auth\LogoutHistory;
18use App\Http\Models\Auth\Role;
19use App\Http\Models\Auth\User;
20use App\Http\Models\FlyMsgUserDailyUsage;
21use App\Http\Models\Plans;
22use App\Http\Models\UserInfo;
23use App\Http\Models\UserPasswordReset;
24use App\Http\Repositories\InstancyRepository;
25use App\Http\Repositories\InstancyUserDTO;
26use App\Http\Services\CacheInvalidationService;
27use App\Http\Services\InstancyServiceV2;
28use App\Jobs\Emails\DeactivateUserNotification;
29use App\Services\Email\EmailService;
30use Carbon\Carbon;
31use Illuminate\Support\Facades\Log;
32use Illuminate\Support\Facades\Queue;
33use MongoDB\BSON\UTCDateTime;
34
35class AdminUsersService extends AccountCenterReporting
36{
37    private string $cantDeactivateOwnAccount = 'You cannot deactivate your own account.';
38
39    private string $cantDeleteOwnAccount = 'You cannot delete your own account.';
40
41    private string $cantUnassignOwnAccount = 'You cannot unassign your own account.';
42
43    public function __construct(
44        private CancelUserPlanAction $cancelUserPlanAction,
45        private InstancyRepository $instancyRepository,
46        private readonly UpdateUserPlanAction $updateUserPlanAction,
47        private readonly EmailService $emailService,
48        private readonly CacheInvalidationService $cacheInvalidationService
49    ) {}
50
51    public function getUsersPaginated($filter, $deactivated, $perPage, $page, $categories, $sortBy, $sortOrder, $license_types, $account_status, $extensions)
52    {
53        $account_status = array_filter($account_status);
54        $license_types = array_filter($license_types);
55        $categories = array_filter($categories);
56
57        $usersQuery = UserInfo::select(
58            'user_id',
59            'first_name',
60            'last_name',
61            'full_name',
62            'status',
63            'is_invite',
64            'email',
65            'avatar',
66            'user_created_at',
67            'user_updated_at',
68            'status_date',
69            'company_id',
70            'company_name',
71            'group_id',
72            'group_name',
73            'subgroup_id',
74            'subgroup_name',
75            // 'sales_pro_team_manager',
76            'is_any_extension_installed',
77            'role_names',
78            'plan_name',
79            'created_at'
80        )->where('deleted_at', null);
81
82        if (filled($filter)) {
83            // $escapedFilter = preg_quote($filter, '/');
84            $escapedFilter = preg_quote($filter, '/');
85            $usersQuery = $usersQuery->where(function ($query) use ($escapedFilter) {
86                $query->where('email', 'regex', "/$escapedFilter/i")
87                    ->orWhere('full_name', 'regex', "/$escapedFilter/i")
88                    ->orWhere('user_id', 'regex', "/$escapedFilter/i");
89            });
90        }
91
92        if (count($extensions) > 0) {
93            $string = str_replace('""', 'empty', $extensions[0]);
94            $extensions = array_map('intval', $extensions);
95            if (count($extensions) > 0) {
96                $extensions = array_map('boolval', $extensions);
97                $usersQuery = $usersQuery->whereIn('is_any_extension_installed', $extensions);
98            }
99        }
100
101        $deactivatedCompanies = Company::whereNotNull('deactivated_at')
102            ->pluck('id')
103            ->toArray();
104
105        if (count($account_status) > 0) {
106            $usersQuery = $usersQuery->whereIn('status', $account_status);
107        } elseif ($deactivated) {
108            $usersQuery = $usersQuery->where(function ($query) use ($deactivatedCompanies) {
109                $query->where('status', 'Deactivated')
110                    ->orWhereIn('company_id', $deactivatedCompanies);
111            });
112        } else {
113            $usersQuery = $usersQuery->where(function ($query) use ($deactivatedCompanies) {
114                $query->whereNull('deactivated_at')
115                    ->WhereNotIn('company_id', $deactivatedCompanies);
116            });
117
118            $usersQuery = $usersQuery->whereNotIn('status', ['Deactivated', 'Deleted']);
119        }
120
121        $emails = array_filter($categories, function ($category) {
122            return str_starts_with($category, '@') && $category !== 'all_individuals';
123        });
124
125        $companyIds = array_filter($categories, function ($category) {
126            return ! str_starts_with($category, '@') && $category !== 'all_individuals';
127        });
128        $categoriesQuery = [];
129
130        if (in_array('all_individuals', $categories)) {
131            $categoriesQuery[] = function ($query) {
132                $query->whereNull('company_id')->where('email_domain_count', '>', 1);
133            };
134        } elseif (filled($emails) && count($emails) > 0) {
135            foreach ($emails as $email) {
136                $categoriesQuery[] = function ($query) use ($email) {
137                    $query->where('email_domain', $email)
138                        ->whereNull('company_id');
139                };
140            }
141        }
142
143        if (filled($companyIds) && count($companyIds) > 0) {
144            foreach ($companyIds as $companyId) {
145                $categoriesQuery[] = function ($query) use ($companyId) {
146                    $query->where('company_id', $companyId);
147                };
148            }
149        }
150
151        if (! empty($categoriesQuery)) {
152            $usersQuery = $usersQuery->where(function ($query) use ($categoriesQuery) {
153                foreach ($categoriesQuery as $filterQuery) {
154                    $query->orWhere($filterQuery);
155                }
156            });
157        }
158
159        if (count($license_types) > 0) {
160            $usersQuery = $usersQuery->whereIn('plan_id', $license_types);
161
162            $freemiumId = Plans::where('identifier', 'freemium')->first()?->_id;
163            if (in_array($freemiumId, $license_types)) {
164                $usersQuery = $usersQuery->orWhereNull('plan_id');
165            }
166        }
167
168        $skip = ($page - 1) * $perPage;
169        $take = $perPage;
170        $totalUsers = $usersQuery->count();
171        $currentPage = $skip / $take + 1;
172        $totalPages = ceil($totalUsers / $take);
173
174        if (! $sortBy) {
175            $sortBy = 'created_at';
176        }
177
178        if (! $sortOrder) {
179            $sortOrder = 'desc';
180        }
181
182        $sortOrder = $sortOrder == 'desc' ? -1 : 1;
183
184        $sortPipeline = match (true) {
185            $sortBy == 'name' => [
186                ['column' => 'first_name', 'direction' => $sortOrder == 1 ? 'asc' : 'desc'],
187                ['column' => 'last_name', 'direction' => $sortOrder == 1 ? 'asc' : 'desc'],
188                ['column' => 'created_at', 'direction' => 'desc'],
189            ],
190            default => [
191                ['column' => 'created_at', 'direction' => $sortOrder == 1 ? 'asc' : 'desc'],
192            ],
193        };
194
195        foreach ($sortPipeline as $sort) {
196            $usersQuery->orderBy($sort['column'], $sort['direction']);
197        }
198
199        $users = $usersQuery->skip($skip)
200            ->take($perPage)
201            ->get();
202
203        return [
204            'users' => $users,
205            'total' => $totalUsers,
206            'total_pages' => $totalPages,
207            'current_page' => $currentPage,
208        ];
209    }
210
211    public function deleteUsers(array $userIds, $adminUser, ?string $company_id)
212    {
213        if (isset($userIds) && in_array($adminUser->id, $userIds)) {
214            throw new ExpectedException($this->cantDeleteOwnAccount);
215        }
216
217        $users = User::whereIn('_id', $userIds)->get();
218
219        foreach ($users as $user) {
220            $this->deleteUser($user, $adminUser, $company_id);
221        }
222
223        $invitations = AdminUserInvitation::whereIn('_id', $userIds)->get();
224
225        if (! empty($invitations)) {
226            foreach ($invitations as $invite) {
227                $this->deleteInvitation($invite);
228            }
229        }
230    }
231
232    public function unassignUsers(array $userIds, $adminUser, ?string $company_id)
233    {
234        if (isset($userIds) && in_array($adminUser->id, $userIds)) {
235            throw new ExpectedException($this->cantUnassignOwnAccount);
236        }
237
238        $users = User::whereIn('_id', $userIds)->get();
239
240        foreach ($users as $user) {
241            $this->unassignUser($user, $adminUser, $company_id);
242        }
243
244        $invitations = AdminUserInvitation::whereIn('_id', $userIds)->get();
245
246        if (! empty($invitations)) {
247            foreach ($invitations as $invite) {
248                $this->deleteInvitation($invite);
249            }
250        }
251    }
252
253    public function deleteInvitation(AdminUserInvitation $invitation)
254    {
255        $invitationSubPlan = $invitation->subscription('invitation');
256
257        $plan = (isset($invitationSubPlan->plan)) ? $invitationSubPlan->plan : '';
258
259        if ($invitation->company_id && $plan && $plan->identifier != Plans::FREEMIUM_IDENTIFIER) {
260            $invitationSubPlan->markAsCancelledOnlyInDB();
261        }
262
263        $invitation->delete();
264    }
265
266    public function delete($user)
267    {
268        $user->deleted_at = Carbon::now()->toDateTimeString();
269        $user->status = 'Deleted';
270        $user->save();
271
272        $this->cancelUserPlanAction->execute($user);
273
274        $searchByIdEmail = ['email' => $user->email];
275
276        // force delete for below tables data for the user
277        AdminUserInvitation::where($searchByIdEmail)->forceDelete();
278        LinkedSocialAccount::where($searchByIdEmail)->forceDelete();
279        LoginHistory::where($searchByIdEmail)->forceDelete();
280        LogoutHistory::where($searchByIdEmail)->forceDelete();
281        UserPasswordReset::where($searchByIdEmail)->forceDelete();
282
283        $user->delete();
284
285        // soft delete the users table
286        $user->fill([
287            'first_name' => '',
288            'last_name' => '',
289            'avatar' => '',
290            'email' => "delete_from_flymsg_$user->email",
291            'password' => '',
292            'remember_token' => '',
293            'card_last_four' => '',
294            'card_brand' => '',
295            'team_id' => '',
296            'pm_last_four' => '',
297            'pm_type' => '',
298        ]);
299        $user->save();
300    }
301
302    public function unassign($user)
303    {
304        $userSub = $user->subscription('main');
305        if ($userSub) {
306            $plan = $userSub->plan;
307
308            if (isset($plan->identifier) && $plan->identifier != Plans::FREEMIUM_IDENTIFIER && $userSub->valid()) {
309                if ($user->company_id) {
310                    $userSub->markAsCancelledOnlyInDB();
311                } else {
312                    // Individual subscriptions: cancel with Stripe so billing stops.
313                    $userSub->cancel();
314                }
315            }
316        }
317        $instancyService = new InstancyServiceV2;
318        $instancyService->updateMembership($user->email, now()->toDateString());
319        $user->company_id = '';
320        $user->company_group_id = '';
321        $user->invited_to_company = null;
322        $user->invited_to_company_by_admin = null;
323        $user->status = 'Active';
324        $user->save();
325    }
326
327    public function deleteUser(User $user, $adminUser)
328    {
329        if ($user->id == $adminUser->id) {
330            throw new ExpectedException($this->cantDeleteOwnAccount);
331        }
332
333        Log::info('Deleting user: ', [
334            'user' => $user->email,
335            'admin' => $adminUser->email,
336            'date' => Carbon::now()->toDateTimeString(),
337        ]);
338        $this->delete($user);
339    }
340
341    public function unassignUser(User $user, $adminUser)
342    {
343        if ($user->id == $adminUser->id) {
344            throw new ExpectedException($this->cantUnassignOwnAccount);
345        }
346
347        Log::info('Unassign user: ', [
348            'user' => $user->email,
349            'admin' => $adminUser->email,
350            'date' => Carbon::now()->toDateTimeString(),
351        ]);
352        $this->unassign($user);
353    }
354
355    public function deactivateUsers(array $userIds, string $adminUserId, ?string $company_id)
356    {
357        if (isset($userIds) && in_array($adminUserId, $userIds)) {
358            throw new ExpectedException($this->cantDeactivateOwnAccount);
359        }
360
361        $users = User::whereIn('_id', $userIds)->get();
362
363        foreach ($users as $user) {
364            $this->deactivateUser($user, $adminUserId, $company_id);
365        }
366    }
367
368    public function deactivateUser(User $user, string $adminUserId, ?string $company_id, $cancellation_date = null)
369    {
370        if ($user->id == $adminUserId) {
371            throw new ExpectedException($this->cantDeactivateOwnAccount);
372        }
373
374        $invitation = AdminUserInvitation::where('email', $user->email)->first();
375        if ($invitation) {
376            $invitation->forceDelete();
377        }
378
379        if ($user->status === 'Invited') {
380            $user->company_id = null;
381        }
382
383        $user->status = 'Deactivated';
384        $user->deactivated_at = Carbon::now()->toDateTimeString();
385        if (! empty($cancellation_date)) {
386            $user->deactivated_at = $cancellation_date->toDateTimeString();
387        }
388
389        $user->company_group_id = null;
390        $user->save();
391
392        $admin = auth()->user();
393        $companyName = $admin->company->name;
394        $companyEmail = $admin->email;
395
396        if ($company_id) {
397            $company = Company::find($user->company_id);
398            $companyLicense = CompanyLicenses::where('company_id', $company->id)->first();
399            $community = $companyLicense->business_pro_enterprise_plus ?? [];
400            $isPro = in_array('yes_dedicated', $community) || in_array('yes_community', $community);
401
402            if ($isPro) {
403                DeactivateUserNotification::dispatch($user->email, $companyEmail, $companyName, $this->emailService)->delay(now()->addSeconds(2));
404            }
405        }
406
407        $this->cancelUserPlanAction->execute($user, true, $cancellation_date);
408    }
409
410    public function deactivateUserBulk(array $userIds, string $adminUserId, string $company_id)
411    {
412        if (isset($userIds) && in_array($adminUserId, $userIds)) {
413            throw new ExpectedException('You cannot deactivate your own account.');
414        }
415
416        $users = User::whereIn('_id', $userIds)->get();
417
418        // create the users ids array those available in users table
419        $existingUsers = User::whereIn('_id', $userIds)->pluck('_id')->toArray();
420
421        // users those not found in users table
422        $invitedUsers = array_diff($userIds, $existingUsers);
423
424        foreach ($users as $user) {
425            $this->deactivateUser($user, $adminUserId, empty($company_id) ? $user->company_id : $company_id);
426        }
427
428        // for the invited users those not yet registered
429        if (! empty($invitedUsers)) {
430            $invitedUsers = AdminUserInvitation::whereIn('_id', $invitedUsers)->get();
431            foreach ($invitedUsers as $invitedUser) {
432                $this->deleteInvitedUser($invitedUser);
433            }
434        }
435    }
436
437    public function deactivateInvitedUser(AdminUserInvitation $user, string $adminUserId)
438    {
439        if ($user->id == $adminUserId) {
440            throw new ExpectedException($this->cantDeactivateOwnAccount);
441        }
442
443        $user->status = 'Deactivated';
444        $user->deactivated_at = Carbon::now()->toDateTimeString();
445        $user->company_group_id = null;
446        $user->save();
447    }
448
449    public function deleteInvitedUser(AdminUserInvitation $user)
450    {
451        $user->forceDelete();
452    }
453
454    public function moveUsersToGroup(array $usersId, ?CompanyGroup $group)
455    {
456        $users = User::whereIn('_id', $usersId)->get();
457
458        // create the users ids array those available in users table
459        $existingUsers = User::whereIn('_id', $usersId)->pluck('_id')->toArray();
460
461        // users those not found in users table
462        $invitedUsers = array_diff($usersId, $existingUsers);
463
464        // for main users those are active
465        foreach ($users as $user) {
466            $user = $this->moveUserToGroup($user, $group);
467        }
468
469        // for the invited users those not yet registered
470        if (! empty($invitedUsers)) {
471            $invitedUsers = AdminUserInvitation::whereIn('_id', $invitedUsers)->get();
472            foreach ($invitedUsers as $invitedUser) {
473                $invitedUser = $this->moveInvitationUserToGroup($invitedUser, $group);
474            }
475        }
476
477        return $users;
478    }
479
480    public function moveUserToGroup(User $user, ?CompanyGroup $group)
481    {
482        $user->company_group_id = $group ? $group->id : null;
483        $user->save();
484
485        if ($user->instancy_id) {
486            if ($group) {
487                $newGroup = $group->instancy_id;
488            } else {
489                $newGroup = $user->company->instancy_id;
490            }
491
492            $instancyUser = new InstancyUserDTO(
493                $user->instancy_id,
494                $newGroup,
495                $user->first_name,
496                $user->last_name,
497                $user->email,
498                $user->company->name,
499            );
500
501            // old code
502            // $this->instancyRepository->updateUser($instancyUser);
503            InstancyUserUpdate::dispatch($instancyUser);
504        }
505
506        return $user;
507    }
508
509    public function moveInvitationUserToGroup(AdminUserInvitation $user, ?CompanyGroup $group)
510    {
511        $user->company_group_id = $group ? $group->id : null;
512        $user->save();
513
514        return $user;
515    }
516
517    private function acceptMoveToInvitation(User $user, AdminUserInvitation $invitation)
518    {
519        $user->removeAllRoles();
520        $group_ids = [];
521
522        $planId = $invitation->plan_id;
523        $companyId = $invitation->company_id;
524        $roleName = $invitation->role_name;
525        $groupId = $invitation->company_group_id;
526        $subgroupId = $invitation->company_subgroup_id;
527        $hasCorporatePlan = $invitation->has_corporate_plan;
528        $plan = Plans::find($planId);
529        $companyLicense = CompanyLicenses::where('company_id', $companyId)
530            ->active()
531            ->first();
532
533        if (($roleName == Role::GROUP_ADMIN || $roleName = Role::REPORTING_ADMIN) && ($groupId || $subgroupId)) {
534            $group_ids = [$groupId] ?? [$subgroupId] ?? [];
535        }
536        $user->assignRole($roleName, $group_ids);
537
538        $user->company_id = $companyId;
539        $user->activation_date = Carbon::now()->toDateTimeString();
540        if (filled($groupId)) {
541            $user->company_group_id = $groupId;
542        }
543
544        if (filled($subgroupId)) {
545            $user->company_group_id = $subgroupId;
546        }
547        $user->status = 'Active';
548        $user->unset('invited_to_company');
549        $user->unset('invited_to_company_by_admin');
550
551        FlyMsgUserDailyUsage::where('user_id', $user->id)
552            ->update([
553                'company_id' => $user->company_id,
554                'group_id' => $user->company_group_id,
555                'user_status' => $user->status,
556            ]);
557
558        $this->updateUserPlanAction->execute($user, $plan, $companyLicense->contract_end_date, $hasCorporatePlan);
559
560        if ($invitation) {
561            if ($invitation->reminder_job_id) {
562                Queue::forget($invitation->reminder_job_id);
563                $invitation->reminder_job_id = null;
564                $invitation->save();
565            }
566
567            $invitation->delete();
568        }
569
570        $user->save();
571
572        $user->refresh();
573
574        return $user;
575    }
576
577    public function acceptUserInvitation(User $user, ?string $company_id)
578    {
579        $invitation = AdminUserInvitation::where('email', $user->email)->withTrashed();
580
581        if ($company_id) {
582            $invitation = $invitation->where('company_id', $company_id);
583        }
584
585        $invitation = $invitation->first();
586
587        if ($invitation && $invitation->move_assign) {
588            return $this->acceptMoveToInvitation($user, $invitation);
589        }
590
591        if ($invitation && $invitation->deleted_at && $company_id == $invitation->company_id) {
592            $user->unset('invited_to_company');
593            $user->unset('invited_to_company_by_admin');
594            $user->save();
595
596            FlyMsgUserDailyUsage::where('user_id', $user->id)
597                ->update([
598                    'company_id' => $user->company_id,
599                    'group_id' => $user->company_group_id,
600                    'user_status' => $user->status,
601                ]);
602
603            return $user;
604        }
605
606        // if (!$invitation) {
607        //     throw new ExpectedException("Invitation not found");
608        // }
609
610        if ($invitation) {
611            $plan = Plans::find($invitation->plan_id);
612            if ($company_id && ! $plan) {
613                throw new ExpectedException("The user was invited without a Plan. This shouldn't have happened.");
614            }
615
616            // Change the users plan to the invited one
617            $useSub = $user->subscription('main');
618
619            $changeSubscription = ! filled($useSub) || ($useSub->stripe_plan != $plan->stripe_id);
620
621            if ($changeSubscription) {
622                if ($user->subscribed('main')) {
623                    $companyLicense = CompanyLicenses::where('company_id', $user->company_id)
624                        ->active()
625                        ->first();
626                    if (filled($useSub->ends_at) && ($useSub->ends_at == $companyLicense?->contract_end_date)) {
627                        $useSub->update([
628                            'stripe_status' => 'canceled',
629                            'ends_at' => Carbon::now()->toDateTimeString(),
630                        ]);
631                    } else {
632                        $useSub->cancel();
633                    }
634                } else {
635                    $subscription = $user->subscriptions()->latest()->first();
636                    if ($subscription) {
637                        $subscription->update([
638                            'ends_at' => Carbon::now()->toDateTimeString(),
639                            'stripe_status' => 'canceled',
640                        ]);
641                    }
642                }
643            }
644
645            $license = CompanyLicenses::where('company_id', $user->company_id)
646                ->active()
647                ->first();
648
649            if ($company_id && ! $license) {
650                throw new ExpectedException("Failed to assign $plan. The company doesn't have any active licenses.");
651            }
652
653            if ($company_id && $changeSubscription) {
654                $user->subscriptions()->create([
655                    'name' => 'main',
656                    'stripe_status' => 'active',
657                    'stripe_plan' => $plan->stripe_id,
658                    'quantity' => '1',
659                    'ends_at' => $license->contract_end_date,
660                    'starts_at' => Carbon::now()->startOfDay()->toDateTimeString(),
661                ]);
662                $license->reduceCompanyLicenseCountForNewUsers($plan);
663            }
664        }
665
666        // Change the users status to Active
667        $user->status = 'Active';
668        $user->activation_date = Carbon::now()->toDateTimeString();
669
670        if ($invitation) {
671            if ($invitation->company_group_id) {
672                $user->company_group_id = $invitation->company_group_id;
673            }
674            if ($invitation->company_subgroup_id) {
675                $user->company_group_id = $invitation->company_subgroup_id;
676            }
677        }
678
679        $user->unset('invited_to_company');
680        $user->unset('invited_to_company_by_admin');
681
682        FlyMsgUserDailyUsage::where('user_id', $user->id)
683            ->update([
684                'company_id' => $user->company_id,
685                'group_id' => $user->company_group_id,
686                'user_status' => $user->status,
687            ]);
688
689        $user->save();
690
691        if ($invitation) {
692            if ($invitation->reminder_job_id) {
693                Queue::forget($invitation->reminder_job_id);
694                $invitation->reminder_job_id = null;
695                $invitation->save();
696            }
697
698            $invitation->delete();
699        }
700
701        if (isset($plan) && ! empty($plan)) {
702            $user->plan = $plan;
703        }
704
705        $user->refresh();
706
707        $this->cacheInvalidationService->invalidateUserSettingsCaches((string) $user->id);
708
709        return $user;
710    }
711
712    public function rejectUserInvitation(User $user, ?string $company_id)
713    {
714        $invitation = AdminUserInvitation::where('email', $user->email)->withTrashed();
715
716        if ($company_id) {
717            $invitation = $invitation->where('company_id', $company_id);
718        }
719
720        $invitation = $invitation->first();
721
722        if ($invitation) {
723            $plan = Plans::find($invitation->plan_id);
724
725            if ($plan) {
726                // Change the users plan to the invited one
727                $useSub = $user->subscription('main');
728
729                $changeSubscription = filled($useSub) && ($useSub->stripe_plan == $plan->stripe_id);
730
731                if ($changeSubscription) {
732                    $companyLicense = CompanyLicenses::where('company_id', $user->company_id)
733                        ->active()
734                        ->first();
735
736                    if ($user->subscribed('main')) {
737                        if (filled($useSub->ends_at) && ($useSub->ends_at == $companyLicense?->contract_end_date)) {
738                            $useSub->update([
739                                'stripe_status' => 'canceled',
740                                'ends_at' => Carbon::now()->toDateTimeString(),
741                            ]);
742                        } else {
743                            $useSub->cancel();
744                        }
745                    } else {
746                        $subscription = $user->subscriptions()->latest()->first();
747                        if ($subscription) {
748                            $subscription->update([
749                                'ends_at' => Carbon::now()->toDateTimeString(),
750                                'stripe_status' => 'canceled',
751                            ]);
752                        }
753                    }
754
755                    if ($changeSubscription && $companyLicense) {
756                        $companyLicense->restoreLicenseCountForDeletedUsers($plan);
757                    }
758                }
759            }
760        }
761
762        // Change the users status to Active
763        $user->status = 'Active';
764        $user->unset('activation_date');
765        $user->unset('company_id');
766        $user->unset('company_group_id');
767        $user->unset('invited_to_company');
768        $user->unset('invited_to_company_by_admin');
769        $user->save();
770        // remove roles
771        $user->removeAllRoles();
772
773        if ($invitation) {
774            if ($invitation->reminder_job_id) {
775                Queue::forget($invitation->reminder_job_id);
776                $invitation->reminder_job_id = null;
777                $invitation->save();
778            }
779
780            if (! $invitation->deleted_at) {
781                $invitation->delete();
782            }
783        }
784
785        $this->cacheInvalidationService->invalidateUserSettingsCaches((string) $user->id);
786
787        return $user;
788    }
789
790    public function createUserForInvitationLogin(AdminUserInvitation $invitation)
791    {
792        $user = new User;
793        $user->status = 'Invited';
794        $user->email = $invitation->email;
795        $user->first_name = '';
796        $user->last_name = '';
797        $user->password = $invitation->password ?? $invitation->temp_password;
798        $user->temp_password = $invitation->password ?? $invitation->temp_password;
799        $user->company_id = $invitation->company_id;
800        $user->email_verified_at = new UTCDateTime(now()->getTimestamp() * 1000);
801        $user->onboardingv2_presented = true;
802
803        $user->save();
804
805        $data = [
806            'email' => $user->email,
807            'first_name' => $user->first_name,
808            'last_name' => $user->last_name,
809            'do_not_send_welcome_notification' => true,
810        ];
811
812        Registered::dispatch($user, $data);
813
814        $user = $this->acceptUserInvitation($user, $invitation->company_id);
815
816        return $user;
817    }
818}