Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 152
0.00% covered (danger)
0.00%
0 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
ProcessUserAsyncJob
0.00% covered (danger)
0.00%
0 / 152
0.00% covered (danger)
0.00%
0 / 13
1980
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 handle
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 backoff
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 created
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 updated
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 performAction
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
56
 updateUserInfoForDailyUsage
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 validateUpdatedFieldsForDailyUsage
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 parseFieldsForDailyUsage
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 createOrUpdateUserInfo
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 validateUpdatedFields
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
2
 parseFields
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 1
182
 formatToCarbonDate
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3namespace App\Jobs;
4
5use App\Helpers\FlyMSGLogger;
6use App\Http\Models\Admin\Company;
7use App\Http\Models\Admin\CompanyGroup;
8use App\Http\Models\Admin\CompanyLicenses;
9use App\Http\Models\Auth\User;
10use App\Http\Models\FlyMsgUserDailyUsage;
11use App\Http\Models\UserInfo;
12use App\Mail\BusinessProEnterpriseMail;
13use App\Services\Email\EmailService;
14use App\Services\UserInfo\SubscriptionService;
15use App\Traits\ObjectMapper;
16use Carbon\Carbon;
17use Illuminate\Bus\Queueable;
18use Illuminate\Contracts\Queue\ShouldQueue;
19use Illuminate\Foundation\Bus\Dispatchable;
20use Illuminate\Queue\InteractsWithQueue;
21use Illuminate\Queue\SerializesModels;
22use MongoDB\BSON\UTCDateTime;
23
24class ProcessUserAsyncJob implements ShouldQueue
25{
26    use Dispatchable, InteractsWithQueue, ObjectMapper, Queueable, SerializesModels;
27
28    public $tries = 5;
29
30    public function __construct(
31        public User $user,
32        public string $action,
33        private readonly EmailService $emailService
34    ) {}
35
36    public function handle(): void
37    {
38        if ($this->action === 'created') {
39            $this->created($this->user);
40        } elseif ($this->action === 'updated') {
41            $this->updated($this->user);
42        }
43    }
44
45    public function backoff()
46    {
47        return [10, 30, 60, 120, 300];
48    }
49
50    private function created(User $user): void
51    {
52        $created = $user->toArray();
53
54        $parsedFields = $this->parseFields($created, $user);
55
56        $this->createOrUpdateUserInfo($parsedFields);
57    }
58
59    private function updated(User $user): void
60    {
61        $updated = $user->toArray();
62
63        if (! empty($updated)) {
64            $fields = $this->validateUpdatedFields($updated);
65
66            if (! empty($fields)) {
67                $parsedFields = $this->parseFields($updated, $user);
68
69                $this->createOrUpdateUserInfo($parsedFields);
70            }
71
72            $dailyFields = $this->validateUpdatedFieldsForDailyUsage($updated);
73
74            if (! empty($dailyFields)) {
75                $parsedFieldsForDailyUsage = $this->parseFieldsForDailyUsage($updated);
76
77                $this->updateUserInfoForDailyUsage($parsedFieldsForDailyUsage, $user);
78            }
79        }
80    }
81
82    private function performAction(User $authUser, User $user, string $action, string $businessProChoice = 'yes_dedicated')
83    {
84        if ($authUser->isAdmin() && filled($user->company)) {
85            try {
86                $companyLicense = CompanyLicenses::where('company_id', $user->company_id)->active()->first();
87
88                if (
89                    $companyLicense &&
90                    $companyLicense->business_pro_enterprise_plus &&
91                    in_array($businessProChoice, $companyLicense->business_pro_enterprise_plus)
92                ) {
93                    $this->emailService->send(
94                        config('romeo.support-email'),
95                        new BusinessProEnterpriseMail($action, $user->company->name, $authUser->email, $user->email),
96                        'admin_user_has_been_added',
97                        true,
98                        null,
99                        true
100                    );
101                }
102            } catch (\Exception $e) {
103                FlyMSGLogger::logError(__METHOD__, $e);
104            }
105        }
106    }
107
108    private function updateUserInfoForDailyUsage(array $data, User $user)
109    {
110        if ($data['hubspot_id'] ?? null) {
111            FlyMsgUserDailyUsage::where('user_id', $user->id)->update(['hubspot_id' => $data['hubspot_id']]);
112        }
113
114        if ($data['group_id'] ?? null) {
115            FlyMsgUserDailyUsage::where('user_id', $user->id)->where('company_id', $user->company_id)->update(['group_id' => $data['group_id']]);
116        }
117
118        $category = $user->company_id ? 'corporate' : 'individuals';
119
120        FlyMsgUserDailyUsage::where('user_id', $user->id)->update(['category' => $category]);
121    }
122
123    private function validateUpdatedFieldsForDailyUsage(array $updated): array
124    {
125        $hubspotFields = [
126            'id',
127            'hubspot_id',
128            'group_id',
129        ];
130
131        return array_filter($updated, fn ($key) => in_array($key, $hubspotFields), ARRAY_FILTER_USE_KEY);
132    }
133
134    private function parseFieldsForDailyUsage(array $fields): array
135    {
136        $map = [
137            'id' => ['rename' => 'user_id'],
138            'company_id' => [],
139            'group_id' => [],
140            'hubspot_id' => [],
141        ];
142
143        return $this->mapObject($fields, $map);
144    }
145
146    private function createOrUpdateUserInfo(array $data)
147    {
148        $userInfo = UserInfo::firstOrNew(['email' => $data['email']]);
149
150        $user = User::find($data['user_id']);
151
152        if (empty($userInfo->subscription_type) && ! empty($user->email_verified_at)) {
153            $subscriptionService = new SubscriptionService;
154            $props = $subscriptionService->initFreemiumSubscription($user->id);
155            $data = array_merge($data, $props);
156        }
157
158        $userInfo->fill($data);
159        $userInfo->save();
160    }
161
162    private function validateUpdatedFields(array $updated): array
163    {
164        $hubspotFields = [
165            'first_name',
166            'last_name',
167            'email',
168            'email_verified_at',
169            'created_at',
170            'updated_at',
171            'id',
172            'signup_source',
173            'company_id',
174            'company_group_id',
175            'stripe_id',
176            'status',
177            'avatar',
178            'hubspot_id',
179            'instancy_id',
180            'group_id',
181            'heap_analytics_id',
182            'is_beta',
183            'developer_mode',
184        ];
185
186        return array_filter($updated, fn ($key) => in_array($key, $hubspotFields), ARRAY_FILTER_USE_KEY);
187    }
188
189    private function parseFields(array $fields, User $user): array
190    {
191        $map = [
192            'first_name' => [],
193            'last_name' => [],
194            'email' => [],
195            'signup_source' => [],
196            'company_id' => [],
197            'hubspot_id' => [],
198            'instancy_id' => [],
199            'avatar' => [],
200            'status' => [],
201            'heap_analytics_id' => [],
202            'is_beta' => [],
203            'developer_mode' => [],
204            'id' => ['rename' => 'user_id'],
205            'company_group_id' => ['rename' => 'group_id'],
206        ];
207
208        $result = $this->mapObject($fields, $map);
209
210        $result['user_id'] = $user->id;
211
212        if (! empty($fields['first_name']) || ! empty($fields['last_name'])) {
213            $result['full_name'] = $user->first_name.' '.$user->last_name;
214        }
215
216        if (! empty($fields['stripe_id'])) {
217            $result['df_stripe_customer_id'] = $fields['stripe_id'];
218            $result['stripe_id'] = $fields['stripe_id'];
219        }
220
221        if (! empty($fields['created_at'])) {
222            $carbonCreationDate = Carbon::parse($fields['created_at']);
223            $result['account_creation_date'] = new UTCDateTime($carbonCreationDate->getTimestamp() * 1000);
224            $result['user_created_at'] = new UTCDateTime($carbonCreationDate->getTimestamp() * 1000);
225        }
226
227        if (! empty($fields['updated_at'])) {
228            $carbonUpdateDate = Carbon::parse($fields['updated_at']);
229            $result['user_updated_at'] = new UTCDateTime($carbonUpdateDate->getTimestamp() * 1000);
230        }
231
232        if (! empty($fields['email_verified_at'])) {
233            $carbonDate = Carbon::createFromTimestamp($fields['email_verified_at']->toDateTime()->getTimestamp());
234            $result['email_verified_at'] = new UTCDateTime($carbonDate->getTimestamp() * 1000);
235        }
236
237        if (! empty($fields['email'])) {
238            $result['email_domain'] = strrchr($user->email, '@');
239            $result['email_domain_count'] = UserInfo::where('email_domain', $result['email_domain'])->count();
240
241            $userInfoDispatcher = UserInfo::getEventDispatcher();
242            UserInfo::unsetEventDispatcher();
243
244            UserInfo::where('email_domain', $result['email_domain'])->update(['email_domain_count' => $result['email_domain_count']]);
245
246            if ($userInfoDispatcher) {
247                UserInfo::setEventDispatcher($userInfoDispatcher);
248            }
249        }
250
251        if (! empty($fields['status'])) {
252            $result['status_date'] = now();
253        }
254
255        if (! empty($fields['company_id'])) {
256            $companyName = Company::find($fields['company_id'])->name;
257            $result['company'] = $companyName;
258            $result['company_name'] = $companyName;
259        } else {
260            $result['company'] = 'Individual';
261            $result['company_name'] = 'Individual';
262        }
263
264        if (! empty($fields['company_group_id'])) {
265            $group = CompanyGroup::find($fields['company_group_id']);
266            $parentGroup = CompanyGroup::where('id', $group->parent_id)->first();
267
268            if ($parentGroup) {
269                $result['group_id'] = $parentGroup->id;
270                $result['group_name'] = $parentGroup->name;
271                $result['subgroup_id'] = $group->id;
272                $result['subgroup_name'] = $group->name;
273            } else {
274                $result['group_id'] = $group->id;
275                $result['group_name'] = $group->name;
276            }
277        } else {
278            $result['group_name'] = null;
279        }
280
281        return $result;
282    }
283
284    private function formatToCarbonDate($value): ?string
285    {
286        if ($value instanceof Carbon) {
287            return $value->toDateString();
288        }
289
290        try {
291            return Carbon::parse($value)->toDateString();
292        } catch (\Exception $e) {
293            try {
294                return Carbon::createFromTimestampMs($value)->toDateString();
295            } catch (\Exception $e) {
296                return null;
297            }
298
299            return null;
300        }
301    }
302}