Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
2.46% covered (danger)
2.46%
7 / 284
16.67% covered (danger)
16.67%
7 / 42
CRAP
0.00% covered (danger)
0.00%
0 / 1
User
2.46% covered (danger)
2.46%
7 / 284
16.67% covered (danger)
16.67%
7 / 42
3864.53
0.00% covered (danger)
0.00%
0 / 1
 getRoleAttribute
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setEmailAttribute
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getEmailAttribute
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 sendWelcomeNotification
0.00% covered (danger)
0.00%
0 / 95
0.00% covered (danger)
0.00%
0 / 1
272
 sendPasswordResetNotification
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isPasswordSet
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 linkedSocialAccounts
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 fileMetaData
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setting
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 shortcuts
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 flyshares
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 shortcutsSharedWithUser
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 shortcutsSharedWithOthers
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 flycutUsage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 charts
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 role_play_projects
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 flycutsUsed
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 charactersTyped
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 charactersSaved
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 timeSaved
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getChartData
0.00% covered (danger)
0.00%
0 / 111
0.00% covered (danger)
0.00%
0 / 1
6
 saved_prompts
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 promptUsage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 subscriptionTrials
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 flyMsgAITracking
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getflyMsgAITrackingUsage
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
6
 convertIntegerKeysToWords
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 convertIntegerToWord
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
 convertIntegerToOrdinal
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 hubspot_property
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 sales_pro_team_manager
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 pocs
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 stripe
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 taxRates
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 planTaxRates
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 company_group
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 company
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 invitation
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getNameAttribute
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getInvitationLinkForAdminPortal
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isPOC
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 newFactory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Http\Models\Auth;
4
5use Carbon\Carbon;
6use Stripe\Stripe;
7use Stripe\StripeClient;
8use App\Http\Models\Chart;
9use App\Http\Models\Setting;
10use App\Helpers\FlyMSGLogger;
11use App\Http\Models\FlyShare;
12use App\Http\Models\Shortcut;
13use Laravel\Cashier\Billable;
14use App\Http\Scopes\UserScope;
15use App\Traits\CustomHasRoles;
16use App\Http\Models\Invitation;
17use App\Observers\UserObserver;
18use App\Http\Models\FlyCutUsage;
19use App\Http\Models\UserReferral;
20use App\Http\Models\Admin\Company;
21use Laravel\Passport\HasApiTokens;
22use App\Http\Models\SharesShortcut;
23use App\Notifications\User\Welcome;
24use Illuminate\Support\Facades\Cache;
25use App\Http\Models\HubspotProperties;
26use App\Http\Models\Admin\CompanyGroup;
27use App\Http\Models\SubscriptionTrials;
28use App\Http\Models\SalesProTeamManager;
29use Illuminate\Notifications\Notifiable;
30use App\Http\Models\ClonedSharedShortcut;
31use App\Http\Models\FlyMsgAI\PromptUsage;
32use App\Http\Models\FlyMsgAI\SavedPrompt;
33use App\Notifications\User\ResetPassword;
34use MongoDB\Laravel\Eloquent\SoftDeletes;
35use Mpociot\Teamwork\Traits\UserHasTeams;
36use App\Http\Models\FlyMsgAI\FlyMsgAITracking;
37use Illuminate\Contracts\Auth\MustVerifyEmail;
38use App\Http\Models\Auth\BaseUser as Authenticatable;
39use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
40use Illuminate\Database\Eloquent\Factories\HasFactory;
41use Illuminate\Database\Eloquent\Attributes\ObservedBy;
42use App\Http\Helpers\FileManagement\S3\model\FileMetaData;
43use App\Http\Models\Admin\AdminUserInvitation;
44use App\Http\Models\Admin\CompanyPOC;
45use App\Http\Models\FlyMsgUserDailyUsage;
46use App\Http\Models\RolePlayProjects;
47use database\factories\http\models\auth\UserFactory;
48use Illuminate\Database\Eloquent\Relations\HasOne;
49use MongoDB\BSON\UTCDateTime;
50
51#[ObservedBy([UserObserver::class])]
52class User extends Authenticatable implements MustVerifyEmail, AuthenticatableContract
53{
54    use Billable, CustomHasRoles, Notifiable, HasApiTokens, HasFactory, UserHasTeams, SoftDeletes;
55
56    protected $table = 'users';
57
58    protected $fillable = [
59        'first_name',
60        'last_name',
61        'email',
62        'password',
63        'avatar',
64        'signup_source',
65        'hubspot_id',
66        'verification_code',
67        'created_by',
68        'refered_by',
69        'rewardable',
70        'rewards_level',
71        'referrals_count',
72        'referral_key',
73        'emails',
74        'instancy_id',
75        'sales_pro_manager_id',
76        'company_group_id',
77        'deactivated_at',
78        'status',
79        'activation_date',
80        'temp_password',
81        'temp_password_expiry',
82        'company_id',
83        'invited_to_company_by_admin',
84        'invited_to_company',
85        'move_assign',
86        'email_verified_at',
87        'onboardingv2_step',
88        'onboardingv2_presented',
89        'heap_analytics_id',
90        'roleplayCredits',
91        'totalTimeCredits',
92        'projectCredits'
93    ];
94
95    protected $hidden = [
96        'password',
97        'remember_token',
98    ];
99
100    protected $casts = [
101        'deactivated_at' => 'datetime',
102        'activation_date' => 'datetime',
103    ];
104
105    /**
106     * The relationships that should always be loaded.
107     *
108     * @var array
109     */
110    protected $with = ['company'];
111
112    /**
113     * The accessors to append to the model's array form.
114     *
115     * @var array
116     */
117    protected $appends = ['role'];
118
119    protected function getRoleAttribute(): string
120    {
121        return implode(',', $this->roles());
122    }
123
124    /**
125     * Set the email to lower case whenever a User is created.
126     */
127    public function setEmailAttribute(string $value): void
128    {
129        $this->attributes['email'] = strtolower($value);
130    }
131
132    /**
133     * Get the user's email and transform it to lower case.
134     */
135    public function getEmailAttribute(string $value): string
136    {
137        return strtolower($value);
138    }
139
140    public function sendWelcomeNotification()
141    {
142        // Update user rewards and the invitation status
143        $searchArray = ['email' => $this->email, 'status' => 'Pending'];
144
145        $invitation = Invitation::where($searchArray);
146        $invitation_count = $invitation->count();
147
148        if ($invitation_count > 0) {
149            $invitation = $invitation->oldest()->first();
150            if (!empty($invitation)) {
151                $invitation_user_id = $invitation->user_id;
152                if ($invitation_user_id && $invitation_user_id != '') {
153                    $userSearchArray = ['_id' => $invitation_user_id];
154                    $invitation_user = User::where($userSearchArray)->first();
155
156                    $rewards_level = (isset($invitation_user['rewards_level']) && $invitation_user['rewards_level'] != 4) ? $invitation_user['rewards_level'] + 1 : 1;
157                    $referrals_count = (isset($invitation_user['referrals_count'])) ? $invitation_user['referrals_count'] + 1 : 1;
158
159                    $invitation_user->rewardable = 1;
160                    $invitation_user->rewards_level = $rewards_level;
161                    $invitation_user->referrals_count = $referrals_count;
162                    $invitation_user->save();
163
164                    $whereUserReferrals = ['user_id' => $invitation_user_id];
165                    $referralCollection = UserReferral::where($whereUserReferrals);
166                    $referral_count = $referralCollection->count();
167
168                    if ($referral_count > 0) {
169                        $referralData = $referralCollection->first();
170                        $whereExistingUserRefeerals = ['user_id' => $invitation_user_id];
171                        $invitedUserData = [
172                            'first_name' => $invitation->first_name,
173                            'last_name' => $invitation->last_name,
174                            'email' => $invitation->email,
175                            'created_at' => now(),
176                            'updated_at' => now(),
177                        ];
178
179                        $current_flycutData = json_encode($invitedUserData, true);
180                        $existingReferralData = $referralData->toArray();
181                        $existingReferralData['total_referrals'] = $rewards_level;
182                        // $existingReferralData['total_referrals'] = $referralData['total_referrals'] + 1;
183
184                        $existingReferralData[] = $current_flycutData; // Directly set the values in the array
185                        // $existingReferralData = $this->convertIntegerKeysToWords($existingReferralData);
186                        unset($existingReferralData['_id']);
187                        UserReferral::where($whereExistingUserRefeerals)->update($existingReferralData);
188                    } else {
189                        $userReferral = new UserReferral();
190                        $invitedUserData = [
191                            'first_name' => $invitation->first_name,
192                            'last_name' => $invitation->last_name,
193                            'email' => $invitation->email,
194                            'created_at' => now(),
195                            'updated_at' => now(),
196                        ];
197
198                        $referralData[0] = json_encode($invitedUserData, true);
199                        $referralData['total_referrals'] = 1;
200                        $referralData['user_id'] = $invitation_user_id;
201                        $userReferral->fill($referralData);
202                        $userReferral->push();
203                    }
204
205                    $invitationUpdateData = ['status' => 'Completed'];
206                    Invitation::where($searchArray)->update($invitationUpdateData);
207                }
208            }
209        } elseif (isset($this->refered_by) && $this->refered_by != '') {
210            // Get user id for the referral if user directly signup with the link
211            $refUserArray = ['referral_key' => $this->refered_by];
212            $refuser = User::where($refUserArray)->first();
213
214            // Update user refferal counts and referral level in user documents
215            $rewards_level = (isset($refuser['rewards_level']) && $refuser['rewards_level'] != 4) ? $refuser['rewards_level'] + 1 : 1;
216            $referrals_count = (isset($refuser['referrals_count'])) ? $refuser['referrals_count'] + 1 : 1;
217
218            $refuser->rewardable = 1;
219            $refuser->rewards_level = $rewards_level;
220            $refuser->referrals_count = $referrals_count;
221            $refuser->save();
222
223            $whereUserReferrals = ['user_id' => $refuser->id];
224            $referralCollection = UserReferral::where($whereUserReferrals);
225            $referral_count = $referralCollection->count();
226
227            if ($referral_count > 0) {
228                $referralData = $referralCollection->first();
229                $whereExistingUserRefeerals = ['user_id' => $refuser->id];
230                $invitedUserData = [
231                    'first_name' => $this->first_name,
232                    'last_name' => $this->last_name,
233                    'email' => $this->email,
234                    'created_at' => now(),
235                    'updated_at' => now(),
236                ];
237
238                $current_refData = json_encode($invitedUserData, true);
239                $existingReferralData = $referralData->toArray();
240                $existingReferralData['total_referrals'] = $rewards_level;
241                // $existingReferralData['total_referrals'] = $referralData['total_referrals'] + 1;
242                $existingReferralData[] = $current_refData; // Directly set the values in the array
243                // $existingReferralData = $this->convertIntegerKeysToWords($existingReferralData);
244                unset($existingReferralData['_id']);
245                UserReferral::where($whereExistingUserRefeerals)->update($existingReferralData);
246            } else {
247                $userReferral = new UserReferral();
248                $invitedUserData = [
249                    'first_name' => $this->first_name,
250                    'last_name' => $this->last_name,
251                    'email' => $this->email,
252                    'created_at' => now(),
253                    'updated_at' => now(),
254                ];
255
256                $referralData[0] = json_encode($invitedUserData, true);
257                $referralData['total_referrals'] = 1;
258                $referralData['user_id'] = $refuser->id;
259                $userReferral->fill($referralData);
260                $userReferral->push();
261            }
262        }
263
264        if (Cache::has('welcome_email_sent_' . $this->email)) {
265            return;
266        }
267
268        // Set cache for email sent to avoid any duplication. At lease for an hour.
269        Cache::put('welcome_email_sent_' . $this->email, true, now()->addHour());
270        $this->notify(new Welcome($this));
271    }
272
273    public function sendPasswordResetNotification($token)
274    {
275        $this->notify(new ResetPassword($token));
276    }
277
278    public function isPasswordSet()
279    {
280        return !is_null($this->password);
281    }
282
283    public function linkedSocialAccounts()
284    {
285        return $this->hasMany(LinkedSocialAccount::class);
286    }
287
288    public function fileMetaData()
289    {
290        return $this->hasMany(FileMetaData::class, 'user_id');
291    }
292
293    /**
294     * Get the setting that belongs to the user.
295     */
296    public function setting()
297    {
298        return $this->hasOne(Setting::class);
299    }
300
301    /**
302     * Get the shortcuts that belongs to the user.
303     */
304    public function shortcuts()
305    {
306        return $this->hasMany(Shortcut::class)->withoutGlobalScope(UserScope::class);
307    }
308
309    /**
310     * Get the flyshares that belongs to the user.
311     */
312    public function flyshares()
313    {
314        return $this->hasMany(FlyShare::class);
315    }
316
317    /**
318     * Get the shortcuts that was shared to the user.
319     */
320    public function shortcutsSharedWithUser()
321    {
322        return $this->hasMany(SharesShortcut::class);
323    }
324
325    /**
326     * Get the shortcuts that the user shared with others.
327     */
328    public function shortcutsSharedWithOthers()
329    {
330        return $this->hasMany(ClonedSharedShortcut::class);
331    }
332
333    /**
334     * Get the flycut usage records for the user.
335     */
336    public function flycutUsage()
337    {
338        return $this->hasMany(FlyCutUsage::class);
339    }
340
341    /**
342     * Get the chart records for the user.
343     */
344    public function charts()
345    {
346        return $this->hasMany(Chart::class);
347    }
348
349    /**
350     * Get the role_play_projects records for the user.
351     */
352    public function role_play_projects()
353    {
354        return $this->hasMany(RolePlayProjects::class, 'user_id');
355    }
356
357    /**
358     * Get the total number of flycuts used by the user in a time range.
359     */
360    public function flycutsUsed(string $from): int
361    {
362        $from = Carbon::parse($from);
363        return $this->flycutUsage()->whereNull('feature')->where('created_at', '>=', $from)->count();
364    }
365
366    /**
367     * Get the total number of characters typed by the user in a time range.
368     */
369    public function charactersTyped(string $from): int
370    {
371        $from = Carbon::parse($from);
372        return $this->flycutUsage()->where('created_at', '>=', $from)->sum('characters_typed');
373    }
374
375    /**
376     * Get the total number of characters saved by the user in a time range.
377     */
378    public function charactersSaved(string $from): int
379    {
380        $from = Carbon::parse($from);
381
382        return $this->flycutUsage()->where('created_at', '>=', $from)->sum('characters_saved');
383    }
384
385    /**
386     * Get the total number of time saved by the user in a time range.
387     */
388    public function timeSaved(string $from): float
389    {
390        $from = Carbon::parse($from);
391
392        return $this->flycutUsage()->where('created_at', '>=', $from)->sum('time_saved');
393    }
394
395    /**
396     * Get chart data for user.
397     */
398    public function getChartData(string $from)
399    {
400        $userId = $this->id;
401        $fromDate = Carbon::parse($from);
402        $startDate = new UTCDateTime($fromDate->getTimestamp() * 1000);
403
404        $result = FlyMsgUserDailyUsage::raw(function ($collection) use ($userId, $startDate) {
405            return $collection->aggregate([
406                [
407                    '$match' => [
408                        'user_id' => $userId,
409                        'created_at' => ['$gte' => $startDate]
410                    ]
411                ],
412                [
413                    '$project' => [
414                        'month' => ['$month' => '$created_at'],
415                        'year' => ['$year' => '$created_at'],
416                        'flycut_count' => ['$ifNull' => ['$flycut_count', 0]],
417                        'sentence_rewrite_count' => ['$ifNull' => ['$sentence_rewrite_count', 0]],
418                        'paragraph_rewrite_count' => ['$ifNull' => ['$paragraph_rewrite_count', 0]],
419                        'fly_grammar_actions' => ['$ifNull' => ['$fly_grammar_actions', 0]],
420                        'fly_grammar_accepted' => ['$ifNull' => ['$fly_grammar_accepted', 0]],
421                        'fly_grammar_autocorrect' => ['$ifNull' => ['$fly_grammar_autocorrect', 0]],
422                        'fly_grammar_autocomplete' => ['$ifNull' => ['$fly_grammar_autocomplete', 0]],
423                        'characters_typed' => ['$ifNull' => ['$characters_typed', 0]],
424                        'time_saved' => ['$ifNull' => ['$time_saved', 0]],
425                        'cost_saved' => ['$ifNull' => ['$cost_savings', 0]],
426                    ]
427                ],
428                [
429                    '$project' => [
430                        'month_year' => [
431                            '$concat' => [
432                                [
433                                    '$arrayElemAt' => [
434                                        ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
435                                        '$month'
436                                    ]
437                                ],
438                                ' ',
439                                ['$toString' => '$year']
440                            ]
441                        ],
442                        'flycut_count' => 1,
443                        'sentence_rewrite_count' => 1,
444                        'paragraph_rewrite_count' => 1,
445                        'fly_grammar_actions' => 1,
446                        'fly_grammar_accepted' => 1,
447                        'fly_grammar_autocorrect' => 1,
448                        'fly_grammar_autocomplete' => 1,
449                        'characters_typed' => 1,
450                        'time_saved' => 1,
451                        'cost_saved' => 1,
452                    ]
453                ],
454                [
455                    '$group' => [
456                        '_id' => '$month_year',
457                        'flycut_count' => ['$sum' => '$flycut_count'],
458                        'sentence_rewrite_count' => ['$sum' => '$sentence_rewrite_count'],
459                        'paragraph_rewrite_count' => ['$sum' => '$paragraph_rewrite_count'],
460                        'fly_grammar_actions' => ['$sum' => '$fly_grammar_actions'],
461                        'fly_grammar_accepted' => ['$sum' => '$fly_grammar_accepted'],
462                        'fly_grammar_autocorrect' => ['$sum' => '$fly_grammar_autocorrect'],
463                        'fly_grammar_autocomplete' => ['$sum' => '$fly_grammar_autocomplete'],
464                        'characters_typed' => ['$sum' => '$characters_typed'],
465                        'time_saved' => ['$sum' => '$time_saved'],
466                        'cost_saved' => ['$sum' => '$cost_saved']
467                    ]
468                ],
469                [
470                    '$sort' => ['_id' => 1]
471                ],
472                [
473                    '$project' => [
474                        'month_year' => '$_id',
475                        '_id' => 0,
476                        'flycut_count' => 1,
477                        'sentence_rewrite_count' => 1,
478                        'paragraph_rewrite_count' => 1,
479                        'fly_grammar_actions' => 1,
480                        'fly_grammar_accepted' => 1,
481                        'fly_grammar_autocorrect' => 1,
482                        'fly_grammar_autocomplete' => 1,
483                        'characters_typed' => 1,
484                        'time_saved' => 1,
485                        'cost_saved' => 1
486                    ]
487                ]
488            ]);
489        });
490
491        $months = Carbon::parse($from)->diffInMonths(now());
492        $line_chart_data = [];
493        for ($i = 0; $i <= $months; $i++) {
494            $month = $fromDate->copy()->addMonths($i)->format('M');
495            $month_year = $fromDate->copy()->addMonths($i)->format('M Y');
496
497            $data = $result->firstWhere('month_year', $month_year);
498
499            $line_chart_data[$month_year] = [
500                'flycut_count' => $data->flycut_count ?? 0,
501                'sentence_rewrite_count' => $data->sentence_rewrite_count ?? 0,
502                'paragraph_rewrite_count' => $data->paragraph_rewrite_count ?? 0,
503                'fly_grammar_actions' => $data->fly_grammar_actions ?? 0,
504                'fly_grammar_accepted' => $data->fly_grammar_accepted ?? 0,
505                'fly_grammar_autocorrect' => $data->fly_grammar_autocorrect ?? 0,
506                'fly_grammar_autocomplete' => $data->fly_grammar_autocomplete ?? 0,
507                'characters_typed' => $data->characters_typed ?? 0,
508                'time_saved' => $data->time_saved ?? 0,
509                'cost_saved' => $data->cost_saved ?? 0,
510                'month_year' => $month_year
511            ];
512        }
513
514        return collect($line_chart_data)->sortBy(function ($item) {
515            return Carbon::parse($item['month_year'])->timestamp;
516        }, SORT_REGULAR, true);
517    }
518
519    public function saved_prompts()
520    {
521        return $this->hasMany(SavedPrompt::class);
522    }
523
524    public function promptUsage()
525    {
526        return $this->hasOne(PromptUsage::class);
527    }
528
529    /**
530     * Get the subscription trials records for the user.
531     *
532     * @return \Illuminate\Database\Eloquent\Relations\HasMany
533     */
534    public function subscriptionTrials()
535    {
536        return $this->hasMany(SubscriptionTrials::class);
537    }
538
539    /**
540     * Get the flymsg AI Tracking records for the user.
541     *
542     * @return \Illuminate\Database\Eloquent\Relations\HasMany
543     */
544    public function flyMsgAITracking()
545    {
546        return $this->hasMany(FlyMsgAITracking::class);
547    }
548
549    /**
550     * Get flyMsgAITracking Usage for user.
551     *
552     * @param string $feature
553     *
554     * @return Illuminate\Database\Eloquent\Collection
555     */
556    public function getflyMsgAITrackingUsage($feature)
557    {
558        $usageData = $this->flyMsgAITracking()
559            ->where('feature', $feature)
560            ->where('created_at', '>=', now()->subMonths(7))
561            ->get(['created_at']);
562
563        $groupedData = $usageData->groupBy(function ($record) {
564            return Carbon::parse($record->created_at)->format('M Y');
565        })
566            ->map(function ($group) {
567                return $group->count();
568            });
569
570        $months = collect([]);
571        for ($i = 6; $i >= 0; $i--) {
572            $month = now()->subMonths($i)->format('M Y');
573            $months[$month] = $groupedData[$month] ?? 0;
574        }
575
576        return $months;
577    }
578
579    private function convertIntegerKeysToWords($array)
580    {
581        $result = [];
582
583        foreach ($array as $key => $value) {
584            if (is_int($key)) {
585                $key = $this->convertIntegerToWord($key);
586            }
587
588            $result[$key] = is_array($value) ? $this->convertIntegerKeysToWords($value) : $value;
589        }
590
591        return $result;
592    }
593
594    private function convertIntegerToWord($integer)
595    {
596        $words = [
597            0 => 'first',
598            1 => 'second',
599            2 => 'third',
600            3 => 'fourth',
601            4 => 'fifth',
602            5 => 'sixth',
603            6 => 'seventh',
604            7 => 'eighth',
605            8 => 'ninth',
606            9 => 'tenth',
607        ];
608
609        return isset($words[$integer]) ? $words[$integer] : "nth";
610    }
611
612    private function convertIntegerToOrdinal($integer)
613    {
614        $ordinal = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth'];
615        return isset($ordinal[$integer - 1]) ? $ordinal[$integer - 1] : (string) $integer . 'th';
616    }
617
618    public function hubspot_property()
619    {
620        return $this->hasOne(HubspotProperties::class, 'email', 'email');
621    }
622
623    public function sales_pro_team_manager()
624    {
625        return $this->hasOne(SalesProTeamManager::class, 'user_id');
626    }
627
628    /**
629     * Company point of contact persons
630     *
631     * @return \Illuminate\Database\Eloquent\Relations\HasMany
632     */
633    public function pocs()
634    {
635        return $this->hasMany(CompanyPOC::class);
636    }
637
638    /**
639     * Get the Stripe client instance.
640     *
641     * @return \Stripe\StripeClient
642     */
643    public function stripe()
644    {
645        return new StripeClient(config: [
646            "api_key" => config('cashier.secret')
647        ]);
648    }
649
650    /**
651     * Tax rates a user pays on a subscription
652     *
653     * @return string[] ['tax-rate-id'];
654     */
655    public function taxRates()
656    {
657        return [];
658    }
659
660    /**
661     * Tax rates a user pays on a subscription per plan
662     *
663     * return [
664     *   'plan-id' => ['tax-rate-id'],
665     * ];
666     * @return []
667     */
668    public function planTaxRates()
669    {
670        return [];
671    }
672
673    public function company_group()
674    {
675        return $this->belongsTo(CompanyGroup::class, 'company_group_id');
676    }
677
678    public function company()
679    {
680        return $this->belongsTo(Company::class, 'company_id');
681    }
682
683    public function invitation(): HasOne
684    {
685        return $this->hasOne(AdminUserInvitation::class, 'email', 'email');
686    }
687
688    public function getNameAttribute()
689    {
690        return $this->first_name . " " . $this->last_name;
691    }
692
693    public function getInvitationLinkForAdminPortal()
694    {
695        return config('romeo.frontend-base-url') . "/session/signup?email=" . $this->email;
696    }
697
698    public function isPOC()
699    {
700        return $this->pocs()->exists();
701    }
702
703    protected static function newFactory()
704    {
705        return UserFactory::new();
706    }
707}