/** * returns all dates the employee have to work on by contract. the feast days are removed already * if the period exceeds the contracts' period, the contracts' period will be used. * freetimes are not respected here, if $respectTakenVacationDays is not set to TRUE * * @param HumanResources_Model_Contract|Tinebase_Record_RecordSet $contracts * @param Tinebase_DateTime $firstDate * @param Tinebase_DateTime $lastDate * @param boolean $respectTakenVacationDays * * @return array */ public function getDatesToWorkOn($contracts, Tinebase_DateTime $firstDate, Tinebase_DateTime $lastDate, $respectTakenVacationDays = FALSE) { $contracts = $this->_convertToRecordSet($contracts); // find out feast days $feastDays = $this->getFeastDays($contracts, $firstDate, $lastDate); $freeDayStrings = array(); foreach ($feastDays as $feastDay) { $freeDayStrings[] = $feastDay->format('Y-m-d'); } if ($respectTakenVacationDays) { $vacationTimes = new Tinebase_Record_RecordSet('HumanResources_Model_FreeTime'); foreach ($contracts as $contract) { $vacationTimes = $vacationTimes->merge($this->getFreeTimes($contract)); } $filter = new HumanResources_Model_FreeDayFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'freetime_id', 'operator' => 'in', 'value' => $vacationTimes->id))); $vacationDays = HumanResources_Controller_FreeDay::getInstance()->search($filter); foreach ($vacationDays as $vDay) { $freeDayStrings[] = $vDay->date->format('Y-m-d'); } } $hoursToWorkOn = 0; $results = array(); $sumHours = 0; foreach ($contracts as $contract) { $firstDate = $this->_getFirstDate($contract, $firstDate); $lastDate = $this->_getLastDate($contract, $lastDate); $date = clone $firstDate; $json = $contract->getWorkingTimeJson(); $weekdays = $json->days; // datetime format w uses day 0 as sunday $monday = array_pop($weekdays); array_unshift($weekdays, $monday); while ($date->isEarlier($lastDate)) { // if calculated working day is not a feast day, add to days to work on $ds = $date->format('Y-m-d'); $weekday = $date->format('w'); $hrs = $weekdays[$weekday]; if (!in_array($ds, $freeDayStrings) && $hrs > 0) { $results[] = clone $date; $sumHours += $hrs; } $date->addDay(1); } } return array('hours' => $sumHours, 'results' => $results); }
/** * resolves all virtual fields for the account * * @param HumanResources_Model_Account $account * @return array with property => value */ public function resolveVirtualFields(HumanResources_Model_Account $account) { $yearBegins = new Tinebase_DateTime($account->year . '-01-01 00:00:00'); $yearEnds = new Tinebase_DateTime($account->year . '-12-31 23:59:59'); $contracts = $this->_contractController->getValidContracts($yearBegins, $yearEnds, $account->employee_id); $contracts->sort('start_date', 'ASC'); // find out feast days by contract(s) of the accounts' year $feastDays = $this->_contractController->getFeastDays($contracts, $yearBegins, $yearEnds); // find out vacation days by contract(s) and interval $possibleVacationDays = round($this->_contractController->calculateVacationDays($contracts, $yearBegins, $yearEnds), 0); // find out free days (vacation, sickness) $freetimeController = HumanResources_Controller_FreeTime::getInstance(); $employeeId = is_object($account->employee_id) ? $account->employee_id->getId() : $account->employee_id; $filter = new HumanResources_Model_FreeTimeFilter(array(), 'AND'); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'account_id', 'operator' => 'equals', 'value' => $account->getId()))); $freeTimes = $freetimeController->search($filter); $filter = new HumanResources_Model_FreeTimeFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_DateTime(array('field' => 'firstday_date', 'operator' => 'isnull', 'value' => TRUE))); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'account_id', 'operator' => 'equals', 'value' => $account->getId()))); $rebookedVacationTimes = $freetimeController->search($filter); $acceptedVacationTimes = $freeTimes->filter('type', 'vacation')->filter('status', 'ACCEPTED'); $unexcusedSicknessTimes = $freeTimes->filter('type', 'sickness')->filter('status', 'UNEXCUSED'); $excusedSicknessTimes = $freeTimes->filter('type', 'sickness')->filter('status', 'EXCUSED'); $freedayController = HumanResources_Controller_FreeDay::getInstance(); $filter = new HumanResources_Model_FreeDayFilter(array(), 'AND'); $acceptedVacationFilter = clone $filter; $acceptedVacationFilter->addFilter(new Tinebase_Model_Filter_Id(array('field' => 'freetime_id', 'operator' => 'in', 'value' => $acceptedVacationTimes->id))); $acceptedVacationDays = $freedayController->search($acceptedVacationFilter); $unexcusedSicknessFilter = clone $filter; $unexcusedSicknessFilter->addFilter(new Tinebase_Model_Filter_Id(array('field' => 'freetime_id', 'operator' => 'in', 'value' => $unexcusedSicknessTimes->id))); $unexcusedSicknessDays = $freedayController->search($unexcusedSicknessFilter); $excusedSicknessFilter = clone $filter; $excusedSicknessFilter->addFilter(new Tinebase_Model_Filter_Id(array('field' => 'freetime_id', 'operator' => 'in', 'value' => $excusedSicknessTimes->id))); $excusedSicknessDays = $freedayController->search($excusedSicknessFilter); $filter = new HumanResources_Model_FreeDayFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_Id(array('field' => 'freetime_id', 'operator' => 'in', 'value' => $rebookedVacationTimes->id))); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'date', 'operator' => 'isnull', 'value' => TRUE))); $rebookedVacationDays = $freedayController->search($filter); $datesToWorkOn = $this->_contractController->getDatesToWorkOn($contracts, $yearBegins, $yearEnds); $datesToWorkOnReal = $this->_contractController->getDatesToWorkOn($contracts, $yearBegins, $yearEnds, TRUE); $expiredVacationDays = 0; // add extra free times of this year, if not expired (defined by account) if ($account->extra_free_times) { $extraFreeTimes = $this->calculateExtraFreeTimes($account, $acceptedVacationDays); $possibleVacationDays += $extraFreeTimes['remaining']; } return array('possible_vacation_days' => intval($possibleVacationDays), 'expired_vacation_days' => isset($extraFreeTimes) ? $extraFreeTimes['expired'] : 0, 'rebooked_vacation_days' => $rebookedVacationDays->count(), 'remaining_vacation_days' => $possibleVacationDays - $acceptedVacationDays->count() - $rebookedVacationDays->count(), 'taken_vacation_days' => $acceptedVacationDays->count(), 'excused_sickness' => $excusedSicknessDays->count(), 'unexcused_sickness' => $unexcusedSicknessDays->count(), 'working_days' => count($datesToWorkOn['results']), 'working_hours' => $datesToWorkOn['hours'], 'working_days_real' => count($datesToWorkOnReal['results']), 'working_hours_real' => $datesToWorkOnReal['hours']); }
/** * returns feast days and freedays of an employee for the freetime edit dialog * * @param string $_employeeId * @param integer $_year * @param string $_freeTimeId * @param string $_accountId */ public function getFeastAndFreeDays($_employeeId, $_year = NULL, $_freeTimeId = NULL, $_accountId = NULL) { $cController = HumanResources_Controller_Contract::getInstance(); $eController = HumanResources_Controller_Employee::getInstance(); $aController = HumanResources_Controller_Account::getInstance(); $ftController = HumanResources_Controller_FreeTime::getInstance(); $fdController = HumanResources_Controller_FreeDay::getInstance(); if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' $_employeeId ' . $_employeeId . ' $_year ' . $_year . ' $_freeTimeId ' . $_freeTimeId . ' $_accountId ' . $_accountId); } // validate employeeId $employee = $eController->get($_employeeId); $_freeTimeId = strlen($_freeTimeId) == 40 ? $_freeTimeId : NULL; // set period to search for $minDate = Tinebase_DateTime::now()->setTimezone(Tinebase_Core::getUserTimezone())->setTime(0, 0, 0); if ($_year && !$_freeTimeId) { $minDate->setDate($_year, 1, 1); } elseif ($_freeTimeId) { // if a freetime id is given, take the year of the freetime $myFreeTime = $ftController->get($_freeTimeId); $minDate->setDate($myFreeTime->firstday_date->format('Y'), 1, 1); } else { $minDate->setDate($minDate->format('Y'), 1, 1); } if (!$_accountId) { // find account $filter = new HumanResources_Model_AccountFilter(array(array('field' => 'year', 'operator' => 'equals', 'value' => intval($_year)))); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'employee_id', 'operator' => 'equals', 'value' => $_employeeId))); $account = $aController->search($filter)->getFirstRecord(); } else { try { $account = $aController->get($_accountId); } catch (Exception $e) { // throws a few lines later: HumanResources_Exception_NoAccount } } if (!$account) { throw new HumanResources_Exception_NoAccount(); } $accountYear = $account->year; $minAccountDate = Tinebase_DateTime::now()->setTimezone(Tinebase_Core::getUserTimezone())->setTime(0, 0, 0); $minAccountDate->setDate($accountYear, 1, 1); $maxAccountDate = clone $minAccountDate; $maxAccountDate->addYear(1)->subSecond(1); $maxDate = clone $minDate; $maxDate->addYear(1)->subSecond(1); // find contracts of the account year $contracts = $cController->getValidContracts($minAccountDate, $maxAccountDate, $_employeeId); $contracts->sort('start_date', 'ASC'); if ($contracts->count() < 1) { throw new HumanResources_Exception_NoContract(); } $remainingVacation = 0; $contracts->setTimezone(Tinebase_Core::getUserTimezone()); // find out total amount of vacation days for the different contracts foreach ($contracts as $contract) { $remainingVacation += $cController->calculateVacationDays($contract, $minDate, $maxDate); } $remainingVacation = round($remainingVacation, 0); $allVacation = $remainingVacation; // find contracts of the year in which the vacation days will be taken $contracts = $cController->getValidContracts($minDate, $maxDate, $_employeeId); $contracts->sort('start_date', 'ASC'); $excludeDates = array(); if ($contracts->count() < 1) { throw new HumanResources_Exception_NoContract(); } $first = TRUE; $feastDays = array(); $contracts->setTimezone(Tinebase_Core::getUserTimezone()); // find out disabled days for the different contracts foreach ($contracts as $contract) { $json = $contract->getWorkingTimeJson(); $startDay = $contract->start_date == NULL ? $minDate : $contract->start_date < $minDate ? $minDate : $contract->start_date; $stopDay = $contract->end_date == NULL ? $maxDate : $contract->end_date > $maxDate ? $maxDate : $contract->end_date; if ($first) { $firstDay = clone $startDay; $first = FALSE; } // find out weekdays to disable if (is_object($json)) { foreach ($json->days as $index => $hours) { $hours = intval($hours); if ($hours === 0) { $day = clone $startDay; $day->setWeekDay($index + 1); while ($day->compare($stopDay) == -1) { $exdate = clone $day; $exdate->setTimezone(Tinebase_Core::getUserTimezone()); $excludeDates[] = $exdate; $day->addWeek(1); } } } } // search feast days $feastDays = array_merge($cController->getFeastDays($contract, $startDay, $stopDay), $feastDays); } // set time to 0 foreach ($feastDays as &$feastDay) { $feastDay->setTimezone(Tinebase_Core::getUserTimezone())->setTime(0, 0, 0); } // search free times for the account and the interval // prepare free time filter, add employee_id $freeTimeFilter = new HumanResources_Model_FreeTimeFilter(array(), 'AND'); $freeTimeFilter->addFilter(new Tinebase_Model_Filter_Id(array('field' => 'employee_id', 'operator' => 'equals', 'value' => $_employeeId))); // don't search for freetimes belonging to the freetime handled itself if ($_freeTimeId) { $freeTimeFilter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'id', 'operator' => 'not', 'value' => $_freeTimeId))); } // prepare vacation times filter $vacationTimesFilter = clone $freeTimeFilter; $vacationTimesFilter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'type', 'operator' => 'equals', 'value' => 'vacation'))); // search all vacation times belonging to the account, regardless which interval we want $accountFreeTimesFilter = clone $vacationTimesFilter; $accountFreeTimesFilter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'account_id', 'operator' => 'equals', 'value' => $account->getId()))); $accountVacationTimeIds = $ftController->search($accountFreeTimesFilter)->id; // search all vacation times for the interval $fddMin = clone $minDate; $fddMin->subDay(1); $fddMax = clone $maxDate; $fddMax->addDay(1); $vacationTimesFilter->addFilter(new Tinebase_Model_Filter_Date(array('field' => 'firstday_date', 'operator' => 'after', 'value' => $fddMin))); $vacationTimesFilter->addFilter(new Tinebase_Model_Filter_Date(array('field' => 'firstday_date', 'operator' => 'before', 'value' => $fddMax))); $vacationTimes = $ftController->search($vacationTimesFilter); $acceptedVacationTimes = $vacationTimes->filter('status', 'ACCEPTED'); // search all sickness times for the interval $sicknessTimesFilter = clone $freeTimeFilter; $sicknessTimesFilter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'type', 'operator' => 'equals', 'value' => 'sickness'))); $sicknessTimesFilter->addFilter(new Tinebase_Model_Filter_Date(array('field' => 'firstday_date', 'operator' => 'after', 'value' => $fddMin))); $sicknessTimesFilter->addFilter(new Tinebase_Model_Filter_Date(array('field' => 'firstday_date', 'operator' => 'before', 'value' => $fddMax))); $sicknessTimes = $ftController->search($sicknessTimesFilter); // search free days belonging the found free times // prepare free day filter $freeDayFilter = new HumanResources_Model_FreeDayFilter(array(), 'AND'); $freeDayFilter->addFilter(new Tinebase_Model_Filter_Int(array('field' => 'duration', 'operator' => 'equals', 'value' => 1))); // find vacation days belonging to the account (date doesn't matter, may be from another year, just count the days) if (count($accountVacationTimeIds)) { $accountFreeDayFilter = clone $freeDayFilter; $accountFreeDayFilter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'freetime_id', 'operator' => 'in', 'value' => $accountVacationTimeIds))); $remainingVacation = $remainingVacation - $fdController->search($accountFreeDayFilter)->count(); } // find all vacation days of the period $vacationDayFilter = clone $freeDayFilter; $vacationDayFilter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'freetime_id', 'operator' => 'in', 'value' => $vacationTimes->id))); $vacationDays = $fdController->search($vacationDayFilter); // find out accepted vacation days. Vacation days will be substracted from remainingVacation only if they are accepted, // but they will be shown in the freetime edit dialog // TODO: discuss this $acceptedVacationDayFilter = clone $freeDayFilter; $acceptedVacationDayFilter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'freetime_id', 'operator' => 'in', 'value' => $acceptedVacationTimes->id))); $acceptedVacationDays = $fdController->search($acceptedVacationDayFilter); // calculate extra vacation days if ($account) { $filter = new HumanResources_Model_ExtraFreeTimeFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'account_id', 'operator' => 'equals', 'value' => $account->getId()))); $account->extra_free_times = HumanResources_Controller_ExtraFreeTime::getInstance()->search($filter); $extraFreeTimes = $aController->calculateExtraFreeTimes($account, $acceptedVacationDays); $allVacation = $allVacation + $extraFreeTimes['remaining']; $remainingVacation = $remainingVacation + $extraFreeTimes['remaining']; } else { $extraFreeTimes = NULL; } // find all sickness days of the period $sicknessDayFilter = clone $freeDayFilter; $sicknessDayFilter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'freetime_id', 'operator' => 'in', 'value' => $sicknessTimes->id))); $sicknessDays = $fdController->search($sicknessDayFilter); $ownFreeDays = NULL; if ($_freeTimeId) { $ownFreeDaysFilter = clone $freeDayFilter; $ownFreeDaysFilter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'freetime_id', 'operator' => 'in', 'value' => array($_freeTimeId)))); $ownFreeDays = $fdController->search($ownFreeDaysFilter); $remainingVacation = $remainingVacation - $ownFreeDays->count(); $ownFreeDays = $ownFreeDays->toArray(); } // TODO: remove results property, just return results array itself return array('results' => array('remainingVacation' => floor($remainingVacation), 'extraFreeTimes' => $extraFreeTimes, 'vacationDays' => $vacationDays->toArray(), 'sicknessDays' => $sicknessDays->toArray(), 'excludeDates' => $excludeDates, 'ownFreeDays' => $ownFreeDays, 'allVacation' => $allVacation, 'feastDays' => $feastDays, 'contracts' => $contracts->toArray(), 'employee' => $employee->toArray(), 'firstDay' => $firstDay, 'lastDay' => $stopDay)); }
/** * finds overwritten by sickness days overwritten vacation days. * deletes the overwritten vacation day and the vacation itself if days_count = 0 * * @param Tinebase_Record_Interface $_record */ protected function _handleOverwrittenVacation($_record) { $fdController = HumanResources_Controller_FreeDay::getInstance(); $changedFreeTimes = array(); foreach ($_record->freedays as $freeday) { $vacationTimeFilter = new HumanResources_Model_FreeTimeFilter(array(array('field' => 'type', 'operator' => 'equals', 'value' => 'vacation'))); $vacationTimeFilter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'employee_id', 'operator' => 'equals', 'value' => $_record->employee_id))); $vacationTimes = $this->search($vacationTimeFilter); $filter = new HumanResources_Model_FreeDayFilter(array(array('field' => 'date', 'operator' => 'equals', 'value' => $freeday['date']))); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'freetime_id', 'operator' => 'not', 'value' => $_record->getId()))); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'freetime_id', 'operator' => 'in', 'value' => $vacationTimes->id))); $vacationDay = $fdController->search($filter)->getFirstRecord(); if ($vacationDay) { $fdController->delete($vacationDay->getId()); $freeTime = $this->get($vacationDay->freetime_id); if (!isset($changedFreeTimes[$vacationDay->freetime_id])) { $changedFreeTimes[$vacationDay->freetime_id] = $freeTime; } $count = (int) $changedFreeTimes[$vacationDay->freetime_id]->days_count - 1; $changedFreeTimes[$vacationDay->freetime_id]->days_count = $count; } } foreach ($changedFreeTimes as $freeTimeId => $freetime) { if ($freetime->days_count == 0) { $this->delete($freetime->getId()); } else { $freeTime->days_count = $count; $this->update($freetime); } } }