%PDF- %PDF-
Direktori : /var/www/html/hrsys/api/app/Models/ |
Current File : /var/www/html/hrsys/api/app/Models/User.php |
<?php namespace App\Models; use App\Exceptions\AppException; use App\Notifications\AccountCreatedNotification; use Carbon\Carbon; use Carbon\CarbonPeriod; use Illuminate\Database\Eloquent\Builder; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; use Laravel\Passport\HasApiTokens; use Spatie\MediaLibrary\Exceptions\FileCannotBeAdded; use Spatie\MediaLibrary\HasMedia\HasMedia; use Spatie\MediaLibrary\HasMedia\HasMediaTrait; use Spatie\MediaLibrary\Models\Media; use Spatie\Permission\Traits\HasRoles; use Throwable; /** * @property integer id * @property string name * @property string surname * @property string email * @property string phone * @property string channel_id * @property string password * @property string external_reference * @property mixed birthday * @property boolean is_confirmed * @property boolean is_enabled * @property mixed created_at * @property mixed confirmed_at * @property mixed updated_at * @property mixed roles * @property mixed projects * @property AnnualLeave annualLeave */ class User extends Authenticatable implements HasMedia { use HasApiTokens, Notifiable, HasRoles, HasMediaTrait; const AVATAR = 'avatars'; protected $perPage = 10; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'surname', 'birthday', 'email', 'phone', 'channel_id', 'password', 'is_confirmed', 'confirmed_at', 'is_enabled', 'external_reference', ]; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = [ 'password', ]; /** * The attributes that should be cast to native types. * * @var array */ protected $casts = [ 'id' => 'integer', 'name' => 'string', 'surname' => 'string', 'birthday' => 'date', 'email' => 'string', 'phone' => 'string', 'channel_id' => 'string', 'is_confirmed' => 'boolean', 'confirmed_at' => 'datetime', 'is_enabled' => 'boolean', 'external_reference' => 'string', ]; /** * @param $data * @param bool $notify * @return User * @throws Throwable */ public static function createItem($data, $notify = false) { try { DB::beginTransaction(); $channelId = BaseModel::generateRandomToken(20); /** @var User $user */ $user = self::query() ->create([ 'name' => $data['name'], 'surname' => $data['surname'], 'email' => $data['email'], 'phone' => $data['phone'], 'channel_id' => $channelId, 'birthday' => $data['birthday'], 'external_reference' => array_key_exists('external_reference', $data) ? $data['external_reference'] : null, 'password' => Str::random(16), 'is_confirmed' => true, 'is_enabled' => true, ]); if (array_key_exists('role_ids', $data)) { $user->roles() ->sync($data['role_ids']); } if (!array_key_exists('configurations', $data)) { $data['configurations'] = []; } $configData = array_merge(Configuration::getCurrent() ->toArray(), $data['configurations']); $configData['user_id'] = $user->id; /** @var Configuration $config */ $config = Configuration::createItem($configData); $days = round(AnnualLeave::calculatePercentage($config->valid_from, $config->valid_to) * $config->annual_leave_quantity); $user->setupAnnualLeave($days, AnnualLeaveTransaction::ADD, $config->holidays_validity_in_months); DB::commit(); if ($notify) { $token = ResetPassword::generateToken($user->email); $user->notify(new AccountCreatedNotification($token)); } return $user; } catch (Throwable $e) { DB::rollBack(); throw $e; } } /** * @param $quantity * @param $type * @param $validity * @throws Throwable */ public function setupAnnualLeave($quantity, $type, $validity) { try { DB::beginTransaction(); /** @var AnnualLeave $leave */ $leave = $this->annualLeave() ->make([ 'quantity' => $quantity, ]); $leave->save(); $leave->transactions() ->create([ 'type' => $type, 'notes' => 'Annual leave', 'quantity' => $quantity, 'expires_at' => Carbon::now() ->startOfYear() ->addMonths($validity), ]); DB::commit(); } catch (Throwable $e) { DB::rollBack(); throw $e; } } public function annualLeave() { return $this->hasOne(AnnualLeave::class); } public static function getHrUsers() { return self::query() ->whereHas('roles', function (Builder $q) { $q->where('name', Role::HR); }) ->get(); } /** * @param $data * @return User * @throws Throwable */ public function updateItem($data) { try { $loggedUser = BaseModel::getLoggedInUser(); DB::beginTransaction(); $this->name = $data['name']; $this->surname = $data['surname']; $this->email = $data['email']; $this->phone = $data['phone']; $this->birthday = $data['birthday']; $this->external_reference = array_key_exists('external_reference', $data) ? $data['external_reference'] : null; $this->save(); if (array_key_exists('role_ids', $data) && ($loggedUser->isAdmin() || $loggedUser->isHR())) { $this->roles() ->sync($data['role_ids']); } DB::commit(); return $this; } catch (Throwable $e) { DB::rollBack(); throw $e; } } public function isAdmin() { return $this->hasRole(Role::ADMIN); } public function isHR() { return $this->hasRole(Role::HR); } /** * @param $value */ public function setPasswordAttribute($value) { $this->attributes['password'] = bcrypt($value); } /** * @param $value * @return string */ public function getCreatedAtAttribute($value) { return Carbon::parse($value) ->toIso8601String(); } /** * @param $value * @return string */ public function getUpdatedAtAttribute($value) { return Carbon::parse($value) ->toIso8601String(); } /** * @return $this */ public function confirm() { $this->is_confirmed = true; $this->confirmed_at = Carbon::now(); $this->save(); return $this; } /** * @return $this */ public function enable() { $this->is_enabled = true; $this->save(); return $this; } /** * @return $this * @throws AppException */ public function disable() { $loggedUser = BaseModel::getLoggedInUser(); if ($this->id === $loggedUser->id) { throw new AppException(trans('users.cant_disable_your_account')); } $this->is_enabled = false; $this->save(); return $this; } /** * @param $password * @return $this */ public function updatePassword($password) { $this->password = $password; $this->save(); return $this; } /** * @param $base64data * @return $this * @throws FileCannotBeAdded */ public function setAvatarBase64($base64data) { $name = md5(time()); DB::beginTransaction(); try { if ($media = $this->getMedia(self::AVATAR) ->first()) { $media->delete(); } $this->addMediaFromBase64($base64data) ->usingName($name) ->usingFileName("$name.png") ->toMediaCollection(self::AVATAR, self::AVATAR); $this->save(); DB::commit(); return $this; } catch (FileCannotBeAdded $e) { DB::rollBack(); throw $e; } } public function getAvatar() { /** @var Media $media */ if ($media = $this->getMedia(self::AVATAR) ->first()) { return asset($media->getUrl()); } return asset('images/user.png'); } public function isFinance() { return $this->hasRole(Role::FINANCE); } public function isManager() { return $this->hasRole(Role::MANAGER); } public function formatDate($date, $format = 'Y-m-d') { if (!$date) { return null; } return Carbon::parse($date) ->toISOString(); } public function getDaysOffIn($start, $end, $includePto = true) { $holidays = Holiday::getHolidaysInDates($start, $end, [ 'name', 'date', ]); $ptos = collect($holidays); if ($includePto) { $daysOff = Pto::getPtos($start, $end) ->where('user_id', $this->id) ->where('status', Pto::APPROVED) ->get(); /** @var Pto $dayOff */ foreach ($daysOff as $dayOff) { $range = CarbonPeriod::create($dayOff->start_date, $dayOff->end_date); foreach ($range as $date) { $isAlreadyOff = $ptos->contains(function ($p) use ($date) { return Carbon::parse($p['date']) ->isSameDay(Carbon::parse($date)); }); if (Day::isWorkingDay($date) && !$isAlreadyOff) { $ptos->push([ 'name' => 'PTO', 'date' => $date, ]); } } } } return $ptos->sortBy('date') ->values() ->all(); } public function getCalendarData($start, $end) { $holidays = Holiday::getHolidaysInDates($start, $end, [ 'name', 'date', ]); $workingDayNames = WorkingDay::getDayNames(); /** @var Configuration $config */ $config = $this->currentConfigurations(); $daysOff = Pto::getPtos($start, $end) ->where('user_id', $this->id) ->where('status', Pto::APPROVED) ->get() ->map(function ($timecard) use ($config) { $timecard['type'] = 'Holiday'; $timecard['hours'] = $config->expected_daily_working_hours; return $timecard; }); $ptos = collect($holidays)->map(function ($holiday) use ($config) { $holiday['type'] = 'Holiday'; $holiday['hours'] = 0; return $holiday; }); /** @var Pto $dayOff */ foreach ($daysOff as $dayOff) { $range = CarbonPeriod::create($dayOff->start_date, $dayOff->end_date); /** @var Carbon $date */ foreach ($range as $date) { $isAlreadyOff = $ptos->contains(function ($p) use ($date) { return Carbon::parse($p['date']) ->isSameDay($date); }); if (in_array($date->dayName, $workingDayNames) && !$isAlreadyOff) { $ptos->push([ 'name' => 'PTO', 'type' => 'PTO', 'hours' => $config->expected_daily_working_hours, 'date' => $date, ]); } } } $timecards = Timecard::query() ->where('user_id', $this->id) ->where('date', '>=', $start) ->where('date', '<=', $end) ->groupBy('date') ->get([ DB::raw('date'), DB::raw('SUM(hours) as hours'), ]) ->map(function ($timecard) use ($ptos, $config, $workingDayNames) { $timecard['type'] = 'Timecard'; $isAlreadyOff = $ptos->contains(function ($p) use ($timecard) { return Carbon::parse($p['date']) ->isSameDay(Carbon::parse($timecard->date)); }); if ($isAlreadyOff) { $timecard['name'] = "Daily tasks ({$timecard['hours']})"; $timecard['has_overtime'] = true; $timecard['has_undertime'] = false; } else { // if (Day::isWorkingDay($timecard->date)) { if (in_array(Carbon::parse($timecard->date)->dayName, $workingDayNames)) { $timecard['name'] = "Daily tasks ({$timecard['hours']} / {$config->expected_daily_working_hours})"; $timecard['has_overtime'] = $timecard['hours'] > $config->expected_daily_working_hours; $timecard['has_undertime'] = $timecard['hours'] < $config->expected_daily_working_hours; } else { $timecard['name'] = "Daily tasks ({$timecard['hours']})"; $timecard['has_overtime'] = true; $timecard['has_undertime'] = false; } } return $timecard; }); $ptos = $ptos->merge(collect($timecards)); $allDates = CarbonPeriod::create($start, Carbon::now()); /** @var Carbon $date */ foreach ($allDates as $date) { $exists = $ptos->contains(function ($p) use ($date) { return Carbon::parse($p['date']) ->isSameDay(Carbon::parse($date)); }); if (!$exists && in_array($date->dayName, $workingDayNames)) { $ptos->push([ 'date' => $date, 'name' => "Daily tasks (0 / $config->expected_daily_working_hours)", 'has_overtime' => false, 'has_undertime' => $config->expected_daily_working_hours > 0, ]); } } return $ptos->sortBy('date') ->values() ->all(); } public function currentConfigurations() { $now = Carbon::now(); return $this->configurations() ->where(function (Builder $q) use ($now) { $q->whereNull('valid_to'); $q->orWhere('valid_to', '>=', $now); }) ->first(); } public function configurations() { return $this->hasMany(Configuration::class); } public function performance($start = null, $end = null) { if (!$start) { $start = BaseModel::getStartOfMonth(); } if (!$end) { $end = BaseModel::getEndOfMonth(); } $totalHours = Timecard::query() ->where('user_id', $this->id) ->where('date', '>=', $start) ->where('date', '<=', $end) ->get([ DB::raw('SUM(hours) as hours'), ]); $approvedHours = Timecard::query() ->where('user_id', $this->id) ->where('date', '>=', $start) ->where('date', '<=', $end) ->where('status', Timecard::APPROVED) ->get([ DB::raw('SUM(hours) as hours'), DB::raw('SUM(approved_hours) as approved_hours'), ]); return [ 'total_worked' => head(head($totalHours))->hours, 'waiting_approval' => head(head($totalHours))->hours - head(head($approvedHours))->hours, 'approved_hours' => head(head($approvedHours))->hours, 'billable_hours' => head(head($approvedHours))->approved_hours, 'performance' => round(100 * head(head($approvedHours))->approved_hours / head(head($approvedHours))->hours, 0) . "%", ]; } public function hasProjectAccess($projectId) { return $this->projects() ->where('project_id', $projectId) ->exists(); } public function projects() { return $this->belongsToMany(Project::class, 'user_project'); } public function managingProjects() { return $this->hasMany(Project::class, 'manager_id'); } public function getActiveProjects(Carbon $date) { return $this->projects() ->where('start_date', '<=', $date) ->where('end_date', '>=', $date) ->get(); } public function markAllNotificationsAsRead() { $this->notifications() ->where('is_read', false) ->update([ 'is_read' => true, 'read_at' => Carbon::now(), ]); return $this; } public function notifications() { return $this->hasMany(Notification::class); } public function getBadge() { return $this->notifications() ->where('is_read', false) ->count(); } public function sendBirthdayNotification() { $this->notifications() ->create([ 'title' => trans('users.happy_birthday', [ 'name' => $this->getFullName(), ]), 'model_type' => 'User', 'model_id' => $this->id, 'type' => Notification::TYPE_SUCCESS, ]); } public function getFullName() { return $this->name . ' ' . $this->surname; } public function notifyForTimecardApproval() { $this->notifications() ->create([ 'title' => trans('timecards.notify_for_approval'), 'model_type' => 'Timecard', 'model_id' => 0, 'type' => Notification::TYPE_WARNING, ]); } public function notifyForPaymentComingUp(MaintenancePayment $payment) { $params = [ 'name' => $payment->name, 'client' => $payment->client->name, 'date' => Carbon::parse($payment->next_payment_date) ->format('d M Y'), ]; $title = trans('maintenancePayments.payment_coming_up', $params); $type = Notification::TYPE_SUCCESS; if ($payment->type === MaintenancePayment::EXPENSE) { $title = trans('maintenancePayments.expense_coming_up', $params); $type = Notification::TYPE_WARNING; } $this->notifications() ->create([ 'title' => $title, 'model_type' => 'MaintenancePayment', 'model_id' => $payment->id, 'type' => $type, ]); } public function notifyForDuePayment(MaintenancePayment $payment) { $this->notifications() ->create([ 'title' => trans('maintenancePayments.due_payment', [ 'name' => $payment->name, 'client' => $payment->client->name, ]), 'model_type' => 'MaintenancePayment', 'model_id' => $payment->id, 'type' => Notification::TYPE_ERROR, ]); } }