Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
67.12% covered (warning)
67.12%
49 / 73
22.22% covered (danger)
22.22%
2 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
UserObserver
67.12% covered (warning)
67.12%
49 / 73
22.22% covered (danger)
22.22%
2 / 9
89.31
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
 applyCorporateRoleplayIfAny
84.62% covered (warning)
84.62%
11 / 13
0.00% covered (danger)
0.00%
0 / 1
6.13
 created
57.14% covered (warning)
57.14%
4 / 7
0.00% covered (danger)
0.00%
0 / 1
5.26
 updated
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
7
 deleted
58.33% covered (warning)
58.33%
7 / 12
0.00% covered (danger)
0.00%
0 / 1
5.16
 restored
76.92% covered (warning)
76.92%
10 / 13
0.00% covered (danger)
0.00%
0 / 1
3.11
 forceDeleted
25.00% covered (danger)
25.00%
1 / 4
0.00% covered (danger)
0.00%
0 / 1
6.80
 performAction
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
56
 canSync
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
3.14
1<?php
2
3namespace App\Observers;
4
5use App\Helpers\FlyMSGLogger;
6use App\Http\Models\Admin\Company;
7use App\Http\Models\Admin\CompanyLicenses;
8use App\Http\Models\Auth\User;
9use App\Http\Services\CacheInvalidationService;
10use App\Http\Services\RoleplayAddonLifecycleService;
11use App\Jobs\ProcessUserAsyncJob;
12use App\Services\Email\EmailService;
13
14class UserObserver
15{
16    public function __construct(
17        private readonly EmailService $emailService,
18        private readonly CacheInvalidationService $cacheInvalidationService,
19        private readonly RoleplayAddonLifecycleService $roleplayAddonLifecycleService,
20    ) {}
21
22    private function applyCorporateRoleplayIfAny(User $user): void
23    {
24        try {
25            if (empty($user->company_id)) {
26                return;
27            }
28            $company = Company::find($user->company_id);
29            if (! $company || empty($company->company_roleplay_addon_id)) {
30                return;
31            }
32            if ($company->roleplay_addon_access === Company::ROLEPLAY_ACCESS_HIDDEN) {
33                return;
34            }
35            $this->roleplayAddonLifecycleService->applyCorporateAddonToUser(
36                $user,
37                $company->company_roleplay_addon_id
38            );
39        } catch (\Throwable $e) {
40            FlyMSGLogger::logError(__METHOD__, $e);
41        }
42    }
43
44    /**
45     * Handle the User "created" event.
46     */
47    public function created(User $user): void
48    {
49        if ($this->canSync($user->id)) {
50            ProcessUserAsyncJob::dispatch($user, 'created', $this->emailService)->onQueue('high');
51        }
52
53        $this->applyCorporateRoleplayIfAny($user);
54
55        if ($authUser = auth()->user()) {
56            $authUserModel = User::find($authUser->id);
57            if ($authUserModel) {
58                $this->performAction($authUserModel, $user, 'added', 'yes_community');
59            }
60        }
61    }
62
63    /**
64     * Handle the User "updated" event.
65     */
66    public function updated(User $user): void
67    {
68        $this->cacheInvalidationService->invalidateUserCache($user->id);
69
70        // Invite-accepted transition: when a previously-invited user becomes
71        // Active (there is no dedicated InviteAccepted event today), make sure
72        // any company corporate roleplay addon gets granted.
73        $changes = $user->getChanges();
74        $original = $user->getOriginal();
75        if (
76            array_key_exists('status', $changes)
77            && strtolower((string) ($changes['status'] ?? '')) === 'active'
78            && strtolower((string) ($original['status'] ?? '')) !== 'active'
79        ) {
80            $this->applyCorporateRoleplayIfAny($user);
81        }
82
83        if ($this->canSync($user->id)) {
84            $dirty = $user->getDirty();
85            if (count($dirty) === 1 && array_key_exists('user_info_id', $dirty)) {
86                return;
87            }
88            ProcessUserAsyncJob::dispatch($user, 'updated', $this->emailService);
89        }
90    }
91
92    /**
93     * Handle the User "deleted" event.
94     */
95    public function deleted(User $user): void
96    {
97        $this->cacheInvalidationService->invalidateUserCache($user->id);
98
99        $user->update([
100            'status' => 'Deactivated',
101            'deactivated_at' => now(),
102        ]);
103
104        // A disabled/deleted user loses their corporate roleplay addon and
105        // falls back to freemium so there is no orphaned active row.
106        try {
107            $this->roleplayAddonLifecycleService->cancelCorporateAddonForUser($user);
108        } catch (\Throwable $e) {
109            FlyMSGLogger::logError(__METHOD__, $e);
110        }
111
112        if ($authUser = auth()->user()) {
113            $authUserModel = User::find($authUser->id);
114            if ($authUserModel) {
115                $this->performAction($authUserModel, $user, 'deactivated');
116            }
117        }
118    }
119
120    /**
121     * Handle the User "restored" event.
122     */
123    public function restored(User $user): void
124    {
125        $this->cacheInvalidationService->invalidateUserCache($user->id);
126
127        $user->update([
128            'status' => 'Active',
129            'activation_date' => now(),
130        ]);
131
132        $user->unset('deleted_at');
133        $user->unset('deactivated_at');
134        $user->save();
135
136        // Reactivating a user re-applies the corporate roleplay addon.
137        $this->applyCorporateRoleplayIfAny($user);
138
139        if ($authUser = auth()->user()) {
140            $authUserModel = User::find($authUser->id);
141            if ($authUserModel) {
142                $this->performAction($authUserModel, $user, 'reactivated');
143            }
144        }
145    }
146
147    /**
148     * Handle the User "force deleted" event.
149     */
150    public function forceDeleted(User $user): void
151    {
152        if ($authUser = auth()->user()) {
153            $authUserModel = User::find($authUser->id);
154            if ($authUserModel) {
155                $this->performAction($authUserModel, $user, 'deleted');
156            }
157        }
158    }
159
160    private function performAction(User $authUser, User $user, string $action, string $businessProChoice = 'yes_dedicated')
161    {
162        if ($authUser->isAdmin() && filled($user->company)) {
163            try {
164                $companyLicense = CompanyLicenses::where('company_id', $user->company_id)->active()->first();
165
166                if (
167                    $companyLicense &&
168                    $companyLicense->business_pro_enterprise_plus &&
169                    in_array($businessProChoice, $companyLicense->business_pro_enterprise_plus)
170                ) {
171                }
172            } catch (\Exception $e) {
173                FlyMSGLogger::logError(__METHOD__, $e);
174            }
175        }
176    }
177
178    private function canSync(string $userId)
179    {
180        $user = User::find($userId);
181
182        if (! $user) {
183            return false;
184        }
185
186        return $user->status !== 'Deactivated' && empty($user->deleted_at);
187    }
188}