Passed
Pull Request — dev (#1242)
by Chris
14:20 queued 06:42
created

JobPoster::overtime_requirement()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 0
cts 0
cp 0
rs 10
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
/**
4
 * Created by Reliese Model.
5
 * Date: Thu, 12 Jul 2018 22:39:27 +0000.
6
 */
7
8
namespace App\Models;
9
10
use App\Events\JobSaved;
11
use Illuminate\Notifications\Notifiable;
12
use Illuminate\Support\Facades\App;
13
use Illuminate\Support\Facades\Lang;
14
use Jenssegers\Date\Date;
15
use \Backpack\CRUD\CrudTrait;
16
use Astrotomic\Translatable\Translatable;
17
18
/**
19
 * Class JobPoster
20
 *
21
 * @property int $id
22
 * @property int $job_term_id
23
 * @property int $term_qty
24
 * @property \Jenssegers\Date\Date $open_date_time
25
 * @property \Jenssegers\Date\Date $close_date_time
26
 * @property \Jenssegers\Date\Date $start_date_time
27
 * @property \Jenssegers\Date\Date $review_requested_at
28
 * @property \Jessengers\Date\Date $published_at
29
 * @property int $department_id
30
 * @property int $province_id
31
 * @property int $salary_min
32
 * @property int $salary_max
33
 * @property int $noc
34
 * @property int $classification_id
35
 * @property int $classification_level
36
 * @property int $security_clearance_id
37
 * @property int $language_requirement_id
38
 * @property boolean $remote_work_allowed
39
 * @property int $manager_id
40
 * @property boolean $published
41
 * @property int $team_size
42
 * @property array $work_env_features This should be an array of boolean flags for features, ie json of shape {[feature: string]: boolean}
43
 * @property int $fast_vs_steady
44
 * @property int $horizontal_vs_vertical
45
 * @property int $experimental_vs_ongoing
46
 * @property int $citizen_facing_vs_back_office
47
 * @property int $collaborative_vs_independent
48
 * @property int $telework_allowed_frequency_id
49
 * @property int $flexible_hours_frequency_id
50
 * @property int $travel_requirement_id
51
 * @property int $overtime_requirement_id
52
 * @property \Jenssegers\Date\Date $created_at
53
 * @property \Jenssegers\Date\Date $updated_at
54
 *
55
 * @property int $submitted_applications_count
56
 *
57
 * @property \App\Models\Lookup\Department $department
58
 * @property \App\Models\Lookup\JobTerm $job_term
59
 * @property \App\Models\Lookup\LanguageRequirement $language_requirement
60
 * @property \App\Models\Manager $manager
61
 * @property \App\Models\Lookup\Province $province
62
 * @property \App\Models\Lookup\SecurityClearance $security_clearance
63
 * @property \Illuminate\Database\Eloquent\Collection $criteria
64
 * @property \Illuminate\Database\Eloquent\Collection $job_applications
65
 * @property \Illuminate\Database\Eloquent\Collection $job_poster_key_tasks
66
 * @property \Illuminate\Database\Eloquent\Collection $job_poster_questions
67
 * @property \Illuminate\Database\Eloquent\Collection $job_poster_translations
68
 * @property \Illuminate\Database\Eloquent\Collection $submitted_applications
69
 * @property \App\Models\Lookup\Frequency $telework_allowed_frequency
70
 * @property \App\Models\Lookup\Frequency $flexible_hours_frequency
71
 *
72
 * Localized Properties:
73
 * @property string $city
74
 * @property string $title
75
 * @property string $dept_impact
76
 * @property string $team_impact
77
 * @property string $hire_impact
78
 * @property string $branch
79
 * @property string $division
80
 * @property string $education
81
 * @property string $work_env_description
82
 * @property string $culture_summary
83
 * @property string $culture_special
84
 *
85
 * Computed Properties
86
 * @property string $classification
87
 * @property string $classification_code
88
 *
89
 * Methods
90
 * @method boolean isOpen()
91
 * @method string timeRemaining()
92
 * @method mixed[] toApiArray()
93
 */
94
class JobPoster extends BaseModel
95
{
96
    use CrudTrait;
0 ignored issues
show
introduced by
The trait Backpack\CRUD\CrudTrait requires some properties which are not provided by App\Models\JobPoster: $Type, $fakeColumns
Loading history...
97
    use Translatable;
98
    use Notifiable;
0 ignored issues
show
introduced by
The trait Illuminate\Notifications\Notifiable requires some properties which are not provided by App\Models\JobPoster: $email, $phone_number
Loading history...
99
100
    const DATE_FORMAT = [
101
        'en' => 'M jS, Y',
102
        'fr' => 'd M Y',
103
    ];
104
    const TIME_FORMAT = [
105
        'en' => 'h:i A T',
106
        'fr' => 'H \h i T',
107
    ];
108
    const TIMEZONE = 'America/Toronto';
109
110
    /**
111
     * @var string[] $translatedAttributes
112
     */
113
    public $translatedAttributes = [
114
        'city',
115
        'title',
116
        'dept_impact',
117
        'team_impact',
118
        'hire_impact',
119
        'branch',
120
        'division',
121
        'education',
122
        'work_env_description',
123
        'culture_summary',
124
        'culture_special',
125
    ];
126
127
    /**
128
     * @var string[] $casts
129
     */
130
    protected $casts = [
131
        'job_term_id' => 'int',
132
        'department_id' => 'int',
133
        'province_id' => 'int',
134
        'salary_min' => 'int',
135
        'salary_max' => 'int',
136
        'noc' => 'int',
137
        'classification_id' => 'int',
138
        'classification_level' => 'int',
139
        'security_clearance_id' => 'int',
140
        'language_requirement_id' => 'int',
141
        'remote_work_allowed' => 'boolean',
142
        'manager_id' => 'int',
143
        'published' => 'boolean',
144
        'team_size' => 'int',
145
        'work_env_features' => 'array',
146
        'fast_vs_steady' => 'int',
147
        'horizontal_vs_vertical' => 'int',
148
        'experimental_vs_ongoing' => 'int',
149
        'citizen_facing_vs_back_office' => 'int',
150
        'collaborative_vs_independent' => 'int',
151
        'telework_allowed_frequency_id' => 'int',
152
        'flexible_hours_frequency_id' => 'int',
153
        'travel_requirement_id' => 'int',
154
        'overtime_requirement_id' => 'int',
155
    ];
156
157
    /**
158
     * @var string[] $dates
159
     */
160
    protected $dates = [
161
        'open_date_time',
162
        'close_date_time',
163
        'start_date_time',
164
        'review_requested_at',
165
        'published_at'
166
    ];
167
168
    /**
169
     * @var string[] $fillable
170
     */
171
    protected $fillable = [
172
        'job_term_id',
173
        'term_qty',
174
        'open_date_time',
175
        'close_date_time',
176
        'start_date_time',
177
        'department_id',
178
        'province_id',
179
        'salary_min',
180
        'salary_max',
181
        'noc',
182
        'classification',
183
        'classification_code',
184
        'classification_level',
185
        'security_clearance_id',
186
        'language_requirement_id',
187
        'remote_work_allowed',
188
        'published',
189
        'team_size',
190
        'work_env_features',
191
        'fast_vs_steady',
192
        'horizontal_vs_vertical',
193
        'experimental_vs_ongoing',
194
        'citizen_facing_vs_back_office',
195
        'collaborative_vs_independent',
196
        'telework_allowed_frequency_id',
197
        'flexible_hours_frequency_id',
198
        'travel_requirement_id',
199
        'overtime_requirement_id',
200
    ];
201
202
    /**
203
     * The attributes that should be visible in arrays.
204
     * In this case, it blocks loaded relations from appearing.
205
     *
206
     * @var array
0 ignored issues
show
introduced by
@var annotation of property \App\Models\JobPoster::$visible does not specify type hint for its items.
Loading history...
207
     */
208
    protected $visible = [
209
        'id',
210
        'manager_id',
211
        'term_qty',
212
        'open_date_time',
213
        'close_date_time',
214
        'start_date_time',
215
        'department_id',
216
        'province_id',
217
        'salary_min',
218
        'salary_max',
219
        'noc',
220
        'classification_code',
221
        'classification_level',
222
        'security_clearance_id',
223
        'language_requirement_id',
224
        'remote_work_allowed',
225
        'published_at',
226
        'published',
227
        'review_requested_at',
228
        'team_size',
229
        'work_env_features',
230
        'fast_vs_steady',
231
        'horizontal_vs_vertical',
232
        'experimental_vs_ongoing',
233
        'citizen_facing_vs_back_office',
234
        'collaborative_vs_independent',
235
        'telework_allowed_frequency_id',
236
        'flexible_hours_frequency_id',
237
        'travel_requirement_id',
238
        'overtime_requirement_id',
239
    ];
240
241
    /**
242
     * The accessors to append to the model's array form.
243
     *
244
     * @var array
0 ignored issues
show
introduced by
@var annotation of property \App\Models\JobPoster::$appends does not specify type hint for its items.
Loading history...
245
     */
246
    protected $appends = ['classification_code'];
247
248
    /**
249
     * @var mixed[] $dispatchesEvents
250
     */
251
    protected $dispatchesEvents = [
252
        'saved' => JobSaved::class,
253
    ];
254
255
    // @codeCoverageIgnoreStart
256
    public function department() // phpcs:ignore
257
    {
258
        return $this->belongsTo(\App\Models\Lookup\Department::class);
259
    }
260
261
    public function job_term() // phpcs:ignore
262
    {
263
        return $this->belongsTo(\App\Models\Lookup\JobTerm::class);
264
    }
265
266
    public function language_requirement() // phpcs:ignore
267
    {
268
        return $this->belongsTo(\App\Models\Lookup\LanguageRequirement::class);
269
    }
270
271
    public function manager() // phpcs:ignore
272
    {
273
        return $this->belongsTo(\App\Models\Manager::class);
274
    }
275
276
    public function province() // phpcs:ignore
277
    {
278
        return $this->belongsTo(\App\Models\Lookup\Province::class);
279
    }
280
281
    public function security_clearance() // phpcs:ignore
282
    {
283
        return $this->belongsTo(\App\Models\Lookup\SecurityClearance::class);
284
    }
285
286
    public function criteria() // phpcs:ignore
287
    {
288
        return $this->hasMany(\App\Models\Criteria::class);
289
    }
290
291
    public function job_applications() // phpcs:ignore
292
    {
293
        return $this->hasMany(\App\Models\JobApplication::class);
294
    }
295
296
    public function job_poster_key_tasks() // phpcs:ignore
297
    {
298
        return $this->hasMany(\App\Models\JobPosterKeyTask::class);
299
    }
300
301
    public function job_poster_questions() // phpcs:ignore
302
    {
303
        return $this->hasMany(\App\Models\JobPosterQuestion::class);
304
    }
305
306
    public function job_poster_translations() // phpcs:ignore
307
    {
308
        return $this->hasMany(\App\Models\JobPosterTranslation::class);
309
    }
310
311
    public function telework_allowed_frequency() // phpcs:ignore
312
    {
313
        return $this->belongsTo(\App\Models\Lookup\Frequency::class);
314
    }
315
316
    public function flexible_hours_frequency() // phpcs:ignore
317
    {
318
        return $this->belongsTo(\App\Models\Lookup\Frequency::class);
319
    }
320
321
    public function travel_requirement() // phpcs:ignore
322
    {
323
        return $this->belongsTo(\App\Models\Lookup\TravelRequirement::class);
324
    }
325
326
    public function overtime_requirement() // phpcs:ignore
327
    {
328
        return $this->belongsTo(\App\Models\Lookup\OvertimeRequirement::class);
329
    }
330
331
    // Artificial Relations
332
333
    /**
334
     * Get all of the Job Applications submitted to this
335
     * Job Poster.
336
     *
337
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
338
     */
339
    public function submitted_applications() // phpcs:ignore
340
    {
341
        return $this->hasMany(\App\Models\JobApplication::class)->whereDoesntHave('application_status', function ($query) : void {
342
            $query->where('name', 'draft');
343
        });
344
    }
345
346
    // Overrides
347
348
    /**
349 10
     * Retrieve the model for a bound value.
350
     * Seems to be a useful workaround for providing submitted_applications_count
351 10
     * to any bound routes that receive a jobPoster instance without using the
352 10
     * withCount property on the model itself.
353
     * See https://github.com/laravel/framework/issues/23957 for more info.
354 1
     *
355
     * @param mixed $value Value used to retrieve the model instance.
356
     *
357
     * @return \Illuminate\Database\Eloquent\Model|null
358
     */
359
    public function resolveRouteBinding($value) // phpcs:ignore
360
    {
361
        return $this->withCount('submitted_applications')->where('id', $value)->first() ?? abort(404);
362
    }
363
364
    // @codeCoverageIgnoreEnd
365
    // Accessors.
366
367
    /**
368 45
     * The database model stores a foreign id to the classifiction table,
369
     * but to simplify the API, this model simply returns the key as classification_code.
370 45
     *
371 24
     * @return void
1 ignored issue
show
Coding Style introduced by
Function return type is void, but function contains return statement
Loading history...
372 40
     */
373 1
    public function getClassificationCodeAttribute()
0 ignored issues
show
introduced by
Method \App\Models\JobPoster::getClassificationCodeAttribute() does not have return type hint for its return value but it should be possible to add it based on @return annotation "void".
Loading history...
374
    {
375 45
        if ($this->classification_id !== null) {
376 45
            $classification = Classification::find($this->classification_id);
377
            return $classification->key;
378
        }
379 6
        return null;
380
    }
381 6
382
    /**
383
     * The classification property is deprecated. To ensure
384
     * Twig template consistency, check for populated
385
     * classification_code and classification_level and return
386
     * the combination of those instead.
387
     *
388
     * @param mixed $value Incoming attribute value.
389 1
     *
390
     * @return string|null
391 1
     */
392 1
    public function getClassificationAttribute($value)
0 ignored issues
show
introduced by
Method \App\Models\JobPoster::getClassificationAttribute() does not have return type hint for its return value but it should be possible to add it based on @return annotation "string|null".
Loading history...
393
    {
394 1
        if (!empty($this->classification_code) && !empty($this->classification_level)) {
395 1
            return "$this->classification_code-$this->classification_level";
396
        } else {
397
            return $value;
398 1
        }
399
    }
400
401
    // Mutators.
402 1
403
    /**
404
     * This model exposes the classification_code attribute, but it is determined
405
     * by the classification_id column in the database, so set that value instead.
406
     *
407
     * @param string $value
1 ignored issue
show
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
408
     * @return void
409
     */
410
    public function setClassificationCodeAttribute($value): void
1 ignored issue
show
introduced by
Method \App\Models\JobPoster::setClassificationCodeAttribute() does not have parameter type hint for its parameter $value but it should be possible to add it based on @param annotation "string".
Loading history...
Coding Style introduced by
Type hint "string" missing for $value
Loading history...
411
    {
412
        $classification = Classification::where('key', $value)->first();
413
        if ($classification !== null) {
414
            $this->attributes['classification_id'] = $classification->id;
415
        } else {
416
            $this->attributes['classification_id'] = null;
417
        }
418
    }
419
420
    /**
421 6
     * Intercept setting the "published" attribute, and set the
422
     * "published_at" timestamp if true.
423 6
     *
424 6
     * @param mixed $value Incoming value for the 'published' attribute.
425 6
     *
426
     * @return void
427
     */
428
    public function setPublishedAttribute($value) : void
429
    {
430
        if ($value && $this->open_date_time->isPast()) {
431
            $this->attributes['published_at'] = new Date();
432
        } elseif ($value && $this->open_date_time->isFuture()) {
433 4
            $this->attributes['published_at'] = $this->open_date_time;
434
        }
435 4
        $this->attributes['published'] = $value;
436 4
    }
437 4
438
    // Methods
439
    public function submitted_applications_count() //phpcs:ignore
440
    {
441
        return $this->submitted_applications()->count();
442
    }
443
444
    /**
445 1
     * Formatted and localized date and time the Job Poster closes.
446
     *
447 1
     * @return string[]
448
     */
449 1
    public function applyBy() : array
450 1
    {
451 1
        $localCloseDate = new Date($this->close_date_time); // This initializes the date object in UTC time
452 1
        $localCloseDate->setTimezone(new \DateTimeZone(self::TIMEZONE)); // Then set the time zone for display
453
        $displayDate = [
454 1
            'date' => $localCloseDate->format(self::DATE_FORMAT[App::getLocale()]),
455 1
            'time' => $localCloseDate->format(self::TIME_FORMAT[App::getLocale()])
456 1
        ];
457 1
458 1
        if (App::isLocale('fr')) {
459 1
            $displayDate['time'] = str_replace(['EST', 'EDT'], ['HNE', 'HAE'], $displayDate['time']);
460 1
        }
461 1
462 1
        return $displayDate;
463
    }
464
465
    /**
466
     * Return whether the Job is Open or Closed.
467
     * Used by the Admin Portal JobPosterCrudController.
468 1
     *
469
     * @return string
470 1
     */
471
    public function displayStatus() : string
472
    {
473
        return $this->isOpen() ? 'Open' : 'Closed';
474
    }
475
476
    /**
477
     * Check if a Job Poster is open for applications.
478
     *
479 3
     * @return boolean
480
     */
481 3
    public function isOpen() : bool
482 3
    {
483 1
        return $this->published
484 3
            && $this->open_date_time->isPast()
485 1
            && $this->close_date_time->isFuture();
486 3
    }
487 3
488
    /**
489 1
     * Check if a Job Poster is closed for applications.
490
     *
491
     * @return boolean
492 3
     */
493
    public function isClosed() : bool
494
    {
495
        return $this->published
496
            && $this->open_date_time->isPast()
497
            && $this->close_date_time->isPast();
498
    }
499
500
    /**
501 4
     * Calculate the remaining time a Job Poster is open.
502
     *
503 4
     * @return string
504 4
     */
505
    public function timeRemaining() : string
506
    {
507
        $interval = $this->close_date_time->diff(Date::now());
508
509
        $d = $interval->d;
510
        $h = $interval->h;
511
        $m = $interval->i;
512
        $s = $interval->s;
513
514
        if ($d > 0) {
515
            $unit = 'day';
516
            $count = $d;
517
        } elseif ($h > 0) {
518
            $unit = 'hour';
519
            $count = $h;
520
        } elseif ($m > 0) {
521
            $unit = 'minute';
522
            $count = $m;
523
        } else {
524
            $unit = 'second';
525
            $count = $s;
526
        }
527
528
        $key = "common/time.$unit";
529
530
        return Lang::choice($key, $count);
531
    }
532
533
    /**
534
     * Return the current status for the Job Poster.
535
     * Possible values are "draft", "submitted", "published" and "closed".
536
     *
537
     * @return string
538
     */
539
    public function status() : string
540
    {
541
        $status = 'draft';
542
        if ($this->isOpen()) {
543
            $status = 'published';
544
        } elseif ($this->isClosed()) {
545
            $status = 'closed';
546
        } elseif ($this->review_requested_at !== null) {
547
            $status = 'submitted';
548
        } else {
549
            $status = 'draft';
550
        }
551
552
        return $status;
553
    }
554
555
    /**
556
     * Return the array of values used to represent this object in an api response.
557
     * This array should contain no nested objects (besides translations).
558
     *
559
     * @return mixed[]
560
     */
561
    public function toApiArray(): array
562
    {
563
        $jobWithTranslations = array_merge($this->toArray(), $this->getTranslationsArray());
564
        return $jobWithTranslations;
565
    }
566
}
567