Test Failed
Branch add-core-tests (a060d8)
by Rafael
05:48
created

Users::startFreeTrial()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 27
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 2.0004

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 20
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 27
ccs 20
cts 21
cp 0.9524
crap 2.0004
rs 9.6
1
<?php
2
declare(strict_types=1);
3
4
namespace Canvas\Models;
5
6
use Canvas\Traits\PermissionsTrait;
7
use Canvas\Traits\SubscriptionPlanLimitTrait;
8
use Phalcon\Cashier\Billable;
9
use Canvas\Exception\ServerErrorHttpException;
10
use Exception;
11
use Carbon\Carbon;
12
use Phalcon\Validation;
13
use Phalcon\Validation\Validator\Email;
0 ignored issues
show
Bug introduced by
The type Phalcon\Validation\Validator\Email was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use Phalcon\Validation\Validator\PresenceOf;
0 ignored issues
show
Bug introduced by
The type Phalcon\Validation\Validator\PresenceOf was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use Phalcon\Validation\Validator\Regex;
0 ignored issues
show
Bug introduced by
The type Phalcon\Validation\Validator\Regex was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use Phalcon\Validation\Validator\Uniqueness;
17
use Canvas\Traits\FileSystemModelTrait;
18
use Phalcon\Security\Random;
0 ignored issues
show
Bug introduced by
The type Phalcon\Security\Random was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use Baka\Database\Contracts\HashTableTrait;
20
use Canvas\Contracts\Notifications\NotifiableTrait;
21
use Canvas\Traits\EventManagerAwareTrait;
22
23
/**
24
 * Class Users.
25
 *
26
 * @package Canvas\Models
27
 *
28
 * @property Users $user
29
 * @property Config $config
30
 * @property Apps $app
31
 * @property Companies $defaultCompany
32
 * @property \Phalcon\Di $di
33
 */
34
class Users extends \Baka\Auth\Models\Users
35
{
36
    use PermissionsTrait;
37
    use Billable;
38
    use SubscriptionPlanLimitTrait;
39
    use FileSystemModelTrait;
40
    use HashTableTrait;
41
    use NotifiableTrait;
42
    use EventManagerAwareTrait;
43
44
    /**
45
     * Default Company Branch.
46
     *
47
     * @var integer
48
     */
49
    public $default_company_branch;
50
51
    /**
52
     * Roles id.
53
     *
54
     * @var integer
55
     */
56
    public $roles_id;
57
58
    /**
59
     * Stripe id.
60
     *
61
     * @var string
62
     */
63
    public $stripe_id;
64
65
    /**
66
     * Card last four numbers.
67
     *
68
     * @var integer
69
     */
70
    public $card_last_four;
71
72
    /**
73
     * Card Brand.
74
     *
75
     * @var integer
76
     */
77
    public $card_brand;
78
79
    /**
80
     * Trial end date.
81
     *
82
     * @var string
83
     */
84
    public $trial_ends_at;
85
86
    /**
87
     * Provide the app plan id
88
     * if the user is signing up a new company.
89
     *
90
     * @var integer
91
     */
92
    public $appPlanId = null;
93
94
    /**
95
     * Active subscription id.Not an actual table field, used temporarily.
96
     * @var string
97
     */
98
    public $active_subscription_id;
99
100
    /**
101
     * System Module Id.
102
     * @var integer
103
     */
104
    public $system_modules_id = 2;
105
106
    /**
107
     * User email activation code.
108
     *
109
     * @var string
110
     */
111
    public $user_activation_email;
112
113
    /**
114
     * Initialize method for model.
115
     */
116
    public function initialize()
117
    {
118
        $this->setSource('users');
119
120
        //overwrite parent relationships
121
        $this->hasOne('id', 'Baka\Auth\Models\Sessions', 'users_id', ['alias' => 'session']);
122
        $this->hasMany('id', 'Baka\Auth\Models\Sessions', 'users_id', ['alias' => 'sessions']);
123
        $this->hasMany('id', 'Baka\Auth\Models\SessionKeys', 'users_id', ['alias' => 'sessionKeys']);
124
        $this->hasMany('id', 'Baka\Auth\Models\Banlist', 'users_id', ['alias' => 'bans']);
125
        $this->hasMany('id', 'Baka\Auth\Models\Sessions', 'users_id', ['alias' => 'sessions']);
126
        $this->hasMany('id', 'Canvas\Models\UserConfig', 'users_id', ['alias' => 'config']);
127
        $this->hasMany('id', 'Canvas\Models\UserLinkedSources', 'users_id', ['alias' => 'sources']);
128
        $this->hasOne('default_company', 'Canvas\Models\Companies', 'id', ['alias' => 'defaultCompany']);
129
        $this->hasOne('default_company', 'Canvas\Models\Companies', 'id', ['alias' => 'currentCompany']);
130
131
        $this->hasOne(
132
            'id',
133
            'Canvas\Models\UserRoles',
134
            'users_id',
135
            ['alias' => 'permission']
136
        );
137
138
        $this->hasMany(
139
            'id',
140
            'Canvas\Models\UserRoles',
141
            'users_id',
142
            ['alias' => 'permissions']
143
        );
144
145
        $this->hasManyToMany(
146
            'id',
147
            'Canvas\Models\UserRoles',
148
            'users_id',
149
            'roles_id',
150
            'Canvas\Models\Roles',
151
            'id',
152
            [
153
                'alias' => 'roles',
154
                'params' => [
155
                    'limit' => 1,
156
                    'conditions' => 'Canvas\Models\UserRoles.apps_id = ' . $this->di->getApp()->getId(),
157
                ]
158
            ]
159
        );
160
161
        $this->hasMany(
162
            'id',
163
            'Canvas\Models\Subscription',
164
            'user_id',
165
            [
166
                'alias' => 'allSubscriptions',
167
                'params' => [
168
                    'conditions' => 'apps_id = ?0',
169
                    'bind' => [$this->di->getApp()->getId()],
170
                    'order' => 'id DESC'
171
                ]
172
            ]
173
        );
174
175
        $this->hasMany(
176
            'id',
177
            'Canvas\Models\UsersAssociatedCompanies',
178
            'users_id',
179
            [
180
                'alias' => 'companies',
181
            ]
182
        );
183
184
        $this->hasMany(
185
            'id',
186
            'Canvas\Models\UsersAssociatedApps',
187
            'users_id',
188
            [
189
                'alias' => 'apps',
190
            ]
191
        );
192
193
        $this->hasMany(
194
            'id',
195
            'Canvas\Models\UserWebhooks',
196
            'users_id',
197
            ['alias' => 'userWebhook']
198
        );
199
200
        $systemModule = SystemModules::getSystemModuleByModelName(self::class);
201
        $this->hasOne(
202
            'id',
203
            'Canvas\Models\FileSystemEntities',
204
            'entity_id',
205
            [
206
                'alias' => 'files',
207
                'conditions' => 'system_modules_id = ?0',
208
                'bind' => [$systemModule->getId()]
209
            ]
210
        );
211
212
        $this->hasOne(
213
            'id',
214
            'Canvas\Models\FileSystemEntities',
215
            'entity_id',
216
            [
217
                'alias' => 'photo',
218
                'conditions' => 'system_modules_id = ?0',
219
                'bind' => [$systemModule->getId()]
220
            ]
221
        );
222
    }
223
224
    /**
225
     * Validations and business logic.
226
     */
227 3
    public function validation()
228
    {
229 3
        $validator = new Validation();
230 3
        $validator->add(
231 3
            'email',
232 3
            new Email([
233 3
                'field' => 'email',
234
                'required' => true,
235
            ])
236
        );
237
238 3
        $validator->add(
239 3
            'displayname',
240 3
            new PresenceOf([
241 3
                'field' => 'displayname',
242
                'required' => true,
243
            ])
244
        );
245
246 3
        $validator->add(
247 3
            'displayname',
248 3
            new Regex([
249 3
                'field' => 'displayname',
250 3
                'message' => _('Please use alphanumerics only.'),
251 3
                'pattern' => '/^[A-Za-z0-9_-]{1,32}$/',
252
            ])
253
        );
254
255
        // Unique values
256 3
        $validator->add(
257 3
            'email',
258 3
            new Uniqueness([
259 3
                'field' => 'email',
260 3
                'message' => _('This email already has an account.'),
261
            ])
262
        );
263
264 3
        return $this->validate($validator);
265
    }
266
267
    /**
268
     * Returns table name mapped in the model.
269
     *
270
     * @return string
271
     */
272 8
    public function getSource() : string
273
    {
274 8
        return 'users';
275
    }
276
277
    /**
278
    * Set hashtable settings table, userConfig ;).
279
    *
280
    * @return void
281
    */
282
    private function createSettingsModel(): void
0 ignored issues
show
Unused Code introduced by
The method createSettingsModel() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
283
    {
284
        $this->settingsModel = new UserConfig();
285
    }
286
287
    /**
288
     * Get the User key for redis.
289
     *
290
     * @return string
291
     */
292
    public function getKey() : int
293
    {
294
        return $this->id;
295
    }
296
297
    /**
298
     * A company owner is the first person that register this company
299
     * This only ocurres when signing up the first time, after that all users invites
300
     * come with a default_company id attached.
301
     *
302
     * @return boolean
303
     */
304
    public function isFirstSignup(): bool
305
    {
306
        return empty($this->default_company);
307
    }
308
309
    /**
310
     * Does the user have a role assign to him?
311
     *
312
     * @return boolean
313
     */
314
    public function hasRole(): bool
315
    {
316
        return !empty($this->roles_id);
317
    }
318
319
    /**
320
     * Get all of the subscriptions for the user.
321
     */
322 2
    public function subscriptions()
323
    {
324 2
        $this->hasMany(
325 2
            'id',
326 2
            'Canvas\Models\Subscription',
327 2
            'user_id',
328
            [
329 2
                'alias' => 'subscriptions',
330
                'params' => [
331 2
                    'conditions' => 'apps_id = ?0 and companies_id = ?1',
332 2
                    'bind' => [$this->di->getApp()->getId(), $this->default_company],
333 2
                    'order' => 'id DESC'
334
                ]
335
            ]
336
        );
337
338 2
        return $this->getRelated('subscriptions');
339
    }
340
341
    /**
342
     * Strat a free trial.
343
     *
344
     * @param Users $user
345
     * @return Subscription
346
     */
347 1
    public function startFreeTrial() : Subscription
348
    {
349 1
        $defaultPlan = AppsPlans::getDefaultPlan();
350 1
        $trialEndsAt = Carbon::now()->addDays($this->di->getApp()->plan->free_trial_dates);
351
352 1
        $subscription = new Subscription();
353 1
        $subscription->user_id = $this->getId();
354 1
        $subscription->companies_id = $this->default_company;
355 1
        $subscription->apps_id = $this->di->getApp()->getId();
356 1
        $subscription->apps_plans_id = $this->di->getApp()->default_apps_plan_id;
357 1
        $subscription->name = $defaultPlan->name;
358 1
        $subscription->stripe_id = $defaultPlan->stripe_id;
359 1
        $subscription->stripe_plan = $defaultPlan->stripe_plan;
360 1
        $subscription->quantity = 1;
361 1
        $subscription->trial_ends_at = $trialEndsAt->toDateTimeString();
362 1
        $subscription->trial_ends_days = $trialEndsAt->diffInDays(Carbon::now());
363 1
        $subscription->is_freetrial = 1;
364 1
        $subscription->is_active = 1;
365
366 1
        if (!$subscription->save()) {
367
            throw new ServerErrorHttpException((string)current($this->getMessages()));
368
        }
369
370 1
        $this->trial_ends_at = $subscription->trial_ends_at;
371 1
        $this->update();
372
373 1
        return $subscription;
374
    }
375
376
    /**
377
     * Before create.
378
     *
379
     * @return void
380
     */
381
    public function beforeCreate()
382
    {
383
        parent::beforeCreate();
384
        $random = new Random();
385
        $this->user_activation_email = $random->uuid();
386
387
        //this is only empty when creating a new user
388
        if (!$this->isFirstSignup()) {
389
            //confirm if the app reach its limit
390
            $this->isAtLimit();
391
        }
392
393
        //Assign admin role to the system if we dont get a specify role
394
        if (!$this->hasRole()) {
395
            $role = Roles::getByName('Admins');
396
            $this->roles_id = $role->getId();
397
        }
398
    }
399
400
    /**
401
     * What the current company the users is logged in with
402
     * in this current session?
403
     *
404
     * @return integer
405
     */
406 24
    public function currentCompanyId(): int
407
    {
408 24
        return (int) $this->default_company;
409
    }
410
411
    /**
412
     * What the current company brach the users is logged in with
413
     * in this current session?
414
     *
415
     * @return integer
416
     */
417 1
    public function currentCompanyBranchId(): int
418
    {
419 1
        return (int) $this->default_company_branch;
420
    }
421
422
    /**
423
     * What to do after the creation of a new users
424
     *  - Assign default role.
425
     *
426
     * @return void
427
     */
428
    public function afterCreate()
429
    {
430
        //need to run it here, since we overwirte the default_company id and null this function objective
431
        $isFirstSignup = $this->isFirstSignup();
432
433
        /**
434
         * if we dont find the userdata di lets create it.
435
         * @todo this is not ideal lets fix it later
436
         */
437
        if (!$this->di->has('userData')) {
438
            $this->di->setShared('userData', $this);
439
        }
440
441
        $this->fire('user:afterSignup', $this, $isFirstSignup);
442
443
        //update user activity when its not a empty user
444
        if (!$isFirstSignup) {
445
            $this->updateAppActivityLimit();
446
        }
447
    }
448
449
    /**
450
     * Upload Files.
451
     *
452
     * @todo move this to the baka class
453
     *
454
     * @return void
455
     */
456 3
    public function afterSave()
457
    {
458 3
        $this->associateFileSystem();
459 3
    }
460
461
    /**
462
     * Get the list of all the associated apps this users has.
463
     *
464
     * @return array
465
     */
466
    public function getAssociatedApps(): array
467
    {
468 2
        return array_map(function ($apps) {
469 2
            return $apps['apps_id'];
470 2
        }, $this->getApps(['columns' => 'apps_id', 'group' => 'apps_id'])->toArray());
471
    }
472
473
    /**
474
     * Get an array of the associates companies Ids.
475
     *
476
     * @return array
477
     */
478
    public function getAssociatedCompanies(): array
479
    {
480
        return array_map(function ($company) {
481
            return $company['companies_id'];
482
        }, $this->getCompanies(['columns' => 'companies_id'])->toArray());
483
    }
484
485
    /**
486
     * Get user by key.
487
     * @param string $userActivationEmail
488
     * @return Users
489
     */
490 1
    public static function getByUserActivationEmail(string $userActivationEmail): Users
491
    {
492 1
        return self::findFirst([
493 1
            'conditions' => 'user_activation_email = ?0 and user_active =?1 and is_deleted = 0',
494 1
            'bind' => [$userActivationEmail, 1],
495
        ]);
496
    }
497
498
    /**
499
     * Overwrite the relationship.
500
     *
501
     * @return void
502
     */
503
    public function getPhoto()
504
    {
505
        return $this->getFileByName('photo');
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getFileByName('photo') targeting Canvas\Models\Users::getFileByName() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
506
    }
507
508
    /**
509
     * Update the user current default company.
510
     *
511
     * @param integer $companyId
512
     * @return void
513
     */
514
    public function switchDefaultCompanyByBranch(int $branchId): void
515
    {
516
        if ($branch = CompaniesBranches::findFirst($branchId)) {
517
            if ($branch->company) {
518
                if ($branch->company->userAssociatedToCompany($this)) {
519
                    $this->default_company = $branch->company->getId();
520
                    $this->default_company_branch = $branch->getId();
521
                }
522
            }
523
        }
524
    }
525
}
526