Test Failed
Pull Request — master (#98)
by Maximo
06:13
created

Users::startFreeTrial()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 27
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 20
nc 2
nop 0
dl 0
loc 27
ccs 0
cts 23
cp 0
crap 6
rs 9.6
c 0
b 0
f 0
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
20
/**
21
 * Class Users.
22
 *
23
 * @package Canvas\Models
24
 *
25
 * @property Users $user
26
 * @property Config $config
27
 * @property Apps $app
28
 * @property Companies $defaultCompany
29
 * @property \Phalcon\Di $di
30
 */
31
class Users extends \Baka\Auth\Models\Users
32
{
33
    use PermissionsTrait;
34
    use Billable;
35
    use SubscriptionPlanLimitTrait;
36
    use FileSystemModelTrait;
37
38
    /**
39
     * Default Company Branch.
40
     *
41
     * @var integer
42
     */
43
    public $default_company_branch;
44
45
    /**
46
     * Roles id.
47
     *
48
     * @var integer
49
     */
50
    public $roles_id;
51
52
    /**
53
     * Stripe id.
54
     *
55
     * @var string
56
     */
57
    public $stripe_id;
58
59
    /**
60
     * Card last four numbers.
61
     *
62
     * @var integer
63
     */
64
    public $card_last_four;
65
66
    /**
67
     * Card Brand.
68
     *
69
     * @var integer
70
     */
71
    public $card_brand;
72
73
    /**
74
     * Trial end date.
75
     *
76
     * @var string
77
     */
78
    public $trial_ends_at;
79
80
    /**
81
     * Provide the app plan id
82
     * if the user is signing up a new company.
83
     *
84
     * @var integer
85
     */
86
    public $appPlanId = null;
87
88
    /**
89
     * Active subscription id.Not an actual table field, used temporarily.
90
     * @var string
91
     */
92
    public $active_subscription_id;
93
94
    /**
95
     * System Module Id.
96
     * @var integer
97
     */
98
    public $system_modules_id = 2;
99
100
    /**
101
     * User email activation code.
102
     *
103
     * @var string
104
     */
105
    public $user_activation_email;
106
107
    /**
108
     * Initialize method for model.
109
     */
110
    public function initialize()
111
    {
112
        $this->setSource('users');
113
114
        //overwrite parent relationships
115
        $this->hasOne('id', 'Baka\Auth\Models\Sessions', 'users_id', ['alias' => 'session']);
116
        $this->hasMany('id', 'Baka\Auth\Models\Sessions', 'users_id', ['alias' => 'sessions']);
117
        $this->hasMany('id', 'Baka\Auth\Models\SessionKeys', 'users_id', ['alias' => 'sessionKeys']);
118
        $this->hasMany('id', 'Baka\Auth\Models\Banlist', 'users_id', ['alias' => 'bans']);
119
        $this->hasMany('id', 'Baka\Auth\Models\Sessions', 'users_id', ['alias' => 'sessions']);
120
        $this->hasMany('id', 'Canvas\Models\UserConfig', 'users_id', ['alias' => 'config']);
121
        $this->hasMany('id', 'Canvas\Models\UserLinkedSources', 'users_id', ['alias' => 'sources']);
122
        $this->hasOne('default_company', 'Canvas\Models\Companies', 'id', ['alias' => 'defaultCompany']);
123
        $this->hasOne('default_company', 'Canvas\Models\Companies', 'id', ['alias' => 'currentCompany']);
124
125
        $this->hasOne(
126
            'id',
127
            'Canvas\Models\UserRoles',
128
            'users_id',
129
            ['alias' => 'permission']
130
        );
131
132
        $this->hasMany(
133
            'id',
134
            'Canvas\Models\UserRoles',
135
            'users_id',
136
            ['alias' => 'permissions']
137
        );
138
139
        $this->hasManyToMany(
140
            'id',
141
            'Canvas\Models\UserRoles',
142
            'users_id',
143
            'roles_id',
144
            'Canvas\Models\Roles',
145
            'id',
146
            [
147
                'alias' => 'roles',
148
                'params' => [
149
                    'limit' => 1,
150
                    'conditions' => 'Canvas\Models\UserRoles.apps_id = ' . $this->di->getApp()->getId(),
151
                ]
152
            ]
153
        );
154
155
        $this->hasMany(
156
            'id',
157
            'Canvas\Models\Subscription',
158
            'user_id',
159
            [
160
                'alias' => 'allSubscriptions',
161
                'params' => [
162
                    'conditions' => 'apps_id = ?0',
163
                    'bind' => [$this->di->getApp()->getId()],
164
                    'order' => 'id DESC'
165
                ]
166
            ]
167
        );
168
169
        $this->hasMany(
170
            'id',
171
            'Canvas\Models\UsersAssociatedCompanies',
172
            'users_id',
173
            [
174
                'alias' => 'companies',
175
            ]
176
        );
177
178
        $this->hasMany(
179
            'id',
180
            'Canvas\Models\UsersAssociatedApps',
181
            'users_id',
182
            [
183
                'alias' => 'apps',
184
            ]
185
        );
186
187
        $this->hasMany(
188
            'id',
189
            'Canvas\Models\UserWebhooks',
190
            'users_id',
191
            ['alias' => 'userWebhook']
192
        );
193
194
        $systemModule = SystemModules::getSystemModuleByModelName(self::class);
195
        $this->hasOne(
196
            'id',
197
            'Canvas\Models\FileSystemEntities',
198
            'entity_id',
199
            [
200
                'alias' => 'filesystem',
201
                'conditions' => 'system_modules_id = ?0',
202
                'bind' => [$systemModule->getId()]
203
            ]
204
        );
205
206
        $this->hasOne(
207
            'id',
208
            'Canvas\Models\FileSystemEntities',
209
            'entity_id',
210
            [
211
                'alias' => 'avatar',
212
                'conditions' => 'system_modules_id = ?0',
213
                'bind' => [$systemModule->getId()]
214
            ]
215
        );
216
    }
217
218
    /**
219
     * Validations and business logic.
220
     */
221
    public function validation()
222
    {
223
        $validator = new Validation();
224
        $validator->add(
225
            'email',
226
            new Email([
227
                'field' => 'email',
228
                'required' => true,
229
            ])
230
        );
231
232
        $validator->add(
233
            'displayname',
234
            new PresenceOf([
235
                'field' => 'displayname',
236
                'required' => true,
237
            ])
238
        );
239
240
        $validator->add(
241
            'displayname',
242
            new Regex([
243
                'field' => 'displayname',
244
                'message' => _('Please use alphanumerics only.'),
245
                'pattern' => '/^[A-Za-z0-9_-]{1,32}$/',
246
            ])
247
        );
248
249
        // Unique values
250
        $validator->add(
251
            'email',
252
            new Uniqueness([
253
                'field' => 'email',
254
                'message' => _('This email already has an account.'),
255
            ])
256
        );
257
258
        return $this->validate($validator);
259
    }
260
261
    /**
262
     * Returns table name mapped in the model.
263
     *
264
     * @return string
265
     */
266
    public function getSource() : string
267
    {
268
        return 'users';
269
    }
270
271
    /**
272
     * Get the User key for redis.
273
     *
274
     * @return string
275
     */
276
    public function getKey() : string
277
    {
278
        return $this->id;
279
    }
280
281
    /**
282
     * A company owner is the first person that register this company
283
     * This only ocurres when signing up the first time, after that all users invites
284
     * come with a default_company id attached.
285
     *
286
     * @return boolean
287
     */
288
    public function isFirstSignup(): bool
289
    {
290
        return empty($this->default_company) ? true : false;
291
    }
292
293
    /**
294
     * Does the user have a role assign to him?
295
     *
296
     * @return boolean
297
     */
298
    public function hasRole(): bool
299
    {
300
        return !empty($this->roles_id) ? true : false;
301
    }
302
303
    /**
304
     * Get all of the subscriptions for the user.
305
     */
306
    public function subscriptions()
307
    {
308
        $this->hasMany(
309
            'id',
310
            'Canvas\Models\Subscription',
311
            'user_id',
312
            [
313
                'alias' => 'subscriptions',
314
                'params' => [
315
                    'conditions' => 'apps_id = ?0 and companies_id = ?1',
316
                    'bind' => [$this->di->getApp()->getId(), $this->default_company],
317
                    'order' => 'id DESC'
318
                ]
319
            ]
320
        );
321
322
        return $this->getRelated('subscriptions');
323
    }
324
325
    /**
326
     * Strat a free trial.
327
     *
328
     * @param Users $user
329
     * @return Subscription
330
     */
331
    public function startFreeTrial() : Subscription
332
    {
333
        $defaultPlan = AppsPlans::getDefaultPlan();
334
        $trialEndsAt = Carbon::now()->addDays($this->di->getApp()->plan->free_trial_dates);
335
336
        $subscription = new Subscription();
337
        $subscription->user_id = $this->getId();
338
        $subscription->companies_id = $this->default_company;
339
        $subscription->apps_id = $this->di->getApp()->getId();
340
        $subscription->apps_plans_id = $this->di->getApp()->default_apps_plan_id;
341
        $subscription->name = $defaultPlan->name;
342
        $subscription->stripe_id = $defaultPlan->stripe_id;
343
        $subscription->stripe_plan = $defaultPlan->stripe_plan;
344
        $subscription->quantity = 1;
345
        $subscription->trial_ends_at = $trialEndsAt->toDateTimeString();
346
        $subscription->trial_ends_days = $trialEndsAt->diffInDays(Carbon::now());
347
        $subscription->is_freetrial = 1;
348
        $subscription->is_active = 1;
349
350
        if (!$subscription->save()) {
351
            throw new ServerErrorHttpException((string)current($this->getMessages()));
352
        }
353
354
        $this->trial_ends_at = $subscription->trial_ends_at;
355
        $this->update();
356
357
        return $subscription;
358
    }
359
360
    /**
361
     * Before create.
362
     *
363
     * @return void
364
     */
365
    public function beforeCreate()
366
    {
367
        parent::beforeCreate();
368
        $random = new Random();
369
        $this->user_activation_email = $random->uuid();
370
371
        //this is only empty when creating a new user
372
        if (!$this->isFirstSignup()) {
373
            //confirm if the app reach its limit
374
            $this->isAtLimit();
375
        }
376
377
        //Assign admin role to the system if we dont get a specify role
378
        if (!$this->hasRole()) {
379
            $role = Roles::findFirstByName('Admins');
380
            $this->roles_id = $role->getId();
381
        }
382
    }
383
384
    /**
385
     * What the current company the users is logged in with
386
     * in this current session?
387
     *
388
     * @return integer
389
     */
390
    public function currentCompanyId(): int
391
    {
392
        return (int) $this->default_company;
393
    }
394
395
    /**
396
     * What the current company brach the users is logged in with
397
     * in this current session?
398
     *
399
     * @return integer
400
     */
401
    public function currentCompanyBranchId(): int
402
    {
403
        return (int) $this->default_company_branch;
404
    }
405
406
    /**
407
     * What to do after the creation of a new users
408
     *  - Assign default role.
409
     *
410
     * @return void
411
     */
412
    public function afterCreate()
413
    {
414
        //need to run it here, since we overwirte the default_company id and null this function objective
415
        $isFirstSignup = $this->isFirstSignup();
416
417
        /**
418
         * if we dont find the userdata di lets create it.
419
         * @todo this is not ideal lets fix it later
420
         */
421
        if (!$this->di->has('userData')) {
422
            $this->di->setShared('userData', $this);
423
        }
424
425
        /**
426
         * User signing up for a new app / plan
427
         * How do we know? well he doesnt have a default_company.
428
         */
429
        if ($isFirstSignup) {
430
            $company = new Companies();
431
            $company->name = $this->defaultCompanyName;
432
            $company->users_id = $this->getId();
433
434
            if (!$company->save()) {
435
                throw new Exception((string) current($company->getMessages()));
436
            }
437
438
            $this->default_company = $company->getId();
439
440
            if (!$this->update()) {
441
                throw new ServerErrorHttpException((string) current($this->getMessages()));
442
            }
443
444
            $this->stripe_id = $company->getPaymentGatewayCustomerId();
445
            $this->default_company_branch = $this->defaultCompany->branch->getId();
446
            $this->update();
447
448
        //update default subscription free trial
449
            //$company->app->subscriptions_id = $this->startFreeTrial()->getId();
450
            //$company->update();
451
        } else {
452
            //we have the company id
453
            if (empty($this->default_company_branch)) {
454
                $this->default_company_branch = $this->defaultCompany->branch->getId();
455
            }
456
        }
457
458
        //Create new company associated company
459
        $this->defaultCompany->associate($this, $this->defaultCompany);
460
461
        //Insert record into user_roles
462
        $userRole = new UserRoles();
463
        $userRole->users_id = $this->id;
464
        $userRole->roles_id = $this->roles_id;
465
        $userRole->apps_id = $this->di->getApp()->getId();
466
        $userRole->companies_id = $this->default_company;
467
468
        if (!$userRole->save()) {
469
            throw new ServerErrorHttpException((string)current($userRole->getMessages()));
470
        }
471
472
        //update user activity when its not a empty user
473
        if (!$isFirstSignup) {
474
            $this->updateAppActivityLimit();
475
        }
476
    }
477
478
    /**
479
     * Upload Files.
480
     *
481
     * @todo move this to the baka class
482
     *
483
     * @return void
484
     */
485
    public function afterSave()
486
    {
487
        $this->associateFileSystem();
488
    }
489
490
    /**
491
     * Get the list of all the associated apps this users has.
492
     *
493
     * @return array
494
     */
495
    public function getAssociatedApps(): array
496
    {
497
        return array_map(function ($apps) {
498
            return $apps['apps_id'];
499
        }, $this->getApps(['columns' => 'apps_id', 'group' => 'apps_id'])->toArray());
500
    }
501
502
    /**
503
     * Get an array of the associates companies Ids.
504
     *
505
     * @return array
506
     */
507
    public function getAssociatedCompanies(): array
508
    {
509
        return array_map(function ($company) {
510
            return $company['companies_id'];
511
        }, $this->getCompanies(['columns' => 'companies_id'])->toArray());
512
    }
513
514
    /**
515
     * Get user by key.
516
     * @param string $userActivationEmail
517
     * @return Users
518
     */
519
    public static function getByUserActivationEmail(string $userActivationEmail): Users
520
    {
521
        return self::findFirst([
522
            'conditions' => 'user_activation_email = ?0 and user_active =?1 and is_deleted = 0',
523
            'bind' => [$userActivationEmail, 1],
524
        ]);
525
    }
526
527
    /**
528
     * Overwrite the relationship.
529
     *
530
     * @return void
531
     */
532
    public function getAvatar(): ?string
533
    {
534
        return $this->getAttachementByName('avatar');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getAttachementByName('avatar') also could return the type string which is incompatible with the documented return type void.
Loading history...
535
    }
536
}
537