1<?php
2/**
3 * OrangeHRM is a comprehensive Human Resource Management (HRM) System that captures
4 * all the essential functionalities required for any enterprise.
5 * Copyright (C) 2006 OrangeHRM Inc., http://www.orangehrm.com
6 *
7 * OrangeHRM is free software; you can redistribute it and/or modify it under the terms of
8 * the GNU General Public License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * OrangeHRM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along with this program;
16 * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA  02110-1301, USA
18 */
19
20namespace Orangehrm\Rest\Api\Leave;
21
22use LeavePeriodService;
23use Orangehrm\Rest\Api\EndPoint;
24use Orangehrm\Rest\Api\Exception\BadRequestException;
25use Orangehrm\Rest\Api\Exception\InvalidParamException;
26use Orangehrm\Rest\Api\Exception\RecordNotFoundException;
27use Orangehrm\Rest\Api\Leave\Entity\LeaveRequest;
28use Orangehrm\Rest\Api\Leave\Model\EmployeeLeaveRequestModel;
29use Orangehrm\Rest\Api\Leave\Model\LeaveListLeaveRequestModel;
30use Orangehrm\Rest\Api\User\Leave\Model\LeaveTypeModel;
31use Orangehrm\Rest\Http\Response;
32use ServiceException;
33use UserRoleManagerFactory;
34
35class LeaveRequestAPI extends EndPoint
36{
37    /**
38     * @var \EmployeeService
39     */
40    private $employeeService;
41
42    /**
43     * @var \LeaveRequestService
44     */
45    private $leaveRequestService;
46
47    private $leaveEntitlementService;
48
49    private $statusList;
50
51    private $subunit;
52
53    /**
54     * @var null|LeavePeriodService
55     */
56    private $leavePeriodService = null;
57
58    /**
59     * Constants
60     */
61    const PARAMETER_FROM_DATE = "fromDate";
62    const PARAMETER_TO_DATE = "toDate";
63    const PARAMETER_REJECTED = "rejected";
64    const PARAMETER_CANCELLED = "cancelled";
65    const PARAMETER_PENDING_APPROVAL = "pendingApproval";
66    const PARAMETER_SCHEDULED = "scheduled";
67    const PARAMETER_TAKEN = 'taken';
68    const PARAMETER_ID = 'id';
69    const PARAMETER_PAST_EMPLOYEE = 'pastEmployee';
70    const PARAMETER_LEAVE_TYPE = 'type';
71    CONST PARAMETER_SUBUNIT = 'subunit';
72    const PARAMETER_LIMIT = 'limit';
73    const PARAMETER_PAGE = 'page';
74    const PARAMETER_EMPLOYEE_NAME = 'employeeName';
75    const PARAMETER_LEAVE_TYPE_ID = 'leaveTypeId';
76
77
78    /**
79     * @return \EmployeeService
80     */
81    public function getEmployeeService(): \EmployeeService
82    {
83        if (is_null($this->employeeService)) {
84            $this->employeeService = new \EmployeeService();
85        }
86        return $this->employeeService;
87    }
88
89    /**
90     * Sets EmployeeService
91     * @param \EmployeeService $service
92     */
93    public function setEmployeeService(\EmployeeService $service)
94    {
95        $this->employeeService = $service;
96    }
97
98    /**
99     *
100     * @return \LeaveRequestService
101     */
102    public function getLeaveRequestService(): \LeaveRequestService
103    {
104        if (is_null($this->leaveRequestService)) {
105            $this->leaveRequestService = new \LeaveRequestService();
106        }
107
108        return $this->leaveRequestService;
109    }
110
111    /**
112     * Get entitlement service
113     *
114     * @return \LeaveEntitlementService
115     */
116    public function getLeaveEntitlementService(): \LeaveEntitlementService
117    {
118        if (empty($this->leaveEntitlementService)) {
119            $this->leaveEntitlementService = new \LeaveEntitlementService();
120        }
121        return $this->leaveEntitlementService;
122    }
123
124    /**
125     * Set entitlement service
126     *
127     * @param $leaveEntitlementService
128     */
129    public function setLeaveEntitlementService(\LeaveEntitlementService $leaveEntitlementService)
130    {
131        $this->leaveEntitlementService = $leaveEntitlementService;
132    }
133
134    /**
135     * @return mixed
136     */
137    public function getSubunit()
138    {
139        return $this->subunit;
140    }
141
142    /**
143     * @param mixed $subunit
144     */
145    public function setSubunit($subunit)
146    {
147        $this->subunit = $subunit;
148    }
149
150    /**
151     *
152     * @param \LeaveRequestService $leaveRequestService
153     */
154    public function setLeaveRequestService(\LeaveRequestService $leaveRequestService)
155    {
156        $this->leaveRequestService = $leaveRequestService;
157    }
158
159    /**
160     * @return LeavePeriodService
161     */
162    public function getLeavePeriodService(): LeavePeriodService
163    {
164        if (is_null($this->leavePeriodService)) {
165            $leavePeriodService = new LeavePeriodService();
166            $leavePeriodService->setLeavePeriodDao(new \LeavePeriodDao());
167            $this->leavePeriodService = $leavePeriodService;
168        }
169        return $this->leavePeriodService;
170    }
171
172    /**
173     * @param LeavePeriodService $leavePeriodService
174     */
175    public function setLeavePeriodService(LeavePeriodService $leavePeriodService)
176    {
177        $this->leavePeriodService = $leavePeriodService;
178    }
179
180    /**
181     * search Leave requests
182     *
183     * @return Response
184     * @throws RecordNotFoundException
185     */
186    public function searchRequests()
187    {
188        $filters = $this->filterParameters();
189        $this->validateInputs($filters);
190
191        $searchParams = $this->createParameters($filters);
192
193        $result = $this->getLeaveRequestService()->searchLeaveRequests($searchParams, 0, false, false,
194            true, true);
195        $list = $result['list'];
196        foreach ($list as $request) {
197
198            $leaveRequest = new LeaveRequest($request->getId(), $request->getLeaveTypeName());
199            $leaveBalance = $this->getLeaveEntitlementService()->getLeaveBalance($request->getEmpNumber(),
200                $request->getLeaveTypeId(), $request->getLeaveDates()[0]);
201            $leaveRequest->buildLeaveRequest($request);
202            $leaveRequest->setLeaveBalance(number_format((float)$leaveBalance->balance, 2, '.', ''));
203            $response [] = $leaveRequest->toArray();
204
205        }
206
207        if (empty($response)) {
208            throw new RecordNotFoundException('No Records Found');
209        }
210
211        return new Response($response, array());
212    }
213
214    /**
215     * Get leave request per employee
216     *
217     * @return Response
218     * @throws RecordNotFoundException
219     */
220    public function getLeaveRequestPerEmployee()
221    {
222        $employee = $this->getEmployeeService()->getEmployee($this->getRequestParams()->getUrlParam(self::PARAMETER_ID));
223        $filters = $this->filterParameters();
224
225        if (!empty($employee)) {
226
227            $searchParams = new \ParameterObject(array(
228                'employeeFilter' => array($employee->getEmpNumber()),
229                'noOfRecordsPerPage' => $filters[self::PARAMETER_LIMIT],
230                'employeeName' => $employee->getFullName()
231            ));
232            $result = $result = $this->getLeaveRequestService()->searchLeaveRequests($searchParams, 0, false, false,
233                true, true);
234            $list = $result['list'];
235
236            $leaveRequestList = null;
237
238            foreach ($list as $request) {
239
240                $leaveRequest = new LeaveRequest($request->getId(), $request->getLeaveTypeName());
241                $leaveBalance = $this->getLeaveEntitlementService()->getLeaveBalance($request->getEmpNumber(),
242                    $request->getLeaveTypeId(), $request->getLeaveDates()[0]);
243                $leaveRequest->buildLeaveRequest($request);
244                $leaveRequest->setLeaveBalance(number_format((float)$leaveBalance->balance, 2, '.', ''));
245                $response [] = $leaveRequest->toArray();
246
247            }
248            if (empty($response)) {
249                throw new RecordNotFoundException('No Records Found');
250            }
251            return new Response($response, array());
252        } else {
253            throw  new RecordNotFoundException('Employee Not Found');
254        }
255
256
257    }
258
259    /**
260     * Get leave requests for accessible leave list employees for current request user
261     * @return Response
262     * @throws InvalidParamException
263     * @throws RecordNotFoundException
264     * @throws ServiceException
265     */
266    public function getLeaveRequests(): Response
267    {
268        $filters = $this->filterParameters();
269        $this->validateInputs($filters);
270        $limit = $filters[self::PARAMETER_LIMIT];
271        $page = empty($filters[self::PARAMETER_PAGE]) ? 1 : $filters[self::PARAMETER_PAGE];
272        $disablePagination = empty($limit) ? true : false;
273        $withTerminated = $this->validatePastEmployee($filters[self::PARAMETER_PAST_EMPLOYEE]);
274        $employeeIds = $this->getAccessibleEmployeeIds($withTerminated);
275
276        $fromDate = $filters[self::PARAMETER_FROM_DATE];
277        $toDate = $filters[self::PARAMETER_TO_DATE];
278
279        if (empty($fromDate) && empty($toDate)) {
280            $currentLeavePeriod = $this->getLeavePeriodService()->getCurrentLeavePeriodByDate(date('Y-m-d'));
281            $fromDate = $currentLeavePeriod[0];
282            $toDate = $currentLeavePeriod[1];
283        }
284
285        $params = [
286            'employeeFilter' => $employeeIds,
287            'dateRange' => new \DateRange($fromDate, $toDate),
288            'statuses' => $this->getStatusesArray($filters),
289            'cmbWithTerminated' => $withTerminated,
290            'subUnit' => $this->subunit
291        ];
292
293        $employeeName = $this->getRequestParams()->getUrlParam(self::PARAMETER_EMPLOYEE_NAME);
294        if (!is_null($employeeName)) {
295            $params['employeeName'] = $employeeName;
296        }
297        if (!empty($limit)) {
298            $params['noOfRecordsPerPage'] = $limit;
299        }
300        if (!empty($filters[self::PARAMETER_LEAVE_TYPE_ID])) {
301            $params['leaveTypeId'] = $filters[self::PARAMETER_LEAVE_TYPE_ID];
302        }
303
304        $searchParams = new \ParameterObject($params);
305        $result = $this->getLeaveRequestService()->searchLeaveRequests($searchParams, $page, $disablePagination, false,
306            false, false);
307
308        if (!$disablePagination) {
309            $result = $result['list'];
310        }
311
312        $leaveRequests = [];
313
314        foreach ($result as $leaveRequest) {
315            if ($leaveRequest instanceof \LeaveRequest) {
316                $leaveRequestEntity = $this->createLeaveRequestEntity($leaveRequest);
317                $leaveRequestModel = new LeaveListLeaveRequestModel($leaveRequestEntity);
318                $leaveTypeModel = new LeaveTypeModel($leaveRequest->getLeaveType());
319                $leaveRequests[] = array_merge(
320                    $leaveRequestModel->toArray(),
321                    ['leaveType' => $leaveTypeModel->toArray()]
322                );
323            }
324        }
325
326        if (empty($leaveRequests)) {
327            throw new RecordNotFoundException('No Records Found');
328        }
329        return new Response($leaveRequests, array());
330    }
331
332    protected function getUserAttribute(string $name): string
333    {
334        return \sfContext::getInstance()->getUser()->getAttribute($name);
335    }
336
337    /**
338     * Return leave request with leaves by leave request id
339     * @return Response
340     * @throws BadRequestException
341     * @throws InvalidParamException
342     * @throws ServiceException
343     */
344    public function getLeaveRequestById(): Response
345    {
346        $leaveRequestId = $this->getRequestParams()->getUrlParam(self::PARAMETER_ID);
347        $leaveRequest = $this->getLeaveRequestService()->fetchLeaveRequest($leaveRequestId);
348        if (!($leaveRequest instanceof \LeaveRequest)) {
349            throw new InvalidParamException('Invalid Leave Request Id');
350        }
351
352        $loggedInEmpNumber = $this->getUserAttribute("auth.empNumber");
353        $accessible = ($loggedInEmpNumber == $leaveRequest->getEmpNumber()) || in_array($leaveRequest->getEmpNumber(), $this->getAccessibleEmployeeIds(true));
354        if (!$accessible) {
355            throw new BadRequestException('Access Denied');
356        }
357
358        $leaveRequestEntity = $this->createLeaveRequestEntity($leaveRequest);
359        $leaveRequestModel = new EmployeeLeaveRequestModel($leaveRequestEntity);
360
361        $leaveTypeModel = new LeaveTypeModel($leaveRequest->getLeaveType());
362        $allowedActions = $this->getLeaveRequestService()->getLeaveRequestActions($leaveRequest, $loggedInEmpNumber);
363        $response = array_merge(
364            $leaveRequestModel->toArray(),
365            [
366                'leaveType' => $leaveTypeModel->toArray(),
367                'allowedActions' => array_values($allowedActions),
368            ]
369        );
370        return new Response($response, array());
371    }
372
373    /**
374     * @param \LeaveRequest $leaveRequest
375     * @return LeaveRequest
376     */
377    public function createLeaveRequestEntity(\LeaveRequest $leaveRequest): LeaveRequest
378    {
379        $leaveRequestEntity = new LeaveRequest($leaveRequest->getId(), $leaveRequest->getLeaveTypeName());
380        $leaveRequestEntity->buildLeaveRequest($leaveRequest);
381        return $leaveRequestEntity;
382    }
383
384
385    /**
386     * Filters
387     *
388     * @return array
389     */
390    protected function filterParameters()
391    {
392        $filters[] = array();
393
394        $filters[self::PARAMETER_ID] = $this->getRequestParams()->getUrlParam(self::PARAMETER_ID);
395        $filters[self::PARAMETER_CANCELLED] = ($this->getRequestParams()->getUrlParam(self::PARAMETER_CANCELLED));
396        $filters[self::PARAMETER_FROM_DATE] = ($this->getRequestParams()->getUrlParam(self::PARAMETER_FROM_DATE));
397        $filters[self::PARAMETER_TO_DATE] = ($this->getRequestParams()->getUrlParam(self::PARAMETER_TO_DATE));
398        $filters[self::PARAMETER_TAKEN] = ($this->getRequestParams()->getUrlParam(self::PARAMETER_TAKEN));
399        $filters[self::PARAMETER_PAST_EMPLOYEE] = ($this->getRequestParams()->getUrlParam(self::PARAMETER_PAST_EMPLOYEE));
400        $filters[self::PARAMETER_REJECTED] = ($this->getRequestParams()->getUrlParam(self::PARAMETER_REJECTED));
401        $filters[self::PARAMETER_SUBUNIT] = ($this->getRequestParams()->getUrlParam(self::PARAMETER_SUBUNIT));
402        $filters[self::PARAMETER_PENDING_APPROVAL] = ($this->getRequestParams()->getUrlParam(self::PARAMETER_PENDING_APPROVAL));
403        $filters[self::PARAMETER_SCHEDULED] = ($this->getRequestParams()->getUrlParam(self::PARAMETER_SCHEDULED));
404        $filters[self::PARAMETER_LIMIT] = ($this->getRequestParams()->getUrlParam(self::PARAMETER_LIMIT));
405        $filters[self::PARAMETER_PAGE] = ($this->getRequestParams()->getUrlParam(self::PARAMETER_PAGE));
406        $filters[self::PARAMETER_LEAVE_TYPE] = ($this->getRequestParams()->getUrlParam(self::PARAMETER_LEAVE_TYPE));
407        $filters[self::PARAMETER_LEAVE_TYPE_ID] = $this->getRequestParams()->getUrlParam(self::PARAMETER_LEAVE_TYPE_ID);
408
409        return $filters;
410    }
411
412    /**
413     * Validate inputs
414     *
415     * @param $filters
416     * @return bool
417     */
418    protected function validateInputs($filters)
419    {
420        $valid = true;
421
422        if (!empty($filters[self::PARAMETER_SUBUNIT]) && !$this->validateSubunit($filters)) {
423            $valid = false;
424
425        }
426        if (!empty($filters[self::PARAMETER_FROM_DATE]) && !empty($filters[self::PARAMETER_TO_DATE])) {
427            if ((strtotime($filters[self::PARAMETER_FROM_DATE])) > (strtotime($filters[self::PARAMETER_TO_DATE]))) {
428                throw new InvalidParamException('To Date Should Be After From Date');
429            }
430
431        }
432
433        return $valid;
434    }
435
436    /**
437     * Get statuses
438     *
439     * @param $filter
440     * @return array|null
441     */
442    protected function getStatusesArray($filter)
443    {
444        $statusIdArray = null;
445        if (!empty($filter[self::PARAMETER_TAKEN]) && $filter[self::PARAMETER_TAKEN] == 'true') {
446            $statusIdArray[] = \PluginLeave::LEAVE_STATUS_LEAVE_TAKEN;
447        }
448        if (!empty($filter[self::PARAMETER_CANCELLED]) && $filter[self::PARAMETER_CANCELLED] == 'true') {
449            $statusIdArray[] = \PluginLeave::LEAVE_STATUS_LEAVE_CANCELLED;
450        }
451        if (!empty($filter[self::PARAMETER_PENDING_APPROVAL]) && $filter[self::PARAMETER_PENDING_APPROVAL] == 'true') {
452            $statusIdArray[] = \PluginLeave::LEAVE_STATUS_LEAVE_PENDING_APPROVAL;
453        }
454        if (!empty($filter[self::PARAMETER_REJECTED]) && $filter[self::PARAMETER_REJECTED] == 'true') {
455            $statusIdArray[] = \PluginLeave::LEAVE_STATUS_LEAVE_REJECTED;
456        }
457        if (!empty($filter[self::PARAMETER_SCHEDULED]) && $filter[self::PARAMETER_SCHEDULED] == 'true') {
458            $statusIdArray[] = \PluginLeave::LEAVE_STATUS_LEAVE_APPROVED;
459        }
460
461        return $statusIdArray;
462
463    }
464
465
466    /**
467     * validateSubunit
468     *
469     * @param $filters
470     * @return bool
471     */
472    public function validateSubunit($filters)
473    {
474        $companyStructureService = new \CompanyStructureService();
475        $treeObject = $companyStructureService->getSubunitTreeObject();
476
477        $tree = $treeObject->fetchTree();
478
479        foreach ($tree as $node) {
480            if ($node->getId() == $filters[self::PARAMETER_SUBUNIT]) {
481                $this->subunit = $node->getId();
482                return true;
483            }
484        }
485        throw new InvalidParamException('Invalid Subunit');
486    }
487
488    /**
489     * Past employee filter
490     *
491     * @param $pastEmp
492     * @return bool
493     */
494    public function validatePastEmployee($pastEmp)
495    {
496        return $pastEmp === 'true';
497    }
498
499    public function getValidationRules()
500    {
501        return array(
502            self::PARAMETER_TO_DATE => array('NotEmpty' => true, 'Date' => array('Y-m-d')),
503            self::PARAMETER_FROM_DATE => array('NotEmpty' => true, 'Date' => array('Y-m-d')),
504
505        );
506    }
507
508    /**
509     * Create parameter object
510     *
511     * @param $filters
512     * @return \ParameterObject
513     */
514    protected function createParameters($filters)
515    {
516        $parameters = array();
517        $fromDate = $filters[self::PARAMETER_FROM_DATE];
518        $employee = $this->getEmployeeService()->getEmployee($filters[self::PARAMETER_ID]);
519        $toDate = $filters[self::PARAMETER_TO_DATE];
520        $parameters['dateRange'] = new \DateRange($fromDate, $toDate);
521        $parameters['statuses'] = $this->getStatusesArray($filters);
522
523        if (!empty($employee)) {
524            $parameters['employeeFilter'] = array($employee->getEmpNumber());
525        }
526
527        $parameters['noOfRecordsPerPage'] = $filters[self::PARAMETER_LIMIT];
528        $parameters['cmbWithTerminated'] = $this->validatePastEmployee($filters[self::PARAMETER_PAST_EMPLOYEE]);
529        $parameters['subUnit'] = $this->subunit;
530
531        return new \ParameterObject($parameters);
532    }
533
534    /**
535     * Return accessible leave list employees for current request user
536     * @param bool $withTerminated
537     * @return array
538     * @throws ServiceException
539     */
540    protected function getAccessibleEmployeeIds(bool $withTerminated):array
541    {
542        $properties = array("empNumber", "firstName", "middleName", "lastName", "termination_id");
543        $requiredPermissions = ['action' => ['view_leave_list']];
544
545        $employeeList = UserRoleManagerFactory::getUserRoleManager()->getAccessibleEntityProperties(
546            'Employee',
547            $properties,
548            null,
549            null,
550            array(),
551            array(),
552            $requiredPermissions
553        );
554
555        if ($withTerminated) {
556            return array_map(
557                function ($employee) {
558                    return $employee['empNumber'];
559                },
560                array_values($employeeList)
561            );
562        }
563        $employeeIds = [];
564        foreach ($employeeList as $employee) {
565            if (is_null($employee['termination_id'])) {
566                $employeeIds[] = $employee['empNumber'];
567            }
568        }
569        return $employeeIds;
570    }
571}
572