OpenXE/classes/Modules/TimeManagement/TimeManagementModule.php

664 lines
20 KiB
PHP
Raw Normal View History

2021-05-21 08:49:41 +02:00
<?php
declare(strict_types=1);
namespace Xentral\Modules\TimeManagement;
use DateTimeImmutable;
use DateTimeInterface;
use Xentral\Modules\TimeManagement\Data\CalendarData;
use Xentral\Modules\TimeManagement\Data\DayInfoData;
use Xentral\Modules\TimeManagement\Data\HolidayData;
use Xentral\Modules\TimeManagement\Data\RequestInfoData;
use Xentral\Modules\TimeManagement\Data\WorkDayData;
use Xentral\Modules\TimeManagement\Exception\InvalidDateFormatException;
use Xentral\Modules\TimeManagement\Exception\InvalidQueryException;
use Xentral\Modules\TimeManagement\Exception\InvalidRequestTokenException;
use Xentral\Modules\TimeManagement\Exception\SupervisorNotFoundException;
use Xentral\Modules\TimeManagement\Service\GroupGateway;
use Xentral\Modules\TimeManagement\Service\HolidayGateway;
use Xentral\Modules\TimeManagement\Service\TimeManagementHistoryService;
use Xentral\Modules\TimeManagement\Service\TimeManagementSettingGateway;
use Xentral\Modules\TimeManagement\Service\TimeManagementTargetHourGateway;
use Xentral\Modules\TimeManagement\Service\TimeManagementTargetHourService;
use Xentral\Modules\TimeManagement\Wrapper\TimeManagementTargetHourWrapper;
class TimeManagementModule
{
/** @var TimeManagementTargetHourGateway $targetHourGateway */
private $targetHourGateway;
/** @var TimeManagementTargetHourService $targetHourService */
private $targetHourService;
/** @var HolidayGateway $holidayGateway */
private $holidayGateway;
/** @var GroupGateway $groupGateway */
private $groupGateway;
/** @var TimeManagementSettingGateway $settingGateway */
private $settingGateway;
/** @var TimeManagementTargetHourWrapper $targetHourWrapper */
private $targetHourWrapper;
/** @var TimeManagementHistoryService $historyService */
private $historyService;
/** @var string UNPAID */
public const UNPAID = 'N';
/** @var string ABSENT_DAY */
public const ABSENT_DAY = 'X';
/** @var string NONE */
public const NONE = '';
/** @var string SICK */
public const SICK = 'K';
/** @var string SICK */
public const SICKREQUEST = 'S';
/** @var string SICKREMOVE */
public const SICKREMOVE = 'V';
/** @var string SICKREJECT */
public const SICKREJECT = 'C';
/** @var string VACATION */
public const VACATION = 'U';
/** @var string VACATIONREQUEST */
public const VACATIONREQUEST = 'R';
/** @var string VACATIONREMOVE */
public const VACATIONREMOVE = 'L';
/** @var string VACATIONREJECT */
public const VACATIONREJECT = 'J';
/**
* @param TimeManagementTargetHourGateway $targetHourGateway
* @param TimeManagementTargetHourService $targetHourService
* @param TimeManagementSettingGateway $settingGateway
* @param HolidayGateway $holidayGateway
* @param GroupGateway $groupGateway
* @param TimeManagementTargetHourWrapper $targetHourWrapper
* @param TimeManagementHistoryService $historyService
*/
public function __construct(
TimeManagementTargetHourGateway $targetHourGateway,
TimeManagementTargetHourService $targetHourService,
TimeManagementSettingGateway $settingGateway,
HolidayGateway $holidayGateway,
GroupGateway $groupGateway,
TimeManagementTargetHourWrapper $targetHourWrapper,
TimeManagementHistoryService $historyService
) {
$this->targetHourGateway = $targetHourGateway;
$this->targetHourService = $targetHourService;
$this->holidayGateway = $holidayGateway;
$this->groupGateway = $groupGateway;
$this->settingGateway = $settingGateway;
$this->targetHourWrapper = $targetHourWrapper;
$this->historyService = $historyService;
}
/**
* @param int $addressId
* @param DateTimeImmutable $fromDate
* @param DateTimeImmutable $tillDate
* @param string $statusOldType
* @param string $statusWishType
*
* @return array
*/
private function findPossibleDays(
int $addressId,
DateTimeImmutable $fromDate,
DateTimeImmutable $tillDate,
string $statusOldType,
string $statusWishType
): array {
$evaluatedDays = [];
while ($fromDate <= $tillDate) {
$dayInfo = $this->targetHourGateway->findDayInfo($addressId, $fromDate);
$dayType = $dayInfo->getType();
$workminutes = (int)$dayInfo->getWorkMinutes();
if ($workminutes < 0) {
$workminutes = 0;
}
$isWorkDay = $workminutes != 0;
//allowed are:
//- days with no type
//- days of the same type like the old
//- rejected days which can be reclaimed
if (
empty($dayType) ||
$dayType === $statusOldType ||
$dayType === self::SICKREJECT ||
$dayType === self::VACATIONREJECT
) {
if ($this->isAddableDay($fromDate, $isWorkDay)) {
$evaluatedDays[] = [
'date' => $fromDate,
'type' => $this->getPossibleDayType($statusOldType, $statusWishType),
'workminutes' => $workminutes,
'vacationminutes' => $dayInfo->getVacationMinutes(),
];
}
}
$fromDate = $fromDate->modify('1 day');
}
return $evaluatedDays;
}
/**
* @param DateTimeInterface $date
* @param bool $isWorkDay
*
* @return bool
*/
private function isAddableDay(DateTimeInterface $date, bool $isWorkDay): bool
{
$year = (int)$date->format('Y');
$holidays = $this->holidayGateway->findHolidayDataByYear($year);
$isHoliday = $this->isHoliday($date, $holidays);
if (!$isHoliday && $isWorkDay) {
return true;
}
return false;
}
/**
* @param string $statusOldType
* @param string $statusWishType
*
* @return string
*/
private function getPossibleDayType(string $statusOldType, string $statusWishType): string
{
$isStatusAcceptedRemove = false;
$isStatusRequestedRemove = false;
if (
strstr($statusOldType, TimeManagementModule::VACATIONREQUEST) !== false ||
strstr($statusOldType, TimeManagementModule::SICKREQUEST) !== false
) {
$isStatusRequestedRemove = true;
}
if (
strstr($statusOldType, TimeManagementModule::VACATION) !== false ||
strstr($statusOldType, TimeManagementModule::SICK) !== false ||
strstr($statusOldType, TimeManagementModule::ABSENT_DAY) !== false ||
strstr($statusOldType, TimeManagementModule::UNPAID) !== false
) {
$isStatusAcceptedRemove = true;
}
$type = self::VACATIONREQUEST;
if ($statusWishType === self::SICK) {
$type = self::SICKREQUEST;
}
if ($isStatusRequestedRemove) {
$type = self::NONE;
} elseif ($isStatusAcceptedRemove) {
$type = self::VACATIONREMOVE;
if ($statusOldType === self::SICK) {
$type = self::SICKREMOVE;
}
}
return $type;
}
/**
* @param DateTimeInterface $date
* @param array $holidays
*
* @return bool
*/
private function isHoliday(DateTimeInterface $date, array $holidays): bool
{
/** @var HolidayData $holiday */
foreach ($holidays as $holiday) {
if ($holiday->getDate()->getTimestamp() === $date->getTimestamp()) {
return true;
}
}
return false;
}
/**
* @param int $addressId
* @param bool $hasSuperPrivileges
*
* @return array
*/
public function findGroupsByAddressId(int $addressId, bool $hasSuperPrivileges): array
{
if ($hasSuperPrivileges) {
return $this->groupGateway->findAllActiveGroupsWithMembers();
}
return $this->groupGateway->findGroupsByAddressId($addressId);
}
/**
* @param string $requestToken
*
* @throws InvalidRequestTokenException
* @throws InvalidDateFormatException
*
* @return RequestInfoData
*/
public function getRequestInfoByToken(string $requestToken): RequestInfoData
{
return $this->targetHourGateway->getRequestInfoByToken($requestToken);
}
/**
* @param $year
*
* @return array|HolidayData[]
*/
public function findHolidayDataByYear($year): array
{
return $this->holidayGateway->findHolidayDataByYear($year);
}
/**
* @param int $addressId
* @param int $year
* @param bool $isAnonymised
* @param int $groupId
*
* @throws InvalidDateFormatException
*
* @return array|CalendarData[]
*/
public function getCalendarData(
int $addressId,
int $year,
bool $isAnonymised,
int $groupId = 0
): array {
if ($isAnonymised) {
if (empty($groupId)) {
$calendarData = $this->targetHourGateway->findAnonymisedVacationCalendarDataByYearAndAddressId(
$year,
$addressId
);
} else {
$calendarData = $this->targetHourGateway->findAnonymisedVacationCalendarDataByYearAndAddressIdAndGroupId(
$year,
$addressId,
$groupId
);
}
} else {
$calendarData = $this->targetHourGateway->findAllVacationCalendarDataByYear($year);
}
return $calendarData;
}
/**
* @param int $addressId
*
* @throws InvalidQueryException
*
* @return WorkDayData
*/
public function getWorkingDaysForAddress(int $addressId): WorkDayData
{
return $this->settingGateway->getWorkingDaysForAddress($addressId);
}
/**
* @param int $addressId
* @param DateTimeInterface $date
*
* @return DayInfoData
*/
public function getDayInfo(int $addressId, DateTimeInterface $date): DayInfoData
{
return $this->targetHourGateway->findDayInfo($addressId, $date);
}
/**
* @param int $addressId
*
* @return float
*/
public function getAmountRequestedVacation(int $addressId): float
{
return $this->targetHourGateway->findAmountRequestedVacation($addressId);
}
/**
* @param string $dayType
* @param bool $isReject
*
* @return string
*/
private function evaluateNextDayType(string $dayType, bool $isReject): string
{
$isVacation =
strstr($dayType, self::VACATIONREMOVE) !== false ||
strstr($dayType, self::VACATIONREQUEST) !== false;
$isRemove =
strstr($dayType, self::VACATIONREMOVE) !== false ||
strstr($dayType, self::SICKREMOVE) !== false;
if ($isRemove) {
if ($isReject) {
if ($isVacation) {
$type = self::VACATION;
} else {
$type = self::SICK;
}
} else {
$type = self::NONE;
}
} else {
if ($isReject) {
if ($isVacation) {
$type = self::VACATIONREJECT;
} else {
$type = self::SICKREJECT;
}
} else {
if ($isVacation) {
$type = self::VACATION;
} else {
$type = self::SICK;
}
}
}
return $type;
}
/**
* @param int $addressId
* @param DateTimeImmutable $from
* @param DateTimeImmutable $till
* @param bool $halfDay
* @param string $comment
* @param string $statusOldType
* @param string $statusWishType
*
* @return string
*/
public function changeDays(
int $addressId,
DateTimeImmutable $from,
DateTimeImmutable $till,
bool $halfDay,
string $comment,
string $statusOldType,
string $statusWishType
): string {
if ($from > $till) {
$till = $from;
}
$possibleDays =
$this->findPossibleDays(
$addressId,
$from,
$till,
$statusOldType,
$statusWishType
);
if (empty($possibleDays)) {
return '';
}
$date = new DateTimeImmutable();
$requestToken = (string)$date->getTimestamp();
$this->historyService->saveActivity(
$addressId,
0,
$statusOldType,
$possibleDays[0]['type'],
$requestToken,
$from,
$till,
$comment
);
foreach ($possibleDays as $day) {
$time = $day['vacationminutes'] / 60;
if ($halfDay) {
$workminutes = $day['workminutes'];
if ($workminutes > 0) {
$time = $workminutes / (2 * 60);
}
}
$this->changeDayType(
$addressId,
$day['date'],
$day['type'],
$comment,
(string)$time,
$requestToken
);
}
if($day['type'] === self::NONE){
return '';
}
else{
return $requestToken;
}
}
/**
* @param int $addressId
* @param DateTimeInterface $date
* @param string $type
* @param string $comment
* @param string $time
* @param string $requestToken
*/
public function changeDayType(
int $addressId,
DateTimeInterface $date,
string $type,
string $comment,
string $time,
string $requestToken = ''
): void {
$this->clearDayType($addressId, $date, $type);
if ($type !== self::NONE) {
$this->targetHourWrapper->handleType($addressId, $date, $type, true, $time, $requestToken);
} else {
if (!empty($requestToken)) {
$this->targetHourService->updateVacationRequestToken($addressId, $date, $requestToken);
}
}
if (!empty($comment)) {
$this->targetHourWrapper->saveComment($addressId, $date, $comment);
}
$this->targetHourWrapper->recalculate($addressId, $date);
}
/**
* @param int $addressId
* @param DateTimeInterface $date
* @param string $type
*/
private function clearDayType(int $addressId, DateTimeInterface $date, string $type): void
{
$types = [
TimeManagementModule::UNPAID,
TimeManagementModule::ABSENT_DAY,
TimeManagementModule::VACATION,
TimeManagementModule::VACATIONREQUEST,
TimeManagementModule::VACATIONREJECT,
TimeManagementModule::VACATIONREMOVE,
TimeManagementModule::SICK,
TimeManagementModule::SICKREQUEST,
TimeManagementModule::SICKREMOVE,
TimeManagementModule::SICKREJECT,
];
if ($type != TimeManagementModule::NONE) {
unset($types[$type]);
}
$this->targetHourWrapper->handleType($addressId, $date, implode('', $types), false);
}
/**
* @param int $employeeAddressId
* @param int $supervisorAddressId
* @param bool $isReject
* @param string $internalComment
* @param string $requestToken
*
* @throws InvalidDateFormatException
* @throws InvalidRequestTokenException
*/
public function handleRequestedDays(
int $employeeAddressId,
int $supervisorAddressId,
bool $isReject,
string $internalComment,
string $requestToken
): void {
$requestedDays = $this->targetHourGateway->getRequestedDaysByToken($requestToken);
if (!empty($requestedDays)) {
$from = $requestedDays[0]['date'];
$till = $requestedDays[count($requestedDays) - 1]['date'];
$oldType = $requestedDays[0]['type'];
$newType = $this->evaluateNextDayType($oldType, $isReject);
$this->historyService->saveActivity(
0,
$supervisorAddressId,
$oldType,
$newType,
$requestToken,
$from,
$till,
$internalComment
);
$date = new DateTimeImmutable();
$requestToken = (string)$date->getTimestamp();
foreach ($requestedDays as $requestedDay) {
$this->targetHourService->updateTargetHourType(
$employeeAddressId,
$requestedDay['date'],
$requestedDay['type'],
$newType
);
$this->targetHourService->updateVacationRequestToken(
$employeeAddressId,
$requestedDay['date'],
$requestToken
);
$this->targetHourWrapper->recalculate($employeeAddressId,$requestedDay['date']);
}
}
if (!empty($internalComment)) {
$this->targetHourService->saveInternalComment($requestToken, $internalComment);
}
}
/**
* @param int $daysTillDeletion
* @param int $addressId
*
* @throws InvalidDateFormatException
*/
public function removeRejectedAfterXDays(int $daysTillDeletion, int $addressId): void
{
$rejectedDays = $this->targetHourGateway->findRejectedDays($daysTillDeletion, $addressId);
if (!empty($rejectedDays)) {
foreach ($rejectedDays as $day) {
$this->clearDayType($addressId, $day['date'], TimeManagementModule::NONE);
}
}
}
/**
* @param int $addressId
* @param int $groupId
*
* @return bool
*/
public function checkAddressHasGroup(int $addressId, int $groupId): bool
{
return $this->groupGateway->isAddressInGroup($addressId, $groupId);
}
/**
* @param string $type
*
* @return string
*/
public function mapTypeToLanguage(string $type): string
{
$mapping =
[
self::NONE => 'Kein Status',
self::VACATION => 'Urlaub',
self::VACATIONREQUEST => 'Urlaubsantrag',
self::VACATIONREJECT => 'Urlaub ablehnen',
self::VACATIONREMOVE => 'Urlaub entfernen',
self::SICK => 'Krank',
self::SICKREQUEST => 'Krankheitsantrag',
self::SICKREMOVE => 'Krankheit entfernen',
self::SICKREJECT => 'Krankheit ablehnen',
self::UNPAID => 'Unbezahlter Urlaub',
self::ABSENT_DAY => 'Fehltag',
];
return $mapping[$type];
}
/**
* @param int $employeeAddressId
* @param int $groupId
*
* @throws SupervisorNotFoundException
*
* @return int[]
*/
public function getSupervisorAddressIds(int $employeeAddressId, int $groupId): array
{
return $this->groupGateway->getSupervisorAddressIds($employeeAddressId, $groupId);
}
}