%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /var/www/html/hr/api/app/Models/
Upload File :
Create Path :
Current File : /var/www/html/hr/api/app/Models/Pto.php

<?php

namespace App\Models;

use App\Events\NotificationSentEvent;
use App\Exceptions\AppException;
use App\Traits\HasNotifications;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Throwable;

/**
 * @property integer id
 * @property mixed start_date
 * @property mixed end_date
 * @property string status
 * @property string type
 * @property string notes
 * @property integer user_id
 * @property integer approved_by
 * @property User user
 * @property User approvedBy
 */
//Stands for Paid time off
class Pto extends BaseModel
{
    use HasNotifications;

    const DRAFT = 'DRAFT';
    const PENDING = 'PENDING';
    const APPROVED = 'APPROVED';
    const REJECTED = 'REJECTED';

    const ANNUAL_LEAVE = 'ANNUAL_LEAVE';

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'start_date',
        'end_date',
        'status',
        'type',
        'notes',
    ];

    protected $guarded = [
        'user_id',
        'approved_by',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'id'          => 'integer',
        'start_date'  => 'date',
        'end_date'    => 'date',
        'notes'       => 'string',
        'status'      => 'string',
        'type'        => 'string',
        'user_id'     => 'integer',
        'approved_by' => 'integer',
    ];

    /**
     * @param $data
     * @return self
     * @throws Throwable
     */
    public static function createItem($data)
    {
        try {
            if (Carbon::parse($data['start_date'])
                      ->isBefore(Carbon::now())) {
                throw new AppException(trans('ptos.should_be_in_future'));
            }
            $user = self::getLoggedInUser();
            $currentPtos = self::getPtos($data['start_date'], $data['end_date'])
                               ->where('user_id', $user->id);
            if ($currentPtos->count() > 0) {
                throw new AppException(trans('ptos.overlapping'));
            }
            DB::beginTransaction();
            /** @var self $item */
            $item = self::query()
                        ->make([
                            'start_date' => $data['start_date'],
                            'end_date'   => $data['end_date'],
                            'notes'      => array_key_exists('notes', $data)
                                ? $data['notes']
                                : null,
                        ]);
            $item->user()
                 ->associate($user);
            $item->save();
            DB::commit();
            return $item->fresh();
        } catch (Throwable $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public static function getPtos($startDate, $endDate)
    {
        return self::query()
                   ->where('status', '!=', self::REJECTED)
                   ->where('start_date', '<=', $endDate)
                   ->where('end_date', '>=', $startDate);
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    /**
     * @param $data
     * @return self
     * @throws Throwable
     */
    public function updateItem($data)
    {
        try {
            if ($this->status !== self::DRAFT) {
                throw new AppException(trans('ptos.cant_modify'));
            }
            $this->start_date = $data['start_date'];
            $this->end_date = $data['end_date'];
            $this->notes = $data['notes'];
            $this->save();
            return $this;
        } catch (Throwable $e) {
            throw $e;
        }
    }

    /**
     * @return $this
     * @throws Throwable
     */
    public function request()
    {
        try {
            if ($this->status === self::PENDING) {
                throw new AppException(trans('ptos.awaiting_response'));
            }
            if ($this->status === self::APPROVED) {
                throw new AppException(trans('ptos.already_approved'));
            }
            if ($this->status === self::REJECTED) {
                throw new AppException('ptos.already_rejected');
            }
            $this->status = self::PENDING;
            $this->save();
            $this->notifyForPtoRequest();
            return $this;
        } catch (Throwable $e) {
            throw $e;
        }
    }

    private function notifyForPtoRequest()
    {
        $hrUsers = User::getHrUsers();
        /** @var User $requester */
        $requester = $this->user;
        /** @var User $user */
        foreach ($hrUsers as $user) {
            /** @var Notification $notification */
            $notification = $user->notifications()
                                 ->create([
                                     'title'      => trans('ptos.requested', [
                                         'name' => $requester->getFullName(),
                                     ]),
                                     'model_type' => 'Pto',
                                     'model_id'   => $this->id,
                                 ]);
            broadcast(new NotificationSentEvent($user, $notification));
        }
    }

    /**
     * @return $this
     * @throws Throwable
     */
    public function approve()
    {
        try {
            if ($this->status === self::APPROVED) {
                return $this;
            }
            if (Carbon::parse($this->start_date)
                      ->isBefore(Carbon::now())) {
                throw new AppException(trans('ptos.is_in_the_past'));
            }
            $loggedUser = self::getLoggedInUser();
            if ($this->user->isAdmin() && !$loggedUser->isAdmin()) {
                throw new AppException(trans('pto.admin_only'));
            }
            $days = $this->calculateWorkingDays();
            DB::beginTransaction();
            /** @var AnnualLeave $leave */
            $leave = $this->user->annualLeave;
            $leave->quantity -= $days;
            $leave->save();
            $leave->transactions()
                  ->create([
                      'type'     => AnnualLeaveTransaction::REMOVE,
                      'notes'    => $this->getTransactionNotesforApproval(),
                      'quantity' => $days,
                  ]);
            $this->status = self::APPROVED;
            $this->approvedBy()
                 ->associate($loggedUser);
            $this->save();
            DB::commit();
            $this->notifyForPtoApproved();
            return $this;
        } catch (Throwable $e) {
            DB::rollBack();
            throw $e;
        }
    }

    private function calculateWorkingDays($includePto = true)
    {
        $start = Carbon::parse($this->start_date);
        $end = Carbon::parse($this->end_date);
        return WorkingDay::getWorkingDaysCount($start, $end, $this->user, $includePto);
    }

    private function getTransactionNotesForApproval()
    {
        return trans('ptos.transaction_notes_for_approval', [
            'date' => $this->getDatesText(),
        ]);
    }

    private function getDatesText()
    {
        $start = Carbon::parse($this->start_date);
        $end = Carbon::parse($this->end_date);
        if ($start->isSameDay($end)) {
            return $start->format('d M Y');
        }
        return $start->format('d M Y') . ' - ' . $end->format('d M Y');
    }

    public function approvedBy()
    {
        return $this->belongsTo(User::class, 'approved_by');
    }

    private function notifyForPtoApproved()
    {
        $approver = $this->approvedBy;
        /** @var Notification $notification */
        $notification = $this->user->notifications()
                                   ->create([
                                       'title'      => trans('ptos.approved', [
                                           'name' => $approver->getFullName(),
                                       ]),
                                       'model_type' => 'Pto',
                                       'model_id'   => $this->id,
                                       'type'       => Notification::TYPE_SUCCESS,
                                   ]);
        broadcast(new NotificationSentEvent($this->user, $notification));
    }

    /**
     * @return $this
     * @throws Throwable
     */
    public function reject()
    {
        try {
            if ($this->status === self::REJECTED) {
                return $this;
            }
            if (Carbon::parse($this->start_date)
                      ->isBefore(Carbon::now())) {
                throw new AppException(trans('ptos.is_in_the_past'));
            }
            $loggedUser = self::getLoggedInUser();
            if ($this->user->isAdmin() && !$loggedUser->isAdmin()) {
                throw new AppException(trans('pto.admin_only'));
            }
            DB::beginTransaction();
            if ($this->status === self::APPROVED) {
                $days = $this->calculateWorkingDays(false);
                $leave = $this->user->annualLeave;
                $leave->quantity += $days;
                $leave->save();
                $leave->transactions()
                      ->create([
                          'type'     => AnnualLeaveTransaction::ADD,
                          'notes'    => $this->getTransactionNotesForRejection(),
                          'quantity' => $days,
                      ]);
            }
            $this->status = self::REJECTED;
            $this->approvedBy()
                 ->associate($loggedUser);
            $this->save();
            DB::commit();
            $this->notifyForPtoRejected();
            return $this;
        } catch (Throwable $e) {
            DB::rollBack();
            throw $e;
        }
    }

    private function getTransactionNotesForRejection()
    {

        return trans('ptos.transaction_notes_for_rejection', [
            'date' => $this->getDatesText(),
        ]);
    }

    private function notifyForPtoRejected()
    {
        $approver = $this->approvedBy;
        /** @var Notification $notification */
        $notification = $this->user->notifications()
                                   ->create([
                                       'title'      => trans('ptos.rejected', [
                                           'name' => $approver->getFullName(),
                                       ]),
                                       'model_type' => 'Pto',
                                       'model_id'   => $this->id,
                                       'type'       => Notification::TYPE_ERROR,
                                   ]);
        broadcast(new NotificationSentEvent($this->user, $notification));
    }

    public function isDeletable()
    {
        return $this->status === self::DRAFT;
    }

    public function isEditable()
    {
        return $this->status === self::DRAFT;
    }

    public function canApprove()
    {
        return $this->status !== self::DRAFT && Carbon::parse($this->start_date)
                                                      ->isAfter(Carbon::now());
    }

    public function canReject()
    {
        return $this->status !== self::DRAFT && Carbon::parse($this->start_date)
                                                      ->isAfter(Carbon::now());
    }

}

Zerion Mini Shell 1.0