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