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